[odb-api] 01/21: Initial commit of version 0.17.0

Alastair McKinstry mckinstry at moszumanska.debian.org
Wed Aug 30 06:42:45 UTC 2017


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

mckinstry pushed a commit to branch debian/master
in repository odb-api.

commit 02522575da384bcfe7150ce750a8a31e0d6dce67
Author: Alastair McKinstry <mckinstry at debian.org>
Date:   Thu May 25 09:05:37 2017 +0100

    Initial commit of version 0.17.0
---
 CMakeLists.txt                                     |    14 +
 VERSION.cmake                                      |     1 +
 bin/CMakeLists.txt                                 |     1 +
 bin/ecbuild                                        |   446 +
 cmake/2.8/CMakePushCheckState.cmake                |    61 +
 cmake/CMakeLists.txt                               |     5 +
 cmake/FindADSM.cmake                               |    43 +
 cmake/FindAEC.cmake                                |    39 +
 cmake/FindAIO.cmake                                |    66 +
 cmake/FindArmadillo.cmake                          |    43 +
 cmake/FindCMath.cmake                              |    26 +
 cmake/FindCairo.cmake                              |    59 +
 cmake/FindDl.cmake                                 |    23 +
 cmake/FindEMOS.cmake                               |    35 +
 cmake/FindFDB.cmake                                |    35 +
 cmake/FindFFTW.cmake                               |   211 +
 cmake/FindGeoTIFF.cmake                            |    46 +
 cmake/FindHPSS.cmake                               |    39 +
 cmake/FindLEX.cmake                                |   129 +
 cmake/FindLibGFortran.cmake                        |    53 +
 cmake/FindLibIFort.cmake                           |    50 +
 cmake/FindLustreAPI.cmake                          |    43 +
 cmake/FindMKL.cmake                                |    76 +
 cmake/FindNAG.cmake                                |    43 +
 cmake/FindNDBM.cmake                               |    34 +
 cmake/FindNetCDF.cmake                             |   164 +
 cmake/FindNetCDF3.cmake                            |   113 +
 cmake/FindODB.cmake                                |    53 +
 cmake/FindOpenCL.cmake                             |    70 +
 cmake/FindOpenJPEG.cmake                           |    54 +
 cmake/FindPGIFortran.cmake                         |    49 +
 cmake/FindPango.cmake                              |    33 +
 cmake/FindPangoCairo.cmake                         |    96 +
 cmake/FindProj4.cmake                              |    69 +
 cmake/FindREADLINE.cmake                           |    87 +
 cmake/FindRPCGEN.cmake                             |    20 +
 cmake/FindRealtime.cmake                           |    24 +
 cmake/FindSZip.cmake                               |    30 +
 cmake/FindTrilinos.cmake                           |    49 +
 cmake/FindViennaCL.cmake                           |    39 +
 cmake/FindXLFortranLibs.cmake                      |    51 +
 cmake/FindYACC.cmake                               |   157 +
 cmake/Findgrib_api.cmake                           |   133 +
 cmake/Findodb_api.cmake                            |    97 +
 cmake/Findspot.cmake                               |    65 +
 cmake/VERSION.cmake                                |     7 +
 cmake/compiler_flags/Clang_C.cmake                 |    13 +
 cmake/compiler_flags/Clang_CXX.cmake               |    13 +
 cmake/compiler_flags/Cray_C.cmake                  |    13 +
 cmake/compiler_flags/Cray_CXX.cmake                |    13 +
 cmake/compiler_flags/Cray_Fortran.cmake            |    15 +
 cmake/compiler_flags/GNU_C.cmake                   |    18 +
 cmake/compiler_flags/GNU_CXX.cmake                 |    18 +
 cmake/compiler_flags/GNU_Fortran.cmake             |    23 +
 cmake/compiler_flags/Intel_C.cmake                 |    13 +
 cmake/compiler_flags/Intel_CXX.cmake               |    13 +
 cmake/compiler_flags/Intel_Fortran.cmake           |    20 +
 cmake/compiler_flags/PGI_C.cmake                   |    11 +
 cmake/compiler_flags/PGI_CXX.cmake                 |    11 +
 cmake/compiler_flags/PGI_Fortran.cmake             |    11 +
 .../CMakeCheckCompilerFlagCommonPatterns.cmake     |    33 +
 cmake/contrib/CheckFortranCompilerFlag.cmake       |    53 +
 cmake/contrib/CheckFortranSourceCompiles.cmake     |   106 +
 cmake/contrib/FindEigen3.cmake                     |    97 +
 cmake/contrib/FindNetCDF4.cmake                    |   337 +
 cmake/contrib/FindNumPy.cmake                      |   101 +
 cmake/contrib/GetGitRevisionDescription.cmake      |   123 +
 cmake/contrib/GetGitRevisionDescription.cmake.in   |    42 +
 cmake/contrib/GreatCMakeCookOff/.gitattributes     |    22 +
 cmake/contrib/GreatCMakeCookOff/.gitignore         |     6 +
 .../contrib/GreatCMakeCookOff/AddCPP11Flags.cmake  |    59 +
 cmake/contrib/GreatCMakeCookOff/AddGTest.cmake     |    72 +
 .../GreatCMakeCookOff/CheckCXX11Features.cmake     |   198 +
 cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake   |    53 +
 cmake/contrib/GreatCMakeCookOff/FindEigen.cmake    |   131 +
 cmake/contrib/GreatCMakeCookOff/LICENSE            |    20 +
 cmake/contrib/GreatCMakeCookOff/README.md          |   176 +
 cmake/contrib/GreatCMakeCookOff/TestCMake.cmake    |    58 +
 .../GreatCMakeCookOff/cpp11/__func__-N2340.cpp     |     8 +
 .../contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp |    12 +
 .../GreatCMakeCookOff/cpp11/begin_function.cc      |    12 +
 .../GreatCMakeCookOff/cpp11/constexpr-N2235.cpp    |    19 +
 .../cpp11/constructor_delegate.cpp                 |    32 +
 cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp  |    10 +
 .../GreatCMakeCookOff/cpp11/decltype-N2343.cpp     |    11 +
 .../cpp11/deleted_constructor.cpp                  |    10 +
 .../cpp11/deleted_constructor_fail_compile.cpp     |    11 +
 .../contrib/GreatCMakeCookOff/cpp11/enable_if.cpp  |     8 +
 .../GreatCMakeCookOff/cpp11/explicit_cast.cpp      |    13 +
 .../GreatCMakeCookOff/cpp11/initialization.cpp     |     7 +
 .../GreatCMakeCookOff/cpp11/lambda-N2927.cpp       |     5 +
 .../GreatCMakeCookOff/cpp11/long_double.cpp        |     4 +
 .../GreatCMakeCookOff/cpp11/long_long-N1811.cpp    |     7 +
 cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp |     9 +
 .../GreatCMakeCookOff/cpp11/nullptr-N2431.cpp      |     5 +
 .../cpp11/nullptr-N2431_fail_compile.cpp           |     5 +
 cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp |    22 +
 .../cpp11/override_fail_compile.cpp                |    22 +
 .../GreatCMakeCookOff/cpp11/random_device.cpp      |    13 +
 .../cpp11/rvalue_references-N2118.cpp              |    15 +
 .../contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp |     6 +
 .../cpp11/sizeof_member-N2253.cpp                  |    14 +
 .../cpp11/static_assert-N1720.cpp                  |     5 +
 .../cpp11/static_assert-N1720_fail_compile.cpp     |     5 +
 .../GreatCMakeCookOff/cpp11/template_alias.cpp     |     8 +
 .../cpp11/trivial_type_traits.cpp                  |     7 +
 .../GreatCMakeCookOff/cpp11/type_traits.cpp        |     7 +
 .../contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp |     6 +
 .../cpp11/variadic_templates-N2555.cpp             |    23 +
 .../contrib/GreatCMakeCookOff/tests/CMakeLists.txt |    12 +
 .../contrib/GreatCMakeCookOff/tests/addgtest.cmake |    20 +
 .../GreatCMakeCookOff/tests/checkcpp11flags.cmake  |     1 +
 .../GreatCMakeCookOff/tests/checkisnan.cmake       |    19 +
 .../GreatCMakeCookOff/tests/cpp11/CMakeLists.txt   |     3 +
 .../tests/cpp11/allfeatures.cmake                  |     7 +
 .../tests/cpp11/check_features.cmake               |     9 +
 .../tests/cpp11/parse_input_features.cmake         |    68 +
 cmake/ecbuild_add_c_flags.cmake                    |    98 +
 cmake/ecbuild_add_cxx11_flags.cmake                |    35 +
 cmake/ecbuild_add_cxx_flags.cmake                  |    98 +
 cmake/ecbuild_add_executable.cmake                 |   346 +
 cmake/ecbuild_add_extra_search_paths.cmake         |    33 +
 cmake/ecbuild_add_fortran_flags.cmake              |   109 +
 cmake/ecbuild_add_library.cmake                    |   578 +
 cmake/ecbuild_add_option.cmake                     |   348 +
 cmake/ecbuild_add_persistent.cmake                 |    85 +
 cmake/ecbuild_add_resources.cmake                  |   151 +
 cmake/ecbuild_add_test.cmake                       |   506 +
 cmake/ecbuild_append_to_rpath.cmake                |   102 +
 cmake/ecbuild_bundle.cmake                         |   201 +
 cmake/ecbuild_cache.cmake                          |    95 +
 cmake/ecbuild_check_c_source_return.cmake          |   154 +
 cmake/ecbuild_check_compiler.cmake                 |   156 +
 cmake/ecbuild_check_cxx11.cmake                    |   138 +
 cmake/ecbuild_check_cxx_source_return.cmake        |   163 +
 cmake/ecbuild_check_fortran.cmake                  |   126 +
 cmake/ecbuild_check_fortran_source_return.cmake    |   154 +
 cmake/ecbuild_check_functions.cmake                |   186 +
 cmake/ecbuild_check_os.cmake                       |   536 +
 cmake/ecbuild_compiler_flags.cmake                 |   217 +
 cmake/ecbuild_config.h.in                          |   201 +
 cmake/ecbuild_declare_project.cmake                |   198 +
 cmake/ecbuild_define_build_types.cmake             |    59 +
 cmake/ecbuild_define_libs_and_execs_target.cmake   |    29 +
 cmake/ecbuild_define_links_target.cmake            |    74 +
 cmake/ecbuild_define_options.cmake                 |    52 +
 cmake/ecbuild_define_paths.cmake                   |    49 +
 cmake/ecbuild_define_uninstall.cmake               |     7 +
 cmake/ecbuild_dont_pack.cmake                      |    82 +
 cmake/ecbuild_download_resource.cmake              |    76 +
 cmake/ecbuild_echo_targets.cmake                   |   233 +
 cmake/ecbuild_enable_fortran.cmake                 |    89 +
 cmake/ecbuild_features.cmake                       |    57 +
 cmake/ecbuild_find_fortranlibs.cmake               |   163 +
 cmake/ecbuild_find_lexyacc.cmake                   |    95 +
 cmake/ecbuild_find_mpi.cmake                       |   328 +
 cmake/ecbuild_find_omp.cmake                       |   259 +
 cmake/ecbuild_find_package.cmake                   |   368 +
 cmake/ecbuild_find_perl.cmake                      |    73 +
 cmake/ecbuild_find_python.cmake                    |   263 +
 cmake/ecbuild_generate_config_headers.cmake        |    63 +
 cmake/ecbuild_generate_fortran_interfaces.cmake    |   143 +
 cmake/ecbuild_generate_rpc.cmake                   |    98 +
 cmake/ecbuild_generate_yy.cmake                    |   201 +
 cmake/ecbuild_get_cxx11_flags.cmake                |    74 +
 cmake/ecbuild_get_date.cmake                       |    52 +
 cmake/ecbuild_get_resources.cmake                  |    52 +
 cmake/ecbuild_get_test_data.cmake                  |   427 +
 cmake/ecbuild_git.cmake                            |   300 +
 cmake/ecbuild_install_project.cmake                |   437 +
 cmake/ecbuild_list_add_pattern.cmake               |   102 +
 cmake/ecbuild_list_exclude_pattern.cmake           |    88 +
 cmake/ecbuild_list_extra_search_paths.cmake        |    81 +
 cmake/ecbuild_list_macros.cmake                    |    58 +
 cmake/ecbuild_log.cmake                            |   249 +
 cmake/ecbuild_pkgconfig.cmake                      |   421 +
 cmake/ecbuild_policies.cmake                       |    67 +
 cmake/ecbuild_print_summary.cmake                  |   116 +
 cmake/ecbuild_project_files.cmake                  |    75 +
 cmake/ecbuild_remove_fortran_flags.cmake           |    64 +
 cmake/ecbuild_requires_macro_version.cmake         |    27 +
 cmake/ecbuild_separate_sources.cmake               |   103 +
 cmake/ecbuild_setup_test_framework.cmake           |    72 +
 cmake/ecbuild_source_flags.cmake                   |    34 +
 cmake/ecbuild_system.cmake                         |   277 +
 cmake/ecbuild_target_flags.cmake                   |    91 +
 cmake/ecbuild_uninstall.cmake.in                   |    28 +
 cmake/ecbuild_use_package.cmake                    |   341 +
 cmake/ecbuild_version.h.in                         |    20 +
 cmake/ecbuild_warn_unused_files.cmake              |    77 +
 cmake/fcm-make-interfaces.cfg                      |    31 +
 cmake/fortran_features/CheckFortranFeatures.cmake  |   167 +
 cmake/fortran_features/c_size_t.F90                |     8 +
 cmake/fortran_features/c_sizeof.F90                |     3 +
 cmake/fortran_features/derivedtype_interface.F90   |    54 +
 cmake/fortran_features/derivedtype_io.F90          |    42 +
 cmake/fortran_features/finalization.F90            |   141 +
 cmake/fortran_features/submodules.F90              |    35 +
 cmake/gen_source_flags.py                          |    84 +
 cmake/include/ecbuild/boost_test_framework.h       |    17 +
 cmake/md5.in                                       |     1 +
 cmake/pkg-config.pc.in                             |    35 +
 cmake/project-config-version.cmake.in              |    12 +
 cmake/project-config.cmake.in                      |    97 +
 cmake/pymain.c                                     |     5 +
 cmake/sg.pl                                        |   573 +
 cmake/sha1.in                                      |     1 +
 ecbuild/.gitignore                                 |     4 +
 ecbuild/AUTHORS                                    |    13 +
 ecbuild/CMakeLists.txt                             |    47 +
 ecbuild/COPYING                                    |   201 +
 ecbuild/INSTALL                                    |    19 +
 ecbuild/LICENSE                                    |   190 +
 ecbuild/NOTICE                                     |    27 +
 ecbuild/VERSION.cmake                              |     1 +
 ecbuild/bamboo/flags.cmake                         |     0
 ecbuild/bin/CMakeLists.txt                         |     1 +
 ecbuild/bin/ecbuild                                |   446 +
 ecbuild/cmake/2.8/CMakePushCheckState.cmake        |    61 +
 ecbuild/cmake/CMakeLists.txt                       |     5 +
 ecbuild/cmake/FindADSM.cmake                       |    43 +
 ecbuild/cmake/FindAEC.cmake                        |    39 +
 ecbuild/cmake/FindAIO.cmake                        |    66 +
 ecbuild/cmake/FindArmadillo.cmake                  |    43 +
 ecbuild/cmake/FindCMath.cmake                      |    26 +
 ecbuild/cmake/FindCairo.cmake                      |    59 +
 ecbuild/cmake/FindDl.cmake                         |    23 +
 ecbuild/cmake/FindEMOS.cmake                       |    35 +
 ecbuild/cmake/FindFDB.cmake                        |    35 +
 ecbuild/cmake/FindFFTW.cmake                       |   211 +
 ecbuild/cmake/FindGeoTIFF.cmake                    |    46 +
 ecbuild/cmake/FindHPSS.cmake                       |    39 +
 ecbuild/cmake/FindLEX.cmake                        |   129 +
 ecbuild/cmake/FindLibGFortran.cmake                |    53 +
 ecbuild/cmake/FindLibIFort.cmake                   |    50 +
 ecbuild/cmake/FindLustreAPI.cmake                  |    43 +
 ecbuild/cmake/FindMKL.cmake                        |    76 +
 ecbuild/cmake/FindNAG.cmake                        |    43 +
 ecbuild/cmake/FindNDBM.cmake                       |    34 +
 ecbuild/cmake/FindNetCDF.cmake                     |   164 +
 ecbuild/cmake/FindNetCDF3.cmake                    |   113 +
 ecbuild/cmake/FindODB.cmake                        |    53 +
 ecbuild/cmake/FindOpenCL.cmake                     |    70 +
 ecbuild/cmake/FindOpenJPEG.cmake                   |    54 +
 ecbuild/cmake/FindPGIFortran.cmake                 |    49 +
 ecbuild/cmake/FindPango.cmake                      |    33 +
 ecbuild/cmake/FindPangoCairo.cmake                 |    96 +
 ecbuild/cmake/FindProj4.cmake                      |    69 +
 ecbuild/cmake/FindREADLINE.cmake                   |    87 +
 ecbuild/cmake/FindRPCGEN.cmake                     |    20 +
 ecbuild/cmake/FindRealtime.cmake                   |    24 +
 ecbuild/cmake/FindSZip.cmake                       |    30 +
 ecbuild/cmake/FindTrilinos.cmake                   |    49 +
 ecbuild/cmake/FindViennaCL.cmake                   |    39 +
 ecbuild/cmake/FindXLFortranLibs.cmake              |    51 +
 ecbuild/cmake/FindYACC.cmake                       |   157 +
 ecbuild/cmake/Findgrib_api.cmake                   |   133 +
 ecbuild/cmake/Findodb_api.cmake                    |    97 +
 ecbuild/cmake/Findspot.cmake                       |    65 +
 ecbuild/cmake/VERSION.cmake                        |     7 +
 ecbuild/cmake/compiler_flags/Clang_C.cmake         |    13 +
 ecbuild/cmake/compiler_flags/Clang_CXX.cmake       |    13 +
 ecbuild/cmake/compiler_flags/Cray_C.cmake          |    13 +
 ecbuild/cmake/compiler_flags/Cray_CXX.cmake        |    13 +
 ecbuild/cmake/compiler_flags/Cray_Fortran.cmake    |    15 +
 ecbuild/cmake/compiler_flags/GNU_C.cmake           |    18 +
 ecbuild/cmake/compiler_flags/GNU_CXX.cmake         |    18 +
 ecbuild/cmake/compiler_flags/GNU_Fortran.cmake     |    23 +
 ecbuild/cmake/compiler_flags/Intel_C.cmake         |    13 +
 ecbuild/cmake/compiler_flags/Intel_CXX.cmake       |    13 +
 ecbuild/cmake/compiler_flags/Intel_Fortran.cmake   |    20 +
 ecbuild/cmake/compiler_flags/PGI_C.cmake           |    11 +
 ecbuild/cmake/compiler_flags/PGI_CXX.cmake         |    11 +
 ecbuild/cmake/compiler_flags/PGI_Fortran.cmake     |    11 +
 .../CMakeCheckCompilerFlagCommonPatterns.cmake     |    33 +
 .../cmake/contrib/CheckFortranCompilerFlag.cmake   |    53 +
 .../cmake/contrib/CheckFortranSourceCompiles.cmake |   106 +
 ecbuild/cmake/contrib/FindEigen3.cmake             |    97 +
 ecbuild/cmake/contrib/FindNetCDF4.cmake            |   337 +
 ecbuild/cmake/contrib/FindNumPy.cmake              |   101 +
 .../cmake/contrib/GetGitRevisionDescription.cmake  |   123 +
 .../contrib/GetGitRevisionDescription.cmake.in     |    42 +
 .../cmake/contrib/GreatCMakeCookOff/.gitattributes |    22 +
 ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore |     6 +
 .../contrib/GreatCMakeCookOff/AddCPP11Flags.cmake  |    59 +
 .../cmake/contrib/GreatCMakeCookOff/AddGTest.cmake |    72 +
 .../GreatCMakeCookOff/CheckCXX11Features.cmake     |   198 +
 .../contrib/GreatCMakeCookOff/CheckIsNaN.cmake     |    53 +
 .../contrib/GreatCMakeCookOff/FindEigen.cmake      |   131 +
 ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE    |    20 +
 ecbuild/cmake/contrib/GreatCMakeCookOff/README.md  |   176 +
 .../contrib/GreatCMakeCookOff/TestCMake.cmake      |    58 +
 .../GreatCMakeCookOff/cpp11/__func__-N2340.cpp     |     8 +
 .../contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp |    12 +
 .../GreatCMakeCookOff/cpp11/begin_function.cc      |    12 +
 .../GreatCMakeCookOff/cpp11/constexpr-N2235.cpp    |    19 +
 .../cpp11/constructor_delegate.cpp                 |    32 +
 .../contrib/GreatCMakeCookOff/cpp11/cstdint.cpp    |    10 +
 .../GreatCMakeCookOff/cpp11/decltype-N2343.cpp     |    11 +
 .../cpp11/deleted_constructor.cpp                  |    10 +
 .../cpp11/deleted_constructor_fail_compile.cpp     |    11 +
 .../contrib/GreatCMakeCookOff/cpp11/enable_if.cpp  |     8 +
 .../GreatCMakeCookOff/cpp11/explicit_cast.cpp      |    13 +
 .../GreatCMakeCookOff/cpp11/initialization.cpp     |     7 +
 .../GreatCMakeCookOff/cpp11/lambda-N2927.cpp       |     5 +
 .../GreatCMakeCookOff/cpp11/long_double.cpp        |     4 +
 .../GreatCMakeCookOff/cpp11/long_long-N1811.cpp    |     7 +
 .../contrib/GreatCMakeCookOff/cpp11/noexcept.cpp   |     9 +
 .../GreatCMakeCookOff/cpp11/nullptr-N2431.cpp      |     5 +
 .../cpp11/nullptr-N2431_fail_compile.cpp           |     5 +
 .../contrib/GreatCMakeCookOff/cpp11/override.cpp   |    22 +
 .../cpp11/override_fail_compile.cpp                |    22 +
 .../GreatCMakeCookOff/cpp11/random_device.cpp      |    13 +
 .../cpp11/rvalue_references-N2118.cpp              |    15 +
 .../contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp |     6 +
 .../cpp11/sizeof_member-N2253.cpp                  |    14 +
 .../cpp11/static_assert-N1720.cpp                  |     5 +
 .../cpp11/static_assert-N1720_fail_compile.cpp     |     5 +
 .../GreatCMakeCookOff/cpp11/template_alias.cpp     |     8 +
 .../cpp11/trivial_type_traits.cpp                  |     7 +
 .../GreatCMakeCookOff/cpp11/type_traits.cpp        |     7 +
 .../contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp |     6 +
 .../cpp11/variadic_templates-N2555.cpp             |    23 +
 .../contrib/GreatCMakeCookOff/tests/CMakeLists.txt |    12 +
 .../contrib/GreatCMakeCookOff/tests/addgtest.cmake |    20 +
 .../GreatCMakeCookOff/tests/checkcpp11flags.cmake  |     1 +
 .../GreatCMakeCookOff/tests/checkisnan.cmake       |    19 +
 .../GreatCMakeCookOff/tests/cpp11/CMakeLists.txt   |     3 +
 .../tests/cpp11/allfeatures.cmake                  |     7 +
 .../tests/cpp11/check_features.cmake               |     9 +
 .../tests/cpp11/parse_input_features.cmake         |    68 +
 ecbuild/cmake/ecbuild_add_c_flags.cmake            |    98 +
 ecbuild/cmake/ecbuild_add_cxx11_flags.cmake        |    35 +
 ecbuild/cmake/ecbuild_add_cxx_flags.cmake          |    98 +
 ecbuild/cmake/ecbuild_add_executable.cmake         |   346 +
 ecbuild/cmake/ecbuild_add_extra_search_paths.cmake |    33 +
 ecbuild/cmake/ecbuild_add_fortran_flags.cmake      |   109 +
 ecbuild/cmake/ecbuild_add_library.cmake            |   578 +
 ecbuild/cmake/ecbuild_add_option.cmake             |   348 +
 ecbuild/cmake/ecbuild_add_persistent.cmake         |    85 +
 ecbuild/cmake/ecbuild_add_resources.cmake          |   151 +
 ecbuild/cmake/ecbuild_add_test.cmake               |   506 +
 ecbuild/cmake/ecbuild_append_to_rpath.cmake        |   102 +
 ecbuild/cmake/ecbuild_bundle.cmake                 |   201 +
 ecbuild/cmake/ecbuild_cache.cmake                  |    95 +
 ecbuild/cmake/ecbuild_check_c_source_return.cmake  |   154 +
 ecbuild/cmake/ecbuild_check_compiler.cmake         |   156 +
 ecbuild/cmake/ecbuild_check_cxx11.cmake            |   138 +
 .../cmake/ecbuild_check_cxx_source_return.cmake    |   163 +
 ecbuild/cmake/ecbuild_check_fortran.cmake          |   126 +
 .../ecbuild_check_fortran_source_return.cmake      |   154 +
 ecbuild/cmake/ecbuild_check_functions.cmake        |   186 +
 ecbuild/cmake/ecbuild_check_os.cmake               |   536 +
 ecbuild/cmake/ecbuild_compiler_flags.cmake         |   217 +
 ecbuild/cmake/ecbuild_config.h.in                  |   201 +
 ecbuild/cmake/ecbuild_declare_project.cmake        |   198 +
 ecbuild/cmake/ecbuild_define_build_types.cmake     |    59 +
 .../ecbuild_define_libs_and_execs_target.cmake     |    29 +
 ecbuild/cmake/ecbuild_define_links_target.cmake    |    74 +
 ecbuild/cmake/ecbuild_define_options.cmake         |    52 +
 ecbuild/cmake/ecbuild_define_paths.cmake           |    49 +
 ecbuild/cmake/ecbuild_define_uninstall.cmake       |     7 +
 ecbuild/cmake/ecbuild_dont_pack.cmake              |    82 +
 ecbuild/cmake/ecbuild_download_resource.cmake      |    76 +
 ecbuild/cmake/ecbuild_echo_targets.cmake           |   233 +
 ecbuild/cmake/ecbuild_enable_fortran.cmake         |    89 +
 ecbuild/cmake/ecbuild_features.cmake               |    57 +
 ecbuild/cmake/ecbuild_find_fortranlibs.cmake       |   163 +
 ecbuild/cmake/ecbuild_find_lexyacc.cmake           |    95 +
 ecbuild/cmake/ecbuild_find_mpi.cmake               |   328 +
 ecbuild/cmake/ecbuild_find_omp.cmake               |   259 +
 ecbuild/cmake/ecbuild_find_package.cmake           |   368 +
 ecbuild/cmake/ecbuild_find_perl.cmake              |    73 +
 ecbuild/cmake/ecbuild_find_python.cmake            |   263 +
 .../cmake/ecbuild_generate_config_headers.cmake    |    63 +
 .../ecbuild_generate_fortran_interfaces.cmake      |   143 +
 ecbuild/cmake/ecbuild_generate_rpc.cmake           |    98 +
 ecbuild/cmake/ecbuild_generate_yy.cmake            |   201 +
 ecbuild/cmake/ecbuild_get_cxx11_flags.cmake        |    74 +
 ecbuild/cmake/ecbuild_get_date.cmake               |    52 +
 ecbuild/cmake/ecbuild_get_resources.cmake          |    52 +
 ecbuild/cmake/ecbuild_get_test_data.cmake          |   427 +
 ecbuild/cmake/ecbuild_git.cmake                    |   300 +
 ecbuild/cmake/ecbuild_install_project.cmake        |   437 +
 ecbuild/cmake/ecbuild_list_add_pattern.cmake       |   102 +
 ecbuild/cmake/ecbuild_list_exclude_pattern.cmake   |    88 +
 .../cmake/ecbuild_list_extra_search_paths.cmake    |    81 +
 ecbuild/cmake/ecbuild_list_macros.cmake            |    58 +
 ecbuild/cmake/ecbuild_log.cmake                    |   249 +
 ecbuild/cmake/ecbuild_pkgconfig.cmake              |   421 +
 ecbuild/cmake/ecbuild_policies.cmake               |    67 +
 ecbuild/cmake/ecbuild_print_summary.cmake          |   116 +
 ecbuild/cmake/ecbuild_project_files.cmake          |    75 +
 ecbuild/cmake/ecbuild_remove_fortran_flags.cmake   |    64 +
 ecbuild/cmake/ecbuild_requires_macro_version.cmake |    27 +
 ecbuild/cmake/ecbuild_separate_sources.cmake       |   103 +
 ecbuild/cmake/ecbuild_setup_test_framework.cmake   |    72 +
 ecbuild/cmake/ecbuild_source_flags.cmake           |    34 +
 ecbuild/cmake/ecbuild_system.cmake                 |   277 +
 ecbuild/cmake/ecbuild_target_flags.cmake           |    91 +
 ecbuild/cmake/ecbuild_uninstall.cmake.in           |    28 +
 ecbuild/cmake/ecbuild_use_package.cmake            |   341 +
 ecbuild/cmake/ecbuild_version.h.in                 |    20 +
 ecbuild/cmake/ecbuild_warn_unused_files.cmake      |    77 +
 ecbuild/cmake/fcm-make-interfaces.cfg              |    31 +
 .../fortran_features/CheckFortranFeatures.cmake    |   167 +
 ecbuild/cmake/fortran_features/c_size_t.F90        |     8 +
 ecbuild/cmake/fortran_features/c_sizeof.F90        |     3 +
 .../fortran_features/derivedtype_interface.F90     |    54 +
 ecbuild/cmake/fortran_features/derivedtype_io.F90  |    42 +
 ecbuild/cmake/fortran_features/finalization.F90    |   141 +
 ecbuild/cmake/fortran_features/submodules.F90      |    35 +
 ecbuild/cmake/gen_source_flags.py                  |    84 +
 .../cmake/include/ecbuild/boost_test_framework.h   |    17 +
 ecbuild/cmake/md5.in                               |     1 +
 ecbuild/cmake/pkg-config.pc.in                     |    35 +
 ecbuild/cmake/project-config-version.cmake.in      |    12 +
 ecbuild/cmake/project-config.cmake.in              |    97 +
 ecbuild/cmake/pymain.c                             |     5 +
 ecbuild/cmake/sg.pl                                |   573 +
 ecbuild/cmake/sha1.in                              |     1 +
 ecbuild/doc/CMakeLists.txt                         |    22 +
 ecbuild/doc/upload.py                              |   148 +
 ecbuild/ecbuild.sublime-project                    |    29 +
 ecbuild/examples/boost-python-lib/CMakeLists.txt   |    60 +
 ecbuild/examples/boost-python-lib/VERSION.cmake    |     1 +
 ecbuild/examples/boost-python-lib/mypython.py      |    10 +
 ecbuild/examples/boost-python-lib/pythonlib.cpp    |     6 +
 ecbuild/examples/boost-python-lib/pythonlib.hpp    |     6 +
 ecbuild/examples/config-bundle/.gitignore          |     1 +
 ecbuild/examples/config-bundle/CMakeLists.txt      |    15 +
 ecbuild/examples/config-bundle/README.md           |     7 +
 .../examples/config-bundle/general_config.cmake    |     6 +
 .../examples/config-bundle/subproj1/CMakeLists.txt |    16 +
 .../examples/config-bundle/subproj2/CMakeLists.txt |    20 +
 .../examples/config-bundle/subproj2_config.cmake   |     3 +
 ecbuild/examples/cpp-bundle/CMakeLists.txt         |    14 +
 ecbuild/examples/cpp-bundle/VERSION.cmake          |     1 +
 ecbuild/examples/cpp-bundle/bar/CMakeLists.txt     |    57 +
 ecbuild/examples/cpp-bundle/bar/VERSION.cmake      |     1 +
 ecbuild/examples/cpp-bundle/bar/bar.c              |     6 +
 ecbuild/examples/cpp-bundle/bar/bar.h              |     6 +
 ecbuild/examples/cpp-bundle/bar/baz.c              |     6 +
 ecbuild/examples/cpp-bundle/bar/baz.h              |     6 +
 ecbuild/examples/cpp-bundle/bar/main.cc            |    14 +
 ecbuild/examples/cpp-bundle/bar/test.cc            |    19 +
 ecbuild/examples/cpp-bundle/bar/zingo.c            |     6 +
 ecbuild/examples/cpp-bundle/bar/zingo.h            |     6 +
 ecbuild/examples/cpp-bundle/foo/CMakeLists.txt     |    38 +
 ecbuild/examples/cpp-bundle/foo/VERSION.cmake      |     1 +
 ecbuild/examples/cpp-bundle/foo/foo.c              |     4 +
 ecbuild/examples/cpp-bundle/foo/foo.h              |     6 +
 ecbuild/examples/cpp-bundle/foo/main.cc            |    10 +
 ecbuild/examples/cpp-bundle/foo/test.cc            |    13 +
 ecbuild/examples/fortran/CMakeLists.txt            |    87 +
 ecbuild/examples/fortran/VERSION.cmake             |     1 +
 ecbuild/examples/fortran/api/area/area.F90         |     4 +
 ecbuild/examples/fortran/api/volume/volume.F90     |     4 +
 ecbuild/examples/fortran/area_circle.f90           |    15 +
 ecbuild/examples/fortran/area_circle.h             |     6 +
 ecbuild/examples/fortran/circle.f90                |    11 +
 .../examples/fortran/cmake/fcm-make-interfaces.cfg |    30 +
 ecbuild/examples/fortran/constants.f90             |    12 +
 ecbuild/examples/fortran/lolo.f95                  |    15 +
 ecbuild/examples/fortran/main.F90                  |    25 +
 ecbuild/examples/fortran/old_circle.f90            |    15 +
 ecbuild/examples/mix/CMakeLists.txt                |    30 +
 ecbuild/examples/mix/README.md                     |     5 +
 ecbuild/examples/mix/VERSION.cmake                 |     1 +
 ecbuild/examples/mix/cpp/CMakeLists.txt            |     1 +
 ecbuild/examples/mix/cpp/foo.cc                    |    48 +
 ecbuild/examples/mix/flags.json                    |    19 +
 ecbuild/examples/mix/fort/CMakeLists.txt           |     3 +
 ecbuild/examples/mix/fort/bar/bar.f90              |    22 +
 ecbuild/examples/mix/fort/baz/baz.f90              |    22 +
 .../examples/override-compile-flags/CMakeLists.txt |    14 +
 ecbuild/examples/override-compile-flags/README.md  |    27 +
 .../override-compile-flags/bar/CMakeLists.txt      |    21 +
 ecbuild/examples/override-compile-flags/bar/bar.c  |     1 +
 .../override-compile-flags/foo/CMakeLists.txt      |    32 +
 .../foo/flags-cray-8.4.1-debug.json                |     8 +
 .../foo/flags-cray-8.4.1-release.json              |     7 +
 .../foo/flags-cray-debug.json                      |     6 +
 .../foo/flags-cray-release.json                    |     6 +
 .../override-compile-flags/foo/flags.cmake         |    37 +
 ecbuild/examples/override-compile-flags/foo/foo.c  |     1 +
 .../examples/override-compile-flags/foo/foo.f90    |     2 +
 .../override-compile-flags/foo/foo_contiguous.f90  |     2 +
 .../override-compile-flags/foo/foo_intolerant.f90  |     2 +
 .../override-compile-flags/foo/foo_ivybridge.f90   |     2 +
 .../foo/foo_no_debug_symbols.f90                   |     2 +
 ecbuild/examples/simple/CMakeLists.txt             |    43 +
 ecbuild/examples/simple/README.md                  |     5 +
 ecbuild/examples/simple/VERSION.cmake              |     1 +
 ecbuild/examples/simple/circle/CMakeLists.txt      |    12 +
 ecbuild/examples/simple/circle/area.f90            |    11 +
 ecbuild/examples/simple/circle/test_area.c         |    18 +
 ecbuild/examples/simple/compute/CMakeLists.txt     |    23 +
 ecbuild/examples/simple/compute/compute.cc         |    67 +
 ecbuild/examples/simple/project_summary.cmake      |    11 +
 ecbuild/project_summary.cmake                      |    21 +
 ecbuild/share/CMakeLists.txt                       |     5 +
 .../ecbuild/cmake/2.8/CMakePushCheckState.cmake    |    61 +
 ecbuild/share/ecbuild/cmake/CMakeLists.txt         |     5 +
 ecbuild/share/ecbuild/cmake/FindADSM.cmake         |    43 +
 ecbuild/share/ecbuild/cmake/FindAEC.cmake          |    39 +
 ecbuild/share/ecbuild/cmake/FindAIO.cmake          |    66 +
 ecbuild/share/ecbuild/cmake/FindArmadillo.cmake    |    43 +
 ecbuild/share/ecbuild/cmake/FindCMath.cmake        |    26 +
 ecbuild/share/ecbuild/cmake/FindCairo.cmake        |    59 +
 ecbuild/share/ecbuild/cmake/FindDl.cmake           |    23 +
 ecbuild/share/ecbuild/cmake/FindEMOS.cmake         |    35 +
 ecbuild/share/ecbuild/cmake/FindFDB.cmake          |    35 +
 ecbuild/share/ecbuild/cmake/FindFFTW.cmake         |   211 +
 ecbuild/share/ecbuild/cmake/FindGeoTIFF.cmake      |    46 +
 ecbuild/share/ecbuild/cmake/FindHPSS.cmake         |    39 +
 ecbuild/share/ecbuild/cmake/FindLEX.cmake          |   129 +
 ecbuild/share/ecbuild/cmake/FindLibGFortran.cmake  |    53 +
 ecbuild/share/ecbuild/cmake/FindLibIFort.cmake     |    50 +
 ecbuild/share/ecbuild/cmake/FindLustreAPI.cmake    |    43 +
 ecbuild/share/ecbuild/cmake/FindMKL.cmake          |    76 +
 ecbuild/share/ecbuild/cmake/FindNAG.cmake          |    43 +
 ecbuild/share/ecbuild/cmake/FindNDBM.cmake         |    34 +
 ecbuild/share/ecbuild/cmake/FindNetCDF.cmake       |   164 +
 ecbuild/share/ecbuild/cmake/FindNetCDF3.cmake      |   113 +
 ecbuild/share/ecbuild/cmake/FindODB.cmake          |    53 +
 ecbuild/share/ecbuild/cmake/FindOpenCL.cmake       |    70 +
 ecbuild/share/ecbuild/cmake/FindOpenJPEG.cmake     |    54 +
 ecbuild/share/ecbuild/cmake/FindPGIFortran.cmake   |    49 +
 ecbuild/share/ecbuild/cmake/FindPango.cmake        |    33 +
 ecbuild/share/ecbuild/cmake/FindPangoCairo.cmake   |    96 +
 ecbuild/share/ecbuild/cmake/FindProj4.cmake        |    69 +
 ecbuild/share/ecbuild/cmake/FindREADLINE.cmake     |    87 +
 ecbuild/share/ecbuild/cmake/FindRPCGEN.cmake       |    20 +
 ecbuild/share/ecbuild/cmake/FindRealtime.cmake     |    24 +
 ecbuild/share/ecbuild/cmake/FindSZip.cmake         |    30 +
 ecbuild/share/ecbuild/cmake/FindTrilinos.cmake     |    49 +
 ecbuild/share/ecbuild/cmake/FindViennaCL.cmake     |    39 +
 .../share/ecbuild/cmake/FindXLFortranLibs.cmake    |    51 +
 ecbuild/share/ecbuild/cmake/FindYACC.cmake         |   157 +
 ecbuild/share/ecbuild/cmake/Findgrib_api.cmake     |   133 +
 ecbuild/share/ecbuild/cmake/Findodb_api.cmake      |    97 +
 ecbuild/share/ecbuild/cmake/Findspot.cmake         |    65 +
 ecbuild/share/ecbuild/cmake/VERSION.cmake          |     7 +
 .../ecbuild/cmake/compiler_flags/Clang_C.cmake     |    13 +
 .../ecbuild/cmake/compiler_flags/Clang_CXX.cmake   |    13 +
 .../ecbuild/cmake/compiler_flags/Cray_C.cmake      |    13 +
 .../ecbuild/cmake/compiler_flags/Cray_CXX.cmake    |    13 +
 .../cmake/compiler_flags/Cray_Fortran.cmake        |    15 +
 .../share/ecbuild/cmake/compiler_flags/GNU_C.cmake |    18 +
 .../ecbuild/cmake/compiler_flags/GNU_CXX.cmake     |    18 +
 .../ecbuild/cmake/compiler_flags/GNU_Fortran.cmake |    23 +
 .../ecbuild/cmake/compiler_flags/Intel_C.cmake     |    13 +
 .../ecbuild/cmake/compiler_flags/Intel_CXX.cmake   |    13 +
 .../cmake/compiler_flags/Intel_Fortran.cmake       |    20 +
 .../share/ecbuild/cmake/compiler_flags/PGI_C.cmake |    11 +
 .../ecbuild/cmake/compiler_flags/PGI_CXX.cmake     |    11 +
 .../ecbuild/cmake/compiler_flags/PGI_Fortran.cmake |    11 +
 .../CMakeCheckCompilerFlagCommonPatterns.cmake     |    33 +
 .../cmake/contrib/CheckFortranCompilerFlag.cmake   |    53 +
 .../cmake/contrib/CheckFortranSourceCompiles.cmake |   106 +
 .../share/ecbuild/cmake/contrib/FindEigen3.cmake   |    97 +
 .../share/ecbuild/cmake/contrib/FindNetCDF4.cmake  |   337 +
 .../share/ecbuild/cmake/contrib/FindNumPy.cmake    |   101 +
 .../cmake/contrib/GetGitRevisionDescription.cmake  |   123 +
 .../contrib/GetGitRevisionDescription.cmake.in     |    42 +
 .../cmake/contrib/GreatCMakeCookOff/.gitattributes |    22 +
 .../cmake/contrib/GreatCMakeCookOff/.gitignore     |     6 +
 .../contrib/GreatCMakeCookOff/AddCPP11Flags.cmake  |    59 +
 .../cmake/contrib/GreatCMakeCookOff/AddGTest.cmake |    72 +
 .../GreatCMakeCookOff/CheckCXX11Features.cmake     |   198 +
 .../contrib/GreatCMakeCookOff/CheckIsNaN.cmake     |    53 +
 .../contrib/GreatCMakeCookOff/FindEigen.cmake      |   131 +
 .../cmake/contrib/GreatCMakeCookOff/LICENSE        |    20 +
 .../cmake/contrib/GreatCMakeCookOff/README.md      |   176 +
 .../contrib/GreatCMakeCookOff/TestCMake.cmake      |    58 +
 .../GreatCMakeCookOff/cpp11/__func__-N2340.cpp     |     8 +
 .../contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp |    12 +
 .../GreatCMakeCookOff/cpp11/begin_function.cc      |    12 +
 .../GreatCMakeCookOff/cpp11/constexpr-N2235.cpp    |    19 +
 .../cpp11/constructor_delegate.cpp                 |    32 +
 .../contrib/GreatCMakeCookOff/cpp11/cstdint.cpp    |    10 +
 .../GreatCMakeCookOff/cpp11/decltype-N2343.cpp     |    11 +
 .../cpp11/deleted_constructor.cpp                  |    10 +
 .../cpp11/deleted_constructor_fail_compile.cpp     |    11 +
 .../contrib/GreatCMakeCookOff/cpp11/enable_if.cpp  |     8 +
 .../GreatCMakeCookOff/cpp11/explicit_cast.cpp      |    13 +
 .../GreatCMakeCookOff/cpp11/initialization.cpp     |     7 +
 .../GreatCMakeCookOff/cpp11/lambda-N2927.cpp       |     5 +
 .../GreatCMakeCookOff/cpp11/long_double.cpp        |     4 +
 .../GreatCMakeCookOff/cpp11/long_long-N1811.cpp    |     7 +
 .../contrib/GreatCMakeCookOff/cpp11/noexcept.cpp   |     9 +
 .../GreatCMakeCookOff/cpp11/nullptr-N2431.cpp      |     5 +
 .../cpp11/nullptr-N2431_fail_compile.cpp           |     5 +
 .../contrib/GreatCMakeCookOff/cpp11/override.cpp   |    22 +
 .../cpp11/override_fail_compile.cpp                |    22 +
 .../GreatCMakeCookOff/cpp11/random_device.cpp      |    13 +
 .../cpp11/rvalue_references-N2118.cpp              |    15 +
 .../contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp |     6 +
 .../cpp11/sizeof_member-N2253.cpp                  |    14 +
 .../cpp11/static_assert-N1720.cpp                  |     5 +
 .../cpp11/static_assert-N1720_fail_compile.cpp     |     5 +
 .../GreatCMakeCookOff/cpp11/template_alias.cpp     |     8 +
 .../cpp11/trivial_type_traits.cpp                  |     7 +
 .../GreatCMakeCookOff/cpp11/type_traits.cpp        |     7 +
 .../contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp |     6 +
 .../cpp11/variadic_templates-N2555.cpp             |    23 +
 .../contrib/GreatCMakeCookOff/tests/CMakeLists.txt |    12 +
 .../contrib/GreatCMakeCookOff/tests/addgtest.cmake |    20 +
 .../GreatCMakeCookOff/tests/checkcpp11flags.cmake  |     1 +
 .../GreatCMakeCookOff/tests/checkisnan.cmake       |    19 +
 .../GreatCMakeCookOff/tests/cpp11/CMakeLists.txt   |     3 +
 .../tests/cpp11/allfeatures.cmake                  |     7 +
 .../tests/cpp11/check_features.cmake               |     9 +
 .../tests/cpp11/parse_input_features.cmake         |    68 +
 .../share/ecbuild/cmake/ecbuild_add_c_flags.cmake  |    98 +
 .../ecbuild/cmake/ecbuild_add_cxx11_flags.cmake    |    35 +
 .../ecbuild/cmake/ecbuild_add_cxx_flags.cmake      |    98 +
 .../ecbuild/cmake/ecbuild_add_executable.cmake     |   346 +
 .../cmake/ecbuild_add_extra_search_paths.cmake     |    33 +
 .../ecbuild/cmake/ecbuild_add_fortran_flags.cmake  |   109 +
 .../share/ecbuild/cmake/ecbuild_add_library.cmake  |   578 +
 .../share/ecbuild/cmake/ecbuild_add_option.cmake   |   348 +
 .../ecbuild/cmake/ecbuild_add_persistent.cmake     |    85 +
 .../ecbuild/cmake/ecbuild_add_resources.cmake      |   151 +
 ecbuild/share/ecbuild/cmake/ecbuild_add_test.cmake |   506 +
 .../ecbuild/cmake/ecbuild_append_to_rpath.cmake    |   102 +
 ecbuild/share/ecbuild/cmake/ecbuild_bundle.cmake   |   201 +
 ecbuild/share/ecbuild/cmake/ecbuild_cache.cmake    |    95 +
 .../cmake/ecbuild_check_c_source_return.cmake      |   154 +
 .../ecbuild/cmake/ecbuild_check_compiler.cmake     |   156 +
 .../share/ecbuild/cmake/ecbuild_check_cxx11.cmake  |   138 +
 .../cmake/ecbuild_check_cxx_source_return.cmake    |   163 +
 .../ecbuild/cmake/ecbuild_check_fortran.cmake      |   126 +
 .../ecbuild_check_fortran_source_return.cmake      |   154 +
 .../ecbuild/cmake/ecbuild_check_functions.cmake    |   186 +
 ecbuild/share/ecbuild/cmake/ecbuild_check_os.cmake |   536 +
 .../ecbuild/cmake/ecbuild_compiler_flags.cmake     |   217 +
 ecbuild/share/ecbuild/cmake/ecbuild_config.h.in    |   201 +
 .../ecbuild/cmake/ecbuild_declare_project.cmake    |   198 +
 .../ecbuild/cmake/ecbuild_define_build_types.cmake |    59 +
 .../ecbuild_define_libs_and_execs_target.cmake     |    29 +
 .../cmake/ecbuild_define_links_target.cmake        |    74 +
 .../ecbuild/cmake/ecbuild_define_options.cmake     |    52 +
 .../share/ecbuild/cmake/ecbuild_define_paths.cmake |    49 +
 .../ecbuild/cmake/ecbuild_define_uninstall.cmake   |     7 +
 .../share/ecbuild/cmake/ecbuild_dont_pack.cmake    |    82 +
 .../ecbuild/cmake/ecbuild_download_resource.cmake  |    76 +
 .../share/ecbuild/cmake/ecbuild_echo_targets.cmake |   233 +
 .../ecbuild/cmake/ecbuild_enable_fortran.cmake     |    89 +
 ecbuild/share/ecbuild/cmake/ecbuild_features.cmake |    57 +
 .../ecbuild/cmake/ecbuild_find_fortranlibs.cmake   |   163 +
 .../share/ecbuild/cmake/ecbuild_find_lexyacc.cmake |    95 +
 ecbuild/share/ecbuild/cmake/ecbuild_find_mpi.cmake |   328 +
 ecbuild/share/ecbuild/cmake/ecbuild_find_omp.cmake |   259 +
 .../share/ecbuild/cmake/ecbuild_find_package.cmake |   368 +
 .../share/ecbuild/cmake/ecbuild_find_perl.cmake    |    73 +
 .../share/ecbuild/cmake/ecbuild_find_python.cmake  |   263 +
 .../cmake/ecbuild_generate_config_headers.cmake    |    63 +
 .../ecbuild_generate_fortran_interfaces.cmake      |   143 +
 .../share/ecbuild/cmake/ecbuild_generate_rpc.cmake |    98 +
 .../share/ecbuild/cmake/ecbuild_generate_yy.cmake  |   201 +
 .../ecbuild/cmake/ecbuild_get_cxx11_flags.cmake    |    74 +
 ecbuild/share/ecbuild/cmake/ecbuild_get_date.cmake |    52 +
 .../ecbuild/cmake/ecbuild_get_resources.cmake      |    52 +
 .../ecbuild/cmake/ecbuild_get_test_data.cmake      |   427 +
 ecbuild/share/ecbuild/cmake/ecbuild_git.cmake      |   300 +
 .../ecbuild/cmake/ecbuild_install_project.cmake    |   437 +
 .../ecbuild/cmake/ecbuild_list_add_pattern.cmake   |   102 +
 .../cmake/ecbuild_list_exclude_pattern.cmake       |    88 +
 .../cmake/ecbuild_list_extra_search_paths.cmake    |    81 +
 .../share/ecbuild/cmake/ecbuild_list_macros.cmake  |    58 +
 ecbuild/share/ecbuild/cmake/ecbuild_log.cmake      |   249 +
 .../share/ecbuild/cmake/ecbuild_pkgconfig.cmake    |   421 +
 ecbuild/share/ecbuild/cmake/ecbuild_policies.cmake |    67 +
 .../ecbuild/cmake/ecbuild_print_summary.cmake      |   116 +
 .../ecbuild/cmake/ecbuild_project_files.cmake      |    75 +
 .../cmake/ecbuild_remove_fortran_flags.cmake       |    64 +
 .../cmake/ecbuild_requires_macro_version.cmake     |    27 +
 .../ecbuild/cmake/ecbuild_separate_sources.cmake   |   103 +
 .../cmake/ecbuild_setup_test_framework.cmake       |    72 +
 .../share/ecbuild/cmake/ecbuild_source_flags.cmake |    34 +
 ecbuild/share/ecbuild/cmake/ecbuild_system.cmake   |   277 +
 .../share/ecbuild/cmake/ecbuild_target_flags.cmake |    91 +
 .../share/ecbuild/cmake/ecbuild_uninstall.cmake.in |    28 +
 .../share/ecbuild/cmake/ecbuild_use_package.cmake  |   341 +
 ecbuild/share/ecbuild/cmake/ecbuild_version.h.in   |    20 +
 .../ecbuild/cmake/ecbuild_warn_unused_files.cmake  |    77 +
 .../share/ecbuild/cmake/fcm-make-interfaces.cfg    |    31 +
 .../fortran_features/CheckFortranFeatures.cmake    |   167 +
 .../ecbuild/cmake/fortran_features/c_size_t.F90    |     8 +
 .../ecbuild/cmake/fortran_features/c_sizeof.F90    |     3 +
 .../fortran_features/derivedtype_interface.F90     |    54 +
 .../cmake/fortran_features/derivedtype_io.F90      |    42 +
 .../cmake/fortran_features/finalization.F90        |   141 +
 .../ecbuild/cmake/fortran_features/submodules.F90  |    35 +
 ecbuild/share/ecbuild/cmake/gen_source_flags.py    |    84 +
 .../cmake/include/ecbuild/boost_test_framework.h   |    17 +
 ecbuild/share/ecbuild/cmake/md5.in                 |     1 +
 ecbuild/share/ecbuild/cmake/pkg-config.pc.in       |    35 +
 .../ecbuild/cmake/project-config-version.cmake.in  |    12 +
 .../share/ecbuild/cmake/project-config.cmake.in    |    97 +
 ecbuild/share/ecbuild/cmake/pymain.c               |     5 +
 ecbuild/share/ecbuild/cmake/sg.pl                  |   573 +
 ecbuild/share/ecbuild/cmake/sha1.in                |     1 +
 .../share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake |    49 +
 .../share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake  |    52 +
 .../ecbuild/toolchains/ecmwf-XC30-Intel.cmake      |    73 +
 .../ecbuild/toolchains/ichec-fionn-Intel.cmake     |    67 +
 ecbuild/share/ecmwf_license_header.txt             |     9 +
 ecbuild/tools/CMakeLists.txt                       |    13 +
 ecbuild/tools/apply_license.sh                     |    55 +
 ecbuild/tools/check_install.sh                     |    76 +
 ecbuild/tools/git-meld                             |     3 +
 ecbuild/tools/git-mproj                            |    54 +
 ecbuild/tools/license.pl                           |   149 +
 ecbuild/tox.ini                                    |     2 +
 eckit/.clang-format                                |     4 +
 eckit/.gitignore                                   |     8 +
 eckit/AUTHORS                                      |    23 +
 eckit/CMakeLists.txt                               |   217 +
 eckit/COPYING                                      |   201 +
 eckit/ChangeLog                                    |     0
 eckit/INSTALL                                      |     0
 eckit/LICENSE                                      |   190 +
 eckit/NOTICE                                       |    13 +
 eckit/README                                       |    26 +
 eckit/TODO                                         |    10 +
 eckit/VERSION.cmake                                |     1 +
 eckit/bamboo/CMakeLists.txt                        |     4 +
 eckit/contrib/CMakeLists.txt                       |    14 +
 eckit/doc/CMakeLists.txt                           |    18 +
 eckit/doc/Doxyfile.in                              |  2280 +++
 eckit/eckit.sublime-project                        |    29 +
 eckit/project_summary.cmake                        |    16 +
 eckit/regressions/CMakeLists.txt                   |    17 +
 eckit/regressions/ECKIT-175.cc                     |   108 +
 eckit/regressions/ECKIT-175.sh.in                  |    36 +
 eckit/src/CMakeLists.txt                           |    19 +
 eckit/src/apps/CMakeLists.txt                      |     4 +
 eckit/src/apps/eckit_version.cc                    |    70 +
 eckit/src/cppcheck-suppress                        |     3 +
 eckit/src/eckit/CMakeLists.txt                     |   686 +
 eckit/src/eckit/bases/Loader.cc                    |    31 +
 eckit/src/eckit/bases/Loader.h                     |    47 +
 eckit/src/eckit/bases/Watcher.cc                   |    34 +
 eckit/src/eckit/bases/Watcher.h                    |    41 +
 eckit/src/eckit/cmd/AliasCmd.cc                    |    56 +
 eckit/src/eckit/cmd/AliasCmd.h                     |    56 +
 eckit/src/eckit/cmd/Arg.cc                         |   255 +
 eckit/src/eckit/cmd/Arg.h                          |    83 +
 eckit/src/eckit/cmd/CMakeLists.txt                 |    78 +
 eckit/src/eckit/cmd/CmdApplication.cc              |   139 +
 eckit/src/eckit/cmd/CmdApplication.h               |    46 +
 eckit/src/eckit/cmd/CmdArg.cc                      |   189 +
 eckit/src/eckit/cmd/CmdArg.h                       |   118 +
 eckit/src/eckit/cmd/CmdParser.cc                   |   501 +
 eckit/src/eckit/cmd/CmdParser.h                    |   101 +
 eckit/src/eckit/cmd/CmdResource.cc                 |   299 +
 eckit/src/eckit/cmd/CmdResource.h                  |    82 +
 eckit/src/eckit/cmd/ConfigCmd.cc                   |    47 +
 eckit/src/eckit/cmd/ConfigCmd.h                    |    56 +
 eckit/src/eckit/cmd/DirCmd.cc                      |    68 +
 eckit/src/eckit/cmd/DirCmd.h                       |    56 +
 eckit/src/eckit/cmd/EchoCmd.cc                     |    56 +
 eckit/src/eckit/cmd/EchoCmd.h                      |    56 +
 eckit/src/eckit/cmd/EnvironmentCmd.cc              |    51 +
 eckit/src/eckit/cmd/EnvironmentCmd.h               |    55 +
 eckit/src/eckit/cmd/ExportCmd.cc                   |    51 +
 eckit/src/eckit/cmd/ExportCmd.h                    |    56 +
 eckit/src/eckit/cmd/HistoryCmd.cc                  |    57 +
 eckit/src/eckit/cmd/HistoryCmd.h                   |    54 +
 eckit/src/eckit/cmd/JSONCmd.cc                     |    43 +
 eckit/src/eckit/cmd/JSONCmd.h                      |    56 +
 eckit/src/eckit/cmd/KillCmd.cc                     |    96 +
 eckit/src/eckit/cmd/KillCmd.h                      |    60 +
 eckit/src/eckit/cmd/LockCmd.cc                     |    75 +
 eckit/src/eckit/cmd/LockCmd.h                      |    55 +
 eckit/src/eckit/cmd/ManCmd.cc                      |    51 +
 eckit/src/eckit/cmd/ManCmd.h                       |    56 +
 eckit/src/eckit/cmd/MemoryCmd.cc                   |    41 +
 eckit/src/eckit/cmd/MemoryCmd.h                    |    56 +
 eckit/src/eckit/cmd/PsCmd.cc                       |   248 +
 eckit/src/eckit/cmd/PsCmd.h                        |    51 +
 eckit/src/eckit/cmd/QuitCmd.cc                     |    54 +
 eckit/src/eckit/cmd/QuitCmd.h                      |    55 +
 eckit/src/eckit/cmd/RemoteCmd.cc                   |   109 +
 eckit/src/eckit/cmd/RemoteCmd.h                    |    56 +
 eckit/src/eckit/cmd/RemoteCommandUser.cc           |    50 +
 eckit/src/eckit/cmd/RemoteCommandUser.h            |    41 +
 eckit/src/eckit/cmd/RemoteCommandable.cc           |    33 +
 eckit/src/eckit/cmd/RemoteCommandable.h            |    48 +
 eckit/src/eckit/cmd/RemoteCommander.cc             |    41 +
 eckit/src/eckit/cmd/RemoteCommander.h              |    49 +
 eckit/src/eckit/cmd/SleepCmd.cc                    |    54 +
 eckit/src/eckit/cmd/SleepCmd.h                     |    56 +
 eckit/src/eckit/cmd/StartCmd.cc                    |    71 +
 eckit/src/eckit/cmd/StartCmd.h                     |    60 +
 eckit/src/eckit/cmd/StatusCmd.cc                   |    43 +
 eckit/src/eckit/cmd/StatusCmd.h                    |    51 +
 eckit/src/eckit/cmd/StopCmd.cc                     |    61 +
 eckit/src/eckit/cmd/StopCmd.h                      |    51 +
 eckit/src/eckit/cmd/TailCmd.cc                     |   118 +
 eckit/src/eckit/cmd/TailCmd.h                      |    51 +
 eckit/src/eckit/cmd/TermBuf.cc                     |    88 +
 eckit/src/eckit/cmd/TermBuf.h                      |    65 +
 eckit/src/eckit/cmd/UpTimeCmd.cc                   |    59 +
 eckit/src/eckit/cmd/UpTimeCmd.h                    |    51 +
 eckit/src/eckit/cmd/UserInput.cc                   |   659 +
 eckit/src/eckit/cmd/UserInput.h                    |    43 +
 eckit/src/eckit/cmd/cmdlib.h                       |    18 +
 eckit/src/eckit/cmd/cmdsl.l                        |   142 +
 eckit/src/eckit/cmd/cmdsy.y                        |   248 +
 eckit/src/eckit/cmd/term.c                         |    42 +
 eckit/src/eckit/compat/Inited.h                    |    80 +
 eckit/src/eckit/compat/StrStream.h                 |    37 +
 eckit/src/eckit/config/Configurable.cc             |    73 +
 eckit/src/eckit/config/Configurable.h              |    83 +
 eckit/src/eckit/config/Configuration.cc            |   399 +
 eckit/src/eckit/config/Configuration.h             |   135 +
 eckit/src/eckit/config/Configured.cc               |    31 +
 eckit/src/eckit/config/Configured.h                |   112 +
 eckit/src/eckit/config/EtcTable.cc                 |   121 +
 eckit/src/eckit/config/EtcTable.h                  |    85 +
 eckit/src/eckit/config/JSONConfiguration.cc        |    73 +
 eckit/src/eckit/config/JSONConfiguration.h         |   117 +
 eckit/src/eckit/config/LibEcKit.cc                 |    51 +
 eckit/src/eckit/config/LibEcKit.h                  |    45 +
 eckit/src/eckit/config/LocalConfiguration.cc       |   132 +
 eckit/src/eckit/config/LocalConfiguration.h        |    73 +
 eckit/src/eckit/config/Parametrisation.h           |    49 +
 eckit/src/eckit/config/Resource.cc                 |    27 +
 eckit/src/eckit/config/Resource.h                  |   121 +
 eckit/src/eckit/config/ResourceBase.cc             |   146 +
 eckit/src/eckit/config/ResourceMgr.cc              |   230 +
 eckit/src/eckit/config/ResourceMgr.h               |   101 +
 eckit/src/eckit/container/BSPTree.h                |    70 +
 eckit/src/eckit/container/BTree.cc                 |   728 +
 eckit/src/eckit/container/BTree.h                  |   348 +
 eckit/src/eckit/container/Cache.h                  |   302 +
 eckit/src/eckit/container/CacheLRU.cc              |   177 +
 eckit/src/eckit/container/CacheLRU.h               |   132 +
 eckit/src/eckit/container/CacheManager.cc          |    28 +
 eckit/src/eckit/container/CacheManager.h           |   277 +
 eckit/src/eckit/container/ClassExtent.h            |   254 +
 eckit/src/eckit/container/DenseMap.h               |   215 +
 eckit/src/eckit/container/KDMapped.cc              |   155 +
 eckit/src/eckit/container/KDMapped.h               |   136 +
 eckit/src/eckit/container/KDMemory.h               |    68 +
 eckit/src/eckit/container/KDTree.h                 |    99 +
 eckit/src/eckit/container/MappedArray.cc           |   104 +
 eckit/src/eckit/container/MappedArray.h            |    99 +
 eckit/src/eckit/container/Recycler.h               |   253 +
 eckit/src/eckit/container/SharedMemArray.cc        |    96 +
 eckit/src/eckit/container/SharedMemArray.h         |   101 +
 eckit/src/eckit/container/StatCollector.h          |    91 +
 eckit/src/eckit/container/bsptree/BSPHyperPlane.h  |    44 +
 eckit/src/eckit/container/bsptree/BSPNode.cc       |   252 +
 eckit/src/eckit/container/bsptree/BSPNode.h        |    66 +
 eckit/src/eckit/container/kdtree/KDNode.cc         |   222 +
 eckit/src/eckit/container/kdtree/KDNode.h          |    62 +
 eckit/src/eckit/container/sptree/SPIterator.h      |    74 +
 eckit/src/eckit/container/sptree/SPMetadata.h      |    30 +
 eckit/src/eckit/container/sptree/SPNode.cc         |   157 +
 eckit/src/eckit/container/sptree/SPNode.h          |   108 +
 eckit/src/eckit/container/sptree/SPNodeInfo.h      |    77 +
 eckit/src/eckit/container/sptree/SPNodeQueue.h     |    84 +
 eckit/src/eckit/container/sptree/SPTree.h          |   160 +
 eckit/src/eckit/container/sptree/SPValue.h         |    60 +
 eckit/src/eckit/eckit.h                            |   294 +
 eckit/src/eckit/eckit_config.h.in                  |    23 +
 eckit/src/eckit/eckit_version.cc.in                |    12 +
 eckit/src/eckit/eckit_version.h.in                 |    19 +
 eckit/src/eckit/exception/Exceptions.cc            |   491 +
 eckit/src/eckit/exception/Exceptions.h             |   303 +
 eckit/src/eckit/filesystem/BasePathName.h          |   106 +
 eckit/src/eckit/filesystem/BasePathNameT.cc        |   276 +
 eckit/src/eckit/filesystem/BasePathNameT.h         |   112 +
 eckit/src/eckit/filesystem/FileManager.cc          |   207 +
 eckit/src/eckit/filesystem/FileManager.h           |    82 +
 eckit/src/eckit/filesystem/FileName.cc             |    80 +
 eckit/src/eckit/filesystem/FileName.h              |    71 +
 eckit/src/eckit/filesystem/FileSpace.cc            |   180 +
 eckit/src/eckit/filesystem/FileSpace.h             |    70 +
 eckit/src/eckit/filesystem/FileSpaceStrategies.cc  |   367 +
 eckit/src/eckit/filesystem/FileSpaceStrategies.h   |    44 +
 eckit/src/eckit/filesystem/FileSystem.cc           |    71 +
 eckit/src/eckit/filesystem/FileSystem.h            |   126 +
 eckit/src/eckit/filesystem/FileSystemSize.h        |    32 +
 eckit/src/eckit/filesystem/LocalPathName.cc        |   842 +
 eckit/src/eckit/filesystem/LocalPathName.h         |   285 +
 eckit/src/eckit/filesystem/PathName.cc             |   439 +
 eckit/src/eckit/filesystem/PathName.h              |   250 +
 eckit/src/eckit/filesystem/TempFile.cc             |    45 +
 eckit/src/eckit/filesystem/TempFile.h              |    50 +
 eckit/src/eckit/filesystem/TmpFile.cc              |    56 +
 eckit/src/eckit/filesystem/TmpFile.h               |    40 +
 eckit/src/eckit/filesystem/marsfs/MarsFSClient.cc  |   752 +
 eckit/src/eckit/filesystem/marsfs/MarsFSClient.h   |    98 +
 eckit/src/eckit/filesystem/marsfs/MarsFSFile.cc    |   152 +
 eckit/src/eckit/filesystem/marsfs/MarsFSFile.h     |   125 +
 eckit/src/eckit/filesystem/marsfs/MarsFSPath.cc    |   337 +
 eckit/src/eckit/filesystem/marsfs/MarsFSPath.h     |   142 +
 eckit/src/eckit/geometry/CMakeLists.txt            |    17 +
 eckit/src/eckit/geometry/KPoint.h                  |   315 +
 eckit/src/eckit/geometry/Point2.cc                 |    57 +
 eckit/src/eckit/geometry/Point2.h                  |    96 +
 eckit/src/eckit/geometry/Point3.cc                 |    96 +
 eckit/src/eckit/geometry/Point3.h                  |   103 +
 eckit/src/eckit/io/AIOHandle.cc                    |   257 +
 eckit/src/eckit/io/AIOHandle.h                     |    90 +
 eckit/src/eckit/io/Buffer.cc                       |    97 +
 eckit/src/eckit/io/Buffer.h                        |    75 +
 eckit/src/eckit/io/BufferCache.cc                  |    83 +
 eckit/src/eckit/io/BufferCache.h                   |   124 +
 eckit/src/eckit/io/BufferedHandle.cc               |   234 +
 eckit/src/eckit/io/BufferedHandle.h                |   111 +
 eckit/src/eckit/io/CommandStream.cc                |    55 +
 eckit/src/eckit/io/CommandStream.h                 |    61 +
 eckit/src/eckit/io/DataBlob.cc                     |   128 +
 eckit/src/eckit/io/DataBlob.h                      |   117 +
 eckit/src/eckit/io/DataHandle.cc                   |   600 +
 eckit/src/eckit/io/DataHandle.h                    |   163 +
 eckit/src/eckit/io/DblBuffer.cc                    |   339 +
 eckit/src/eckit/io/DblBuffer.h                     |    81 +
 eckit/src/eckit/io/EmptyHandle.cc                  |    26 +
 eckit/src/eckit/io/EmptyHandle.h                   |    75 +
 eckit/src/eckit/io/FTPHandle.cc                    |   158 +
 eckit/src/eckit/io/FTPHandle.h                     |    94 +
 eckit/src/eckit/io/FileBase.cc                     |    85 +
 eckit/src/eckit/io/FileBase.h                      |   125 +
 eckit/src/eckit/io/FileDescHandle.cc               |    85 +
 eckit/src/eckit/io/FileDescHandle.h                |    75 +
 eckit/src/eckit/io/FileHandle.cc                   |   349 +
 eckit/src/eckit/io/FileHandle.h                    |   107 +
 eckit/src/eckit/io/FileLock.cc                     |    60 +
 eckit/src/eckit/io/FileLock.h                      |    53 +
 eckit/src/eckit/io/FileLocker.cc                   |    59 +
 eckit/src/eckit/io/FileLocker.h                    |   113 +
 eckit/src/eckit/io/FilePool.cc                     |   128 +
 eckit/src/eckit/io/FilePool.h                      |    94 +
 eckit/src/eckit/io/HandleBuf.cc                    |    87 +
 eckit/src/eckit/io/HandleBuf.h                     |    66 +
 eckit/src/eckit/io/HandleHolder.cc                 |    53 +
 eckit/src/eckit/io/HandleHolder.h                  |    66 +
 eckit/src/eckit/io/Length.cc                       |    33 +
 eckit/src/eckit/io/Length.h                        |   103 +
 eckit/src/eckit/io/MarsFSHandle.cc                 |   198 +
 eckit/src/eckit/io/MarsFSHandle.h                  |   108 +
 eckit/src/eckit/io/MarsFSPartHandle.cc             |   306 +
 eckit/src/eckit/io/MarsFSPartHandle.h              |   110 +
 eckit/src/eckit/io/MemoryHandle.cc                 |   178 +
 eckit/src/eckit/io/MemoryHandle.h                  |   109 +
 eckit/src/eckit/io/MoverHandle.cc                  |   203 +
 eckit/src/eckit/io/MoverHandle.h                   |   124 +
 eckit/src/eckit/io/MoverTransfer.cc                |   116 +
 eckit/src/eckit/io/MoverTransfer.h                 |   115 +
 eckit/src/eckit/io/MultiHandle.cc                  |   478 +
 eckit/src/eckit/io/MultiHandle.h                   |   112 +
 eckit/src/eckit/io/MultiPartHandle.cc              |   233 +
 eckit/src/eckit/io/MultiPartHandle.h               |   103 +
 eckit/src/eckit/io/Offset.cc                       |    84 +
 eckit/src/eckit/io/Offset.h                        |   128 +
 eckit/src/eckit/io/PartFileHandle.cc               |   391 +
 eckit/src/eckit/io/PartFileHandle.h                |   112 +
 eckit/src/eckit/io/PartHandle.cc                   |   239 +
 eckit/src/eckit/io/PartHandle.h                    |   101 +
 eckit/src/eckit/io/PipeHandle.cc                   |   130 +
 eckit/src/eckit/io/PipeHandle.h                    |    98 +
 eckit/src/eckit/io/RawFileHandle.cc                |   117 +
 eckit/src/eckit/io/RawFileHandle.h                 |    78 +
 eckit/src/eckit/io/ResizableBuffer.cc              |    87 +
 eckit/src/eckit/io/ResizableBuffer.h               |    79 +
 eckit/src/eckit/io/Select.cc                       |   152 +
 eckit/src/eckit/io/Select.h                        |    79 +
 eckit/src/eckit/io/SharedHandle.cc                 |   185 +
 eckit/src/eckit/io/SharedHandle.h                  |   106 +
 eckit/src/eckit/io/SockBuf.cc                      |   105 +
 eckit/src/eckit/io/SockBuf.h                       |    66 +
 eckit/src/eckit/io/StatsHandle.cc                  |   256 +
 eckit/src/eckit/io/StatsHandle.h                   |   116 +
 eckit/src/eckit/io/StdFile.cc                      |    41 +
 eckit/src/eckit/io/StdFile.h                       |    52 +
 eckit/src/eckit/io/StdFileHandle.cc                |    73 +
 eckit/src/eckit/io/StdFileHandle.h                 |    73 +
 eckit/src/eckit/io/StdPipe.cc                      |    39 +
 eckit/src/eckit/io/StdPipe.h                       |    63 +
 eckit/src/eckit/io/StdioBuf.cc                     |    92 +
 eckit/src/eckit/io/StdioBuf.h                      |    66 +
 eckit/src/eckit/io/TCPHandle.cc                    |    99 +
 eckit/src/eckit/io/TCPHandle.h                     |    86 +
 eckit/src/eckit/io/TCPSocketHandle.cc              |    71 +
 eckit/src/eckit/io/TCPSocketHandle.h               |    80 +
 eckit/src/eckit/io/TeeHandle.cc                    |   186 +
 eckit/src/eckit/io/TeeHandle.h                     |    96 +
 eckit/src/eckit/io/TransferWatcher.cc              |    34 +
 eckit/src/eckit/io/TransferWatcher.h               |    43 +
 eckit/src/eckit/io/cluster/ClusterDisks.cc         |   467 +
 eckit/src/eckit/io/cluster/ClusterDisks.h          |    59 +
 eckit/src/eckit/io/cluster/ClusterNode.cc          |   115 +
 eckit/src/eckit/io/cluster/ClusterNode.h           |    90 +
 eckit/src/eckit/io/cluster/ClusterNodes.cc         |   469 +
 eckit/src/eckit/io/cluster/ClusterNodes.h          |    65 +
 eckit/src/eckit/io/cluster/NodeInfo.cc             |   182 +
 eckit/src/eckit/io/cluster/NodeInfo.h              |   110 +
 eckit/src/eckit/linalg/CMakeLists.txt              |    41 +
 eckit/src/eckit/linalg/LinearAlgebra.cc            |   155 +
 eckit/src/eckit/linalg/LinearAlgebra.h             |    98 +
 eckit/src/eckit/linalg/LinearAlgebraArmadillo.cc   |   105 +
 eckit/src/eckit/linalg/LinearAlgebraArmadillo.h    |    56 +
 eckit/src/eckit/linalg/LinearAlgebraCUDA.cc        |   318 +
 eckit/src/eckit/linalg/LinearAlgebraCUDA.h         |    55 +
 eckit/src/eckit/linalg/LinearAlgebraEigen.cc       |   119 +
 eckit/src/eckit/linalg/LinearAlgebraEigen.h        |    56 +
 eckit/src/eckit/linalg/LinearAlgebraGeneric.cc     |   144 +
 eckit/src/eckit/linalg/LinearAlgebraGeneric.h      |    50 +
 eckit/src/eckit/linalg/LinearAlgebraMKL.cc         |   154 +
 eckit/src/eckit/linalg/LinearAlgebraMKL.h          |    56 +
 eckit/src/eckit/linalg/LinearAlgebraViennaCL.cc    |   123 +
 eckit/src/eckit/linalg/LinearAlgebraViennaCL.h     |    55 +
 eckit/src/eckit/linalg/Matrix.cc                   |   157 +
 eckit/src/eckit/linalg/Matrix.h                    |   134 +
 eckit/src/eckit/linalg/SparseMatrix.cc             |   513 +
 eckit/src/eckit/linalg/SparseMatrix.h              |   238 +
 eckit/src/eckit/linalg/Triplet.h                   |    63 +
 eckit/src/eckit/linalg/Vector.cc                   |   134 +
 eckit/src/eckit/linalg/Vector.h                    |   121 +
 eckit/src/eckit/linalg/types.h                     |    35 +
 eckit/src/eckit/log/BigNum.cc                      |    46 +
 eckit/src/eckit/log/BigNum.h                       |    61 +
 eckit/src/eckit/log/Bytes.cc                       |    75 +
 eckit/src/eckit/log/Bytes.h                        |    70 +
 eckit/src/eckit/log/CallbackTarget.cc              |    41 +
 eckit/src/eckit/log/CallbackTarget.h               |    49 +
 eckit/src/eckit/log/Channel.cc                     |   100 +
 eckit/src/eckit/log/Channel.h                      |    92 +
 eckit/src/eckit/log/ChannelBuffer.cc               |   152 +
 eckit/src/eckit/log/ChannelBuffer.h                |    98 +
 eckit/src/eckit/log/CodeLocation.cc                |    43 +
 eckit/src/eckit/log/CodeLocation.h                 |    74 +
 eckit/src/eckit/log/Colour.cc                      |   181 +
 eckit/src/eckit/log/Colour.h                       |    64 +
 eckit/src/eckit/log/ColouringTarget.cc             |    53 +
 eckit/src/eckit/log/ColouringTarget.h              |    53 +
 eckit/src/eckit/log/ETA.cc                         |    51 +
 eckit/src/eckit/log/ETA.h                          |    56 +
 eckit/src/eckit/log/FileTarget.cc                  |    45 +
 eckit/src/eckit/log/FileTarget.h                   |    50 +
 eckit/src/eckit/log/IndentTarget.cc                |    35 +
 eckit/src/eckit/log/IndentTarget.h                 |    38 +
 eckit/src/eckit/log/LineBasedTarget.cc             |    68 +
 eckit/src/eckit/log/LineBasedTarget.h              |    49 +
 eckit/src/eckit/log/Log.cc                         |   398 +
 eckit/src/eckit/log/Log.h                          |   128 +
 eckit/src/eckit/log/LogTarget.cc                   |    32 +
 eckit/src/eckit/log/LogTarget.h                    |    52 +
 eckit/src/eckit/log/MessageTarget.cc               |    32 +
 eckit/src/eckit/log/MessageTarget.h                |    42 +
 eckit/src/eckit/log/OStreamHandle.h                |    97 +
 eckit/src/eckit/log/OStreamTarget.cc               |    41 +
 eckit/src/eckit/log/OStreamTarget.h                |    46 +
 eckit/src/eckit/log/Plural.h                       |    62 +
 eckit/src/eckit/log/PrefixTarget.cc                |    44 +
 eckit/src/eckit/log/PrefixTarget.h                 |    53 +
 eckit/src/eckit/log/Progress.cc                    |    40 +
 eckit/src/eckit/log/Progress.h                     |   111 +
 eckit/src/eckit/log/ResourceUsage.cc               |    95 +
 eckit/src/eckit/log/ResourceUsage.h                |    73 +
 eckit/src/eckit/log/RotationTarget.cc              |    82 +
 eckit/src/eckit/log/RotationTarget.h               |    43 +
 eckit/src/eckit/log/SavedStatus.cc                 |    31 +
 eckit/src/eckit/log/SavedStatus.h                  |    37 +
 eckit/src/eckit/log/Seconds.cc                     |    74 +
 eckit/src/eckit/log/Seconds.h                      |    63 +
 eckit/src/eckit/log/Statistics.cc                  |   148 +
 eckit/src/eckit/log/Statistics.h                   |    76 +
 eckit/src/eckit/log/StatusTarget.cc                |    32 +
 eckit/src/eckit/log/StatusTarget.h                 |    41 +
 eckit/src/eckit/log/TeeTarget.cc                   |    73 +
 eckit/src/eckit/log/TeeTarget.h                    |    52 +
 eckit/src/eckit/log/TimeStamp.cc                   |    58 +
 eckit/src/eckit/log/TimeStamp.h                    |    59 +
 eckit/src/eckit/log/TimeStampTarget.cc             |    63 +
 eckit/src/eckit/log/TimeStampTarget.h              |    50 +
 eckit/src/eckit/log/Timer.cc                       |   135 +
 eckit/src/eckit/log/Timer.h                        |    95 +
 eckit/src/eckit/log/UserChannel.cc                 |   122 +
 eckit/src/eckit/log/UserChannel.h                  |    68 +
 eckit/src/eckit/log/WrapperTarget.cc               |    72 +
 eckit/src/eckit/log/WrapperTarget.h                |    57 +
 eckit/src/eckit/maths/CMakeLists.txt               |    15 +
 eckit/src/eckit/maths/Eigen.h                      |    48 +
 eckit/src/eckit/maths/Functions.cc                 |    24 +
 eckit/src/eckit/maths/Functions.h                  |    32 +
 eckit/src/eckit/maths/Lapack.cc                    |    64 +
 eckit/src/eckit/maths/Lapack.h                     |    20 +
 eckit/src/eckit/maths/Matrix.h                     |    58 +
 eckit/src/eckit/maths/MatrixEigen.h                |   151 +
 eckit/src/eckit/maths/MatrixLapack.h               |   741 +
 eckit/src/eckit/memory/Builder.cc                  |    24 +
 eckit/src/eckit/memory/Builder.h                   |   291 +
 eckit/src/eckit/memory/Counted.cc                  |    22 +
 eckit/src/eckit/memory/Counted.h                   |   110 +
 eckit/src/eckit/memory/Factory.h                   |   194 +
 eckit/src/eckit/memory/MapAllocator.cc             |   126 +
 eckit/src/eckit/memory/MapAllocator.h              |    62 +
 eckit/src/eckit/memory/MemoryPool.cc               |   425 +
 eckit/src/eckit/memory/MemoryPool.h                |    66 +
 eckit/src/eckit/memory/NonCopyable.cc              |    26 +
 eckit/src/eckit/memory/NonCopyable.h               |    39 +
 eckit/src/eckit/memory/Owned.h                     |    76 +
 eckit/src/eckit/memory/Padded.h                    |    44 +
 eckit/src/eckit/memory/ScopedPtr.h                 |   118 +
 eckit/src/eckit/memory/SharedPtr.h                 |   243 +
 eckit/src/eckit/mpi/Buffer.h                       |    52 +
 eckit/src/eckit/mpi/CMakeLists.txt                 |    49 +
 eckit/src/eckit/mpi/Comm.cc                        |   231 +
 eckit/src/eckit/mpi/Comm.h                         |   843 +
 eckit/src/eckit/mpi/DataType.cc                    |    69 +
 eckit/src/eckit/mpi/DataType.h                     |    72 +
 eckit/src/eckit/mpi/Operation.cc                   |    33 +
 eckit/src/eckit/mpi/Operation.h                    |    44 +
 eckit/src/eckit/mpi/Parallel.cc                    |   462 +
 eckit/src/eckit/mpi/Parallel.h                     |   125 +
 eckit/src/eckit/mpi/ParallelRequest.cc             |    36 +
 eckit/src/eckit/mpi/ParallelRequest.h              |    50 +
 eckit/src/eckit/mpi/ParallelStatus.cc              |    29 +
 eckit/src/eckit/mpi/ParallelStatus.h               |    48 +
 eckit/src/eckit/mpi/Request.cc                     |    74 +
 eckit/src/eckit/mpi/Request.h                      |    84 +
 eckit/src/eckit/mpi/Serial.cc                      |   321 +
 eckit/src/eckit/mpi/Serial.h                       |   104 +
 eckit/src/eckit/mpi/SerialData.h                   |    50 +
 eckit/src/eckit/mpi/SerialRequest.cc               |    62 +
 eckit/src/eckit/mpi/SerialRequest.h                |   108 +
 eckit/src/eckit/mpi/SerialStatus.cc                |    36 +
 eckit/src/eckit/mpi/SerialStatus.h                 |    51 +
 eckit/src/eckit/mpi/Status.cc                      |    60 +
 eckit/src/eckit/mpi/Status.h                       |   100 +
 eckit/src/eckit/net/Connector.cc                   |   463 +
 eckit/src/eckit/net/Connector.h                    |   154 +
 eckit/src/eckit/net/NetAddress.cc                  |    92 +
 eckit/src/eckit/net/NetAddress.h                   |    83 +
 eckit/src/eckit/net/NetService.cc                  |    58 +
 eckit/src/eckit/net/NetService.h                   |    77 +
 eckit/src/eckit/net/NetUser.cc                     |    46 +
 eckit/src/eckit/net/NetUser.h                      |    62 +
 eckit/src/eckit/net/Port.cc                        |    28 +
 eckit/src/eckit/net/Port.h                         |    48 +
 eckit/src/eckit/net/TCPClient.cc                   |    46 +
 eckit/src/eckit/net/TCPClient.h                    |    66 +
 eckit/src/eckit/net/TCPServer.cc                   |   136 +
 eckit/src/eckit/net/TCPServer.h                    |    95 +
 eckit/src/eckit/net/TCPSocket.cc                   |   704 +
 eckit/src/eckit/net/TCPSocket.h                    |   144 +
 eckit/src/eckit/net/TCPStream.cc                   |    62 +
 eckit/src/eckit/net/TCPStream.h                    |   153 +
 eckit/src/eckit/net/Telnet.cc                      |    70 +
 eckit/src/eckit/net/Telnet.h                       |    58 +
 eckit/src/eckit/net/TelnetUser.h                   |    43 +
 eckit/src/eckit/net/Telnetable.cc                  |    37 +
 eckit/src/eckit/net/Telnetable.h                   |    56 +
 eckit/src/eckit/option/CMakeLists.txt              |    29 +
 eckit/src/eckit/option/CmdArgs.cc                  |   176 +
 eckit/src/eckit/option/CmdArgs.h                   |    90 +
 eckit/src/eckit/option/FactoryOption.cc            |    59 +
 eckit/src/eckit/option/FactoryOption.h             |   121 +
 eckit/src/eckit/option/Option.cc                   |   108 +
 eckit/src/eckit/option/Option.h                    |   127 +
 eckit/src/eckit/option/Separator.cc                |    54 +
 eckit/src/eckit/option/Separator.h                 |   118 +
 eckit/src/eckit/option/SimpleOption.cc             |   101 +
 eckit/src/eckit/option/SimpleOption.h              |   120 +
 eckit/src/eckit/option/VectorOption.cc             |    96 +
 eckit/src/eckit/option/VectorOption.h              |   122 +
 eckit/src/eckit/os/AutoAlarm.cc                    |    61 +
 eckit/src/eckit/os/AutoAlarm.h                     |    68 +
 eckit/src/eckit/os/AutoUmask.h                     |    38 +
 eckit/src/eckit/os/BackTrace.cc                    |   129 +
 eckit/src/eckit/os/BackTrace.h                     |    31 +
 eckit/src/eckit/os/Password.cc                     |    64 +
 eckit/src/eckit/os/Password.h                      |    38 +
 eckit/src/eckit/os/SemLocker.cc                    |   104 +
 eckit/src/eckit/os/SemLocker.h                     |    48 +
 eckit/src/eckit/os/Semaphore.cc                    |   132 +
 eckit/src/eckit/os/Semaphore.h                     |    61 +
 eckit/src/eckit/os/SharedInt.cc                    |    69 +
 eckit/src/eckit/os/SharedInt.h                     |   117 +
 eckit/src/eckit/os/SignalHandler.cc                |    85 +
 eckit/src/eckit/os/SignalHandler.h                 |    70 +
 eckit/src/eckit/os/Stat.h                          |    45 +
 eckit/src/eckit/os/System.cc                       |    55 +
 eckit/src/eckit/os/System.h                        |    31 +
 eckit/src/eckit/parser/JSON.cc                     |   226 +
 eckit/src/eckit/parser/JSON.h                      |   127 +
 eckit/src/eckit/parser/JSONDataBlob.cc             |    56 +
 eckit/src/eckit/parser/JSONDataBlob.h              |    49 +
 eckit/src/eckit/parser/JSONMetadata.cc             |   111 +
 eckit/src/eckit/parser/JSONMetadata.h              |    66 +
 eckit/src/eckit/parser/JSONParser.cc               |   297 +
 eckit/src/eckit/parser/JSONParser.h                |    52 +
 eckit/src/eckit/parser/StreamParser.cc             |   125 +
 eckit/src/eckit/parser/StreamParser.h              |    65 +
 eckit/src/eckit/parser/StringTools.cc              |   229 +
 eckit/src/eckit/parser/StringTools.h               |    65 +
 eckit/src/eckit/parser/Tokenizer.cc                |   108 +
 eckit/src/eckit/parser/Tokenizer.h                 |    68 +
 eckit/src/eckit/persist/DumpLoad.cc                |    29 +
 eckit/src/eckit/persist/DumpLoad.h                 |   214 +
 eckit/src/eckit/persist/Exporter.cc                |   766 +
 eckit/src/eckit/persist/Exporter.h                 |   203 +
 eckit/src/eckit/persist/Isa.cc                     |    74 +
 eckit/src/eckit/runtime/Application.cc             |   188 +
 eckit/src/eckit/runtime/Application.h              |    86 +
 eckit/src/eckit/runtime/Dispatcher.h               |   588 +
 eckit/src/eckit/runtime/Library.cc                 |    39 +
 eckit/src/eckit/runtime/Library.h                  |    45 +
 eckit/src/eckit/runtime/Main.cc                    |   186 +
 eckit/src/eckit/runtime/Main.h                     |    97 +
 eckit/src/eckit/runtime/Monitor.cc                 |   450 +
 eckit/src/eckit/runtime/Monitor.h                  |   171 +
 eckit/src/eckit/runtime/Monitorable.cc             |    56 +
 eckit/src/eckit/runtime/Monitorable.h              |    63 +
 eckit/src/eckit/runtime/Pipe.h                     |   289 +
 eckit/src/eckit/runtime/PipeApplication.cc         |   240 +
 eckit/src/eckit/runtime/PipeApplication.h          |    57 +
 eckit/src/eckit/runtime/PipeHandler.cc             |   331 +
 eckit/src/eckit/runtime/PipeHandler.h              |    93 +
 eckit/src/eckit/runtime/ProcessControler.cc        |   263 +
 eckit/src/eckit/runtime/ProcessControler.h         |    88 +
 eckit/src/eckit/runtime/ProducerConsumer.h         |   279 +
 eckit/src/eckit/runtime/Task.cc                    |    31 +
 eckit/src/eckit/runtime/Task.h                     |    51 +
 eckit/src/eckit/runtime/TaskID.h                   |    22 +
 eckit/src/eckit/runtime/TaskInfo.cc                |   212 +
 eckit/src/eckit/runtime/TaskInfo.h                 |   206 +
 eckit/src/eckit/runtime/Tool.cc                    |    53 +
 eckit/src/eckit/runtime/Tool.h                     |    46 +
 eckit/src/eckit/serialisation/FileStream.cc        |   118 +
 eckit/src/eckit/serialisation/FileStream.h         |    64 +
 eckit/src/eckit/serialisation/HandleStream.h       |    66 +
 eckit/src/eckit/serialisation/MemoryStream.cc      |   100 +
 eckit/src/eckit/serialisation/MemoryStream.h       |    70 +
 eckit/src/eckit/serialisation/PipeStream.cc        |   146 +
 eckit/src/eckit/serialisation/PipeStream.h         |    74 +
 eckit/src/eckit/serialisation/Reanimator.cc        |    43 +
 eckit/src/eckit/serialisation/Reanimator.h         |   114 +
 eckit/src/eckit/serialisation/ReanimatorBase.cc    |    94 +
 eckit/src/eckit/serialisation/Stream.cc            |   785 +
 eckit/src/eckit/serialisation/Stream.h             |   227 +
 eckit/src/eckit/serialisation/Streamable.cc        |    54 +
 eckit/src/eckit/serialisation/Streamable.h         |    80 +
 eckit/src/eckit/system/Library.cc                  |   267 +
 eckit/src/eckit/system/Library.h                   |   108 +
 eckit/src/eckit/system/ResourceUsage.cc            |    68 +
 eckit/src/eckit/system/ResourceUsage.h             |    55 +
 eckit/src/eckit/system/SystemInfo.cc               |    95 +
 eckit/src/eckit/system/SystemInfo.h                |    65 +
 eckit/src/eckit/system/SystemInfoFreeBSD.cc        |    71 +
 eckit/src/eckit/system/SystemInfoFreeBSD.h         |    51 +
 eckit/src/eckit/system/SystemInfoLinux.cc          |    59 +
 eckit/src/eckit/system/SystemInfoLinux.h           |    50 +
 eckit/src/eckit/system/SystemInfoMacOSX.cc         |    79 +
 eckit/src/eckit/system/SystemInfoMacOSX.h          |    50 +
 eckit/src/eckit/testing/Setup.h                    |    40 +
 eckit/src/eckit/thread/AutoLock.h                  |   128 +
 eckit/src/eckit/thread/Mutex.cc                    |    62 +
 eckit/src/eckit/thread/Mutex.h                     |    57 +
 eckit/src/eckit/thread/MutexCond.cc                |    91 +
 eckit/src/eckit/thread/MutexCond.h                 |    67 +
 eckit/src/eckit/thread/Once.h                      |    98 +
 eckit/src/eckit/thread/StaticMutex.cc              |   142 +
 eckit/src/eckit/thread/StaticMutex.h               |    58 +
 eckit/src/eckit/thread/Thread.cc                   |    47 +
 eckit/src/eckit/thread/Thread.h                    |    72 +
 eckit/src/eckit/thread/ThreadControler.cc          |   196 +
 eckit/src/eckit/thread/ThreadControler.h           |    81 +
 eckit/src/eckit/thread/ThreadPool.cc               |   237 +
 eckit/src/eckit/thread/ThreadPool.h                |    93 +
 eckit/src/eckit/thread/ThreadSingleton.h           |   129 +
 eckit/src/eckit/transaction/TxnEvent.cc            |    49 +
 eckit/src/eckit/transaction/TxnEvent.h             |    89 +
 eckit/src/eckit/transaction/TxnLog.cc              |   352 +
 eckit/src/eckit/transaction/TxnLog.h               |    94 +
 eckit/src/eckit/types/ClimateDate.cc               |    95 +
 eckit/src/eckit/types/ClimateDate.h                |   116 +
 eckit/src/eckit/types/Coord.cc                     |    46 +
 eckit/src/eckit/types/Coord.h                      |   127 +
 eckit/src/eckit/types/Date.cc                      |   339 +
 eckit/src/eckit/types/Date.h                       |   174 +
 eckit/src/eckit/types/DateTime.cc                  |   195 +
 eckit/src/eckit/types/DateTime.h                   |    99 +
 eckit/src/eckit/types/DayOfYear.cc                 |    75 +
 eckit/src/eckit/types/DayOfYear.h                  |   115 +
 eckit/src/eckit/types/Double.cc                    |    67 +
 eckit/src/eckit/types/Double.h                     |    78 +
 eckit/src/eckit/types/FixedString.h                |   176 +
 eckit/src/eckit/types/FloatCompare.cc              |   147 +
 eckit/src/eckit/types/FloatCompare.h               |   139 +
 eckit/src/eckit/types/Fraction.cc                  |   148 +
 eckit/src/eckit/types/Fraction.h                   |   274 +
 eckit/src/eckit/types/Grid.cc                      |   233 +
 eckit/src/eckit/types/Grid.h                       |   145 +
 eckit/src/eckit/types/Metadata.cc                  |    27 +
 eckit/src/eckit/types/Metadata.h                   |    59 +
 eckit/src/eckit/types/Month.cc                     |   121 +
 eckit/src/eckit/types/Month.h                      |   117 +
 eckit/src/eckit/types/Time.cc                      |   216 +
 eckit/src/eckit/types/Time.h                       |   136 +
 eckit/src/eckit/types/TimeInterval.cc              |    41 +
 eckit/src/eckit/types/TimeInterval.h               |    73 +
 eckit/src/eckit/types/Types.cc                     |   144 +
 eckit/src/eckit/types/Types.h                      |   195 +
 eckit/src/eckit/types/UUID.cc                      |   127 +
 eckit/src/eckit/types/UUID.h                       |   116 +
 eckit/src/eckit/types/VerifyingDate.cc             |    57 +
 eckit/src/eckit/types/VerifyingDate.h              |    87 +
 eckit/src/eckit/utils/Hash.cc                      |   127 +
 eckit/src/eckit/utils/Hash.h                       |   147 +
 eckit/src/eckit/utils/HyperCube.cc                 |    80 +
 eckit/src/eckit/utils/HyperCube.h                  |   123 +
 eckit/src/eckit/utils/MD4.cc                       |    96 +
 eckit/src/eckit/utils/MD4.h                        |    62 +
 eckit/src/eckit/utils/MD5.cc                       |   365 +
 eckit/src/eckit/utils/MD5.h                        |    76 +
 eckit/src/eckit/utils/RLE.cc                       |   341 +
 eckit/src/eckit/utils/RLE.h                        |    68 +
 eckit/src/eckit/utils/Regex.cc                     |   122 +
 eckit/src/eckit/utils/Regex.h                      |    70 +
 eckit/src/eckit/utils/RendezvousHash.cc            |   112 +
 eckit/src/eckit/utils/RendezvousHash.h             |    85 +
 eckit/src/eckit/utils/SHA1.cc                      |    87 +
 eckit/src/eckit/utils/SHA1.h                       |    62 +
 eckit/src/eckit/utils/Translator.cc                |   272 +
 eckit/src/eckit/utils/Translator.h                 |   139 +
 eckit/src/eckit/utils/xxHash.cc                    |    98 +
 eckit/src/eckit/utils/xxHash.h                     |    58 +
 eckit/src/eckit/value/BoolContent.cc               |   124 +
 eckit/src/eckit/value/BoolContent.h                |   122 +
 eckit/src/eckit/value/CompositeParams.cc           |    80 +
 eckit/src/eckit/value/CompositeParams.h            |    57 +
 eckit/src/eckit/value/Content.cc                   |   577 +
 eckit/src/eckit/value/Content.h                    |   269 +
 eckit/src/eckit/value/DateContent.cc               |   115 +
 eckit/src/eckit/value/DateContent.h                |   114 +
 eckit/src/eckit/value/DispatchParams.h             |    81 +
 eckit/src/eckit/value/DoubleContent.cc             |   152 +
 eckit/src/eckit/value/DoubleContent.h              |   127 +
 eckit/src/eckit/value/Expression.h                 |   157 +
 eckit/src/eckit/value/ListContent.cc               |   215 +
 eckit/src/eckit/value/ListContent.h                |   121 +
 eckit/src/eckit/value/MapContent.cc                |   166 +
 eckit/src/eckit/value/MapContent.h                 |   122 +
 eckit/src/eckit/value/NilContent.cc                |   126 +
 eckit/src/eckit/value/NilContent.h                 |   112 +
 eckit/src/eckit/value/NumberContent.cc             |   173 +
 eckit/src/eckit/value/NumberContent.h              |   126 +
 eckit/src/eckit/value/Params.cc                    |   121 +
 eckit/src/eckit/value/Params.h                     |   199 +
 eckit/src/eckit/value/Properties.cc                |   142 +
 eckit/src/eckit/value/Properties.h                 |   108 +
 eckit/src/eckit/value/ScopeParams.cc               |    59 +
 eckit/src/eckit/value/ScopeParams.h                |    53 +
 eckit/src/eckit/value/StringContent.cc             |   140 +
 eckit/src/eckit/value/StringContent.h              |   115 +
 eckit/src/eckit/value/Value.cc                     |   375 +
 eckit/src/eckit/value/Value.h                      |   353 +
 eckit/src/eckit/web/AgentResource.cc               |   106 +
 eckit/src/eckit/web/AgentResource.h                |    48 +
 eckit/src/eckit/web/CMakeLists.txt                 |    51 +
 eckit/src/eckit/web/CgiResource.cc                 |    67 +
 eckit/src/eckit/web/CgiResource.h                  |    46 +
 eckit/src/eckit/web/Configure.cc                   |    87 +
 eckit/src/eckit/web/FileResource.cc                |    52 +
 eckit/src/eckit/web/FileResource.h                 |    45 +
 eckit/src/eckit/web/FtpRequest.h                   |    58 +
 eckit/src/eckit/web/Html.cc                        |   399 +
 eckit/src/eckit/web/Html.h                         |   310 +
 eckit/src/eckit/web/HtmlObject.cc                  |    51 +
 eckit/src/eckit/web/HtmlObject.h                   |    58 +
 eckit/src/eckit/web/HtmlResource.cc                |    86 +
 eckit/src/eckit/web/HtmlResource.h                 |    89 +
 eckit/src/eckit/web/HttpBuf.cc                     |   168 +
 eckit/src/eckit/web/HttpBuf.h                      |    66 +
 eckit/src/eckit/web/HttpHeader.cc                  |   232 +
 eckit/src/eckit/web/HttpHeader.h                   |    86 +
 eckit/src/eckit/web/HttpServer.cc                  |    36 +
 eckit/src/eckit/web/HttpServer.h                   |    58 +
 eckit/src/eckit/web/HttpService.cc                 |    72 +
 eckit/src/eckit/web/HttpService.h                  |    53 +
 eckit/src/eckit/web/HttpUser.h                     |    37 +
 eckit/src/eckit/web/JSONResource.cc                |    41 +
 eckit/src/eckit/web/JSONResource.h                 |    50 +
 eckit/src/eckit/web/JavaAgent.cc                   |    96 +
 eckit/src/eckit/web/JavaAgent.h                    |    86 +
 eckit/src/eckit/web/JavaResource.cc                |    62 +
 eckit/src/eckit/web/JavaResource.h                 |   115 +
 eckit/src/eckit/web/JavaServer.cc                  |    36 +
 eckit/src/eckit/web/JavaServer.h                   |    55 +
 eckit/src/eckit/web/JavaService.cc                 |    39 +
 eckit/src/eckit/web/JavaService.h                  |   112 +
 eckit/src/eckit/web/JavaUser.cc                    |    39 +
 eckit/src/eckit/web/JavaUser.h                     |    43 +
 eckit/src/eckit/web/Url.cc                         |   326 +
 eckit/src/eckit/web/Url.h                          |   149 +
 eckit/src/experimental/CMakeLists.txt              |    13 +
 eckit/src/experimental/eckit/CMakeLists.txt        |     9 +
 .../src/experimental/eckit/testing/CMakeLists.txt  |    11 +
 eckit/src/experimental/eckit/testing/UnitTest.cc   |    84 +
 eckit/src/experimental/eckit/testing/UnitTest.h    |    92 +
 eckit/src/experimental/tests/CMakeLists.txt        |     9 +
 .../experimental/tests/singleton/CMakeLists.txt    |    20 +
 .../experimental/tests/singleton/TestBuilder1.cc   |    25 +
 .../experimental/tests/singleton/TestBuilder2.cc   |    25 +
 .../experimental/tests/singleton/TestFactory.cc    |    75 +
 .../src/experimental/tests/singleton/TestFactory.h |    51 +
 .../experimental/tests/singleton/test_singleton.cc |    24 +
 eckit/src/tests/CMakeLists.txt                     |    21 +
 eckit/src/tests/config/CMakeLists.txt              |    11 +
 eckit/src/tests/config/test_config.cc              |   128 +
 eckit/src/tests/config/test_resource.cc            |   125 +
 eckit/src/tests/container/CMakeLists.txt           |    24 +
 eckit/src/tests/container/benchmark_densemap.cc    |   112 +
 eckit/src/tests/container/test_btree.cc            |   365 +
 eckit/src/tests/container/test_cache_lru.cc        |   193 +
 eckit/src/tests/container/test_densemap.cc         |   191 +
 eckit/src/tests/container/test_sharedmemarray.cc   |   124 +
 eckit/src/tests/filesystem/CMakeLists.txt          |    22 +
 eckit/src/tests/filesystem/test_aiohandle.cc       |   126 +
 eckit/src/tests/filesystem/test_localpathname.cc   |   199 +
 eckit/src/tests/filesystem/test_multihandle.cc     |   159 +
 eckit/src/tests/filesystem/test_restarthandle.cc   |   270 +
 eckit/src/tests/geometry/CMakeLists.txt            |     6 +
 eckit/src/tests/geometry/test_kdtree.cc            |   131 +
 eckit/src/tests/io/CMakeLists.txt                  |     9 +
 eckit/src/tests/io/test_datablob.cc                |   107 +
 eckit/src/tests/io/test_filepool.cc                |   243 +
 eckit/src/tests/large_file/CMakeLists.txt          |     3 +
 eckit/src/tests/large_file/test_large_file.cc      |    88 +
 eckit/src/tests/linalg/CMakeLists.txt              |    95 +
 eckit/src/tests/linalg/test_la_factory.cc          |    89 +
 eckit/src/tests/linalg/test_la_linalg.cc           |    91 +
 eckit/src/tests/linalg/test_la_sparse.cc           |   312 +
 eckit/src/tests/linalg/test_la_streaming.cc        |    96 +
 eckit/src/tests/linalg/util.h                      |    52 +
 eckit/src/tests/log/CMakeLists.txt                 |    27 +
 eckit/src/tests/log/test_colour.cc                 |    59 +
 eckit/src/tests/log/test_log.cc                    |    89 +
 eckit/src/tests/log/test_log_callback.cc           |   102 +
 eckit/src/tests/log/test_log_channels.cc           |   180 +
 eckit/src/tests/log/test_log_threads.cc            |   117 +
 eckit/src/tests/log/test_log_user_channels.cc      |    45 +
 eckit/src/tests/maths/CMakeLists.txt               |    11 +
 eckit/src/tests/maths/test_eigen.cc                |    38 +
 eckit/src/tests/maths/test_matrix.cc               |    84 +
 eckit/src/tests/memory/CMakeLists.txt              |    19 +
 eckit/src/tests/memory/test_counted.cc             |   223 +
 eckit/src/tests/memory/test_factory.cc             |   275 +
 eckit/src/tests/memory/test_scoped_ptr.cc          |   135 +
 eckit/src/tests/memory/test_shared_ptr.cc          |   209 +
 eckit/src/tests/memory_map/CMakeLists.txt          |     3 +
 eckit/src/tests/memory_map/test_memory_map.cc      |   119 +
 eckit/src/tests/mpi/CMakeLists.txt                 |    18 +
 eckit/src/tests/mpi/eckit_test_mpi.cc              |   556 +
 eckit/src/tests/mpi/eckit_test_mpi_addcomm.cc      |    28 +
 eckit/src/tests/option/CMakeLists.txt              |    16 +
 .../src/tests/option/eckit_test_option_cmdargs.cc  |   234 +
 .../src/tests/option/eckit_test_option_factory.cc  |    94 +
 eckit/src/tests/parser/CMakeLists.txt              |    14 +
 eckit/src/tests/parser/test_json.cc                |   138 +
 eckit/src/tests/parser/test_json_metadata.cc       |   174 +
 eckit/src/tests/parser/test_stream_parser.cc       |   134 +
 eckit/src/tests/runtime/CMakeLists.txt             |    19 +
 eckit/src/tests/runtime/boost_auto_param.h         |    46 +
 eckit/src/tests/runtime/test_context.cc            |    46 +
 eckit/src/tests/runtime/test_producer.cc           |    80 +
 eckit/src/tests/serialisation/CMakeLists.txt       |    11 +
 eckit/src/tests/serialisation/test_file_stream.cc  |   185 +
 eckit/src/tests/serialisation/test_streamable.cc   |   221 +
 eckit/src/tests/system/CMakeLists.txt              |     8 +
 eckit/src/tests/system/test_system.cc              |    92 +
 eckit/src/tests/thread/CMakeLists.txt              |     3 +
 eckit/src/tests/thread/test_mutex.cc               |    69 +
 eckit/src/tests/types/CMakeLists.txt               |    40 +
 eckit/src/tests/types/test-double-compare-speed.cc |    82 +
 eckit/src/tests/types/test_cache.cc                |   223 +
 eckit/src/tests/types/test_doublecompare.cc        |   344 +
 eckit/src/tests/types/test_fixedstring.cc          |   485 +
 eckit/src/tests/types/test_floatcompare.cc         |   343 +
 eckit/src/tests/types/test_fraction.cc             |   149 +
 eckit/src/tests/types/test_print_vector.cc         |   104 +
 eckit/src/tests/types/test_uuid.cc                 |    82 +
 eckit/src/tests/utils/CMakeLists.txt               |    47 +
 eckit/src/tests/utils/hash-performance.cc          |    89 +
 eckit/src/tests/utils/test_md4.cc                  |   101 +
 eckit/src/tests/utils/test_md5.cc                  |   125 +
 eckit/src/tests/utils/test_rendezvoushash.cc       |   233 +
 eckit/src/tests/utils/test_sha1.cc                 |   148 +
 eckit/src/tests/utils/test_string_tools.cc         |   183 +
 eckit/src/tests/utils/test_tokenizer.cc            |   157 +
 eckit/src/tests/utils/test_translator.cc           |   109 +
 eckit/src/tests/utils/test_xxhash.cc               |    95 +
 eckit/src/tests/value/AnyKeyParams.cc              |    44 +
 eckit/src/tests/value/AnyKeyParams.h               |    45 +
 eckit/src/tests/value/CMakeLists.txt               |    22 +
 eckit/src/tests/value/test_value.cc                |  2624 +++
 eckit/src/tests/value/test_value_params.cc         |   385 +
 eckit/src/tests/value/test_value_properties.cc     |   119 +
 metkit/.gitignore                                  |     8 +
 metkit/CMakeLists.txt                              |    71 +
 metkit/VERSION.cmake                               |     1 +
 metkit/bamboo/CLANG-env.sh                         |     8 +
 metkit/bamboo/INTEL-env.sh                         |    12 +
 metkit/etc/CMakeLists.txt                          |     3 +
 metkit/etc/language.json                           |   547 +
 metkit/etc/param.json                              | 19553 +++++++++++++++++++
 metkit/metkit.sublime-project                      |    28 +
 metkit/src/CMakeLists.txt                          |     3 +
 metkit/src/metkit/BaseProtocol.cc                  |    32 +
 metkit/src/metkit/BaseProtocol.h                   |    54 +
 metkit/src/metkit/CMakeLists.txt                   |   121 +
 metkit/src/metkit/ClientTask.cc                    |    75 +
 metkit/src/metkit/ClientTask.h                     |   202 +
 metkit/src/metkit/DHSProtocol.cc                   |   274 +
 metkit/src/metkit/DHSProtocol.h                    |    67 +
 metkit/src/metkit/MarsExpension.cc                 |   104 +
 metkit/src/metkit/MarsExpension.h                  |    75 +
 metkit/src/metkit/MarsHandle.cc                    |   260 +
 metkit/src/metkit/MarsHandle.h                     |    83 +
 metkit/src/metkit/MarsLanguage.cc                  |   348 +
 metkit/src/metkit/MarsLanguage.h                   |    86 +
 metkit/src/metkit/MarsLocation.cc                  |   109 +
 metkit/src/metkit/MarsLocation.h                   |    96 +
 metkit/src/metkit/MarsParser.cc                    |   216 +
 metkit/src/metkit/MarsParser.h                     |    60 +
 metkit/src/metkit/MarsRequest.cc                   |   436 +
 metkit/src/metkit/MarsRequest.h                    |   182 +
 metkit/src/metkit/MarsRequestHandle.cc             |    80 +
 metkit/src/metkit/MarsRequestHandle.h              |    48 +
 metkit/src/metkit/RequestEnvironment.cc            |    70 +
 metkit/src/metkit/RequestEnvironment.h             |    57 +
 metkit/src/metkit/config/LibMetkit.cc              |    54 +
 metkit/src/metkit/config/LibMetkit.h               |    47 +
 metkit/src/metkit/grib/EmosFile.cc                 |   102 +
 metkit/src/metkit/grib/EmosFile.h                  |    76 +
 metkit/src/metkit/grib/GribAccessor.cc             |    93 +
 metkit/src/metkit/grib/GribAccessor.h              |    87 +
 metkit/src/metkit/grib/GribDataBlob.cc             |    70 +
 metkit/src/metkit/grib/GribDataBlob.h              |    59 +
 metkit/src/metkit/grib/GribFile.cc                 |    61 +
 metkit/src/metkit/grib/GribFile.h                  |    59 +
 metkit/src/metkit/grib/GribHandle.cc               |   227 +
 metkit/src/metkit/grib/GribHandle.h                |   120 +
 metkit/src/metkit/grib/GribIndex.cc                |    55 +
 metkit/src/metkit/grib/GribIndex.h                 |    47 +
 metkit/src/metkit/grib/GribMetaData.cc             |   203 +
 metkit/src/metkit/grib/GribMetaData.h              |    82 +
 metkit/src/metkit/grib/GribMutator.cc              |    78 +
 metkit/src/metkit/grib/GribMutator.h               |    71 +
 metkit/src/metkit/grib/GribToRequest.cc            |   192 +
 metkit/src/metkit/grib/GribToRequest.h             |    58 +
 metkit/src/metkit/metkit_config.h.in               |    26 +
 metkit/src/metkit/metkit_version.cc.in             |    12 +
 metkit/src/metkit/metkit_version.h.in              |    19 +
 metkit/src/metkit/types/Type.cc                    |   243 +
 metkit/src/metkit/types/Type.h                     |    89 +
 metkit/src/metkit/types/TypeAny.cc                 |    35 +
 metkit/src/metkit/types/TypeAny.h                  |    42 +
 metkit/src/metkit/types/TypeDate.cc                |    53 +
 metkit/src/metkit/types/TypeDate.h                 |    46 +
 metkit/src/metkit/types/TypeEnum.cc                |   126 +
 metkit/src/metkit/types/TypeEnum.h                 |    56 +
 metkit/src/metkit/types/TypeExpver.cc              |    44 +
 metkit/src/metkit/types/TypeExpver.h               |    45 +
 metkit/src/metkit/types/TypeFloat.cc               |    68 +
 metkit/src/metkit/types/TypeFloat.h                |    44 +
 metkit/src/metkit/types/TypeInteger.cc             |    84 +
 metkit/src/metkit/types/TypeInteger.h              |    49 +
 metkit/src/metkit/types/TypeMixed.cc               |    57 +
 metkit/src/metkit/types/TypeMixed.h                |    47 +
 metkit/src/metkit/types/TypeParam.cc               |   178 +
 metkit/src/metkit/types/TypeParam.h                |    49 +
 metkit/src/metkit/types/TypeRange.cc               |   101 +
 metkit/src/metkit/types/TypeRange.h                |    44 +
 metkit/src/metkit/types/TypeTime.cc                |   122 +
 metkit/src/metkit/types/TypeTime.h                 |    48 +
 metkit/src/metkit/types/TypeToByList.cc            |    75 +
 metkit/src/metkit/types/TypeToByList.h             |    46 +
 metkit/src/metkit/types/TypesFactory.cc            |    98 +
 metkit/src/metkit/types/TypesFactory.h             |    91 +
 metkit/src/tests/CMakeLists.txt                    |    35 +
 metkit/src/tests/test_emosfile.cc                  |    73 +
 metkit/src/tests/test_multihandle.cc               |    78 +
 metkit/src/tests/test_typesfactory.cc              |    91 +
 metkit/src/tools/CMakeLists.txt                    |    29 +
 metkit/src/tools/grib-to-mars-request.cc           |    82 +
 metkit/src/tools/parse-mars-request.cc             |   121 +
 odb_api/.gitignore                                 |     8 +
 odb_api/CMakeLists.txt                             |   138 +
 odb_api/INSTALL                                    |   148 +
 odb_api/LICENSE                                    |   190 +
 odb_api/VERSION.cmake                              |     1 +
 odb_api/bamboo/CLANG-env.sh                        |    10 +
 odb_api/bamboo/INTEL-env.sh                        |    11 +
 odb_api/bamboo/env.sh                              |     4 +
 odb_api/bamboo/flags.cmake                         |     1 +
 odb_api/etc/ODA2RequestTool.cfg                    |     8 +
 odb_api/etc/README.TXT                             |    13 +
 odb_api/etc/class.table                            |    49 +
 odb_api/etc/group.txt                              |    60 +
 odb_api/etc/rtablel_2031                           |    34 +
 odb_api/etc/stream.table                           |    95 +
 odb_api/etc/type.table                             |    66 +
 odb_api/project_summary.cmake                      |    30 +
 odb_api/src/CMakeLists.txt                         |    30 +
 odb_api/src/api/CMakeLists.txt                     |    16 +
 odb_api/src/api/README.md                          |    27 +
 odb_api/src/api/odbql_c_example.c                  |   219 +
 odb_api/src/api/odbql_c_test.c                     |    62 +
 odb_api/src/api/odbql_fortran_example.f90          |   116 +
 odb_api/src/api/odbql_python_example.py            |   108 +
 odb_api/src/ecml/CMakeLists.txt                    |   176 +
 odb_api/src/ecml/README.txt                        |    70 +
 odb_api/src/ecml/ast/Closure.cc                    |   114 +
 odb_api/src/ecml/ast/Closure.h                     |    48 +
 odb_api/src/ecml/ast/FunctionDefinition.cc         |    58 +
 odb_api/src/ecml/ast/FunctionDefinition.h          |    41 +
 odb_api/src/ecml/core/Environment.cc               |   124 +
 odb_api/src/ecml/core/Environment.h                |    54 +
 odb_api/src/ecml/core/ExecutionContext.cc          |   139 +
 odb_api/src/ecml/core/ExecutionContext.h           |    62 +
 odb_api/src/ecml/core/Interpreter.cc               |   202 +
 odb_api/src/ecml/core/Interpreter.h                |    46 +
 odb_api/src/ecml/core/Module.cc                    |    19 +
 odb_api/src/ecml/core/Module.h                     |    44 +
 odb_api/src/ecml/core/RequestHandler.cc            |    82 +
 odb_api/src/ecml/core/RequestHandler.h             |    49 +
 odb_api/src/ecml/core/SpecialFormHandler.cc        |    44 +
 odb_api/src/ecml/core/SpecialFormHandler.h         |    40 +
 odb_api/src/ecml/data/DataHandleFactory.cc         |   117 +
 odb_api/src/ecml/data/DataHandleFactory.h          |    62 +
 odb_api/src/ecml/data/FileHandleFactory.cc         |    30 +
 odb_api/src/ecml/data/FileHandleFactory.h          |    29 +
 odb_api/src/ecml/data/HttpHandle.cc                |    80 +
 odb_api/src/ecml/data/HttpHandle.h                 |    49 +
 odb_api/src/ecml/data/HttpHandleFactory.cc         |    33 +
 odb_api/src/ecml/data/HttpHandleFactory.h          |    29 +
 odb_api/src/ecml/data/Makefile                     |     2 +
 odb_api/src/ecml/data/MarsHandleFactory.cc         |    79 +
 odb_api/src/ecml/data/MarsHandleFactory.h          |    29 +
 odb_api/src/ecml/data/PartFileHandleFactory.cc     |    38 +
 odb_api/src/ecml/data/PartFileHandleFactory.h      |    29 +
 odb_api/src/ecml/ecml_config.h.in                  |     7 +
 odb_api/src/ecml/misc/DynamicParametrisation.cc    |   107 +
 odb_api/src/ecml/misc/DynamicParametrisation.h     |    50 +
 .../src/ecml/misc/ParameterizedRequestHandler.cc   |    40 +
 .../src/ecml/misc/ParameterizedRequestHandler.h    |    39 +
 odb_api/src/ecml/parser/Cell.cc                    |   216 +
 odb_api/src/ecml/parser/Cell.h                     |    88 +
 odb_api/src/ecml/parser/CellDotPrinter.cc          |   145 +
 odb_api/src/ecml/parser/CellDotPrinter.h           |    34 +
 odb_api/src/ecml/parser/CellPrinter.cc             |   157 +
 odb_api/src/ecml/parser/CellPrinter.h              |    40 +
 odb_api/src/ecml/parser/List.cc                    |    61 +
 odb_api/src/ecml/parser/List.h                     |    48 +
 odb_api/src/ecml/parser/Makefile                   |     2 +
 odb_api/src/ecml/parser/Request.cc                 |    22 +
 odb_api/src/ecml/parser/Request.h                  |    56 +
 odb_api/src/ecml/parser/RequestParser.cc           |   153 +
 odb_api/src/ecml/parser/RequestParser.h            |    35 +
 odb_api/src/ecml/parser/requestl.l                 |    55 +
 odb_api/src/ecml/parser/requesty.y                 |    75 +
 odb_api/src/ecml/parser/update.cc                  |    24 +
 odb_api/src/ecml/prelude/ApplyHandler.cc           |    72 +
 odb_api/src/ecml/prelude/ApplyHandler.h            |    32 +
 odb_api/src/ecml/prelude/Autocompleter.cc          |   106 +
 odb_api/src/ecml/prelude/Autocompleter.h           |    36 +
 odb_api/src/ecml/prelude/ClosureHandler.cc         |    27 +
 odb_api/src/ecml/prelude/ClosureHandler.h          |    31 +
 odb_api/src/ecml/prelude/DefineFunctionHandler.cc  |    42 +
 odb_api/src/ecml/prelude/DefineFunctionHandler.h   |    31 +
 odb_api/src/ecml/prelude/FirstHandler.cc           |    27 +
 odb_api/src/ecml/prelude/FirstHandler.h            |    30 +
 odb_api/src/ecml/prelude/ForHandler.cc             |   137 +
 odb_api/src/ecml/prelude/ForHandler.h              |    35 +
 odb_api/src/ecml/prelude/GetenvHandler.cc          |    38 +
 odb_api/src/ecml/prelude/GetenvHandler.h           |    30 +
 odb_api/src/ecml/prelude/GlobHandler.cc            |    64 +
 odb_api/src/ecml/prelude/GlobHandler.h             |    30 +
 odb_api/src/ecml/prelude/IfHandler.cc              |    69 +
 odb_api/src/ecml/prelude/IfHandler.h               |    32 +
 odb_api/src/ecml/prelude/JoinStringsHandler.cc     |    50 +
 odb_api/src/ecml/prelude/JoinStringsHandler.h      |    30 +
 odb_api/src/ecml/prelude/LetHandler.cc             |    51 +
 odb_api/src/ecml/prelude/LetHandler.h              |    32 +
 odb_api/src/ecml/prelude/ListHandler.cc            |    28 +
 odb_api/src/ecml/prelude/ListHandler.h             |    30 +
 odb_api/src/ecml/prelude/Makefile                  |     2 +
 odb_api/src/ecml/prelude/MatchHandler.cc           |    51 +
 odb_api/src/ecml/prelude/MatchHandler.h            |    30 +
 odb_api/src/ecml/prelude/NullHandler.cc            |    24 +
 odb_api/src/ecml/prelude/NullHandler.h             |    28 +
 odb_api/src/ecml/prelude/Prelude.cc                |   159 +
 odb_api/src/ecml/prelude/Prelude.h                 |    36 +
 odb_api/src/ecml/prelude/PrintHandler.cc           |    75 +
 odb_api/src/ecml/prelude/PrintHandler.h            |    41 +
 odb_api/src/ecml/prelude/QuoteHandler.cc           |    39 +
 odb_api/src/ecml/prelude/QuoteHandler.h            |    31 +
 odb_api/src/ecml/prelude/REPLHandler.cc            |   125 +
 odb_api/src/ecml/prelude/REPLHandler.h             |    43 +
 odb_api/src/ecml/prelude/RangeHandler.cc           |    52 +
 odb_api/src/ecml/prelude/RangeHandler.h            |    30 +
 odb_api/src/ecml/prelude/ReadTextFileHandler.cc    |    60 +
 odb_api/src/ecml/prelude/ReadTextFileHandler.h     |    32 +
 odb_api/src/ecml/prelude/RestHandler.cc            |    31 +
 odb_api/src/ecml/prelude/RestHandler.h             |    30 +
 odb_api/src/ecml/prelude/RunHandler.cc             |    68 +
 odb_api/src/ecml/prelude/RunHandler.h              |    30 +
 odb_api/src/ecml/prelude/SequenceHandler.cc        |    27 +
 odb_api/src/ecml/prelude/SequenceHandler.h         |    30 +
 odb_api/src/ecml/prelude/SystemHandler.cc          |    43 +
 odb_api/src/ecml/prelude/SystemHandler.h           |    30 +
 odb_api/src/ecml/prelude/TemporaryFileHandler.cc   |    49 +
 odb_api/src/ecml/prelude/TemporaryFileHandler.h    |    30 +
 odb_api/src/ecml/prelude/TestHandler.cc            |    57 +
 odb_api/src/ecml/prelude/TestHandler.h             |    32 +
 odb_api/src/ecml/prelude/ThrowHandler.cc           |    43 +
 odb_api/src/ecml/prelude/ThrowHandler.h            |    30 +
 odb_api/src/ecml/prelude/TryHandler.cc             |    95 +
 odb_api/src/ecml/prelude/TryHandler.h              |    32 +
 odb_api/src/ecml/prelude/UpdateHandler.cc          |    60 +
 odb_api/src/ecml/prelude/UpdateHandler.h           |    32 +
 odb_api/src/ecml/prelude/VariableLookupHandler.cc  |    58 +
 odb_api/src/ecml/prelude/VariableLookupHandler.h   |    32 +
 odb_api/src/ecml/prelude/graph.gv                  |     8 +
 odb_api/src/ecml/prelude/graph.png                 |   Bin 0 -> 13575 bytes
 odb_api/src/ecml/prelude/prelude.ecml              |    35 +
 odb_api/src/ecml/tests/CMakeLists.txt              |    46 +
 odb_api/src/ecml/tests/ecml_test.cc                |    64 +
 odb_api/src/ecml/tests/ecml_unittests.cc           |   199 +
 odb_api/src/ecml/tests/s.ecml                      |     1 +
 odb_api/src/ecml/tests/test_apply.ecml             |    34 +
 odb_api/src/ecml/tests/test_dictionary.ecml        |    27 +
 odb_api/src/ecml/tests/test_first.ecml             |     6 +
 odb_api/src/ecml/tests/test_for.ecml               |    51 +
 odb_api/src/ecml/tests/test_function.ecml          |    16 +
 odb_api/src/ecml/tests/test_getenv.ecml            |     7 +
 .../ecml/tests/test_higher_order_functions.ecml    |    18 +
 odb_api/src/ecml/tests/test_let_and_value.ecml     |     8 +
 odb_api/src/ecml/tests/test_lists.ecml             |     6 +
 odb_api/src/ecml/tests/test_map.ecml               |    22 +
 odb_api/src/ecml/tests/test_match.ecml             |     3 +
 .../src/ecml/tests/test_one_letter_variables.ecml  |     9 +
 odb_api/src/ecml/tests/test_parallel_map.ecml      |    10 +
 odb_api/src/ecml/tests/test_print.ecml             |     5 +
 odb_api/src/ecml/tests/test_recursion.ecml         |    18 +
 odb_api/src/ecml/tests/test_rest.ecml              |    12 +
 odb_api/src/ecml/tests/test_system.ecml            |     9 +
 odb_api/src/fortran/CMakeLists.txt                 |    62 +
 .../src/fortran/legacy_fortran_api_examples.f90    |   406 +
 .../legacy_test_client_lib_fortran_local.ecml      |     6 +
 .../legacy_test_client_lib_fortran_local.f90       |   169 +
 ...acy_test_fortran_api_open_non_existing_file.f90 |    46 +
 odb_api/src/fortran/odb_c_binding.f90              |   304 +
 odb_api/src/fortran/odbapi.fortran.doxyfile        |  1295 ++
 odb_api/src/fortran/odbql_binding.f90              |   229 +
 odb_api/src/fortran/odbql_constants.f90            |   366 +
 odb_api/src/fortran/odbql_wrappers.f90             |   481 +
 odb_api/src/fortran/test_regression.f90            |    85 +
 odb_api/src/odb_api/Archiver.cc                    |   110 +
 odb_api/src/odb_api/Archiver.h                     |    35 +
 odb_api/src/odb_api/Array.h                        |    75 +
 odb_api/src/odb_api/BitColumnExpression.cc         |   130 +
 odb_api/src/odb_api/BitColumnExpression.h          |    56 +
 odb_api/src/odb_api/Block.cc                       |    48 +
 odb_api/src/odb_api/Block.h                        |    85 +
 odb_api/src/odb_api/CMakeLists.txt                 |   478 +
 odb_api/src/odb_api/Codec.cc                       |   148 +
 odb_api/src/odb_api/Codec.h                        |   822 +
 odb_api/src/odb_api/CodecFactory.cc                |    14 +
 odb_api/src/odb_api/CodecFactory.h                 |   133 +
 odb_api/src/odb_api/CodecOptimizer.cc              |    46 +
 odb_api/src/odb_api/CodecOptimizer.h               |   142 +
 odb_api/src/odb_api/Column.cc                      |   131 +
 odb_api/src/odb_api/Column.h                       |   159 +
 odb_api/src/odb_api/ColumnExpression.cc            |   197 +
 odb_api/src/odb_api/ColumnExpression.h             |    76 +
 odb_api/src/odb_api/ColumnType.h                   |    28 +
 odb_api/src/odb_api/CommandLineParser.cc           |   160 +
 odb_api/src/odb_api/CommandLineParser.h            |    74 +
 odb_api/src/odb_api/Comparator.cc                  |   196 +
 odb_api/src/odb_api/Comparator.h                   |   115 +
 odb_api/src/odb_api/ConstantExpression.cc          |    33 +
 odb_api/src/odb_api/ConstantExpression.h           |    95 +
 odb_api/src/odb_api/ConstantSetter.cc              |    14 +
 odb_api/src/odb_api/ConstantSetter.h               |    47 +
 odb_api/src/odb_api/DataColumn.cc                  |    75 +
 odb_api/src/odb_api/DataColumn.h                   |    90 +
 odb_api/src/odb_api/DataColumns.cc                 |   112 +
 odb_api/src/odb_api/DataColumns.h                  |    82 +
 odb_api/src/odb_api/DataField.cc                   |    39 +
 odb_api/src/odb_api/DataField.h                    |    71 +
 odb_api/src/odb_api/DataJoin.cc                    |    40 +
 odb_api/src/odb_api/DataJoin.h                     |    82 +
 odb_api/src/odb_api/DataJoinIterator.cc            |   188 +
 odb_api/src/odb_api/DataJoinIterator.h             |    71 +
 odb_api/src/odb_api/DataLink.cc                    |   104 +
 odb_api/src/odb_api/DataLink.h                     |   161 +
 odb_api/src/odb_api/DataLinkFiller.cc              |    60 +
 odb_api/src/odb_api/DataLinkFiller.h               |    69 +
 odb_api/src/odb_api/DataLinkIterator.cc            |    40 +
 odb_api/src/odb_api/DataLinkIterator.h             |   125 +
 odb_api/src/odb_api/DataLinks.cc                   |    39 +
 odb_api/src/odb_api/DataLinks.h                    |    73 +
 odb_api/src/odb_api/DataLoader.cc                  |   190 +
 odb_api/src/odb_api/DataLoader.h                   |    90 +
 odb_api/src/odb_api/DataPage.cc                    |   236 +
 odb_api/src/odb_api/DataPage.h                     |   168 +
 odb_api/src/odb_api/DataRecord.cc                  |    32 +
 odb_api/src/odb_api/DataRecord.h                   |   112 +
 odb_api/src/odb_api/DataRecordIterator.h           |    68 +
 odb_api/src/odb_api/DataRow.cc                     |    98 +
 odb_api/src/odb_api/DataRow.h                      |   224 +
 odb_api/src/odb_api/DataSaver.cc                   |    67 +
 odb_api/src/odb_api/DataSaver.h                    |    57 +
 odb_api/src/odb_api/DataSelect.cc                  |   125 +
 odb_api/src/odb_api/DataSelect.h                   |    95 +
 odb_api/src/odb_api/DataSelectIterator.cc          |    98 +
 odb_api/src/odb_api/DataSelectIterator.h           |    70 +
 odb_api/src/odb_api/DataSelectOutput.cc            |    36 +
 odb_api/src/odb_api/DataSelectOutput.h             |    50 +
 odb_api/src/odb_api/DataSelectSession.cc           |    47 +
 odb_api/src/odb_api/DataSelectSession.h            |    36 +
 odb_api/src/odb_api/DataSet.cc                     |    32 +
 odb_api/src/odb_api/DataSet.h                      |    62 +
 odb_api/src/odb_api/DataSetBuilder.cc              |   241 +
 odb_api/src/odb_api/DataSetBuilder.h               |    54 +
 odb_api/src/odb_api/DataSetFiller.cc               |   139 +
 odb_api/src/odb_api/DataSetFiller.h                |    73 +
 odb_api/src/odb_api/DataStream.cc                  |   316 +
 odb_api/src/odb_api/DataStream.h                   |   117 +
 odb_api/src/odb_api/DataTable.cc                   |   278 +
 odb_api/src/odb_api/DataTable.h                    |   363 +
 odb_api/src/odb_api/DataTableFiller.cc             |   119 +
 odb_api/src/odb_api/DataTableFiller.h              |    79 +
 odb_api/src/odb_api/DataTableIterator.cc           |   160 +
 odb_api/src/odb_api/DataTableIterator.h            |    92 +
 odb_api/src/odb_api/DataTableMappings.cc           |    19 +
 odb_api/src/odb_api/DataTableMappings.h            |    30 +
 odb_api/src/odb_api/DataTables.cc                  |    54 +
 odb_api/src/odb_api/DataTables.h                   |    95 +
 odb_api/src/odb_api/DataView.cc                    |   249 +
 odb_api/src/odb_api/DataView.h                     |   144 +
 odb_api/src/odb_api/DateTime.cc                    |   551 +
 odb_api/src/odb_api/DateTime.h                     |   126 +
 odb_api/src/odb_api/Decoder.cc                     |    89 +
 odb_api/src/odb_api/Decoder.h                      |    34 +
 odb_api/src/odb_api/Dictionary.cc                  |    57 +
 odb_api/src/odb_api/Dictionary.h                   |    77 +
 odb_api/src/odb_api/DirectAccess.cc                |   254 +
 odb_api/src/odb_api/DirectAccess.h                 |   178 +
 odb_api/src/odb_api/DirectAccessIterator.cc        |    43 +
 odb_api/src/odb_api/DirectAccessIterator.h         |    47 +
 odb_api/src/odb_api/DispatchingWriter.cc           |    44 +
 odb_api/src/odb_api/DispatchingWriter.h            |    54 +
 odb_api/src/odb_api/Duration.cc                    |   227 +
 odb_api/src/odb_api/Duration.h                     |    96 +
 odb_api/src/odb_api/EmbeddedCodeExpression.cc      |    85 +
 odb_api/src/odb_api/EmbeddedCodeExpression.h       |    54 +
 odb_api/src/odb_api/EmbeddedCodeParser.cc          |    61 +
 odb_api/src/odb_api/EmbeddedCodeParser.h           |    37 +
 odb_api/src/odb_api/Endian.h                       |    53 +
 odb_api/src/odb_api/Environment.cc                 |    28 +
 odb_api/src/odb_api/Environment.h                  |    41 +
 odb_api/src/odb_api/EqRegionCache.cc               |   952 +
 odb_api/src/odb_api/EqRegionCache.h                |    76 +
 odb_api/src/odb_api/Expressions.cc                 |    56 +
 odb_api/src/odb_api/Expressions.h                  |    81 +
 odb_api/src/odb_api/FastODA2Request.cc             |   279 +
 odb_api/src/odb_api/FastODA2Request.h              |    95 +
 odb_api/src/odb_api/FileCollector.cc               |   149 +
 odb_api/src/odb_api/FileCollector.h                |    51 +
 odb_api/src/odb_api/FileMapper.cc                  |   188 +
 odb_api/src/odb_api/FileMapper.h                   |    47 +
 odb_api/src/odb_api/FixedSizeWriterIterator.cc     |    54 +
 odb_api/src/odb_api/FixedSizeWriterIterator.h      |    45 +
 odb_api/src/odb_api/FunctionAND.cc                 |   100 +
 odb_api/src/odb_api/FunctionAND.h                  |    52 +
 odb_api/src/odb_api/FunctionAVG.cc                 |    79 +
 odb_api/src/odb_api/FunctionAVG.h                  |    59 +
 odb_api/src/odb_api/FunctionCOUNT.cc               |    66 +
 odb_api/src/odb_api/FunctionCOUNT.h                |    57 +
 odb_api/src/odb_api/FunctionDOTP.cc                |    71 +
 odb_api/src/odb_api/FunctionDOTP.h                 |    59 +
 odb_api/src/odb_api/FunctionEQ.cc                  |   123 +
 odb_api/src/odb_api/FunctionEQ.h                   |    57 +
 odb_api/src/odb_api/FunctionEQ_BOXLAT.cc           |    48 +
 odb_api/src/odb_api/FunctionEQ_BOXLAT.h            |    51 +
 odb_api/src/odb_api/FunctionEQ_BOXLON.cc           |    49 +
 odb_api/src/odb_api/FunctionEQ_BOXLON.h            |    51 +
 odb_api/src/odb_api/FunctionExpression.cc          |   114 +
 odb_api/src/odb_api/FunctionExpression.h           |    63 +
 odb_api/src/odb_api/FunctionFIRST.cc               |    79 +
 odb_api/src/odb_api/FunctionFIRST.h                |    58 +
 odb_api/src/odb_api/FunctionFactory.cc             |   657 +
 odb_api/src/odb_api/FunctionFactory.h              |    82 +
 odb_api/src/odb_api/FunctionIN.cc                  |    47 +
 odb_api/src/odb_api/FunctionIN.h                   |    51 +
 odb_api/src/odb_api/FunctionIntegerExpression.cc   |   115 +
 odb_api/src/odb_api/FunctionIntegerExpression.h    |    50 +
 odb_api/src/odb_api/FunctionJOIN.cc                |    58 +
 odb_api/src/odb_api/FunctionJOIN.h                 |    52 +
 odb_api/src/odb_api/FunctionJULIAN.cc              |    57 +
 odb_api/src/odb_api/FunctionJULIAN.h               |    49 +
 odb_api/src/odb_api/FunctionJULIAN_SECONDS.cc      |    57 +
 odb_api/src/odb_api/FunctionJULIAN_SECONDS.h       |    49 +
 odb_api/src/odb_api/FunctionLAST.cc                |    73 +
 odb_api/src/odb_api/FunctionLAST.h                 |    57 +
 odb_api/src/odb_api/FunctionMATCH.cc               |    87 +
 odb_api/src/odb_api/FunctionMATCH.h                |    60 +
 odb_api/src/odb_api/FunctionMAX.cc                 |    77 +
 odb_api/src/odb_api/FunctionMAX.h                  |    58 +
 odb_api/src/odb_api/FunctionMIN.cc                 |    76 +
 odb_api/src/odb_api/FunctionMIN.h                  |    57 +
 odb_api/src/odb_api/FunctionNORM.cc                |    77 +
 odb_api/src/odb_api/FunctionNORM.h                 |    56 +
 odb_api/src/odb_api/FunctionNOT_IN.cc              |    52 +
 odb_api/src/odb_api/FunctionNOT_IN.h               |    52 +
 odb_api/src/odb_api/FunctionNOT_NULL.cc            |    49 +
 odb_api/src/odb_api/FunctionNOT_NULL.h             |    50 +
 odb_api/src/odb_api/FunctionNULL.cc                |    44 +
 odb_api/src/odb_api/FunctionNULL.h                 |    49 +
 odb_api/src/odb_api/FunctionNVL.cc                 |    45 +
 odb_api/src/odb_api/FunctionNVL.h                  |    50 +
 odb_api/src/odb_api/FunctionOR.cc                  |    60 +
 odb_api/src/odb_api/FunctionOR.h                   |    51 +
 odb_api/src/odb_api/FunctionRGG_BOXLAT.cc          |    51 +
 odb_api/src/odb_api/FunctionRGG_BOXLAT.h           |    50 +
 odb_api/src/odb_api/FunctionRGG_BOXLON.cc          |    49 +
 odb_api/src/odb_api/FunctionRGG_BOXLON.h           |    50 +
 odb_api/src/odb_api/FunctionRLIKE.cc               |   104 +
 odb_api/src/odb_api/FunctionRLIKE.h                |    56 +
 odb_api/src/odb_api/FunctionRMS.cc                 |    80 +
 odb_api/src/odb_api/FunctionRMS.h                  |    60 +
 odb_api/src/odb_api/FunctionROWNUMBER.cc           |    66 +
 odb_api/src/odb_api/FunctionROWNUMBER.h            |    62 +
 odb_api/src/odb_api/FunctionSTDEV.cc               |    45 +
 odb_api/src/odb_api/FunctionSTDEV.h                |    49 +
 odb_api/src/odb_api/FunctionSUM.cc                 |    69 +
 odb_api/src/odb_api/FunctionSUM.h                  |    58 +
 odb_api/src/odb_api/FunctionTDIFF.cc               |    77 +
 odb_api/src/odb_api/FunctionTDIFF.h                |    53 +
 odb_api/src/odb_api/FunctionTHIN.cc                |    81 +
 odb_api/src/odb_api/FunctionTHIN.h                 |    60 +
 odb_api/src/odb_api/FunctionTIMESTAMP.cc           |    65 +
 odb_api/src/odb_api/FunctionTIMESTAMP.h            |    52 +
 odb_api/src/odb_api/FunctionVAR.cc                 |    87 +
 odb_api/src/odb_api/FunctionVAR.h                  |    62 +
 odb_api/src/odb_api/GribCodes.cc                   |   147 +
 odb_api/src/odb_api/GribCodes.h                    |    83 +
 odb_api/src/odb_api/HashTable.cc                   |   230 +
 odb_api/src/odb_api/HashTable.h                    |    89 +
 odb_api/src/odb_api/Header.cc                      |   188 +
 odb_api/src/odb_api/Header.h                       |    73 +
 odb_api/src/odb_api/InMemoryDataHandle.cc          |    35 +
 odb_api/src/odb_api/InMemoryDataHandle.h           |    81 +
 odb_api/src/odb_api/Indexer.cc                     |   123 +
 odb_api/src/odb_api/Indexer.h                      |    34 +
 odb_api/src/odb_api/IteratorFacade.h               |   184 +
 odb_api/src/odb_api/IteratorProxy.h                |   303 +
 odb_api/src/odb_api/MD5.cc                         |    35 +
 odb_api/src/odb_api/MD5.h                          |    41 +
 odb_api/src/odb_api/MDI.cc                         |    19 +
 odb_api/src/odb_api/MDI.h                          |    37 +
 odb_api/src/odb_api/MDSetter.cc                    |    13 +
 odb_api/src/odb_api/MDSetter.h                     |    44 +
 odb_api/src/odb_api/MDUpdatingIterator.cc          |   108 +
 odb_api/src/odb_api/MDUpdatingIterator.h           |    71 +
 odb_api/src/odb_api/MemoryBlock.cc                 |    57 +
 odb_api/src/odb_api/MemoryBlock.h                  |    53 +
 odb_api/src/odb_api/MetaData.cc                    |   273 +
 odb_api/src/odb_api/MetaData.h                     |   160 +
 odb_api/src/odb_api/MetaDataReader.cc              |    98 +
 odb_api/src/odb_api/MetaDataReader.h               |    75 +
 odb_api/src/odb_api/MetaDataReaderIterator.cc      |   269 +
 odb_api/src/odb_api/MetaDataReaderIterator.h       |   134 +
 odb_api/src/odb_api/NullColumn.cc                  |    43 +
 odb_api/src/odb_api/NullColumn.h                   |    51 +
 odb_api/src/odb_api/NumberExpression.cc            |    41 +
 odb_api/src/odb_api/NumberExpression.h             |    54 +
 odb_api/src/odb_api/ODAColumn.cc                   |    43 +
 odb_api/src/odb_api/ODAColumn.h                    |    51 +
 odb_api/src/odb_api/ODADatabase.cc                 |    97 +
 odb_api/src/odb_api/ODADatabase.h                  |    51 +
 odb_api/src/odb_api/ODAHandle.cc                   |    44 +
 odb_api/src/odb_api/ODAHandle.h                    |    61 +
 odb_api/src/odb_api/ODATranslator.h                |    70 +
 odb_api/src/odb_api/ODAUpdatingIterator.cc         |    97 +
 odb_api/src/odb_api/ODAUpdatingIterator.h          |    71 +
 odb_api/src/odb_api/ODBAPISettings.cc              |   157 +
 odb_api/src/odb_api/ODBAPISettings.h               |    59 +
 odb_api/src/odb_api/ODBAPIVersion.cc               |    29 +
 odb_api/src/odb_api/ODBAPIVersion.h                |    29 +
 odb_api/src/odb_api/ODBAPIVersionSHA1.cc.in        |    18 +
 odb_api/src/odb_api/ODBApplication.cc              |    47 +
 odb_api/src/odb_api/ODBApplication.h               |    45 +
 odb_api/src/odb_api/ODBModule.cc                   |    76 +
 odb_api/src/odb_api/ODBModule.h                    |    31 +
 odb_api/src/odb_api/ODBTarget.cc                   |    50 +
 odb_api/src/odb_api/ODBTarget.h                    |    45 +
 odb_api/src/odb_api/Odb2Hub.cc                     |    68 +
 odb_api/src/odb_api/Odb2Hub.h                      |    18 +
 odb_api/src/odb_api/OrderByExpressions.cc          |    62 +
 odb_api/src/odb_api/OrderByExpressions.h           |    45 +
 odb_api/src/odb_api/ParameterExpression.cc         |    61 +
 odb_api/src/odb_api/ParameterExpression.h          |    53 +
 odb_api/src/odb_api/Partition.cc                   |   241 +
 odb_api/src/odb_api/Partition.h                    |    62 +
 odb_api/src/odb_api/Partitioner.cc                 |    87 +
 odb_api/src/odb_api/Partitioner.h                  |    33 +
 odb_api/src/odb_api/Partitions.cc                  |   133 +
 odb_api/src/odb_api/Partitions.h                   |    45 +
 odb_api/src/odb_api/Reader.cc                      |   110 +
 odb_api/src/odb_api/Reader.h                       |    78 +
 odb_api/src/odb_api/ReaderIterator.cc              |   279 +
 odb_api/src/odb_api/ReaderIterator.h               |   139 +
 odb_api/src/odb_api/RegionCache.cc                 |   305 +
 odb_api/src/odb_api/RegionCache.h                  |   105 +
 odb_api/src/odb_api/Retriever.cc                   |   186 +
 odb_api/src/odb_api/Retriever.h                    |    37 +
 odb_api/src/odb_api/RggRegionCache.cc              |   340 +
 odb_api/src/odb_api/RggRegionCache.h               |    52 +
 odb_api/src/odb_api/RowsCounter.cc                 |    49 +
 odb_api/src/odb_api/RowsCounter.h                  |    29 +
 odb_api/src/odb_api/SQLAST.cc                      |    86 +
 odb_api/src/odb_api/SQLAST.h                       |   244 +
 odb_api/src/odb_api/SQLBit.cc                      |    44 +
 odb_api/src/odb_api/SQLBit.h                       |    54 +
 odb_api/src/odb_api/SQLBitColumn.cc                |    53 +
 odb_api/src/odb_api/SQLBitColumn.h                 |    50 +
 odb_api/src/odb_api/SQLBitfield.cc                 |   117 +
 odb_api/src/odb_api/SQLBitfield.h                  |    61 +
 odb_api/src/odb_api/SQLCallbackOutput.cc           |   148 +
 odb_api/src/odb_api/SQLCallbackOutput.h            |    61 +
 odb_api/src/odb_api/SQLColumn.cc                   |   165 +
 odb_api/src/odb_api/SQLColumn.h                    |   101 +
 odb_api/src/odb_api/SQLCreateTable.cc              |    98 +
 odb_api/src/odb_api/SQLCreateTable.h               |   117 +
 odb_api/src/odb_api/SQLDataColumn.cc               |    33 +
 odb_api/src/odb_api/SQLDataColumn.h                |    41 +
 odb_api/src/odb_api/SQLDataSet.cc                  |    44 +
 odb_api/src/odb_api/SQLDataSet.h                   |    33 +
 odb_api/src/odb_api/SQLDataTable.cc                |   155 +
 odb_api/src/odb_api/SQLDataTable.h                 |    51 +
 odb_api/src/odb_api/SQLDataTableIterator.cc        |    46 +
 odb_api/src/odb_api/SQLDataTableIterator.h         |    36 +
 odb_api/src/odb_api/SQLDatabase.cc                 |   362 +
 odb_api/src/odb_api/SQLDatabase.h                  |   116 +
 odb_api/src/odb_api/SQLDistinctOutput.cc           |    70 +
 odb_api/src/odb_api/SQLDistinctOutput.h            |    61 +
 odb_api/src/odb_api/SQLDouble.cc                   |    28 +
 odb_api/src/odb_api/SQLDouble.h                    |    52 +
 odb_api/src/odb_api/SQLEmbedded.cc                 |    41 +
 odb_api/src/odb_api/SQLEmbedded.h                  |    57 +
 odb_api/src/odb_api/SQLExpression.cc               |    95 +
 odb_api/src/odb_api/SQLExpression.h                |   114 +
 odb_api/src/odb_api/SQLExpressionEvaluated.cc      |    52 +
 odb_api/src/odb_api/SQLExpressionEvaluated.h       |    63 +
 odb_api/src/odb_api/SQLIndex.cc                    |   119 +
 odb_api/src/odb_api/SQLIndex.h                     |   115 +
 odb_api/src/odb_api/SQLInsert.cc                   |    43 +
 odb_api/src/odb_api/SQLInsert.h                    |    61 +
 odb_api/src/odb_api/SQLInsertFactory.cc            |    36 +
 odb_api/src/odb_api/SQLInsertFactory.h             |    49 +
 odb_api/src/odb_api/SQLInt.cc                      |    29 +
 odb_api/src/odb_api/SQLInt.h                       |    49 +
 odb_api/src/odb_api/SQLInteractiveSession.cc       |    70 +
 odb_api/src/odb_api/SQLInteractiveSession.h        |    51 +
 odb_api/src/odb_api/SQLIterator.h                  |    62 +
 odb_api/src/odb_api/SQLIteratorOutput.cc           |    67 +
 odb_api/src/odb_api/SQLIteratorOutput.h            |    74 +
 odb_api/src/odb_api/SQLIteratorSession.cc          |    74 +
 odb_api/src/odb_api/SQLIteratorSession.h           |    70 +
 odb_api/src/odb_api/SQLMATCHSubquerySession.cc     |    51 +
 odb_api/src/odb_api/SQLMATCHSubquerySession.h      |    55 +
 .../src/odb_api/SQLMATCHSubquerySessionOutput.cc   |    72 +
 .../src/odb_api/SQLMATCHSubquerySessionOutput.h    |    68 +
 odb_api/src/odb_api/SQLNonInteractiveSession.cc    |    54 +
 odb_api/src/odb_api/SQLNonInteractiveSession.h     |    45 +
 odb_api/src/odb_api/SQLODAOutput.cc                |   125 +
 odb_api/src/odb_api/SQLODAOutput.h                 |    69 +
 odb_api/src/odb_api/SQLOrderOutput.cc              |   116 +
 odb_api/src/odb_api/SQLOrderOutput.h               |    74 +
 odb_api/src/odb_api/SQLOutput.cc                   |    30 +
 odb_api/src/odb_api/SQLOutput.h                    |    75 +
 odb_api/src/odb_api/SQLOutputConfig.cc             |    95 +
 odb_api/src/odb_api/SQLOutputConfig.h              |    84 +
 odb_api/src/odb_api/SQLParser.cc                   |   174 +
 odb_api/src/odb_api/SQLParser.h                    |    61 +
 odb_api/src/odb_api/SQLReal.cc                     |    28 +
 odb_api/src/odb_api/SQLReal.h                      |   112 +
 odb_api/src/odb_api/SQLSelect.cc                   |   616 +
 odb_api/src/odb_api/SQLSelect.h                    |   138 +
 odb_api/src/odb_api/SQLSelectFactory.cc            |   357 +
 odb_api/src/odb_api/SQLSelectFactory.h             |   103 +
 odb_api/src/odb_api/SQLSession.cc                  |   231 +
 odb_api/src/odb_api/SQLSession.h                   |   117 +
 odb_api/src/odb_api/SQLSimpleOutput.cc             |   162 +
 odb_api/src/odb_api/SQLSimpleOutput.h              |    67 +
 odb_api/src/odb_api/SQLStatement.cc                |    23 +
 odb_api/src/odb_api/SQLStatement.h                 |    51 +
 odb_api/src/odb_api/SQLString.cc                   |    32 +
 odb_api/src/odb_api/SQLString.h                    |    52 +
 odb_api/src/odb_api/SQLTable.cc                    |   236 +
 odb_api/src/odb_api/SQLTable.h                     |   120 +
 odb_api/src/odb_api/SQLType.cc                     |   111 +
 odb_api/src/odb_api/SQLType.h                      |    87 +
 odb_api/src/odb_api/SchemaAnalyzer.cc              |   248 +
 odb_api/src/odb_api/SchemaAnalyzer.h               |    61 +
 odb_api/src/odb_api/Select.cc                      |   185 +
 odb_api/src/odb_api/Select.h                       |    88 +
 odb_api/src/odb_api/SelectIterator.cc              |   232 +
 odb_api/src/odb_api/SelectIterator.h               |   103 +
 odb_api/src/odb_api/SelectOneTable.cc              |    32 +
 odb_api/src/odb_api/SelectOneTable.h               |    59 +
 odb_api/src/odb_api/SharedIterator.h               |   177 +
 odb_api/src/odb_api/ShiftedBitColumnExpression.cc  |    28 +
 odb_api/src/odb_api/ShiftedBitColumnExpression.h   |    30 +
 odb_api/src/odb_api/ShiftedColumnExpression.cc     |   138 +
 odb_api/src/odb_api/ShiftedColumnExpression.h      |    74 +
 odb_api/src/odb_api/Stack.cc                       |    52 +
 odb_api/src/odb_api/Stack.h                        |    49 +
 odb_api/src/odb_api/Stager.cc                      |   157 +
 odb_api/src/odb_api/Stager.h                       |    39 +
 odb_api/src/odb_api/StringExpression.cc            |   100 +
 odb_api/src/odb_api/StringExpression.h             |    55 +
 odb_api/src/odb_api/StringTool.cc                  |   215 +
 odb_api/src/odb_api/StringTool.h                   |    70 +
 odb_api/src/odb_api/TODATable.cc                   |   231 +
 odb_api/src/odb_api/TODATable.h                    |    71 +
 odb_api/src/odb_api/TODATableIterator.cc           |    87 +
 odb_api/src/odb_api/TODATableIterator.h            |    47 +
 odb_api/src/odb_api/TReadOnlyMemoryDataHandle.cc   |    16 +
 odb_api/src/odb_api/TReadOnlyMemoryDataHandle.h    |   104 +
 odb_api/src/odb_api/TemplateParameters.cc          |    68 +
 odb_api/src/odb_api/TemplateParameters.h           |    65 +
 odb_api/src/odb_api/TextReader.cc                  |    95 +
 odb_api/src/odb_api/TextReader.h                   |    74 +
 odb_api/src/odb_api/TextReaderIterator.cc          |   230 +
 odb_api/src/odb_api/TextReaderIterator.h           |    92 +
 odb_api/src/odb_api/Tracer.cc                      |    24 +
 odb_api/src/odb_api/Tracer.h                       |    27 +
 odb_api/src/odb_api/Types.h                        |    23 +
 odb_api/src/odb_api/UnsafeInMemoryDataHandle.h     |    98 +
 odb_api/src/odb_api/VariableExpression.cc          |    62 +
 odb_api/src/odb_api/VariableExpression.h           |    54 +
 odb_api/src/odb_api/VariablesTable.cc              |    98 +
 odb_api/src/odb_api/VariablesTable.h               |    65 +
 odb_api/src/odb_api/Writer.cc                      |   130 +
 odb_api/src/odb_api/Writer.h                       |    69 +
 odb_api/src/odb_api/WriterBufferingIterator.cc     |   330 +
 odb_api/src/odb_api/WriterBufferingIterator.h      |   198 +
 odb_api/src/odb_api/WriterDispatchingIterator.cc   |   515 +
 odb_api/src/odb_api/WriterDispatchingIterator.h    |   136 +
 odb_api/src/odb_api/calc.sh                        |    11 +
 .../src/odb_api/ecml_data/LocalHandleFactory.cc    |    99 +
 odb_api/src/odb_api/ecml_data/LocalHandleFactory.h |    26 +
 odb_api/src/odb_api/ecml_data/Matrix.cc            |    41 +
 odb_api/src/odb_api/ecml_data/Matrix.h             |    53 +
 odb_api/src/odb_api/ecml_data/ResultSet.cc         |    36 +
 odb_api/src/odb_api/ecml_data/ResultSet.h          |    51 +
 odb_api/src/odb_api/ecml_data/ResultSetStore.cc    |    41 +
 odb_api/src/odb_api/ecml_data/ResultSetStore.h     |    52 +
 odb_api/src/odb_api/ecml_verbs/ArchiveHandler.cc   |   149 +
 odb_api/src/odb_api/ecml_verbs/ArchiveHandler.h    |    35 +
 odb_api/src/odb_api/ecml_verbs/ChunkHandler.cc     |    69 +
 odb_api/src/odb_api/ecml_verbs/ChunkHandler.h      |    30 +
 odb_api/src/odb_api/ecml_verbs/CompareHandler.cc   |    61 +
 odb_api/src/odb_api/ecml_verbs/CompareHandler.h    |    30 +
 .../src/odb_api/ecml_verbs/CreateIndexHandler.cc   |    54 +
 .../src/odb_api/ecml_verbs/CreateIndexHandler.h    |    30 +
 .../odb_api/ecml_verbs/CreatePartitionsHandler.cc  |    86 +
 .../odb_api/ecml_verbs/CreatePartitionsHandler.h   |    30 +
 .../src/odb_api/ecml_verbs/ImportTextHandler.cc    |    81 +
 odb_api/src/odb_api/ecml_verbs/ImportTextHandler.h |    34 +
 odb_api/src/odb_api/ecml_verbs/RetrieveHandler.cc  |   114 +
 odb_api/src/odb_api/ecml_verbs/RetrieveHandler.h   |    39 +
 odb_api/src/odb_api/ecml_verbs/SQLHandler.cc       |   151 +
 odb_api/src/odb_api/ecml_verbs/SQLHandler.h        |    35 +
 odb_api/src/odb_api/ecml_verbs/SQLTestHandler.cc   |   121 +
 odb_api/src/odb_api/ecml_verbs/SQLTestHandler.h    |    36 +
 odb_api/src/odb_api/ecml_verbs/StageHandler.cc     |   104 +
 odb_api/src/odb_api/ecml_verbs/StageHandler.h      |    34 +
 .../ecml_verbs/tests/odb_server_management.ecml    |    43 +
 .../src/odb_api/ecml_verbs/tests/odbgov_defs.ecml  |   415 +
 .../ecml_verbs/tests/split_and_archive.ecml        |    42 +
 .../src/odb_api/ecml_verbs/tests/start_server.ecml |     2 +
 .../src/odb_api/ecml_verbs/tests/stop_server.ecml  |     2 +
 .../src/odb_api/ecml_verbs/tests/test_chunk.ecml   |    17 +
 .../src/odb_api/ecml_verbs/tests/test_chunk2.ecml  |    26 +
 .../ecml_verbs/tests/test_client_lib_cpp.cc        |    69 +
 .../ecml_verbs/tests/test_client_lib_cpp.ecml      |     7 +
 .../ecml_verbs/tests/test_client_lib_fortran.ecml  |     6 +
 .../ecml_verbs/tests/test_client_lib_fortran.f90   |    91 +
 .../tests/test_client_lib_fortran_local.ecml       |     6 +
 .../test_client_lib_fortran_server_side_ecml.f90   |    95 +
 .../ecml_verbs/tests/test_cope_archiving.ecml      |    16 +
 .../ecml_verbs/tests/test_create_partitions.ecml   |    35 +
 .../ecml_verbs/tests/test_ec_archiving.ecml        |    61 +
 .../tests/test_embedded_ecml_in_from_clause.ecml   |     9 +
 .../odb_api/ecml_verbs/tests/test_local_stage.ecml |    76 +
 .../ecml_verbs/tests/test_mo_archiving.ecml        |    86 +
 .../ecml_verbs/tests/test_multithreaded_sql.ecml   |    15 +
 .../ecml_verbs/tests/test_odb_governance.ecml      |    59 +
 .../ecml_verbs/tests/test_parallel_sql.ecml        |    42 +
 .../tests/test_server_side_processing.ecml         |    59 +
 .../odb_api/ecml_verbs/tests/test_sql_like.ecml    |    17 +
 .../ecml_verbs/tests/test_sql_match_in.ecml        |    30 +
 .../ecml_verbs/tests/test_sql_splitting.ecml       |    10 +
 .../ecml_verbs/tests/test_sql_variables.ecml       |    15 +
 .../src/odb_api/ecml_verbs/tests/test_stage.ecml   |    72 +
 .../ecml_verbs/tests/test_temporary_file.ecml      |     6 +
 odb_api/src/odb_api/fwrap.py                       |   410 +
 odb_api/src/odb_api/md5_hash.c                     |   306 +
 odb_api/src/odb_api/md5_hash.h                     |    44 +
 odb_api/src/odb_api/migrator/2oda                  |    49 +
 odb_api/src/odb_api/migrator/CMakeLists.txt        |    84 +
 odb_api/src/odb_api/migrator/FakeODBIterator.cc    |   160 +
 odb_api/src/odb_api/migrator/FakeODBIterator.h     |    96 +
 odb_api/src/odb_api/migrator/ImportODBTool.cc      |   207 +
 odb_api/src/odb_api/migrator/ImportODBTool.h       |    66 +
 odb_api/src/odb_api/migrator/Makefile              |     2 +
 odb_api/src/odb_api/migrator/Makefile.old          |    60 +
 odb_api/src/odb_api/migrator/MigrateHandler.cc     |    80 +
 odb_api/src/odb_api/migrator/MigrateHandler.h      |    30 +
 odb_api/src/odb_api/migrator/MigratorTool.cc       |   144 +
 odb_api/src/odb_api/migrator/MigratorTool.h        |    39 +
 odb_api/src/odb_api/migrator/ODB2ODATool.cc        |   139 +
 odb_api/src/odb_api/migrator/ODB2ODATool.h         |    37 +
 odb_api/src/odb_api/migrator/ODBIterator.cc        |   250 +
 odb_api/src/odb_api/migrator/ODBIterator.h         |    86 +
 odb_api/src/odb_api/migrator/ODBMigratorModule.cc  |    41 +
 odb_api/src/odb_api/migrator/ODBMigratorModule.h   |    31 +
 odb_api/src/odb_api/migrator/Odb2_to_odb1_era.f90  |   432 +
 odb_api/src/odb_api/migrator/OldODBReader.h        |    25 +
 odb_api/src/odb_api/migrator/ReptypeGenIterator.cc |   234 +
 odb_api/src/odb_api/migrator/ReptypeGenIterator.h  |    91 +
 odb_api/src/odb_api/migrator/TSQLReader.cc         |    14 +
 odb_api/src/odb_api/migrator/TSQLReader.h          |    60 +
 odb_api/src/odb_api/migrator/all                   |    36 +
 odb_api/src/odb_api/migrator/diurnal.f90           |    76 +
 odb_api/src/odb_api/migrator/migrator_api.cc       |    38 +
 odb_api/src/odb_api/migrator/migrator_api.h        |    22 +
 odb_api/src/odb_api/migrator/odb1.f90              |   561 +
 odb_api/src/odb_api/migrator/odb2.f90              |   506 +
 .../src/odb_api/migrator/odb2_flag_definitions.f90 |   264 +
 odb_api/src/odb_api/migrator/odb2oda.cc            |    66 +
 odb_api/src/odb_api/migrator/odbdump.h             |    45 +
 odb_api/src/odb_api/migrator/pyodbdump.i           |    92 +
 odb_api/src/odb_api/migrator/pyodbdump_example.py  |    12 +
 odb_api/src/odb_api/migrator/solar_elevation.f90   |    84 +
 odb_api/src/odb_api/migrator/solar_elevation.sc    |    26 +
 odb_api/src/odb_api/migrator/test_migrator.ecml    |    27 +
 odb_api/src/odb_api/odb2_to_odb1/CMakeLists.txt    |    19 +
 odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.cc       |   269 +
 odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.h        |   111 +
 odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1Main.cc   |    24 +
 odb_api/src/odb_api/odb2_to_odb1/mpi_wrapper.F90   |    70 +
 odb_api/src/odb_api/odb2_to_odb1/mpif.h            |   221 +
 odb_api/src/odb_api/odb2_to_odb1/odb_wrapper.F90   |   753 +
 odb_api/src/odb_api/odb2_to_odb1/odbi.F90          |   286 +
 odb_api/src/odb_api/odb2netcdf/CMakeLists.txt      |    35 +
 odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.cc       |   318 +
 odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.h        |    43 +
 odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.cc |    36 +
 odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.h  |    31 +
 .../odb2netcdf/ecml_verbs/Odb2NetcdfHandler.cc     |    62 +
 .../odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h      |    26 +
 odb_api/src/odb_api/odb2netcdf/odb2netcdf_main.cc  |    80 +
 odb_api/src/odb_api/odb_api.h                      |    46 +
 odb_api/src/odb_api/odbcapi.cc                     |   481 +
 odb_api/src/odb_api/odbcapi.h                      |   111 +
 odb_api/src/odb_api/odblib_lex.h                   |    39 +
 odb_api/src/odb_api/odbql.cc                       |   757 +
 odb_api/src/odb_api/odbql.h                        | 10193 ++++++++++
 odb_api/src/odb_api/piconst.h                      |    29 +
 odb_api/src/odb_api/pyodbapi.h                     |    19 +
 odb_api/src/odb_api/sqll.l                         |   269 +
 odb_api/src/odb_api/sqly.y                         |   768 +
 odb_api/src/odb_api/test.py                        |    58 +
 odb_api/src/odb_api/tools/CAPIExamples.cc          |   184 +
 odb_api/src/odb_api/tools/CMakeLists.txt           |   145 +
 odb_api/src/odb_api/tools/CompactTool.cc           |    62 +
 odb_api/src/odb_api/tools/CompactTool.h            |    42 +
 odb_api/src/odb_api/tools/CompareTool.cc           |    75 +
 odb_api/src/odb_api/tools/CompareTool.h            |    57 +
 odb_api/src/odb_api/tools/CountTool.cc             |    76 +
 odb_api/src/odb_api/tools/CountTool.h              |    47 +
 odb_api/src/odb_api/tools/DefineFunctionHandler.cc |    61 +
 odb_api/src/odb_api/tools/DefineFunctionHandler.h  |    34 +
 odb_api/src/odb_api/tools/ECMLTool.cc              |    76 +
 odb_api/src/odb_api/tools/ECMLTool.h               |    43 +
 odb_api/src/odb_api/tools/Examples.cc              |   222 +
 odb_api/src/odb_api/tools/FixedSizeRowTool.cc      |    58 +
 odb_api/src/odb_api/tools/FixedSizeRowTool.h       |    43 +
 odb_api/src/odb_api/tools/ImportTool.cc            |    98 +
 odb_api/src/odb_api/tools/ImportTool.h             |    49 +
 odb_api/src/odb_api/tools/IndexTool.cc             |    51 +
 odb_api/src/odb_api/tools/IndexTool.h              |    50 +
 odb_api/src/odb_api/tools/LSTool.cc                |    96 +
 odb_api/src/odb_api/tools/LSTool.h                 |    44 +
 odb_api/src/odb_api/tools/LetHandler.cc            |    54 +
 odb_api/src/odb_api/tools/LetHandler.h             |    35 +
 odb_api/src/odb_api/tools/ListHandler.cc           |    27 +
 odb_api/src/odb_api/tools/ListHandler.h            |    26 +
 odb_api/src/odb_api/tools/MDSetTool.cc             |   144 +
 odb_api/src/odb_api/tools/MDSetTool.h              |    64 +
 odb_api/src/odb_api/tools/Makefile                 |     2 +
 odb_api/src/odb_api/tools/MergeTool.cc             |   151 +
 odb_api/src/odb_api/tools/MergeTool.h              |    59 +
 odb_api/src/odb_api/tools/MockReader.cc            |    12 +
 odb_api/src/odb_api/tools/MockReader.h             |    36 +
 odb_api/src/odb_api/tools/ODA2RequestTool.cc       |   235 +
 odb_api/src/odb_api/tools/ODA2RequestTool.h        |    56 +
 odb_api/src/odb_api/tools/ODAHeaderTool.cc         |   194 +
 odb_api/src/odb_api/tools/ODAHeaderTool.h          |    45 +
 odb_api/src/odb_api/tools/PrintHandler.cc          |    41 +
 odb_api/src/odb_api/tools/PrintHandler.h           |    28 +
 odb_api/src/odb_api/tools/SQLTool.cc               |   148 +
 odb_api/src/odb_api/tools/SQLTool.h                |    78 +
 odb_api/src/odb_api/tools/SetTool.cc               |   110 +
 odb_api/src/odb_api/tools/SetTool.h                |    44 +
 odb_api/src/odb_api/tools/SplitTool.cc             |   157 +
 odb_api/src/odb_api/tools/SplitTool.h              |    55 +
 odb_api/src/odb_api/tools/TestAAAImportODB.cc      |    53 +
 .../odb_api/tools/TestAAAImportODBDispatching.cc   |    38 +
 .../src/odb_api/tools/TestAggregateFunctions.cc    |    70 +
 .../src/odb_api/tools/TestAggregateFunctions.sql   |    37 +
 .../src/odb_api/tools/TestAggregateFunctions2.cc   |    70 +
 .../src/odb_api/tools/TestAggregateFunctions3.cc   |    50 +
 .../src/odb_api/tools/TestAtTableInTheOutput.cc    |    99 +
 odb_api/src/odb_api/tools/TestBitfields.cc         |    67 +
 odb_api/src/odb_api/tools/TestCase.cc              |    48 +
 odb_api/src/odb_api/tools/TestCase.h               |    96 +
 odb_api/src/odb_api/tools/TestCatFiles.cc          |   173 +
 odb_api/src/odb_api/tools/TestCodec.cc             |    90 +
 odb_api/src/odb_api/tools/TestCodecOptimization.cc |    76 +
 .../src/odb_api/tools/TestCommandLineParsing.cc    |    65 +
 odb_api/src/odb_api/tools/TestConstCodec.cc        |   128 +
 odb_api/src/odb_api/tools/TestConstIntegerCodec.cc |    96 +
 odb_api/src/odb_api/tools/TestDataJoin.cc          |   188 +
 odb_api/src/odb_api/tools/TestDataLink.cc          |   209 +
 odb_api/src/odb_api/tools/TestDataLoader.cc        |   226 +
 odb_api/src/odb_api/tools/TestDataPage.cc          |   409 +
 odb_api/src/odb_api/tools/TestDataRow.cc           |   164 +
 odb_api/src/odb_api/tools/TestDataSelect.cc        |   207 +
 odb_api/src/odb_api/tools/TestDataSet.cc           |    41 +
 odb_api/src/odb_api/tools/TestDataTable.cc         |   307 +
 odb_api/src/odb_api/tools/TestDecoding.cc          |    46 +
 odb_api/src/odb_api/tools/TestDispatchingWriter.cc |    59 +
 odb_api/src/odb_api/tools/TestDistinct.cc          |    55 +
 odb_api/src/odb_api/tools/TestFastODA2Request.cc   |    56 +
 odb_api/src/odb_api/tools/TestFastODA2Request2.cc  |   130 +
 odb_api/src/odb_api/tools/TestFastODA2Request3.cc  |    68 +
 odb_api/src/odb_api/tools/TestFunctionCircle.cc    |    69 +
 .../src/odb_api/tools/TestFunctionDateAndTime.cc   |    76 +
 odb_api/src/odb_api/tools/TestFunctionDistance.cc  |    79 +
 odb_api/src/odb_api/tools/TestFunctionDotp.cc      |    75 +
 odb_api/src/odb_api/tools/TestFunctionEqBox.cc     |    70 +
 odb_api/src/odb_api/tools/TestFunctionNorm.cc      |    74 +
 odb_api/src/odb_api/tools/TestFunctionRggBox.cc    |    73 +
 odb_api/src/odb_api/tools/TestFunctionTdiff.cc     |    68 +
 odb_api/src/odb_api/tools/TestFunctionThin.cc      |    74 +
 .../odb_api/tools/TestFunctionTypeConversion.cc    |    68 +
 .../tools/TestFunctionsForAngleConversion.cc       |   102 +
 .../tools/TestFunctionsForTemperatureConversion.cc |    74 +
 .../src/odb_api/tools/TestInMemoryDataHandle.cc    |    51 +
 .../src/odb_api/tools/TestInt16_MissingCodec.cc    |   134 +
 odb_api/src/odb_api/tools/TestIntegerValues.cc     |    87 +
 odb_api/src/odb_api/tools/TestMetaData.cc          |    56 +
 odb_api/src/odb_api/tools/TestMetaDataReader.cc    |    44 +
 odb_api/src/odb_api/tools/TestMetaDataReader.ksh   |    46 +
 odb_api/src/odb_api/tools/TestMinMax.cc            |    86 +
 odb_api/src/odb_api/tools/TestMissingValue.cc      |   145 +
 odb_api/src/odb_api/tools/TestODBModule.cc         |    53 +
 odb_api/src/odb_api/tools/TestOdaCAPI.cc           |   261 +
 odb_api/src/odb_api/tools/TestOdaCAPI.h            |    30 +
 odb_api/src/odb_api/tools/TestOrderBy.cc           |   135 +
 odb_api/src/odb_api/tools/TestRunner.cc            |   202 +
 odb_api/src/odb_api/tools/TestRunner.h             |    58 +
 odb_api/src/odb_api/tools/TestRunnerApplication.cc |    42 +
 .../src/odb_api/tools/TestRunnerApplication.cfg    |    13 +
 odb_api/src/odb_api/tools/TestRunnerApplication.h  |    36 +
 odb_api/src/odb_api/tools/TestSQLFunctionsInfo.cc  |    44 +
 odb_api/src/odb_api/tools/TestSelectDataHandle.cc  |    78 +
 odb_api/src/odb_api/tools/TestSelectIterator.cc    |   219 +
 odb_api/src/odb_api/tools/TestSelectIterator2.cc   |    70 +
 odb_api/src/odb_api/tools/TestSelectIterator3.cc   |    70 +
 odb_api/src/odb_api/tools/TestSelectStarAt.cc      |    58 +
 odb_api/src/odb_api/tools/TestSelectTwoFiles.cc    |    72 +
 odb_api/src/odb_api/tools/TestSetvbuffer.cc        |    77 +
 odb_api/src/odb_api/tools/TestStar.cc              |    42 +
 odb_api/src/odb_api/tools/TestTEMPLATE.cc          |    37 +
 odb_api/src/odb_api/tools/TestTextSelect.cc        |    83 +
 odb_api/src/odb_api/tools/TestTextSelect.txt       |    16 +
 odb_api/src/odb_api/tools/TestTextSelect2.cc       |   101 +
 odb_api/src/odb_api/tools/TestTextSelect2.txt      |    16 +
 odb_api/src/odb_api/tools/TestTextSelect3.cc       |    87 +
 odb_api/src/odb_api/tools/TestWriteCatFiles.cc     |   107 +
 odb_api/src/odb_api/tools/Tool.cc                  |    69 +
 odb_api/src/odb_api/tools/Tool.h                   |    55 +
 odb_api/src/odb_api/tools/ToolFactory.cc           |   171 +
 odb_api/src/odb_api/tools/ToolFactory.h            |    65 +
 odb_api/src/odb_api/tools/ToolRunnerApplication.cc |    79 +
 odb_api/src/odb_api/tools/ToolRunnerApplication.h  |    43 +
 odb_api/src/odb_api/tools/UnitTests.cc             |  1126 ++
 odb_api/src/odb_api/tools/VariableLookupHandler.cc |    35 +
 odb_api/src/odb_api/tools/VariableLookupHandler.h  |    26 +
 odb_api/src/odb_api/tools/XYVTool.cc               |    64 +
 odb_api/src/odb_api/tools/XYVTool.h                |    40 +
 odb_api/src/odb_api/tools/odb.cc                   |   170 +
 odb_api/src/odb_api/tools/odb_api_tools_c.cc       |    46 +
 odb_api/src/odb_api/tools/odb_api_tools_c.h        |    35 +
 odb_api/src/odb_api/tools/test.ksh                 |   109 +
 odb_api/src/odb_api/tools/xxx                      |     6 +
 odb_api/src/odb_api_config.h.in                    |    16 +
 odb_api/src/python/CMakeLists.txt                  |    46 +
 .../src/python/legacy_odb_api_python_examples.py   |    57 +
 odb_api/src/python/legacy_test_python_odb_api.py   |    57 +
 odb_api/src/python/odb/__init__.py                 |     2 +
 odb_api/src/python/odb/odb.py                      |    70 +
 odb_api/src/python/odb/odbql.py                    |   422 +
 odb_api/src/python/odb/pyodbapi.i                  |   119 +
 odb_api/src/python/odb269.py                       |    23 +
 odb_api/src/python/odbless.py                      |    41 +
 odb_api/src/python/psql.py                         |    59 +
 odb_api/src/python/setup.py.in                     |    24 +
 odb_api/src/python/test_python_odb_api.py          |   392 +
 odb_api/src/python/to_sqlite.py                    |    60 +
 odb_api/tests/CMakeLists.txt                       |   458 +
 odb_api/tests/dhshome/etc/config/local             |    25 +
 odb_api/tests/dhshome/etc/disks/transfer           |     1 +
 odb_api/tests/dhshome/etc/marsLimits               |     1 +
 odb_api/tests/dhshome/etc/marsPermissions          |     1 +
 odb_api/tests/dhshome/etc/marsPriorities           |     1 +
 odb_api/tests/dhshome/scripts/prestage.py          |   100 +
 odb_api/tests/test_odb2_to_odb1.sh                 |    31 +
 odb_api/tests/test_odb2netcdf_1d.sh                |    13 +
 odb_api/tests/test_odb2netcdf_2d.sh                |    13 +
 pkgpy.py                                           |    84 +
 python_package/pkgpy.py                            |   141 +
 share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake     |    49 +
 share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake      |    52 +
 share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake    |    73 +
 share/ecbuild/toolchains/ichec-fionn-Intel.cmake   |    67 +
 2455 files changed, 272793 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..08014df
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_CURRENT_SOURCE_DIR}/ecbuild/cmake;${CMAKE_MODULE_PATH}" )
+include( ecbuild_bundle )
+
+project( odb_api_bundle C CXX )
+
+ecbuild_bundle_initialize()
+ecbuild_bundle( PROJECT ecbuild  STASH "ecsdk/ecbuild"    BRANCH  2.7.0 )
+ecbuild_bundle( PROJECT eckit    STASH "ecsdk/eckit"      BRANCH  0.16.3 )
+ecbuild_bundle( PROJECT metkit   STASH "ecsdk/metkit"     BRANCH  0.5.2 )
+ecbuild_bundle( PROJECT odb_api  STASH "odb/odb_api"      BRANCH  0.17.0 )
+ecbuild_bundle_finalize()
+
diff --git a/VERSION.cmake b/VERSION.cmake
new file mode 100644
index 0000000..be758bf
--- /dev/null
+++ b/VERSION.cmake
@@ -0,0 +1 @@
+set(${PROJECT_NAME}_VERSION_STR "0.17.0")
diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt
new file mode 100644
index 0000000..93cef4e
--- /dev/null
+++ b/bin/CMakeLists.txt
@@ -0,0 +1 @@
+install( PROGRAMS ecbuild DESTINATION ${INSTALL_BIN_DIR} )
diff --git a/bin/ecbuild b/bin/ecbuild
new file mode 100755
index 0000000..22ae01f
--- /dev/null
+++ b/bin/ecbuild
@@ -0,0 +1,446 @@
+#!/bin/bash --noprofile
+
+set -eua
+
+CMAKE_MIN_REQUIRED=2.8.11
+CMAKE_BUILD_VERSION=3.5.2
+
+usage()
+{
+  echo "Usage: ecbuild [--help] [--version]"
+  exit $1
+}
+
+help()
+{
+    cat <<EOF
+USAGE:
+
+  ecbuild [--help] [--version] [--toolchains]
+  ecbuild [option...] [--] [cmake-argument...] <path-to-source>
+  ecbuild [option...] [--] [cmake-argument...] <path-to-existing-build>
+
+DESCRIPTION:
+
+  ecbuild is a build system based on CMake, but providing a lot of macro's
+  to make it easier to work with. Upon execution,
+  the equivalent cmake command is printed.
+
+  ecbuild/cmake must be called from an out-of-source build directory and
+  forbids in-source builds.
+
+SYNOPSIS:
+
+    --help         Display this help
+    --version      Display ecbuild version
+    --toolchains   Display list of pre-installed toolchains (see below)
+
+
+Available values for "option":
+
+    --cmakebin=<path>
+          Set which cmake binary to use. Default is 'cmake'
+
+    --prefix=<prefix>
+          Set the install path to <prefix>.
+          Equivalent to cmake argument "-DCMAKE_INSTALL_PREFIX=<prefix>"
+
+    --build=<build-type>
+          Set the build-type to <build-type>.
+          Equivalent to cmake argument "-DCMAKE_BUILD_TYPE=<build-type>"
+          <build-type> can be any of:
+             - debug : Lowest optimization level, useful for debugging
+             - release : Highest optimization level, for best performance
+             - bit : Highest optimization level while staying bit-reproducible
+             - ...others depending on project
+
+    --log=<log-level>
+          Set the ecbuild log-level
+          Equivalent to "-DECBUILD_LOG_LEVEL=<log-level>"
+          <log-level> can be any of:
+             - DEBUG
+             - INFO
+             - WARN
+             - ERROR
+             - CRITICAL
+             - OFF
+          Every choice outputs also the log-levels listed below itself
+
+    --static
+          Build static libraries.
+          Equivalent to "-DBUILD_SHARED_LIBS=OFF"
+
+    --dynamic, --shared
+          Build dynamic libraries (usually the default).
+          Equivalent to "-DBUILD_SHARED_LIBS=ON"
+
+    --config=<config>
+          Configuration file using CMake syntax that gets included
+          Equivalent to cmake argument "-DECBUILD_CONFIG=<config-file>"
+
+    --toolchain=<toolchain>
+          Use a platform specific toolchain, containing settings such
+          as compilation flags, locations of commonly used dependencies.
+          <toolchain> can be the path to a custom toolchain file, or a
+          pre-installed toolchain provided with ecbuild. For a list of
+          pre-installed toolchains, run "ecbuild --toolchains".
+          Equivalent to cmake argument "-DCMAKE_TOOLCHAIN_FILE=<toolchain-file>"
+
+    --cache=<ecbuild-cache-file>    (advanced)
+          A file called "ecbuild-cache.cmake" is generated during configuration.
+          This file can be moved to a safe location, and specified for future
+          builds to speed up checking of compiler/platform capabilities. Note
+          that this is only accelerating fresh builds, as cmake internally
+          caches also. Therefore this option is *not* recommended.
+
+    --build-cmake[=<prefix>]
+          Automatically download and build CMake version $CMAKE_BUILD_VERSION.
+          Requires an internet connection and may take a while. If no prefix
+          is given, install into $PWD.
+
+    --dryrun
+          Don't actually execute the cmake call, just print what would have
+          been executed.
+
+
+Available values for "cmake-argument":
+
+    Any value that can be usually passed to cmake to (re)configure the build.
+    Typically these values start with "-D".
+        example:  -DENABLE_TESTS=ON  -DENABLE_MPI=OFF  -DECKIT_PATH=...
+
+    They can be explicitly separated from [option...] with a "--", for the case
+    there is a conflicting option with the "cmake" executable, and the latter's
+    option is requested.
+
+------------------------------------------------------------------------
+
+NOTE: When reconfiguring a build, it is only necessary to change the relevant
+options, as everything stays cached. For example:
+  > ecbuild --prefix=PREFIX .
+  > ecbuild -DENABLE_TESTS=ON .
+
+------------------------------------------------------------------------
+
+Compiling:
+
+  To compile the project with <N> threads:
+    > make -j<N>
+
+  To get verbose compilation/linking output:
+    > make VERBOSE=1
+
+Testing:
+
+  To run the project's tests
+    > ctest
+
+  Also check the ctest manual/help for more options on running tests
+
+Installing:
+
+  To install the project in location PREFIX with
+       "--prefix=PREFIX" or
+       "-DCMAKE_INSTALL_PREFIX=PREFIX"
+    > make install
+
+------------------------------------------------------------------------
+ECMWF"
+
+EOF
+    exit $1
+}
+
+INSTALL_DIR="$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd -P )"
+ECBUILD_MODULE_PATH=""
+# If there is a directory share/ecbuild/cmake relative to the parent directory
+# (as in an install tree), add it to CMAKE_MODULE_PATH
+if [ -d $INSTALL_DIR/../share/ecbuild/cmake ]; then
+  ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../share/ecbuild/cmake" && pwd -P )"
+# If there is a cmake subdirectory relative to the script directory (as in a
+# tarball), add it to CMAKE_MODULE_PATH
+elif [ -d $INSTALL_DIR/../cmake ]; then
+  ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../cmake" && pwd -P )"
+fi
+
+# Fail if we couldn't find ecBuild modules
+if [ ! -f "$ECBUILD_MODULE_PATH/VERSION.cmake" ]; then
+  echo "FATAL: ecBuild modules could not be found in either $INSTALL_DIR/../share/ecbuild/cmake or $INSTALL_DIR/../cmake" >&2
+  exit 1
+fi
+
+ADD_ECBUILD_OPTIONS="-DCMAKE_MODULE_PATH=$ECBUILD_MODULE_PATH"
+
+if [ -d $INSTALL_DIR/../share/ecbuild/toolchains ]; then
+  ECBUILD_TOOLCHAIN_DIR="$( cd "$INSTALL_DIR/../share/ecbuild/toolchains" && pwd -P )"
+elif [ -d $INSTALL_DIR/share/ecbuild/toolchains ]; then
+  ECBUILD_TOOLCHAIN_DIR="$( cd "$INSTALL_DIR/share/ecbuild/toolchains" && pwd -P )"
+fi
+
+version()
+{
+  ecbuild_version=$(cat ${ECBUILD_MODULE_PATH}/VERSION.cmake | grep ECBUILD_VERSION_STR |  perl -p -e 's/.*([\d]\.[\d]\.[\d]).*/\1/' )
+  echo "ecbuild version ${ecbuild_version}"
+  command -v cmake >/dev/null 2>&1 || { exit 0; }
+  cmake --version | head -1
+  exit 0
+}
+
+log()
+{
+  log_level=$(tr "[a-z]" "[A-Z]" <<< "$1")
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_LOG_LEVEL=${log_level}"
+}
+
+toolchains()
+{
+  if [ -d $ECBUILD_TOOLCHAIN_DIR ]; then
+    cd $ECBUILD_TOOLCHAIN_DIR
+    echo "Available toolchains:"
+    ls | while read fname
+    do
+        echo "  - ${fname%%.*}"
+    done
+    exit 0
+  else
+    echo "No toolchains available."
+    exit 1
+  fi
+}
+
+prefix()
+{
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_INSTALL_PREFIX=${1/#\~\//$HOME/}"
+}
+
+config()
+{
+  arg=${1/#\~\//$HOME/}
+  if [ -f $arg ]; then
+    config_file=$arg
+    config_file="$( cd $( dirname "${config_file}" ) && pwd -P )/$( basename ${config_file} )"
+  else
+    echo "Error:"
+    echo "   Config file [$arg] is not found or is not a file."
+    exit 1
+  fi
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CONFIG=${config_file}"
+}
+
+toolchain()
+{
+  arg=${1/#\~\//$HOME/}
+  if [ -f $arg ]; then
+    toolchain_file=$arg
+  else
+    if [ -f $ECBUILD_TOOLCHAIN_DIR/$arg.cmake ]; then
+      toolchain_file=$ECBUILD_TOOLCHAIN_DIR/$arg.cmake
+    fi
+  fi
+  if [ -z ${toolchain_file+x} ]; then
+    echo "Error:"
+    echo "   Toolchain [$arg] is not valid: [$arg.cmake] cannot be"
+    echo "   found in [$ECBUILD_TOOLCHAIN_DIR]"
+    exit 1
+  else
+    ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${toolchain_file}"
+  fi
+}
+
+cache()
+{
+  arg=$1
+  if [ -f $arg ]; then
+    cache_file=$arg
+    cache_file="$( cd $( dirname "${cache_file}" ) && pwd -P )/$( basename ${cache_file} )"
+  else
+    echo "Error:"
+    echo "   Cache file [$arg] is not found or is not a file."
+    exit 1
+  fi
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CACHE=${cache_file}"
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+while test $# -gt 0; do
+
+    # Split --option=value in $opt="--option" and $val="value"
+
+    opt=""
+    val=""
+
+    case "$1" in
+    --*=*)
+      opt=`echo "$1" | sed 's/=.*//'`
+      val=`echo "$1" | sed 's/--[_a-zA-Z0-9-]*=//'`
+      ;;
+    --*)
+      opt=$1
+      ;;
+    # -D*)
+    #   ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS $1"
+    #   ;;
+    *)
+      break
+      ;;
+    esac
+
+    # echo "debug opt: $opt $val"
+
+    # Parse options
+    case "$opt" in
+      --help)
+        help 0
+  	    ;;
+      --version)
+        version
+        ;;
+      --dryrun)
+        dryrun="yes"
+        ;;
+      --toolchains)
+        toolchains
+        ;;
+      --cmakebin)
+        cmakebin="$val"
+        ;;
+      --prefix)
+        prefix "$val"
+        ;;
+      --build)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_BUILD_TYPE=$val"
+        ;;
+      --log)
+        log $val
+        ;;
+      --static)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=OFF"
+        ;;
+      --dynamic)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON"
+        ;;
+      --shared)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON"
+        ;;
+      --toolchain)
+        toolchain $val
+        ;;
+      --config)
+        config $val
+        ;;
+      --cache)
+        cache $val
+        ;;
+      --build-cmake)
+        build_cmake="yes"
+        if [[ -n $val ]]; then
+          cmake_prefix="$val"
+        fi
+        ;;
+      --)
+        shift
+        break
+        ;;
+      *)
+        echo "unknown option: $opt"
+	      usage 1
+        ;;
+    esac
+    shift
+done
+
+# If no arguments remain, set srcARG to "."
+if [ $# -eq 0 ]; then
+  srcARG="."
+fi
+
+if [ -z ${toolchain_file+x} ]; then
+  if [ -z ${ECBUILD_TOOLCHAIN+x} ]; then :
+  else
+    toolchain ${ECBUILD_TOOLCHAIN}
+    echo "ecbuild toolchain set using environment variable ECBUILD_TOOLCHAIN"
+  fi
+fi
+
+src=${srcARG:=""}
+cmake=${cmakebin:=cmake}
+dryrun=${dryrun:=no}
+build_cmake=${build_cmake:=""}
+cmake_prefix=${cmake_prefix:=$PWD}
+cmake_found=""
+cmake_version_sufficient=""
+
+
+# Check that version $1 satisfies $2
+# CMake versions have no more than 4 fields
+# Version sort (sort -V) is not available on all platforms
+version_gte() {
+  [ "$2" = "$(echo -e "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g | head -n1)" ]
+}
+
+# Use already built CMake if any
+if [[ -x "${cmake_prefix}/bin/cmake" ]]; then
+  echo "Using already built CMake in ${cmake_prefix}/bin/cmake" >&2
+  cmake="${cmake_prefix}/bin/cmake"
+# Build CMake if requested and no sufficient version found
+elif [[ $build_cmake ]]; then
+  echo "Building CMake version ${CMAKE_BUILD_VERSION} and installing into ${cmake_prefix} ..." >&2
+  tarball=cmake-${CMAKE_BUILD_VERSION}.tar.gz
+  if [[ ! -r $tarball ]]; then
+    url=http://www.cmake.org/files/v${CMAKE_BUILD_VERSION:0:3}/$tarball
+    # -N          Download only if the remote version of the file is newer
+    # --continue  Continue an interrupted download
+    # -T 60       Time out a download attempt after 60 seconds
+    # -t 3        Only make 3 download attempts
+    wget -N --continue -T 60 -t 3 $url || {
+      echo "Failed to download CMake release $CMAKE_BUILD_VERSION." >&2
+      echo "Please download from $url" >&2
+      echo "and place $tarball in $PWD" >&2
+      exit 1
+    }
+  fi
+  tar xzf cmake-${CMAKE_BUILD_VERSION}.tar.gz
+  (
+    mkdir -p build_cmake
+    cd build_cmake
+    ../cmake-${CMAKE_BUILD_VERSION}/bootstrap --prefix="${cmake_prefix}" && make && make install
+  )
+  cmake="${cmake_prefix}/bin/cmake"
+fi
+
+# Check if the cmake version is sufficient
+if $(command -v $cmake >/dev/null 2>&1); then
+  cmake_found="yes"
+  cmake_version=$($cmake --version | head -n1 | awk '{ print $3 }')
+  echo "Found CMake version $cmake_version" >& 2
+  if version_gte $cmake_version $CMAKE_MIN_REQUIRED; then
+    cmake_version_sufficient="yes"
+  fi
+fi
+
+# Fail if we don't have a sufficient CMake
+if [[ ! $cmake_version_sufficient ]]; then
+  if [[ ! $cmake_found ]]; then
+    echo "CMake is required and cannot be found in the PATH." >&2
+  else
+    echo "CMake version $CMAKE_MIN_REQUIRED is required but only $cmake_version was found." >&2
+  fi
+  echo "" >&2
+  echo "  Try 'module load cmake', specify a CMake binary with --cmakebin=/path/to/cmake" >&2
+  echo "  or  let ecbuild download and build CMake with the --build-cmake option." >&2
+  exit 1
+fi
+
+echo ""
+echo "$cmake ${ADD_ECBUILD_OPTIONS} $@ $src"
+echo ""
+
+if [ ${dryrun} == "yes" ]; then
+  echo "[DRYRUN] -- not executing"
+  exit 0
+fi
+
+$cmake ${ADD_ECBUILD_OPTIONS} "$@" $src
diff --git a/cmake/2.8/CMakePushCheckState.cmake b/cmake/2.8/CMakePushCheckState.cmake
new file mode 100644
index 0000000..0a42128
--- /dev/null
+++ b/cmake/2.8/CMakePushCheckState.cmake
@@ -0,0 +1,61 @@
+# This module defines two macros:
+# CMAKE_PUSH_CHECK_STATE()
+# and
+# CMAKE_POP_CHECK_STATE()
+# These two macros can be used to save and restore the state of the variables
+# CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_LIBRARIES
+# and CMAKE_REQUIRED_INCLUDES used by the various Check-files coming with CMake,
+# like e.g. check_function_exists() etc.
+# The variable contents are pushed on a stack, pushing multiple times is supported.
+# This is useful e.g. when executing such tests in a Find-module, where they have to be set,
+# but after the Find-module has been executed they should have the same value
+# as they had before.
+#
+# Usage:
+#   cmake_push_check_state()
+#   set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF)
+#   check_function_exists(...)
+#   cmake_pop_check_state()
+
+#=============================================================================
+# Copyright 2006-2011 Alexander Neundorf, <neundorf at kde.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+
+MACRO(CMAKE_PUSH_CHECK_STATE)
+
+   IF(NOT DEFINED _CMAKE_PUSH_CHECK_STATE_COUNTER)
+      SET(_CMAKE_PUSH_CHECK_STATE_COUNTER 0)
+   ENDIF()
+
+   MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1")
+
+   SET(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}    ${CMAKE_REQUIRED_INCLUDES})
+   SET(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS})
+   SET(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}   ${CMAKE_REQUIRED_LIBRARIES})
+   SET(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}       ${CMAKE_REQUIRED_FLAGS})
+ENDMACRO(CMAKE_PUSH_CHECK_STATE)
+
+MACRO(CMAKE_POP_CHECK_STATE)
+
+# don't pop more than we pushed
+   IF("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0")
+
+      SET(CMAKE_REQUIRED_INCLUDES    ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_LIBRARIES   ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_FLAGS       ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+
+      MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1")
+   ENDIF()
+
+ENDMACRO(CMAKE_POP_CHECK_STATE)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
new file mode 100644
index 0000000..d42a153
--- /dev/null
+++ b/cmake/CMakeLists.txt
@@ -0,0 +1,5 @@
+file( GLOB_RECURSE ecbuild_support_files  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" )
+
+ecbuild_add_resources(  TARGET ${PROJECT_NAME}_ecbuild_support_files
+						SOURCES_PACK
+							${ecbuild_support_files} )
diff --git a/cmake/FindADSM.cmake b/cmake/FindADSM.cmake
new file mode 100644
index 0000000..daadf0e
--- /dev/null
+++ b/cmake/FindADSM.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ADSM
+# Once done this will define
+#  ADSM_FOUND - System has ADSM
+#  ADSM_INCLUDE_DIRS - The ADSM include directories
+#  ADSM_LIBRARIES - The libraries needed to use ADSM
+
+if( EC_OS_BITS EQUAL 32 )
+	set( ADSM_LIBNAME ApiDS )
+endif()
+if( EC_OS_BITS EQUAL 64 )
+	set( ADSM_LIBNAME ApiTSM64 )
+endif()
+if( NOT DEFINED ADSM_LIBNAME )
+	message( STATUS "MARS only supports ADSM with 32 or 64 bits" )
+endif()
+
+if( DEFINED ADSM_PATH )
+	find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATHS ${ADSM_PATH} ${ADSM_PATH}/include ${ADSM_PATH}/sample NO_DEFAULT_PATH )
+	find_library(ADSM_LIBRARY  ${ADSM_LIBNAME} PATHS ${ADSM_PATH} ${ADSM_PATH}/lib     ${ADSM_PATH}/lib64  NO_DEFAULT_PATH )
+endif()
+
+find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATH_SUFFIXES bin64 )
+find_library( ADSM_LIBRARY ${ADSM_LIBNAME} PATH_SUFFIXES bin64 )
+
+set( ADSM_LIBRARIES    ${ADSM_LIBRARY} )
+set( ADSM_INCLUDE_DIRS ${ADSM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set ADSM_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ADSM  DEFAULT_MSG
+								  ADSM_LIBRARY ADSM_INCLUDE_DIR)
+
+mark_as_advanced(ADSM_INCLUDE_DIR ADSM_LIBRARY )
diff --git a/cmake/FindAEC.cmake b/cmake/FindAEC.cmake
new file mode 100644
index 0000000..0b0f69b
--- /dev/null
+++ b/cmake/FindAEC.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find AEC (Adaptive Entropy Coding library)
+# See https://www.dkrz.de/redmine/projects/aec/wiki
+
+# Once done this will define
+#  AEC_FOUND        - System has AEC
+#  AEC_INCLUDE_DIRS - The AEC include directories
+#  AEC_LIBRARIES    - The libraries needed to use AEC
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  AEC_DIR          - prefix path of the AEC installation
+#  AEC_PATH         - prefix path of the AEC installation
+
+find_path( AEC_INCLUDE_DIR szlib.h
+           PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+           PATH_SUFFIXES include include/aec NO_DEFAULT_PATH )
+find_path( AEC_INCLUDE_DIR szlib.h PATH_SUFFIXES include include/aec )
+
+find_library( AEC_LIBRARY  NAMES aec
+              PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+              PATH_SUFFIXES lib lib64 lib/aec lib64/aec NO_DEFAULT_PATH )
+find_library( AEC_LIBRARY NAMES aec PATH_SUFFIXES lib lib64 lib/aec lib64/aec )
+
+set( AEC_LIBRARIES    ${AEC_LIBRARY} )
+set( AEC_INCLUDE_DIRS ${AEC_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(AEC  DEFAULT_MSG AEC_LIBRARY AEC_INCLUDE_DIR)
+
+mark_as_advanced(AEC_INCLUDE_DIR AEC_LIBRARY )
diff --git a/cmake/FindAIO.cmake b/cmake/FindAIO.cmake
new file mode 100644
index 0000000..5dd9244
--- /dev/null
+++ b/cmake/FindAIO.cmake
@@ -0,0 +1,66 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# OUTPUT:
+# RT_LIB  = the library to link against
+
+if( CMAKE_SYSTEM_NAME MATCHES "Linux" )
+
+	find_package( Realtime )
+
+	if( REALTIME_FOUND ) # check that aio needs realtime
+		set( AIO_LIBRARIES ${RT_LIB} )
+	endif()
+
+endif()
+
+find_path( AIO_INCLUDE_DIRS NAMES aio.h HINTS ENV AIO_PATH ${AIO_PATH} )
+
+mark_as_advanced( AIO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( AIO  DEFAULT_MSG  AIO_INCLUDE_DIRS  )
+
+# checks for AIO64 vs AIO
+if( AIO_FOUND )
+
+	include( CheckCSourceCompiles )
+	include( CMakePushCheckState )
+    
+    cmake_push_check_state()
+    
+		set( CMAKE_REQUIRED_INCLUDES ${AIO_INCLUDE_DIRS} )
+
+		if( AIO_LIBRARIES )
+			set( CMAKE_REQUIRED_LIBRARIES ${AIO_LIBRARIES} )
+		endif()
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb* aiocbp;
+									  int n = aio_write(aiocbp);
+									  n = aio_read(aiocbp);
+									  n = aio_fsync(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB )
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb64* aiocbp;
+									  int n = aio_write64(aiocbp);
+									  n = aio_read64(aiocbp);
+									  n = aio_fsync64(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB64 )
+
+    cmake_pop_check_state()
+
+endif()
diff --git a/cmake/FindArmadillo.cmake b/cmake/FindArmadillo.cmake
new file mode 100644
index 0000000..dfe77a2
--- /dev/null
+++ b/cmake/FindArmadillo.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Armadillo
+# Once done this will define
+#
+#  ARMADILLO_FOUND         - system has Armadillo
+#  ARMADILLO_INCLUDE_DIRS  - the Armadillo include directory
+#  ARMADILLO_LIBRARIES     - the Armadillo library
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  ARMADILLO_PATH          - prefix path of the Armadillo installation
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_path(ARMADILLO_INCLUDE_DIR armadillo
+          PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(ARMADILLO_INCLUDE_DIR  armadillo PATH_SUFFIXES include )
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_library(ARMADILLO_LIBRARY armadillo
+             PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+             PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+find_library( ARMADILLO_LIBRARY  armadillo   PATH_SUFFIXES lib64 lib )
+
+set( ARMADILLO_LIBRARIES    ${ARMADILLO_LIBRARY} )
+set( ARMADILLO_INCLUDE_DIRS ${ARMADILLO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set ARMADILLO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Armadillo  DEFAULT_MSG
+                                  ARMADILLO_LIBRARY ARMADILLO_INCLUDE_DIR)
+
+mark_as_advanced(ARMADILLO_INCLUDE_DIR ARMADILLO_LIBRARY )
diff --git a/cmake/FindCMath.cmake b/cmake/FindCMath.cmake
new file mode 100644
index 0000000..741728a
--- /dev/null
+++ b/cmake/FindCMath.cmake
@@ -0,0 +1,26 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# CMATH_LIBRARIES      = the library to link against (RT etc)
+
+IF(UNIX)
+  if( DEFINED CMATH_PATH )
+    find_library(CMATH_LIBRARIES m PATHS ${CMATH_PATH}/lib NO_DEFAULT_PATH )
+  endif()
+
+  find_library(CMATH_LIBRARIES m )
+
+  include(FindPackageHandleStandardArgs)
+
+  # handle the QUIET and REQUIRED arguments and set CMATH_FOUND to TRUE
+  # if all listed variables are TRUE
+  # Note: capitalisation of the package name must be the same as in the file name
+  find_package_handle_standard_args(CMath DEFAULT_MSG CMATH_LIBRARIES )
+
+ENDIF(UNIX)
diff --git a/cmake/FindCairo.cmake b/cmake/FindCairo.cmake
new file mode 100644
index 0000000..4298a1d
--- /dev/null
+++ b/cmake/FindCairo.cmake
@@ -0,0 +1,59 @@
+# - Try to find the cairo library
+# Once done this will define
+#
+# CAIRO_FOUND - system has cairo
+# CAIRO_INCLUDE_DIRS - the cairo include directory
+# CAIRO_LIBRARIES - Link these to use cairo
+#
+# Define CAIRO_MIN_VERSION for which version desired.
+
+
+if( NOT DEFINED CAIRO_PATH AND NOT "$ENV{CAIRO_PATH}" STREQUAL "" )
+    set( APPEND CAIRO_PATH "$ENV{CAIRO_PATH}" )
+endif()
+
+if( NOT DEFINED CAIRO_PATH )
+
+    include(FindPkgConfig)
+
+    if(Cairo_FIND_REQUIRED)
+        set(_pkgconfig_REQUIRED "REQUIRED")
+    else()
+        set(_pkgconfig_REQUIRED "")
+    endif()
+
+    if(CAIRO_MIN_VERSION)
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION})
+    else()
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKCAIRO_FOUND )
+
+        find_path(CAIRO_INCLUDE_DIR cairo.h HINTS ${PKCAIRO_INCLUDEDIR} ${PKCAIRO_INCLUDE_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+        find_library(CAIRO_LIBRARY  cairo   HINTS ${PKCAIRO_LIBDIR}     ${PKCAIRO_LIBRARY_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+    endif()
+
+else()
+
+    find_path(CAIRO_INCLUDE_DIR cairo.h PATHS ${CAIRO_PATH}/include PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+    find_library(CAIRO_LIBRARY  cairo   PATHS ${CAIRO_PATH}/lib     PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+endif()
+
+find_path(CAIRO_INCLUDE_DIR cairo.h PATH_SUFFIXES cairo )
+find_library( CAIRO_LIBRARY cairo   PATH_SUFFIXES cairo )
+
+set( CAIRO_LIBRARIES    ${CAIRO_LIBRARY} )
+set( CAIRO_INCLUDE_DIRS ${CAIRO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set CAIRO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Cairo  DEFAULT_MSG
+                                  CAIRO_LIBRARY CAIRO_INCLUDE_DIR)
+
+mark_as_advanced( CAIRO_INCLUDE_DIR CAIRO_LIBRARY )
diff --git a/cmake/FindDl.cmake b/cmake/FindDl.cmake
new file mode 100644
index 0000000..31e426f
--- /dev/null
+++ b/cmake/FindDl.cmake
@@ -0,0 +1,23 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# DL_LIBRARIES      = the library to link against (RT etc)
+
+if( DEFINED DL_PATH )
+    find_library(DL_LIBRARIES dl PATHS ${DL_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library(DL_LIBRARIES dl )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set DL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Dl DEFAULT_MSG DL_LIBRARIES )
diff --git a/cmake/FindEMOS.cmake b/cmake/FindEMOS.cmake
new file mode 100644
index 0000000..43f896e
--- /dev/null
+++ b/cmake/FindEMOS.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find EMOS
+# Once done this will define
+#  EMOS_FOUND - System has EMOS
+#  EMOS_INCLUDE_DIRS - The EMOS include directories
+#  EMOS_LIBRARIES - The libraries needed to use EMOS
+
+if( NOT DEFINED EMOS_PATH AND DEFINED $ENV{EMOS_PATH} )
+	set( EMOS_PATH $ENV{EMOS_PATH} )
+endif()
+
+if( DEFINED EMOS_PATH )
+    find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos PATHS ${EMOS_PATH} PATH_SUFFIXES lib lib/emos NO_DEFAULT_PATH)
+endif()
+
+find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos)
+
+ecbuild_find_fortranlibs()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( EMOS  DEFAULT_MSG  EMOS_LIBRARY FORTRANLIBS_FOUND )
+
+mark_as_advanced(EMOS_LIBRARY)
+
+if( EMOS_FOUND )
+    set( EMOS_LIBRARIES  ${EMOS_LIBRARY} ${FORTRAN_LIBRARIES} )
+endif()
diff --git a/cmake/FindFDB.cmake b/cmake/FindFDB.cmake
new file mode 100644
index 0000000..66879c1
--- /dev/null
+++ b/cmake/FindFDB.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find FDB
+# Once done this will define
+#  FDB_FOUND - System has FDB
+#  FDB_INCLUDE_DIRS - The FDB include directories
+#  FDB_LIBRARIES - The libraries needed to use FDB
+
+
+if( NOT FDB_FOUND )
+
+	if( DEFINED FDB_PATH )
+		find_library( FDB_LIBRARY NAMES fdb PATHS ${FDB_PATH} ${FDB_PATH}/lib NO_DEFAULT_PATH)
+	endif()
+	
+	find_library( FDB_LIBRARY NAMES fdb )
+	
+	set( FDB_LIBRARIES  ${FDB_LIBRARY} )
+	
+	include(FindPackageHandleStandardArgs)
+	
+	# handle the QUIETLY and REQUIRED arguments and set FDB_FOUND to TRUE
+	# if all listed variables are TRUE
+	find_package_handle_standard_args(FDB  DEFAULT_MSG
+									  FDB_LIBRARY )
+	
+	mark_as_advanced(FDB_LIBRARY)
+
+endif()
diff --git a/cmake/FindFFTW.cmake b/cmake/FindFFTW.cmake
new file mode 100644
index 0000000..d57c09a
--- /dev/null
+++ b/cmake/FindFFTW.cmake
@@ -0,0 +1,211 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# FindFFTW
+# ========
+#
+# Find the FFTW library. ::
+#
+#   find_package(FFTW [REQUIRED] [QUIET]
+#                [COMPONENTS [single] [double] [long_double] [quad]])
+#
+# By default, search for the double precision library ``fftw3``
+#
+# Components
+# ----------
+#
+# If a different version or multiple versions of the library are required,
+# these need to be specified as ``COMPONENTS``. Note that double must be given
+# explicitly if any ``COMPONENTS`` are specified.
+#
+# The libraries corresponding to each of the ``COMPONENTS`` are:
+#
+# :single:      ``fftw3f``
+# :double:      ``fftw3``
+# :long_double: ``fftw3l``
+# :quad:        ``fftw3q``
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set on completion:
+#
+# :FFTW_FOUND:      true if FFTW is found on the system
+# :FFTW_LIBRARIES:  full paths to requested FFTW libraries
+# :FFTW_INCLUDES:   FFTW include directory
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are checked by the function:
+#
+# :FFTW_USE_STATIC_LIBS:  if true, only static libraries are found
+# :FFTW_ROOT:             if set, this path is exclusively searched
+# :FFTW_DIR:              equivalent to FFTW_ROOT
+# :FFTW_PATH:             equivalent to FFTW_ROOT
+# :FFTW_LIBRARY:          FFTW library to use
+# :FFTW_INCLUDE_DIR:      FFTW include directory
+#
+##############################################################################
+
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_ROOT} )
+  set( FFTW_ROOT ${FFTW_ROOT} )
+endif()
+if( NOT FFTW_ROOT AND $FFTW_DIR )
+  set( FFTW_ROOT ${FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_DIR} )
+  set( FFTW_ROOT $ENV{FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTWDIR )
+  set( FFTW_ROOT ${FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTWDIR} )
+  set( FFTW_ROOT $ENV{FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTW_PATH )
+  set( FFTW_ROOT ${FFTW_PATH} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_PATH})
+  set( FFTW_ROOT $ENV{FFTW_PATH} )
+endif()
+
+if( FFTW_ROOT ) # On cc[a|b|t] FFTW_DIR is set to the lib directory :(
+  get_filename_component(_dirname ${FFTW_ROOT} NAME)
+  if( _dirname MATCHES "lib" )
+    set( FFTW_ROOT "${FFTW_ROOT}/.." )
+  endif()
+endif()
+
+if( NOT FFTW_ROOT )
+  # Check if we can use PkgConfig
+  find_package(PkgConfig)
+
+  #Determine from PKG
+  if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT )
+    pkg_check_modules( PKG_FFTW QUIET "fftw3" )
+  endif()
+endif()
+
+#Check whether to search static or dynamic libs
+set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} )
+
+if( ${FFTW_USE_STATIC_LIBS} )
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} )
+else()
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} )
+endif()
+
+if( FFTW_FIND_COMPONENTS )
+  ecbuild_debug( "FindFFTW: looking for components: ${FFTW_FIND_COMPONENTS}" )
+  foreach( _component ${FFTW_FIND_COMPONENTS} )
+    if( _component MATCHES "single" )
+      ecbuild_debug( "FindFFTW: looking for single precision (fftw3f)" )
+      set( _require_sp TRUE )
+    elseif( _component MATCHES "double" )
+      ecbuild_debug( "FindFFTW: looking for double precision (fftw3)" )
+      set( _require_dp TRUE )
+    elseif( _component MATCHES "long_double" )
+      ecbuild_debug( "FindFFTW: looking for long double precision (fftw3l)" )
+      set( _require_lp TRUE )
+    elseif( _component MATCHES "quad" )
+      ecbuild_debug( "FindFFTW: looking for quad precision (fftw3q)" )
+      set( _require_qp TRUE )
+    else()
+    endif()
+  endforeach()
+else()
+  ecbuild_debug( "FindFFTW: no components specified, looking for double precision (fftw3)" )
+  set( _require_dp TRUE )
+endif()
+
+if( FFTW_ROOT )
+  set( _default_paths NO_DEFAULT_PATH )
+  set( _lib_paths ${FFTW_ROOT} )
+  set( _include_paths ${FFTW_ROOT} )
+else()
+  set( _lib_paths ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} )
+  set( _include_paths ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} )
+endif()
+
+#find libs
+
+if( _require_dp )
+  find_library(
+    FFTW_LIB
+    NAMES "fftw3"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTW_LIB )
+    ecbuild_warn("FindFFTW: double precision required, but fftw3 was not found")
+  endif()
+endif()
+
+if( _require_sp )
+  find_library(
+    FFTWF_LIB
+    NAMES "fftw3f"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWF_LIB )
+    ecbuild_warn("FindFFTW: single precision required, but fftw3f was not found")
+  endif()
+endif()
+
+if( _require_lp )
+  find_library(
+    FFTWL_LIB
+    NAMES "fftw3l"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWL_LIB )
+    ecbuild_warn("FindFFTW: long double precision required, but fftw3l was not found")
+  endif()
+endif()
+
+if( _require_qp )
+  find_library(
+    FFTWQ_LIB
+    NAMES "fftw3q"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWQ_LIB )
+    ecbuild_warn("FindFFTW: quad precision required, but fftw3q was not found")
+  endif()
+endif()
+
+#find includes
+
+find_path(
+  FFTW_INCLUDES
+  NAMES "fftw3.h"
+  PATHS ${_include_paths}
+  PATH_SUFFIXES "include"
+  ${_default_paths}
+)
+
+set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB} ${FFTWL_LIB} ${FFTWQ_LIB})
+
+set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} )
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(FFTW DEFAULT_MSG
+                                  FFTW_INCLUDES FFTW_LIBRARIES)
+
+mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB)
diff --git a/cmake/FindGeoTIFF.cmake b/cmake/FindGeoTIFF.cmake
new file mode 100644
index 0000000..7226f61
--- /dev/null
+++ b/cmake/FindGeoTIFF.cmake
@@ -0,0 +1,46 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the GeoTIFF includes and library
+# This module defines
+#
+#  GEOTIFF_FOUND         - System has GeoTIFF
+#  GEOTIFF_INCLUDE_DIRS  - the GeoTIFF include directories
+#  GEOTIFF_LIBRARIES     - the libraries needed to use GeoTIFF
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  GEOTIFF_DIR   - root folder of the GeoTIFF installation
+#  GEOTIFF_PATH  - root folder of the GeoTIFF installation
+
+find_path( GEOTIFF_INCLUDE_DIR geotiff.h
+           PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                 ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+           PATH_SUFFIXES include include/libgeotiff
+           NO_DEFAULT_PATH )
+find_path( GEOTIFF_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES include include/libgeotiff )
+
+find_library( GEOTIFF_LIBRARY NAMES geotiff
+              PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                    ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+find_library( GEOTIFF_LIBRARY NAMES geotiff )
+
+set( GEOTIFF_LIBRARIES    ${GEOTIFF_LIBRARY} )
+set( GEOTIFF_INCLUDE_DIRS ${GEOTIFF_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GEOTIFF_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( GeoTIFF DEFAULT_MSG
+                                   GEOTIFF_LIBRARY GEOTIFF_INCLUDE_DIR )
+
+mark_as_advanced( GEOTIFF_INCLUDE_DIR GEOTIFF_LIBRARY )
diff --git a/cmake/FindHPSS.cmake b/cmake/FindHPSS.cmake
new file mode 100644
index 0000000..1cc829e
--- /dev/null
+++ b/cmake/FindHPSS.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find HPSS
+# Once done this will define
+#  HPSS_FOUND - System has HPSS
+#  HPSS_INCLUDE_DIRS - The HPSS include directories
+#  HPSS_LIBRARIES - The libraries needed to use HPSS
+#  HPSS_DEFINITIONS - Compiler switches required for using HPSS
+
+if( DEFINED HPSS_PATH )
+	find_path(HPSS_INCLUDE_DIR hpss_api.h PATHS ${HPSS_PATH}/include PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+	find_library(HPSS_LIBRARY  hpss       PATHS ${HPSS_PATH}/lib     PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+endif()
+
+find_path(HPSS_INCLUDE_DIR hpss_api.h PATH_SUFFIXES hpss )
+find_library( HPSS_LIBRARY hpss       PATH_SUFFIXES hpss )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set HPSS_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(HPSS  DEFAULT_MSG
+                                  HPSS_LIBRARY HPSS_INCLUDE_DIR)
+
+mark_as_advanced(HPSS_INCLUDE_DIR HPSS_LIBRARY )
+
+if( HPSS_FOUND )
+    set( HPSS_LIBRARIES    ${HPSS_LIBRARY} )
+    set( HPSS_INCLUDE_DIRS ${HPSS_INCLUDE_DIR} )
+else()
+    set( HPSS_LIBRARIES    "" )
+    set( HPSS_INCLUDE_DIRS "" )
+endif()
diff --git a/cmake/FindLEX.cmake b/cmake/FindLEX.cmake
new file mode 100644
index 0000000..b6ab838
--- /dev/null
+++ b/cmake/FindLEX.cmake
@@ -0,0 +1,129 @@
+# - Find lex executable and provides a macro to generate custom build rules
+#
+# The module defines the following variables:
+#  LEX_FOUND - true is lex executable is found
+#  LEX_EXECUTABLE - the path to the lex executable
+#  LEX_LIBRARIES - The lex libraries
+#  LEX_INCLUDE_DIRS - The path to the lex headers
+#
+#
+# If lex is found on the system, the module provides the macro:
+#  LEX_TARGET(Name LexInput LexOutput [COMPILE_FLAGS <string>])
+# which creates a custom command  to generate the <LexOutput> file from
+# the <LexInput> file.  If  COMPILE_FLAGS option is specified, the next
+# parameter is added to the lex  command line. Name is an alias used to
+# get  details of  this custom  command.  Indeed the  macro defines  the
+# following variables:
+#  LEX_${Name}_DEFINED - true is the macro ran successfully
+#  LEX_${Name}_OUTPUTS - the source file generated by the custom rule, an
+#  alias for LexOutput
+#  LEX_${Name}_INPUT - the lex source file, an alias for ${LexInput}
+#
+# Lex scanners oftenly use tokens  defined by Yacc: the code generated
+# by Lex  depends of the header  generated by Yacc.   This module also
+# defines a macro:
+#  ADD_LEX_YACC_DEPENDENCY(LexTarget YaccTarget)
+# which  adds the  required dependency  between a  scanner and  a parser
+# where  <LexTarget>  and <YaccTarget>  are  the  first parameters  of
+# respectively LEX_TARGET and YACC_TARGET macros.
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(LEX_EXECUTABLE lex DOC "path to the lex executable")
+MARK_AS_ADVANCED(LEX_EXECUTABLE)
+
+FIND_LIBRARY(FL_LIBRARY NAMES fl
+  DOC "Path to the fl library")
+
+FIND_PATH(LEX_INCLUDE_DIR LexLexer.h
+  DOC "Path to the lex headers")
+
+MARK_AS_ADVANCED(FL_LIBRARY LEX_INCLUDE_DIR)
+
+SET(LEX_INCLUDE_DIRS ${LEX_INCLUDE_DIR})
+SET(LEX_LIBRARIES ${FL_LIBRARY})
+
+IF(LEX_EXECUTABLE)
+
+  #============================================================
+  # LEX_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(LEX_TARGET Name Input Output)
+    SET(LEX_TARGET_usage "LEX_TARGET(<Name> <Input> <Output> [COMPILE_FLAGS <string>]")
+    IF(${ARGC} GREATER 3)
+      IF(${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          SET(LEX_EXECUTABLE_opts  "${ARGV4}")
+          SEPARATE_ARGUMENTS(LEX_EXECUTABLE_opts)
+        ELSE()
+          MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+        ENDIF()
+      ELSE()
+        MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+      ENDIF()
+    ENDIF()
+
+    message( STATUS "${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}" )
+
+    ADD_CUSTOM_COMMAND(OUTPUT ${Output}
+      COMMAND ${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}
+      DEPENDS ${Input}
+      COMMENT "[LEX][${Name}] Building scanner with lex ${LEX_VERSION}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+    SET(LEX_${Name}_DEFINED TRUE)
+    SET(LEX_${Name}_OUTPUTS ${Output})
+    SET(LEX_${Name}_INPUT ${Input})
+    SET(LEX_${Name}_COMPILE_FLAGS ${LEX_EXECUTABLE_opts})
+  ENDMACRO(LEX_TARGET)
+  #============================================================
+
+
+  #============================================================
+  # ADD_LEX_YACC_DEPENDENCY (public macro)
+  #============================================================
+  #
+  MACRO(ADD_LEX_YACC_DEPENDENCY LexTarget YaccTarget)
+
+    IF(NOT LEX_${LexTarget}_OUTPUTS)
+      MESSAGE(SEND_ERROR "Lex target `${LexTarget}' does not exists.")
+    ENDIF()
+
+    IF(NOT YACC_${YaccTarget}_OUTPUT_HEADER)
+      MESSAGE(SEND_ERROR "Yacc target `${YaccTarget}' does not exists.")
+    ENDIF()
+
+    SET_SOURCE_FILES_PROPERTIES(${LEX_${LexTarget}_OUTPUTS}
+      PROPERTIES OBJECT_DEPENDS ${YACC_${YaccTarget}_OUTPUT_HEADER})
+  ENDMACRO(ADD_LEX_YACC_DEPENDENCY)
+  #============================================================
+
+ENDIF(LEX_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LEX REQUIRED_VARS LEX_EXECUTABLE)
+
+# FindLEX.cmake ends here
diff --git a/cmake/FindLibGFortran.cmake b/cmake/FindLibGFortran.cmake
new file mode 100644
index 0000000..7f9cc64
--- /dev/null
+++ b/cmake/FindLibGFortran.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# gfortran libs
+
+set( __libgfortran_names gfortran libgfortran.so.1 libgfortran.so.3 )
+
+# use gfortran to find the library
+
+find_program( GFORTRAN_EXECUTABLE gfortran )
+
+if( GFORTRAN_EXECUTABLE )
+
+	execute_process(COMMAND ${GFORTRAN_EXECUTABLE} "-print-search-dirs"
+		RESULT_VARIABLE _GFORTRAN_SEARCH_SUCCESS
+		OUTPUT_VARIABLE _GFORTRAN_VALUES_OUTPUT
+		ERROR_VARIABLE _GFORTRAN_ERROR_VALUE
+		OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+#	ecbuild_debug_var(_GFORTRAN_SEARCH_SUCCESS)
+#	ecbuild_debug_var(_GFORTRAN_VALUES_OUTPUT)
+#	ecbuild_debug_var(_GFORTRAN_ERROR_VALUE)
+
+	if(_GFORTRAN_SEARCH_SUCCESS MATCHES 0)
+		string(REGEX REPLACE ".*libraries: =(.*)" "\\1" _result  ${_GFORTRAN_VALUES_OUTPUT})
+		string(REGEX REPLACE ":" ";" _gfortran_hints ${_result} )
+	endif()
+
+	ecbuild_debug_var( _gfortran_hints )
+
+endif()
+
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${LIBGFORTRAN_PATH} ENV LIBGFORTRAN_PATH PATHS PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH )
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${_gfortran_hints} PATHS PATH_SUFFIXES lib64 lib )
+
+mark_as_advanced( GFORTRAN_LIB )
+
+if( GFORTRAN_LIB )
+	set( GFORTRAN_LIBRARIES ${GFORTRAN_LIB} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBGFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibGFortran DEFAULT_MSG GFORTRAN_LIBRARIES  )
diff --git a/cmake/FindLibIFort.cmake b/cmake/FindLibIFort.cmake
new file mode 100644
index 0000000..4c3e299
--- /dev/null
+++ b/cmake/FindLibIFort.cmake
@@ -0,0 +1,50 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# date:   July 2015
+# author: Florian Rathgeber
+
+###############################################################################
+
+# - Try to find Intel Fortran (ifort) runtime libraries libifcore and libifport
+# Once done this will define
+#
+#  LIBIFORT_FOUND   - system has Intel Fortran (ifort) runtime libraries
+#  IFORT_LIBRARIES  - the Intel Fortran (ifort) runtime libraries
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  INTEL_PATH       - prefix path of the Intel installation
+#
+# Otherwise the libraries are assumed to be on LIBRARY_PATH or LD_LIBRARY_PATH
+
+# FIXME: might need to add further libraries in future, see
+# http://nf.nci.org.au/facilities/software/Compilers/Intel8/doc/f_ug1/files_32.htm
+
+# Search with priority for INTEL_PATH if given as CMake or env var
+find_library( IFORT_LIB_CORE ifcore PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+
+# Otherwise, search LIBRARY_PATH and LD_LIBRARY_PATH
+find_library( IFORT_LIB_CORE ifcore PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+
+mark_as_advanced( IFORT_LIB_CORE IFORT_LIB_PORT )
+
+if( IFORT_LIB_CORE AND IFORT_LIB_PORT )
+  set( IFORT_LIBRARIES ${IFORT_LIB_CORE} ${IFORT_LIB_PORT} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBIFORT_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibIFort DEFAULT_MSG IFORT_LIBRARIES )
diff --git a/cmake/FindLustreAPI.cmake b/cmake/FindLustreAPI.cmake
new file mode 100644
index 0000000..6db0ba2
--- /dev/null
+++ b/cmake/FindLustreAPI.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find lib Lustre API
+
+# usually installed on Cray systems under /opt/cray/lustre-cray_ari_s/default / create_test.c -L
+# .../include/lustre/lustreapi.h
+# .../lib64/liblustreapi.so
+
+# Once done this will define
+#  LUSTREAPI_FOUND        - System has LustreAPI
+#  LUSTREAPI_INCLUDE_DIRS - The LustreAPI include directories
+#  LUSTREAPI_LIBRARIES    - The libraries needed to use LustreAPI
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  LUSTREAPI_DIR          - prefix path of the LustreAPI installation
+#  LUSTREAPI_PATH         - prefix path of the LustreAPI installation
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h
+           PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+           PATH_SUFFIXES include NO_DEFAULT_PATH )
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h PATH_SUFFIXES include )
+
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi
+              PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+              PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi PATH_SUFFIXES lib lib64 )
+
+set( LUSTREAPI_LIBRARIES    ${LUSTREAPI_LIBRARY} )
+set( LUSTREAPI_INCLUDE_DIRS ${LUSTREAPI_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(LUSTREAPI  DEFAULT_MSG LUSTREAPI_LIBRARY LUSTREAPI_INCLUDE_DIR)
+
+mark_as_advanced(LUSTREAPI_INCLUDE_DIR LUSTREAPI_LIBRARY )
diff --git a/cmake/FindMKL.cmake b/cmake/FindMKL.cmake
new file mode 100644
index 0000000..59eaa60
--- /dev/null
+++ b/cmake/FindMKL.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find MKL
+# Once done this will define
+#
+#  MKL_FOUND         - system has Intel MKL
+#  MKL_INCLUDE_DIRS  - the MKL include directories
+#  MKL_LIBRARIES     - link these to use MKL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  MKLROOT           - root directory of the MKL installation
+#  MKL_PATH          - root directory of the MKL installation
+#  MKL_ROOT          - root directory of the MKL installation
+
+option( MKL_PARALLEL "if mkl shoudl be parallel" OFF )
+
+if( MKL_PARALLEL )
+
+  set( __mkl_lib_par  MKL_LIB_INTEL_THREAD )
+  set( __mkl_lib_name mkl_intel_thread )
+
+  find_package(Threads)
+
+else()
+
+  set( __mkl_lib_par MKL_LIB_SEQUENTIAL )
+  set( __mkl_lib_name mkl_sequential )
+
+endif()
+
+# Search with priority for MKLROOT, MKL_PATH and MKL_ROOT if set in CMake or env
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATHS ${MKLROOT} ${MKL_PATH} ${MKL_ROOT} ENV MKLROOT ENV MKL_PATH ENV MKL_ROOT
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATH_SUFFIXES include)
+
+if( MKL_INCLUDE_DIR ) # use include dir to find libs
+
+  set( MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} )
+
+  if( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" )
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/intel64 ABSOLUTE )
+    set( __libsfx _lp64 )
+  else()
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/ia32 ABSOLUTE )
+    set( __libsfx "" )
+  endif()
+
+  find_library( MKL_LIB_INTEL         NAMES mkl_intel${__libsfx} PATHS ${MKL_LIB_PATH} )
+  find_library( ${__mkl_lib_par}      NAMES ${__mkl_lib_name} PATHS ${MKL_LIB_PATH} )
+  find_library( MKL_LIB_CORE          NAMES mkl_core PATHS ${MKL_LIB_PATH} )
+
+  if( MKL_PARALLEL )
+    find_library( MKL_LIB_IOMP5  NAMES iomp5 PATHS ${MKL_LIB_PATH} )
+  endif()
+
+  if( MKL_LIB_INTEL AND ${__mkl_lib_par} AND MKL_LIB_CORE )
+    set( MKL_LIBRARIES ${MKL_LIB_INTEL} ${${__mkl_lib_par}} ${MKL_LIB_CORE} ${MKL_LIB_IOMP5} ${CMAKE_THREAD_LIBS_INIT} )
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( MKL DEFAULT_MSG
+                                   MKL_LIBRARIES MKL_INCLUDE_DIRS )
+
+mark_as_advanced( MKL_INCLUDE_DIR MKL_LIB_LAPACK MKL_LIB_INTEL MKL_LIB_SEQUENTIAL MKL_LIB_CORE )
diff --git a/cmake/FindNAG.cmake b/cmake/FindNAG.cmake
new file mode 100644
index 0000000..883a375
--- /dev/null
+++ b/cmake/FindNAG.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the NAG includes and library
+# This module defines
+#
+#  NAG_FOUND         - System has NAG
+#  NAG_INCLUDE_DIRS  - the NAG include directories
+#  NAG_LIBRARIES     - the libraries needed to use NAG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  NAG_DIR   - root folder of the NAG installation
+#  NAG_PATH  - root folder of the NAG installation
+
+find_path( NAG_INCLUDE_DIR nag_library.mod
+           PATHS ${NAG_PATH} ENV NAG_PATH
+                 ${NAG_DIR}  ENV NAG_DIR
+           PATH_SUFFIXES include
+           NO_DEFAULT_PATH )
+
+find_library( NAG_LIBRARY NAMES nag nag_nag
+              PATHS ${NAG_PATH} ENV NAG_PATH
+                    ${NAG_DIR}  ENV NAG_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+
+set( NAG_LIBRARIES    ${NAG_LIBRARY} )
+set( NAG_INCLUDE_DIRS ${NAG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set NAG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( NAG DEFAULT_MSG
+                                   NAG_LIBRARY NAG_INCLUDE_DIR )
+
+mark_as_advanced( NAG_INCLUDE_DIR NAG_LIBRARY )
diff --git a/cmake/FindNDBM.cmake b/cmake/FindNDBM.cmake
new file mode 100644
index 0000000..8cd350e
--- /dev/null
+++ b/cmake/FindNDBM.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find NetCDF
+# Once done this will define
+#  NDBM_FOUND - System has NetCDF
+#  NDBM_INCLUDE_DIRS - The NetCDF include directories
+#  NDBM_LIBRARIES - The libraries needed to use NetCDF
+#  NDBM_DEFINITIONS - Compiler switches required for using NetCDF
+
+if( DEFINED NDBM_PATH )
+	find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATHS ${NDBM_PATH} ${NDBM_PATH}/include PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+	find_library(NDBM_LIBRARY  NAMES ndbm dbm PATHS ${NDBM_PATH} ${NDBM_PATH}/lib     PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+endif()
+
+find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATH_SUFFIXES ndbm )
+find_library( NDBM_LIBRARY NAMES ndbm dbm PATH_SUFFIXES ndbm )
+
+set( NDBM_LIBRARIES    ${NDBM_LIBRARY} )
+set( NDBM_INCLUDE_DIRS ${NDBM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(NDBM  DEFAULT_MSG
+								  NDBM_LIBRARY NDBM_INCLUDE_DIR)
+
+mark_as_advanced(NDBM_INCLUDE_DIR NDBM_LIBRARY )
diff --git a/cmake/FindNetCDF.cmake b/cmake/FindNetCDF.cmake
new file mode 100644
index 0000000..69b88bd
--- /dev/null
+++ b/cmake/FindNetCDF.cmake
@@ -0,0 +1,164 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF3 or NetCDF4 -- default is 4
+#
+# find_package( NetCDF <version> COMPONENTS C CXX Fortran )
+#
+# Input:
+#  * NETCDF_PATH    - user defined path where to search for the library first
+#  * NETCDF_DIR     - user defined path where to search for the library first
+#  * NETCDF_ROOT    - user defined path where to search for the library first
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_DEFINITIONS
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+# default is netcdf4
+if( NetCDF_FIND_VERSION STREQUAL "3" )
+  set( PREFER_NETCDF3 1 )
+endif()
+
+if( NOT PREFER_NETCDF3 )
+  set( PREFER_NETCDF4 1 )
+else()
+  set( PREFER_NETCDF4 0 )
+endif()
+mark_as_advanced( PREFER_NETCDF4 PREFER_NETCDF3 )
+
+set( NETCDF_FIND_REQUIRED   ${NetCDF_FIND_REQUIRED} )
+set( NETCDF_FIND_QUIETLY    ${NetCDF_FIND_QUIETLY} )
+set( NETCDF_FIND_COMPONENTS ${NetCDF_FIND_COMPONENTS} )
+
+list( APPEND NETCDF_FIND_COMPONENTS C )
+
+if( NETCDF_CXX )
+  ecbuild_debug( "FindNetCDF: also looking for C++ libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS CXX )
+endif()
+
+if( NETCDF_Fortran OR NETCDF_FORTRAN OR NETCDF_F90 )
+  ecbuild_debug( "FindNetCDF: also looking for Fortran libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "FORTRAN" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS F90 )
+endif()
+
+list (FIND NETCDF_FIND_COMPONENTS "F90" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "Fortran" _index)
+if(${_index} GREATER -1)
+  list( REMOVE_ITEM NETCDF_FIND_COMPONENTS Fortran )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_FIND_COMPONENTS )
+ecbuild_debug( "FindNetCDF: looking for components ${NETCDF_FIND_COMPONENTS}" )
+
+### NetCDF4
+
+if( PREFER_NETCDF4 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF4" )
+
+  ## hdf5
+
+  # Note: Only the HDF5 C-library is required for NetCDF
+  #       ( even for Fortan and CXX bindings)
+  find_package( HDF5 COMPONENTS C QUIET )
+
+  ## netcdf4
+
+  # CONFIGURE the NETCDF_FIND_COMPONENTS variable
+
+  # Find NetCDF4
+
+  # message( "NETCDF CMAKE_PREFIX_PATH = [${CMAKE_PREFIX_PATH}]")
+  # ecbuild_debug_var( NETCDF_ROOT )
+  # ecbuild_debug_var( NETCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NETCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NETCDF_FIND_REQUIRED )
+  find_package( NetCDF4 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+  # ecbuild_debug_var( NETCDF4_FOUND )
+  # ecbuild_debug_var( NETCDF_FOUND )
+  # ecbuild_debug_var( NETCDF_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+
+  list( APPEND NETCDF_Fortran_LIBRARIES ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_F90_LIBRARIES} )
+  if( NETCDF_Fortran_LIBRARIES )
+    list( REMOVE_DUPLICATES NETCDF_Fortran_LIBRARIES )
+  endif()
+
+  # ecbuild_debug_var( NETCDF_Fortran_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_C_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_CXX_LIBRARIES )
+
+
+  set_package_properties( NetCDF4 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF4 file format" )
+
+  if( NETCDF_FOUND AND HDF5_FOUND )
+    # list( APPEND NETCDF_DEFINITIONS  ${HDF5_DEFINITIONS} )
+    list( APPEND NETCDF_LIBRARIES    ${HDF5_HL_LIBRARIES} ${HDF5_LIBRARIES}  )
+    list( APPEND NETCDF_INCLUDE_DIRS ${HDF5_INCLUDE_DIRS} )
+  endif()
+
+  #ecbuild_debug_var( NETCDF_FOUND )
+  #ecbuild_debug_var( NETCDF_LIBRARIES )
+  #ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_FOUND )
+  #ecbuild_debug_var( HDF5_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_HL_LIBRARIES )
+  #ecbuild_debug_var( HDF5_LIBRARIES )
+
+endif()
+
+### NetCDF3
+
+if( PREFER_NETCDF3 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF3" )
+
+  # ecbuild_debug_var( NetCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NetCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NetCDF_FIND_REQUIRED )
+
+  list(FIND NetCDF_FIND_COMPONENTS "CXX" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_CXX 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "Fortran" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "FORTRAN" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "F90" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  #message( "NETCDF CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}" )
+
+  find_package( NetCDF3 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+
+  set_package_properties( NetCDF3 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF3 file format" )
+
+endif()
diff --git a/cmake/FindNetCDF3.cmake b/cmake/FindNetCDF3.cmake
new file mode 100644
index 0000000..1783a72
--- /dev/null
+++ b/cmake/FindNetCDF3.cmake
@@ -0,0 +1,113 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF
+#
+# Input:
+#  * NETCDF_PATH     - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_DIR      - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_CXX      - search also for netcdf_c++ wrapper library
+#  * NETCDF_Fortran  - search also for netcdff wrapper library
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+### TODO: generalize this into a macro for all ecbuild
+
+if( DEFINED NETCDF_PATH )
+    list( APPEND _netcdf_incs ${NETCDF_PATH} ${NETCDF_PATH}/include )
+    list( APPEND _netcdf_libs ${NETCDF_PATH} ${NETCDF_PATH}/lib )
+endif()
+	
+if( DEFINED NETCDF_DIR )
+    list( APPEND _netcdf_incs ${NETCDF_DIR} ${NETCDF_DIR}/include )
+    list( APPEND _netcdf_libs ${NETCDF_DIR} ${NETCDF_DIR}/lib )
+endif()
+
+# Honour environment variables NETCDF_DIR, NETCDF_PATH
+list( APPEND _netcdf_incs ENV NETCDF_DIR ENV NETCDF_PATH )
+list( APPEND _netcdf_libs ENV NETCDF_DIR ENV NETCDF_PATH )
+
+###
+
+set( _inc_sfx netcdf include )
+set( _lib_sfx netcdf lib64 lib )
+
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH )
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  NO_DEFAULT_PATH )
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  )
+
+set( NETCDF_LIBRARIES    ${NETCDF_LIBRARY} )
+set( NETCDF_INCLUDE_DIRS ${NETCDF_INCLUDE_DIR} )
+
+mark_as_advanced(NETCDF_INCLUDE_DIR NETCDF_LIBRARY )
+
+list( APPEND NETCDF_REQUIRED_VARS NETCDF_LIBRARY NETCDF_INCLUDE_DIR )
+
+if( NETCDF_CXX )
+
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_cxx netcdf_c++ netcdf_c++ netcdf_c++4 )
+
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_CXX_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_CXX_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+    mark_as_advanced(NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+endif()
+
+if( NETCDF_Fortran )
+
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_fortran netcdff )
+
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_Fortran_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_Fortran_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+    mark_as_advanced(NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+if( NETCDF_FIND_QUIETLY )
+  set( NETCDF3_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+endif()
+if( NETCDF_FIND_REQUIRED )
+  set( NETCDF3_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+endif()
+
+# Handle the QUIET and REQUIRED arguments and set NETCDF3_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF3  DEFAULT_MSG ${NETCDF_REQUIRED_VARS} )
+
+set( NETCDF_FOUND ${NETCDF3_FOUND} )
+
diff --git a/cmake/FindODB.cmake b/cmake/FindODB.cmake
new file mode 100644
index 0000000..7c35d6e
--- /dev/null
+++ b/cmake/FindODB.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB
+# Once done this will define
+#  ODB_FOUND - System has ODB
+#  ODB_INCLUDE_DIRS - The ODB include directories
+#  ODB_LIBRARIES - The libraries needed to use ODB
+
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/include/odbdump.h
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/module/odb.mod
+
+# -lodb -lodbec -lifsaux -lmpi_serial -lodbdummy
+
+find_package( Dl ) # find the dynamic linker
+
+list( APPEND _odb_search_libs odb odbec ifsaux mpi_serial odbdummy  )
+
+if( DEFINED ODB_PATH )
+    find_path(ODB_INCLUDE_DIR odbdump.h PATHS  ${ODB_ROOT} ${ODB_ROOT}/include ${ODB_PATH} ${ODB_PATH}/include PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    find_path(ODB_MODULE_DIR odb.mod PATHS ${ODB_ROOT} ${ODB_ROOT}/module ${ODB_PATH} ${ODB_PATH}/module PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    foreach( _lib ${_odb_search_libs} )
+      find_library(ODB_LIBRARY_${_lib}  ${_lib} PATHS ${ODB_ROOT} ${ODB_ROOT}/lib ${ODB_PATH} ${ODB_PATH}/lib     PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    endforeach()
+endif()
+
+find_path(ODB_INCLUDE_DIR odbdump.h PATH_SUFFIXES odb )
+find_path(ODB_MODULE_DIR odb.mod PATH_SUFFIXES odb )
+foreach( _lib ${_odb_search_libs} )
+  find_library( ODB_LIBRARY_${_lib} ${_lib} PATH_SUFFIXES odb )
+endforeach()
+
+foreach( _lib ${_odb_search_libs} )
+  list( APPEND ODB_LIB_LIST   ODB_LIBRARY_${_lib} )
+  list( APPEND ODB_LIBRARIES  ${ODB_LIBRARY_${_lib}} )
+  mark_as_advanced(${ODB_LIBRARY_${_lib}})
+endforeach()
+
+set( ODB_INCLUDE_DIRS ${ODB_INCLUDE_DIR} ${ODB_MODULE_DIR})
+mark_as_advanced(ODB_INCLUDE_DIR )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ODB  DEFAULT_MSG
+                                  ODB_INCLUDE_DIR ${ODB_LIB_LIST} )
+
diff --git a/cmake/FindOpenCL.cmake b/cmake/FindOpenCL.cmake
new file mode 100644
index 0000000..510a3a9
--- /dev/null
+++ b/cmake/FindOpenCL.cmake
@@ -0,0 +1,70 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find OpenCL
+# Once done this will define
+#
+#  OPENCL_FOUND           - system has OpenCL
+#  OPENCL_INCLUDE_DIRS    - the OpenCL include directory
+#  OPENCL_LIBRARIES       - link these to use OpenCL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENCL_ROOT            - root folder of the OpenCL installation
+#  CUDA_TOOLKIT_ROOT_DIR  - root folder of the CUDA installation (ships OpenCL)
+#  CUDA_ROOT              - root folder of the CUDA installation (ships OpenCL)
+
+if(UNIX)
+
+  if(APPLE)
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATH_SUFFIXES lib )
+
+  else()
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+                 PATH_SUFFIXES lib64 lib )
+
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set OPENCL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( OpenCL DEFAULT_MSG
+                                   OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS )
+
+mark_as_advanced( OPENCL_INCLUDE_DIRS OPENCL_LIBRARIES )
diff --git a/cmake/FindOpenJPEG.cmake b/cmake/FindOpenJPEG.cmake
new file mode 100644
index 0000000..66d976e
--- /dev/null
+++ b/cmake/FindOpenJPEG.cmake
@@ -0,0 +1,54 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find the OpenJPEG includes and library (version 1.5.x or 2.1.x)
+# This module defines
+#
+#  OPENJPEG_FOUND         - System has OpenJPEG
+#  OPENJPEG_INCLUDE_DIRS  - the OpenJPEG include directories
+#  OPENJPEG_LIBRARIES     - the libraries needed to use OpenJPEG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENJPEG_DIR   - root folder of the OpenJPEG installation
+#  OPENJPEG_PATH  - root folder of the OpenJPEG installation
+
+# Note: OpenJPEG has a version-specific subdirectory in the include
+# e.g. include/openjpeg-2.0 or include/openjpeg-2.1.
+# Only version 1.5.x and 2.1.x are supported.
+# The library name is different for 1.x (libopenjpeg) and 2.x (libopenjp2).
+
+set( _suff include include/openjpeg include/openjpeg-1.5 include/openjpeg-2.1 )
+find_path( OPENJPEG_INCLUDE_DIR openjpeg.h
+           PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                 ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+           PATH_SUFFIXES ${_suff}
+           NO_DEFAULT_PATH )
+find_path( OPENJPEG_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES ${_suff} )
+unset( _suff )
+
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                    ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+              PATH_SUFFIXES lib lib/openjpeg
+              NO_DEFAULT_PATH )
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATH_SUFFIXES lib lib/openjpeg )
+
+set( OPENJPEG_LIBRARIES    ${OPENJPEG_LIBRARY} )
+set( OPENJPEG_INCLUDE_DIRS ${OPENJPEG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set OPENJPEG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(OpenJPEG  DEFAULT_MSG
+                                  OPENJPEG_LIBRARY OPENJPEG_INCLUDE_DIR)
+
+mark_as_advanced( OPENJPEG_INCLUDE_DIR OPENJPEG_LIBRARY )
diff --git a/cmake/FindPGIFortran.cmake b/cmake/FindPGIFortran.cmake
new file mode 100644
index 0000000..5d33239
--- /dev/null
+++ b/cmake/FindPGIFortran.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# FORTRAN support
+
+# set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl  pgftnrtl nspgc pgc rt pgsse1 pgsse2 ) # init
+# set( PGIFORTRAN_SEARCH_LIBS pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf pgc pgf90 rt pgsse1 pgsse2 )             # mars client linux.2
+# set( PGIFORTRAN_SEARCH_LIBS pgftnrtl nspgc pgc rt pgsse1 pgsse2 )                                                    # mars client linux.3
+
+if( NOT DEFINED PGIFORTRAN_SEARCH_LIBS )
+	set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf nspgc pgc pgf90 pgf902 pghpf_rpm1 pghpf2 pgsse1 pgsse2 ) # better ?                                                    #
+endif()
+
+set( pgi_fortran_all_libs_found 1 )
+
+foreach( pglib ${PGIFORTRAN_SEARCH_LIBS} )
+
+	find_library( ${pglib}_lib  ${pglib} PATHS ${PGI_PATH} PATH_SUFFIXES lib libso NO_DEFAULT_PATH )
+
+	find_library( ${pglib}_lib  ${pglib} HINTS /usr/local/apps/pgi/pgi-10.8/linux86-64/10.8 PATH PATH_SUFFIXES lib libso )
+
+    if( ${pglib}_lib )
+        list( APPEND PGIFORTRAN_LIBRARIES ${${pglib}_lib} )
+#	else()
+#		set( pgi_fortran_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set PGIFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( PGIFortran DEFAULT_MSG pgi_fortran_all_libs_found PGIFORTRAN_LIBRARIES  )
+
+if( PGIFORTRAN_FOUND )
+	find_package( Realtime )
+endif()
+
+if( REALTIME_FOUND )
+	set( PGIFORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} ${RT_LIB} )
+endif()
diff --git a/cmake/FindPango.cmake b/cmake/FindPango.cmake
new file mode 100644
index 0000000..f135864
--- /dev/null
+++ b/cmake/FindPango.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Pango
+
+# Output:
+#   PANGO_FOUND
+#   PANGO_LIBRARIES
+#   PANGO_INCLUDE_DIRS
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGO QUIET pango)
+
+ecbuild_debug_var( PC_LIBPANGO_FOUND )
+ecbuild_debug_var( PC_LIBPANGO_VERSION )
+ecbuild_debug_var( PC_LIBPANGO_LIBRARIES )
+ecbuild_debug_var( PC_LIBPANGO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set PANGO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( Pango DEFAULT_MSG PC_LIBPANGO_LIBRARIES PC_LIBPANGO_INCLUDE_DIRS )
+
+set( PANGO_VERSION ${PC_LIBPANGO_VERSION} )
+set( PANGO_LIBRARIES ${PC_LIBPANGO_LIBRARIES} )
+set( PANGO_INCLUDE_DIRS ${PC_LIBPANGO_INCLUDE_DIRS} )
diff --git a/cmake/FindPangoCairo.cmake b/cmake/FindPangoCairo.cmake
new file mode 100644
index 0000000..cb70737
--- /dev/null
+++ b/cmake/FindPangoCairo.cmake
@@ -0,0 +1,96 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find PangoCairo
+
+# Output:
+#   PANGOCAIRO_FOUND
+#   PANGOCAIRO_LIBRARIES
+#   PANGOCAIRO_INCLUDE_DIRS
+
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGOCAIRO QUIET pangocairo)
+
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_FOUND )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_VERSION )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LIBRARIES )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS_OTHER )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+
+if(PC_LIBPANGOCAIRO_FOUND)
+
+    include(FindPackageHandleStandardArgs)
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo DEFAULT_MSG PC_LIBPANGOCAIRO_LIBRARIES PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+    set( PANGOCAIRO_VERSION ${PC_LIBPANGOCAIRO_VERSION} )
+    set( PANGOCAIRO_LIBRARIES "${PC_LIBPANGOCAIRO_LDFLAGS} ${PC_LIBPANGOCAIRO_LDFLAGS_OTHER}" )
+    set( PANGOCAIRO_INCLUDE_DIRS ${PC_LIBPANGOCAIRO_INCLUDE_DIRS} )
+
+else()
+
+    # this is to get magics compiling on mac with macbrew
+
+    include(FindPackageHandleStandardArgs)
+
+    set(PANGO_VERSION 1.0)
+    set(GLIB_VERSION 2.0)
+
+    find_path( _PANGOCAIRO_INCLUDE_DIRS
+        NAMES pango/pangocairo.h
+        HINTS /usr/local/include PATH_SUFFIXES pango-${PANGO_VERSION})
+
+    find_path( _CAIRO_INCLUDE_DIRS
+        NAMES cairo.h
+        HINTS /usr/local/include PATH_SUFFIXES cairo)
+
+    find_path( _GLIB_INCLUDE_DIRS_1
+        NAMES glib.h
+        HINTS /usr/local/include PATH_SUFFIXES glib-${GLIB_VERSION})
+
+    find_path( _GLIB_INCLUDE_DIRS_2
+        NAMES glibconfig.h
+        HINTS /usr/local/lib/glib-${GLIB_VERSION} PATH_SUFFIXES include)
+
+
+    find_package(X11)
+
+    set(PANGOCAIRO_INCLUDE_DIRS
+        ${_PANGOCAIRO_INCLUDE_DIRS}
+        ${_CAIRO_INCLUDE_DIRS}
+        ${_GLIB_INCLUDE_DIRS_1}
+        ${_GLIB_INCLUDE_DIRS_2}
+	${X11_INCLUDE_DIR}
+    )
+
+    find_library( _PANGOCAIRO_LIBRARIES NAMES pangocairo pangocairo-${PANGO_VERSION})
+    find_library( _PANGO_LIBRARIES NAMES pango pango-${PANGO_VERSION})
+    find_library( _CAIRO_LIBRARIES NAMES cairo)
+    find_library( _GLIB_LIBRARIES NAMES glib-${GLIB_VERSION})
+
+    set(PANGOCAIRO_LIBRARIES
+        ${_PANGOCAIRO_LIBRARIES}
+        ${_PANGO_LIBRARIES}
+        ${_CAIRO_LIBRARIES}
+        ${_GLIB_LIBRARIES}
+	${X11_LIBRARIES}
+    )
+
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo  DEFAULT_MSG
+                                       PANGOCAIRO_LIBRARIES
+                                       PANGOCAIRO_INCLUDE_DIRS  )
+
+endif()
+
diff --git a/cmake/FindProj4.cmake b/cmake/FindProj4.cmake
new file mode 100644
index 0000000..a3560b2
--- /dev/null
+++ b/cmake/FindProj4.cmake
@@ -0,0 +1,69 @@
+# - Try to find the proj4 library
+# Once done this will define
+#
+# PROJ4_FOUND - system has proj4
+# PROJ4_INCLUDE_DIRS - the proj4 include directory
+# PROJ4_LIBRARIES - Link these to use proj4
+#
+# Define PROJ4_MIN_VERSION for which version desired.
+
+if( NOT PROJ4_PATH )
+    if ( NOT "$ENV{PROJ4_PATH}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_PATH}" )
+    elseif ( NOT "$ENV{PROJ4_DIR}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_DIR}" )
+    endif()
+endif()
+
+if( NOT PROJ4_PATH )
+
+    include(FindPkgConfig)
+
+#    if(Proj4_FIND_REQUIRED)
+#        set(_pkgconfig_REQUIRED "REQUIRED")
+#    else()
+#        set(_pkgconfig_REQUIRED "")
+#    endif()
+
+    if(PROJ4_MIN_VERSION)
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4>=${PROJ4_MIN_VERSION})
+    else()
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKPROJ4_FOUND )
+
+        find_path(PROJ4_INCLUDE_DIR proj_api.h HINTS ${PKPROJ4_INCLUDEDIR} ${PKPROJ4_INCLUDE_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+        find_library(PROJ4_LIBRARY  proj       HINTS ${PKPROJ4_LIBDIR}     ${PKPROJ4_LIBRARY_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+    endif()
+
+#    ecbuild_debug_var( PKG_CONFIG_FOUND )
+#    ecbuild_debug_var( PKPROJ4_FOUND )
+#    ecbuild_debug_var( PROJ4_MIN_VERSION )
+
+endif()
+
+if( PROJ4_PATH )
+
+    find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS ${PROJ4_PATH} ${PROJ4_PATH}/include PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+    find_library(PROJ4_LIBRARY  NAMES proj       PATHS ${PROJ4_PATH} ${PROJ4_PATH}/lib     PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+endif()
+
+find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS PATH_SUFFIXES proj4 )
+find_library( PROJ4_LIBRARY NAMES proj       PATHS PATH_SUFFIXES proj4 )
+
+
+# ecbuild_debug_var( PROJ4_INCLUDE_DIR )
+# ecbuild_debug_var( PROJ4_LIBRARY )
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Proj4  DEFAULT_MSG
+                                  PROJ4_LIBRARY PROJ4_INCLUDE_DIR)
+
+set( PROJ4_LIBRARIES    ${PROJ4_LIBRARY} )
+set( PROJ4_INCLUDE_DIRS ${PROJ4_INCLUDE_DIR} )
+
+mark_as_advanced( PROJ4_INCLUDE_DIR PROJ4_LIBRARY )
diff --git a/cmake/FindREADLINE.cmake b/cmake/FindREADLINE.cmake
new file mode 100644
index 0000000..a146c62
--- /dev/null
+++ b/cmake/FindREADLINE.cmake
@@ -0,0 +1,87 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find READLINE
+# Once done this will define
+#  READLINE_FOUND - System has READLINE
+#  READLINE_INCLUDE_DIRS - The READLINE include directories
+#  READLINE_LIBRARIES - The libraries needed to use READLINE
+#  READLINE_DEFINITIONS - Compiler switches required for using READLINE
+
+if( DEFINED READLINE_PATH )
+    find_path(READLINE_INCLUDE_DIR readline/readline.h PATHS ${READLINE_PATH}/include NO_DEFAULT_PATH)
+    find_library(READLINE_LIBRARY  readline            PATHS ${READLINE_PATH}/lib     PATH_SUFFIXES readline NO_DEFAULT_PATH)
+endif()
+
+find_path(READLINE_INCLUDE_DIR readline/readline.h )
+find_library( READLINE_LIBRARY readline            PATH_SUFFIXES readline )
+
+# check what version we got
+cmake_push_check_state()
+
+  set( CMAKE_REQUIRED_LIBRARIES ${READLINE_LIBRARY} )
+  set( CMAKE_REQUIRED_INCLUDES  ${READLINE_INCLUDE_DIR} )
+
+  # sometimes the link might fail missing -ltermcap or -l(n)curses
+  # if we searched before for Curses, then lets try to use it
+  if(CURSES_FOUND)
+      list( APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARIES} )
+      list( APPEND CMAKE_REQUIRED_INCLUDES  ${CURSES_INCLUDE_DIR} )
+  endif()
+
+  ecbuild_check_cxx_source_return(
+     "#include <stdio.h>
+      #include <readline/readline.h>
+      #include <iostream>
+      int main() {
+          std::cout << rl_library_version << std::flush;
+          return 0;
+     }"
+     VAR readline_version
+     OUTPUT __readline_version_out )
+
+cmake_pop_check_state()
+
+# ecbuild_debug_var( readline_version )
+# ecbuild_debug_var( __readline_version_out )
+
+set( __readline_fail 0 )
+if( __readline_version_out )
+
+    if( "${__readline_version_out}" MATCHES "^EditLine" )
+      message( STATUS "Found EditLine instead of Readline at '${READLINE_INCLUDE_DIR}'" )
+      if( READLINE_WRAPPER_OK )
+        set( READLINE_WRAPPER      "EditLine" )
+        set( __readline_fail 0 )
+      else()
+        message( STATUS "Readline wrapper not accepted -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+        set( __readline_fail 1 )
+      endif()
+    endif()
+
+else()
+    message( STATUS "Readline test run failed -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+    set( __readline_fail 1 )
+endif()
+
+if( __readline_fail )
+    set( READLINE_LIBRARY      READLINE_LIBRARY-NOTFOUND )
+    set( READLINE_INCLUDE_DIR  READLINE_INCLUDE_DIR-NOTFOUND )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(READLINE  DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR)
+
+if( READLINE_FOUND )
+    set( READLINE_VERSION      ${__readline_version_out} )
+    set( READLINE_LIBRARIES    ${READLINE_LIBRARY} )
+    set( READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR} )
+endif()
+
+mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY )
diff --git a/cmake/FindRPCGEN.cmake b/cmake/FindRPCGEN.cmake
new file mode 100644
index 0000000..42ed90c
--- /dev/null
+++ b/cmake/FindRPCGEN.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RPCGEN_FOUND           = prcgen was found
+# RPCGEN_EXECUTABLE      = the executable rpcgen
+
+if( DEFINED RPCGEN_PATH )
+    find_program( RPCGEN_EXECUTABLE NAMES rpcgen PATHS ${RPCGEN_PATH} PATH_SUFFIXES bin NO_DEFAULT_PATH )
+endif()
+find_program( RPCGEN_EXECUTABLE NAMES rpcgen )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( RPCGEN  DEFAULT_MSG RPCGEN_EXECUTABLE )
diff --git a/cmake/FindRealtime.cmake b/cmake/FindRealtime.cmake
new file mode 100644
index 0000000..78fa4c5
--- /dev/null
+++ b/cmake/FindRealtime.cmake
@@ -0,0 +1,24 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RT_LIB      = the library to link against
+
+if( DEFINED REALTIME_PATH )
+    find_library(RT_LIB rt PATHS ${REALTIME_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library( RT_LIB rt )
+
+mark_as_advanced( RT_LIB )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set REALTIME_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Realtime  DEFAULT_MSG RT_LIB )
diff --git a/cmake/FindSZip.cmake b/cmake/FindSZip.cmake
new file mode 100644
index 0000000..f005546
--- /dev/null
+++ b/cmake/FindSZip.cmake
@@ -0,0 +1,30 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find SZip
+# Once done this will define
+#  SZIP_FOUND - System has SZip
+#  SZIP_INCLUDE_DIRS - The SZip include directories
+#  SZIP_LIBRARIES - The libraries needed to use SZip
+
+if( DEFINED SZIP_PATH )
+    find_path( SZIP_INCLUDE_DIR szlib.h       PATHS ${SZIP_PATH}/include PATH_SUFFIXES szip NO_DEFAULT_PATH )
+    find_library( SZIP_LIBRARY  NAMES szip sz PATHS ${SZIP_PATH}/lib     PATH_SUFFIXES szip NO_DEFAULT_PATH )
+endif()
+
+find_path( SZIP_INCLUDE_DIR szlib.h PATH_SUFFIXES szip )
+find_library( SZIP_LIBRARY NAMES szip sz  PATH_SUFFIXES szip )
+
+set( SZIP_LIBRARIES    ${SZIP_LIBRARY} )
+set( SZIP_INCLUDE_DIRS ${SZIP_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SZip  DEFAULT_MSG SZIP_LIBRARY SZIP_INCLUDE_DIR)
+
+mark_as_advanced(SZIP_INCLUDE_DIR SZIP_LIBRARY )
diff --git a/cmake/FindTrilinos.cmake b/cmake/FindTrilinos.cmake
new file mode 100644
index 0000000..b1584f8
--- /dev/null
+++ b/cmake/FindTrilinos.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find the Trilinos library
+#
+# Needs environmental variables
+#   TRILINOS_PATH
+# Sets
+#   TRILINOS_INCLUDE_DIRS
+#   TRILINOS_LIBRARIES
+#   TRILINOS_FOUND
+
+# Try to find Trilinos using Trilinos recommendations
+
+if( DEFINED $ENV{TRILINOS_PATH} )
+    find_package(Trilinos PATHS $ENV{TRILINOS_PATH}/lib/cmake/Trilinos $ENV{TRILINOS_PATH}/include )
+endif()
+
+if( TRILINOS_PATH )
+    find_package(Trilinos PATHS ${TRILINOS_PATH}/lib/cmake/Trilinos ${TRILINOS_PATH}/include )
+endif()
+
+if( Trilinos_FOUND )
+
+        set( TRILINOS_INCLUDE_DIRS "" )
+
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_INCLUDE_DIRS} )
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_TPL_INCLUDE_DIRS} )
+
+        foreach( test_lib ${Trilinos_LIBRARIES} )
+          if(NOT ${test_lib} STREQUAL "pytrilinos")
+            find_library( ${test_lib}_lib ${test_lib} PATHS  ${Trilinos_LIBRARY_DIRS}  NO_DEFAULT_PATH)
+            find_library( ${test_lib}_lib ${test_lib})
+            mark_as_advanced( ${test_lib}_lib )
+            list( APPEND TRILINOS_LIBRARIES ${${test_lib}_lib} )
+          endif()
+        endforeach()
+
+        list( APPEND TRILINOS_LIBRARIES ${Trilinos_TPL_LIBRARIES} )
+
+    set( TRILINOS_FOUND TRUE )
+	set( TRILINOS_VERSION ${Trilinos_VERSION} )
+
+endif()
diff --git a/cmake/FindViennaCL.cmake b/cmake/FindViennaCL.cmake
new file mode 100644
index 0000000..8f442c9
--- /dev/null
+++ b/cmake/FindViennaCL.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find ViennaCL
+# Once done this will define
+#
+#  VIENNACL_FOUND         - system has ViennaCL
+#  VIENNACL_INCLUDE_DIRS  - the ViennaCL include directories
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  VIENNACL_PATH          - prefix path of the ViennaCL installation
+#
+# ViennaCL is header only, so there are no libraries to be found
+
+# Search with priority for VIENNACL_PATH if given as CMake or env var
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATHS ${VIENNACL_PATH} ENV VIENNACL_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATH_SUFFIXES include )
+
+set( VIENNACL_INCLUDE_DIRS ${VIENNACL_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set VIENNACL_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(ViennaCL  DEFAULT_MSG
+                                  VIENNACL_INCLUDE_DIR)
+
+mark_as_advanced(VIENNACL_INCLUDE_DIRS)
diff --git a/cmake/FindXLFortranLibs.cmake b/cmake/FindXLFortranLibs.cmake
new file mode 100644
index 0000000..80f2923
--- /dev/null
+++ b/cmake/FindXLFortranLibs.cmake
@@ -0,0 +1,51 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+
+list( APPEND xl_libs xlf90 xlopt xlf xlsmp )
+
+set( xlf_all_libs_found 1 )
+
+foreach( lib ${xl_libs} )
+
+	find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+	find_library( ${lib}_lib  ${lib} )
+
+	if( ${lib}_lib )
+        list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+	else()
+		set( xlf_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIET and REQUIRED arguments and set XLFORTRANLIBS_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( XLFortranLibs DEFAULT_MSG xlf_all_libs_found XLFORTRAN_LIBRARIES  )
+
+# HACK for support libraries
+
+if(  LIBXLFORTRAN_FOUND )
+	list( APPEND xl_extra_libs pthreads m essl )
+	foreach( lib ${xl_extra_libs} )
+
+		find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+		find_library( ${lib}_lib  ${lib} )
+
+		if( ${lib}_lib )
+			list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+		endif()
+
+	endforeach()
+endif()
+
diff --git a/cmake/FindYACC.cmake b/cmake/FindYACC.cmake
new file mode 100644
index 0000000..3eb3b5c
--- /dev/null
+++ b/cmake/FindYACC.cmake
@@ -0,0 +1,157 @@
+# - Find yacc executable and provides macros to generate custom build rules
+# The module defines the following variables:
+#
+#  YACC_EXECUTABLE - path to the yacc program
+#  YACC_FOUND - true if the program was found
+#
+# The minimum required version of yacc can be specified using the
+# standard CMake syntax, e.g. find_package(YACC 2.1.3)
+#
+# If yacc is found, the module defines the macros:
+#  YACC_TARGET(<Name> <YaccInput> <CodeOutput> [VERBOSE <file>]
+#              [COMPILE_FLAGS <string>])
+# which will create  a custom rule to generate  a parser. <YaccInput> is
+# the path to  a yacc file. <CodeOutput> is the name  of the source file
+# generated by yacc.  A header file is also  be generated, and contains
+# the  token  list.  If  COMPILE_FLAGS  option is  specified,  the  next
+# parameter is  added in the yacc  command line.  if  VERBOSE option is
+# specified, <file> is created  and contains verbose descriptions of the
+# grammar and parser. The macro defines a set of variables:
+#  YACC_${Name}_DEFINED - true is the macro ran successfully
+#  YACC_${Name}_INPUT - The input source file, an alias for <YaccInput>
+#  YACC_${Name}_OUTPUT_SOURCE - The source file generated by yacc
+#  YACC_${Name}_OUTPUT_HEADER - The header file generated by yacc
+#  YACC_${Name}_OUTPUTS - The sources files generated by yacc
+#  YACC_${Name}_COMPILE_FLAGS - Options used in the yacc command line
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(YACC_EXECUTABLE yacc DOC "path to the yacc/yacc executable")
+MARK_AS_ADVANCED(YACC_EXECUTABLE)
+
+IF(YACC_EXECUTABLE)
+  # the yacc commands should be executed with the C locale, otherwise
+  # the message (which are parsed) may be translated
+  SET(_Yacc_SAVED_LC_ALL "$ENV{LC_ALL}")
+  SET(ENV{LC_ALL} C)
+
+  SET(ENV{LC_ALL} ${_Yacc_SAVED_LC_ALL})
+
+  # internal macro
+  MACRO(YACC_TARGET_option_verbose Name YaccOutput filename)
+    LIST(APPEND YACC_TARGET_cmdopt "--verbose")
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_path "${YaccOutput}" PATH)
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_name "${YaccOutput}" NAME_WE)
+    ADD_CUSTOM_COMMAND(OUTPUT ${filename}
+      COMMAND ${CMAKE_COMMAND}
+      ARGS -E copy
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      "${filename}"
+      DEPENDS
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      COMMENT "[YACC][${Name}] Copying yacc verbose table to ${filename}"
+      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+    SET(YACC_${Name}_VERBOSE_FILE ${filename})
+    LIST(APPEND YACC_TARGET_extraoutputs
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output")
+  ENDMACRO(YACC_TARGET_option_verbose)
+
+  # internal macro
+  MACRO(YACC_TARGET_option_extraopts Options)
+    SET(YACC_TARGET_extraopts "${Options}")
+    SEPARATE_ARGUMENTS(YACC_TARGET_extraopts)
+    LIST(APPEND YACC_TARGET_cmdopt ${YACC_TARGET_extraopts})
+  ENDMACRO(YACC_TARGET_option_extraopts)
+
+  #============================================================
+  # YACC_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(YACC_TARGET Name YaccInput YaccOutput)
+    SET(YACC_TARGET_output_header "")
+    SET(YACC_TARGET_cmdopt "")
+    SET(YACC_TARGET_outputs "${YaccOutput}")
+    IF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+      MESSAGE(SEND_ERROR "Usage")
+    ELSE()
+      # Parsing parameters
+      IF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV4}")
+        ENDIF()
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV4}")
+        ENDIF()
+      ENDIF()
+
+      IF(${ARGC} EQUAL 7)
+        IF("${ARGV5}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV6}")
+        ENDIF()
+
+        IF("${ARGV5}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV6}")
+        ENDIF()
+      ENDIF()
+
+      # Header's name generated by yacc (see option -d)
+      LIST(APPEND YACC_TARGET_cmdopt "-d")
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${ARGV2}")
+      STRING(REPLACE "c" "h" _fileext ${_fileext})
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}"
+          YACC_${Name}_OUTPUT_HEADER "${ARGV2}")
+      LIST(APPEND YACC_TARGET_outputs "${YACC_${Name}_OUTPUT_HEADER}")
+
+#       message ( STATUS "${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}" )
+
+      ADD_CUSTOM_COMMAND(OUTPUT ${YACC_TARGET_outputs} ${YACC_TARGET_extraoutputs}
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${ARGV1} ${CMAKE_CURRENT_BINARY_DIR}
+        COMMAND ${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}
+        DEPENDS ${ARGV1}
+        COMMENT "[YACC][${Name}] Building parser with yacc"
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+      # define target variables
+      SET(YACC_${Name}_DEFINED TRUE)
+      SET(YACC_${Name}_INPUT ${ARGV1})
+      SET(YACC_${Name}_OUTPUTS ${YACC_TARGET_outputs})
+      SET(YACC_${Name}_COMPILE_FLAGS ${YACC_TARGET_cmdopt})
+      SET(YACC_${Name}_OUTPUT_SOURCE "${YaccOutput}")
+
+    ENDIF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+  ENDMACRO(YACC_TARGET)
+  #
+  #============================================================
+
+ENDIF(YACC_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(YACC REQUIRED_VARS  YACC_EXECUTABLE )
+
+# FindYACC.cmake ends here
diff --git a/cmake/Findgrib_api.cmake b/cmake/Findgrib_api.cmake
new file mode 100644
index 0000000..c6f6421
--- /dev/null
+++ b/cmake/Findgrib_api.cmake
@@ -0,0 +1,133 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find GRIB_API
+# Once done this will define
+#  GRIB_API_FOUND - System has GRIB_API
+#  GRIB_API_INCLUDE_DIRS - The GRIB_API include directories
+#  GRIB_API_LIBRARIES - The libraries needed to use GRIB_API
+#  GRIB_API_DEFINITIONS - Compiler switches required for using GRIB_API
+
+option( NO_GRIB_API_BINARIES "skip trying to find grib_api installed binaries" OFF )
+option( GRIB_API_PNG "use png with grib_api" ON )
+option( GRIB_API_JPG "use jpg with grib_api" ON )
+
+if( NOT grib_api_FOUND AND NOT NO_GRIB_API_BINARIES )
+
+    if( GRIB_API_JPG ) # jpeg support
+
+        find_package( JPEG     QUIET ) # grib_api might be a static .a library in which
+
+        if( NOT "$ENV{JASPER_PATH}" STREQUAL "" )
+            list( APPEND CMAKE_PREFIX_PATH "$ENV{JASPER_PATH}" )
+        endif()
+        find_package( Jasper   QUIET ) # case we don't know if which jpeg library was used
+
+        find_package( OpenJPEG QUIET ) # so we try to find all jpeg libs and link to them
+
+        if(JPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JPEG_LIBRARIES} )
+        endif()
+        if(JASPER_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JASPER_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JASPER_LIBRARIES} )
+        endif()
+        if(OPENJPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${OPENJPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${OPENJPEG_LIBRARIES} )
+        endif()
+
+    endif()
+
+    if( GRIB_API_PNG ) # png support
+
+        find_package(PNG)
+
+        if( DEFINED PNG_PNG_INCLUDE_DIR AND NOT DEFINED PNG_INCLUDE_DIRS )
+          set( PNG_INCLUDE_DIRS ${PNG_PNG_INCLUDE_DIR}  CACHE INTERNAL "PNG include dirs" )
+        endif()
+        if( DEFINED PNG_LIBRARY AND NOT DEFINED PNG_LIBRARIES )
+          set( PNG_LIBRARIES ${PNG_LIBRARY} CACHE INTERNAL "PNG libraries" )
+        endif()
+
+        if(PNG_FOUND)
+            list( APPEND _grib_api_png_defs ${PNG_DEFINITIONS} )
+            list( APPEND _grib_api_png_incs ${PNG_INCLUDE_DIRS} )
+            list( APPEND _grib_api_png_libs ${PNG_LIBRARIES} )
+        endif()
+
+    endif()
+
+	# The grib_api on macos that comes with 'port' is linked against ghostscript
+	if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+		find_library(GS_LIBRARIES NAMES gs)
+		if( GS_LIBRARIES )
+			list( APPEND GRIB_API_LIBRARIES ${GS_LIBRARIES} )
+		endif()
+	endif()
+
+    # find external grib_api
+
+    if( NOT DEFINED GRIB_API_PATH AND NOT "$ENV{GRIB_API_PATH}" STREQUAL "" )
+        list( APPEND GRIB_API_PATH "$ENV{GRIB_API_PATH}" )
+    endif()
+
+    if( DEFINED GRIB_API_PATH )
+        find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/include PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIBRARY  NAMES grib_api   PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F90  NAMES grib_api_f90 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F77  NAMES grib_api_f77 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_program(GRIB_API_INFO     NAMES grib_info  PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/bin     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+    endif()
+
+    find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIBRARY NAMES grib_api   PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F90 NAMES grib_api_f90 PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F77 NAMES grib_api_f77 PATHS PATH_SUFFIXES grib_api )
+    find_program(GRIB_API_INFO     NAMES grib_info  PATHS PATH_SUFFIXES grib_api )
+
+    list( APPEND GRIB_API_LIBRARIES    ${GRIB_API_LIBRARY} ${GRIB_API_LIB_F90} ${GRIB_API_LIB_F77} )
+    set( GRIB_API_INCLUDE_DIRS ${GRIB_API_INCLUDE_DIR} )
+
+    if( GRIB_API_INFO )
+
+        execute_process( COMMAND ${GRIB_API_INFO} -v  OUTPUT_VARIABLE _grib_info_out ERROR_VARIABLE _grib_info_err OUTPUT_STRIP_TRAILING_WHITESPACE )
+
+        # ecbuild_debug_var( _grib_info_out )
+
+        string( REPLACE "." " " _version_list ${_grib_info_out} ) # dots to spaces
+        separate_arguments( _version_list )
+
+        list( GET _version_list 0 GRIB_API_MAJOR_VERSION )
+        list( GET _version_list 1 GRIB_API_MINOR_VERSION )
+        list( GET _version_list 2 GRIB_API_PATCH_VERSION )
+
+        set( GRIB_API_VERSION     "${GRIB_API_MAJOR_VERSION}.${GRIB_API_MINOR_VERSION}.${GRIB_API_PATCH_VERSION}" )
+        set( GRIB_API_VERSION_STR "${_grib_info_out}" )
+
+        set( grib_api_VERSION     "${GRIB_API_VERSION}" )
+        set( grib_api_VERSION_STR "${GRIB_API_VERSION_STR}" )
+
+    endif()
+
+    include(FindPackageHandleStandardArgs)
+
+    # handle the QUIETLY and REQUIRED arguments and set GRIB_API_FOUND to TRUE
+    find_package_handle_standard_args( grib_api DEFAULT_MSG
+                                       GRIB_API_LIBRARY GRIB_API_INCLUDE_DIR GRIB_API_INFO )
+
+    mark_as_advanced( GRIB_API_INCLUDE_DIR GRIB_API_LIBRARY GRIB_API_INFO )
+
+    list( APPEND GRIB_API_DEFINITIONS  ${_grib_api_jpg_defs} ${_grib_api_png_defs} )
+    list( APPEND GRIB_API_INCLUDE_DIRS ${_grib_api_jpg_incs} ${_grib_api_png_incs} )
+	list( APPEND GRIB_API_LIBRARIES    ${_grib_api_jpg_libs} ${_grib_api_png_libs} )
+
+    set( grib_api_FOUND ${GRIB_API_FOUND} )
+
+endif()
diff --git a/cmake/Findodb_api.cmake b/cmake/Findodb_api.cmake
new file mode 100644
index 0000000..00f4152
--- /dev/null
+++ b/cmake/Findodb_api.cmake
@@ -0,0 +1,97 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT odb_api_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED ODB_API_PATH AND NOT "$ENV{ODB_API_PATH}" STREQUAL "" )
+        list( APPEND ODB_API_PATH "$ENV{ODB_API_PATH}" )
+    endif()
+
+    if( DEFINED ODB_API_PATH )
+
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS ${ODB_API_PATH} ${ODB_API_PATH}/include
+                   PATH_SUFFIXES odb_api
+                   NO_DEFAULT_PATH )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+    endif()
+    
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS
+                   PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+
+    # get the version
+
+    if( ODB_API_INCLUDE_DIR )
+
+        set(_odb_api_VERSION_REGEX "([0-9]+)")
+
+        foreach( v MAJOR MINOR PATCH )
+
+          file(STRINGS "${ODB_API_INCLUDE_DIR}/odb_api_config.h" _odb_api_${v}_VERSION_CONTENTS REGEX "#define ODB_API_${v}_VERSION ")
+
+          if( "${_odb_api_${v}_VERSION_CONTENTS}" MATCHES ".*#define ODB_API_${v}_VERSION ${_odb_api_VERSION_REGEX}.*")
+
+              set( ODB_API_${v}_VERSION "${CMAKE_MATCH_1}" )
+
+          endif()
+
+        endforeach()
+
+    endif()
+
+    set( ODB_API_VERSION     "${ODB_API_MAJOR_VERSION}.${ODB_API_MINOR_VERSION}.${ODB_API_PATCH_VERSION}" )
+    set( ODB_API_VERSION_STR "${_odb_info_out}" )
+
+    set( odb_api_VERSION     "${ODB_API_VERSION}" )
+    set( odb_api_VERSION_STR "${ODB_API_VERSION_STR}" )
+
+    # handle the QUIETLY and REQUIRED arguments and set ODB_API_FOUND to TRUE
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( odb_api DEFAULT_MSG
+                                       ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY ODB_API_INCLUDE_DIR )
+    
+    set( ODB_API_LIBRARIES    ${ODB_API_LIBRARY} ${ODB_API_ECLIB_LIBRARY} )
+    set( ODB_API_INCLUDE_DIRS ${ODB_API_INCLUDE_DIR} )
+
+    mark_as_advanced( ODB_API_INCLUDE_DIR ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY )
+    
+    set( odb_api_FOUND ${ODB_API_FOUND} )
+
+endif()
diff --git a/cmake/Findspot.cmake b/cmake/Findspot.cmake
new file mode 100644
index 0000000..d932bfe
--- /dev/null
+++ b/cmake/Findspot.cmake
@@ -0,0 +1,65 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT spot_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED SPOT_PATH AND NOT "$ENV{SPOT_PATH}" STREQUAL "" )
+        list( APPEND SPOT_PATH "$ENV{SPOT_PATH}" )
+    endif()
+
+    if( DEFINED SPOT_PATH )
+
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS ${SPOT_PATH} ${SPOT_PATH}/include
+                   NO_DEFAULT_PATH )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS ${SPOT_PATH} ${SPOT_PATH}/lib
+                      NO_DEFAULT_PATH )
+
+    endif()
+    
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS
+                   PATH_SUFFIXES spot )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES spot )
+
+
+    # get the version
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( spot DEFAULT_MSG
+                                       SPOT_LIBRARY SPOT_INCLUDE_DIR )
+    
+    set( SPOT_LIBRARIES    ${SPOT_LIBRARY} )
+    set( SPOT_INCLUDE_DIRS ${SPOT_INCLUDE_DIR} )
+
+    mark_as_advanced( SPOT_INCLUDE_DIR SPOT_LIBRARY )
+    
+    set( spot_FOUND ${SPOT_FOUND} )
+
+endif()
diff --git a/cmake/VERSION.cmake b/cmake/VERSION.cmake
new file mode 100644
index 0000000..a95e4a9
--- /dev/null
+++ b/cmake/VERSION.cmake
@@ -0,0 +1,7 @@
+set( ECBUILD_MAJOR_VERSION "2" )
+set( ECBUILD_MINOR_VERSION "7" )
+set( ECBUILD_PATCH_VERSION "0" )
+
+set( ECBUILD_VERSION_STR  "2.7.0" )
+
+set( ECBUILD_MACRO_VERSION "${ECBUILD_VERSION_STR}" )
diff --git a/cmake/compiler_flags/Clang_C.cmake b/cmake/compiler_flags/Clang_C.cmake
new file mode 100644
index 0000000..51cf72b
--- /dev/null
+++ b/cmake/compiler_flags/Clang_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/cmake/compiler_flags/Clang_CXX.cmake b/cmake/compiler_flags/Clang_CXX.cmake
new file mode 100644
index 0000000..7f6524e
--- /dev/null
+++ b/cmake/compiler_flags/Clang_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/cmake/compiler_flags/Cray_C.cmake b/cmake/compiler_flags/Cray_C.cmake
new file mode 100644
index 0000000..f9b6e4b
--- /dev/null
+++ b/cmake/compiler_flags/Cray_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C flags"  FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C flags"               FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug Cflags"                     FORCE )
diff --git a/cmake/compiler_flags/Cray_CXX.cmake b/cmake/compiler_flags/Cray_CXX.cmake
new file mode 100644
index 0000000..fdc4749
--- /dev/null
+++ b/cmake/compiler_flags/Cray_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C++ flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C++ flags" FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C++ flags"              FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C++ flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug CXX flags"                   FORCE )
diff --git a/cmake/compiler_flags/Cray_Fortran.cmake b/cmake/compiler_flags/Cray_Fortran.cmake
new file mode 100644
index 0000000..8575bba
--- /dev/null
+++ b/cmake/compiler_flags/Cray_Fortran.cmake
@@ -0,0 +1,15 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# -emf activates .mods and uses lower case
+# -rmoid produces a listing file
+set( CMAKE_Fortran_FLAGS_RELEASE        "-emf -rmoid -O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"                    CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-emf -rmoid -O2 -hfp1 -Gfast -DNDEBUG"                                 CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-emf -rmoid -O2 -hfp1 -G2"                                             CACHE STRING "Production Fortran flags"              FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-emf -rmoid -O2 -hfp1 -G2 -hflex_mp=conservative -hadd_paren -DNDEBUG" CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-emf -rmoid -O0 -G0"                                                   CACHE STRING "Debug Fortran flags"                   FORCE )
diff --git a/cmake/compiler_flags/GNU_C.cmake b/cmake/compiler_flags/GNU_C.cmake
new file mode 100644
index 0000000..288fc70
--- /dev/null
+++ b/cmake/compiler_flags/GNU_C.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/cmake/compiler_flags/GNU_CXX.cmake b/cmake/compiler_flags/GNU_CXX.cmake
new file mode 100644
index 0000000..1a01e2d
--- /dev/null
+++ b/cmake/compiler_flags/GNU_CXX.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/cmake/compiler_flags/GNU_Fortran.cmake b/cmake/compiler_flags/GNU_Fortran.cmake
new file mode 100644
index 0000000..cd4dbfa
--- /dev/null
+++ b/cmake/compiler_flags/GNU_Fortran.cmake
@@ -0,0 +1,23 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -funroll-all-loops -finline-functions"                                CACHE STRING "Fortran compiler flags for Release builds"          FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG -fno-range-check -fconvert=big-endian" CACHE STRING "Fortran compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -fcheck=bounds -fbacktrace -finit-real=snan"                       CACHE STRING "Fortran compiler flags for Debug builds"            FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for Production builds."      FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for RelWithDebInfo builds."  FORCE )
+
+set( Fortran_FLAG_STACK_ARRAYS "-fstack-arrays" )
+
+####################################################################
+
+# Meaning of flags
+# ----------------
+# -fstack-arrays     : Allocate automatic arrays on the stack (needs large stacksize!!!)
+# -funroll-all-loops : Unroll all loops
+# -fcheck=bounds     : Bounds checking
diff --git a/cmake/compiler_flags/Intel_C.cmake b/cmake/compiler_flags/Intel_C.cmake
new file mode 100644
index 0000000..5736004
--- /dev/null
+++ b/cmake/compiler_flags/Intel_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C compiler flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C compiler flags"  FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C compiler flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C compiler flags"                    FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C compiler flags"               FORCE )
diff --git a/cmake/compiler_flags/Intel_CXX.cmake b/cmake/compiler_flags/Intel_CXX.cmake
new file mode 100644
index 0000000..8e466e5
--- /dev/null
+++ b/cmake/compiler_flags/Intel_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C++ compiler flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C++ compiler flags" FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C++ compiler flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C++ compiler flags"                   FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C++ compiler flags"              FORCE )
diff --git a/cmake/compiler_flags/Intel_Fortran.cmake b/cmake/compiler_flags/Intel_Fortran.cmake
new file mode 100644
index 0000000..d6e9719
--- /dev/null
+++ b/cmake/compiler_flags/Intel_Fortran.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( Fortran_AUTOMATIC_ARRAYS_LIMIT 32768 )  # (32 kb)
+math( EXPR Fortran_AUTOMATIC_ARRAYS_LIMIT_KB "${Fortran_AUTOMATIC_ARRAYS_LIMIT}/1024" )
+
+set( Fortran_FLAG_STACK_ARRAYS     "-no-heap-arrays" )
+set( Fortran_FLAG_AUTOMATIC_ARRAYS "-heap-arrays ${Fortran_AUTOMATIC_ARRAYS_LIMIT_KB}" )
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-O2 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+# -check all implies -check bounds
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -traceback ${Fortran_FLAG_AUTOMATIC_ARRAYS} -check all" CACHE STRING "Debug Fortran flags"                   FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O3 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Production Fortran compiler flags"     FORCE )
diff --git a/cmake/compiler_flags/PGI_C.cmake b/cmake/compiler_flags/PGI_C.cmake
new file mode 100644
index 0000000..1cc6485
--- /dev/null
+++ b/cmake/compiler_flags/PGI_C.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C compiler flags" FORCE )
+
+set( CMAKE_C_LINK_FLAGS "" CACHE STRING "" )
diff --git a/cmake/compiler_flags/PGI_CXX.cmake b/cmake/compiler_flags/PGI_CXX.cmake
new file mode 100644
index 0000000..6d31cf4
--- /dev/null
+++ b/cmake/compiler_flags/PGI_CXX.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C++ compiler flags" FORCE )
+
+set( CMAKE_CXX_LINK_FLAGS "" CACHE STRING "" )
diff --git a/cmake/compiler_flags/PGI_Fortran.cmake b/cmake/compiler_flags/PGI_Fortran.cmake
new file mode 100644
index 0000000..86c2a35
--- /dev/null
+++ b/cmake/compiler_flags/PGI_Fortran.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE "-fast -O3" CACHE STRING "Release Fortran compiler flags" FORCE )
+
+set( CMAKE_Fortran_LINK_FLAGS "" CACHE STRING "" )
diff --git a/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake b/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
new file mode 100644
index 0000000..1b5178d
--- /dev/null
+++ b/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
@@ -0,0 +1,33 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# Do NOT include this module directly into any of your code. It is meant as
+# a library for Check*CompilerFlag.cmake modules. It's content may change in
+# any way between releases.
+
+macro (CHECK_COMPILER_FLAG_COMMON_PATTERNS _VAR)
+   set(${_VAR}
+     FAIL_REGEX "[Uu]nrecogni[sz]ed .*option"               # GNU, NAG
+     FAIL_REGEX "unknown .*option"                          # Clang
+     FAIL_REGEX "optimization flag .* not supported"        # Clang
+     FAIL_REGEX "unknown argument ignored"                  # Clang (cl)
+     FAIL_REGEX "ignoring unknown option"                   # MSVC, Intel
+     FAIL_REGEX "warning D9002"                             # MSVC, any lang
+     FAIL_REGEX "option.*not supported"                     # Intel
+     FAIL_REGEX "invalid argument .*option"                 # Intel
+     FAIL_REGEX "ignoring option .*argument required"       # Intel
+     FAIL_REGEX "ignoring option .*argument is of wrong type" # Intel
+     FAIL_REGEX "[Uu]nknown option"                         # HP
+     FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+     FAIL_REGEX "command option .* is not recognized"       # XL
+     FAIL_REGEX "command option .* contains an incorrect subargument" # XL
+     FAIL_REGEX "not supported in this configuration. ignored"       # AIX
+     FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+     FAIL_REGEX "[Uu]nknown switch"                         # PGI
+     FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+     FAIL_REGEX "Incorrect command line option:"            # Borland
+     FAIL_REGEX "Warning: illegal option"                   # SunStudio 12
+     FAIL_REGEX "[Ww]arning: Invalid suboption"             # Fujitsu
+   )
+endmacro ()
diff --git a/cmake/contrib/CheckFortranCompilerFlag.cmake b/cmake/contrib/CheckFortranCompilerFlag.cmake
new file mode 100644
index 0000000..8519fcc
--- /dev/null
+++ b/cmake/contrib/CheckFortranCompilerFlag.cmake
@@ -0,0 +1,53 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranCompilerFlag
+# ------------------------
+#
+# Check whether the Fortran compiler supports a given flag.
+#
+# CHECK_Fortran_COMPILER_FLAG(<flag> <var>)
+#
+# ::
+#
+#   <flag> - the compiler flag
+#   <var>  - variable to store the result
+#            Will be created as an internal cache variable.
+#
+# This internally calls the check_fortran_source_compiles macro and
+# sets CMAKE_REQUIRED_DEFINITIONS to <flag>.  See help for
+# CheckFortranSourceCompiles for a listing of variables that can
+# otherwise modify the build.  The result only tells that the compiler
+# does not give an error message when it encounters the flag.  If the
+# flag has any effect or even a specific one is beyond the scope of
+# this module.
+
+include(CheckFortranSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+macro (CHECK_Fortran_COMPILER_FLAG _FLAG _RESULT)
+  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+
+  # Normalize locale during test compilation.
+  set(_CheckFortranCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(_CheckFortranCompilerFlag_SAVED_${v} "$ENV{${v}}")
+    set(ENV{${v}} C)
+  endforeach()
+  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+  CHECK_Fortran_SOURCE_COMPILES("       program test\n       stop\n       end program" ${_RESULT}
+    # Some compilers do not fail with a bad flag
+    FAIL_REGEX "command line option .* is valid for .* but not for Fortran" # GNU
+    ${_CheckFortranCompilerFlag_COMMON_PATTERNS}
+    )
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(ENV{${v}} ${_CheckFortranCompilerFlag_SAVED_${v}})
+    unset(_CheckFortranCompilerFlag_SAVED_${v})
+  endforeach()
+  unset(_CheckFortranCompilerFlag_LOCALE_VARS)
+  unset(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+
+  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+endmacro ()
diff --git a/cmake/contrib/CheckFortranSourceCompiles.cmake b/cmake/contrib/CheckFortranSourceCompiles.cmake
new file mode 100644
index 0000000..c42254c
--- /dev/null
+++ b/cmake/contrib/CheckFortranSourceCompiles.cmake
@@ -0,0 +1,106 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranSourceCompiles
+# --------------------------
+#
+# Check if given Fortran source compiles and links into an executable::
+#
+#   CHECK_Fortran_SOURCE_COMPILES(<code> <var> [FAIL_REGEX <fail-regex>]
+#                                 [SRC_EXT <ext>])
+#
+# The arguments are:
+#
+# ``<code>``
+#   Source code to try to compile.  It must define a PROGRAM entry point.
+# ``<var>``
+#   Variable to store whether the source code compiled.
+#   Will be created as an internal cache variable.
+# ``FAIL_REGEX <fail-regex>``
+#   Fail if test output matches this regex.
+# ``SRC_EXT <ext>``
+#   Use source extension ``.<ext>`` instead of the default ``.F``.
+#
+# The following variables may be set before calling this macro to modify
+# the way the check is run::
+#
+#   CMAKE_REQUIRED_FLAGS = string of compile command line flags
+#   CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+#   CMAKE_REQUIRED_INCLUDES = list of include directories
+#   CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+#   CMAKE_REQUIRED_QUIET = execute quietly without messages
+
+macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(_FAIL_REGEX)
+    set(_SRC_EXT)
+    set(_key)
+    foreach(arg ${ARGN})
+      if("${arg}" MATCHES "^(FAIL_REGEX|SRC_EXT)$")
+        set(_key "${arg}")
+      elseif(_key)
+        list(APPEND _${_key} "${arg}")
+      else()
+        message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
+      endif()
+    endforeach()
+    if(NOT _SRC_EXT)
+      set(_SRC_EXT F)
+    endif()
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_compile(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+
+    foreach(_regex ${_FAIL_REGEX})
+      if("${OUTPUT}" MATCHES "${_regex}")
+        set(${VAR} 0)
+      endif()
+    endforeach()
+
+    if(${VAR})
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Fortran SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Fortran SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/cmake/contrib/FindEigen3.cmake b/cmake/contrib/FindEigen3.cmake
new file mode 100644
index 0000000..9315294
--- /dev/null
+++ b/cmake/contrib/FindEigen3.cmake
@@ -0,0 +1,97 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  else()
+	set( EIGEN3_VERSION ${EIGEN3_VERSION} CACHE INTERNAL "Eigen3 version" )
+  endif()
+
+endmacro(_eigen3_check_version)
+
+if(EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else(EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      ${EIGEN3_PATH}/include
+      ${EIGEN3_DIR}/include
+      ${EIGEN3_ROOT}/include
+      ${EIGEN_PATH}/include
+      ${EIGEN_DIR}/include
+      ${EIGEN_ROOT}/include
+      ENV EIGEN3_PATH
+      ENV EIGEN3_DIR
+      ENV EIGEN3_ROOT
+      ENV EIGEN_PATH
+      ENV EIGEN_DIR
+      ENV EIGEN_ROOT
+      PATH_SUFFIXES eigen3 eigen include/eigen3 include/eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+set( EIGEN3_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR} )
diff --git a/cmake/contrib/FindNetCDF4.cmake b/cmake/contrib/FindNetCDF4.cmake
new file mode 100644
index 0000000..70e54af
--- /dev/null
+++ b/cmake/contrib/FindNetCDF4.cmake
@@ -0,0 +1,337 @@
+# Project uclales
+# http://gitorious.org/uclales
+# License: Academic Free License v3.0
+#
+# - Find NETCDF, a library for reading and writing self describing array data.
+#
+# This module invokes the NETCDF wrapper compiler that should be installed
+# alongside NETCDF.  Depending upon the NETCDF Configuration, the wrapper compiler
+# is called either h5cc or h5pcc.  If this succeeds, the module will then call
+# the compiler with the -show argument to see what flags are used when compiling
+# an NETCDF client application.
+#
+# The module will optionally accept the COMPONENTS argument.  If no COMPONENTS
+# are specified, then the find module will default to finding only the NETCDF C
+# library.  If one or more COMPONENTS are specified, the module will attempt to
+# find the language bindings for the specified components.  Currently, the only
+# valid components are C, CXX, FORTRAN and F90.
+#
+# On UNIX systems, this module will read the variable NETCDF_USE_STATIC_LIBRARIES
+# to determine whether or not to prefer a static link to a dynamic link for NETCDF
+# and all of it's dependencies.  To use this feature, make sure that the
+# NETCDF_USE_STATIC_LIBRARIES variable is set before the call to find_package.
+#
+# To provide the module with a hint about where to find your NETCDF installation,
+# set the CMake or environment variable NETCDF_ROOT, NETCDF_DIR, NETCDF_PATH or
+# NETCDF4_DIR. The Find module will then look in this path when searching for
+# NETCDF executables, paths, and libraries.
+#
+# In addition to finding the includes and libraries required to compile an NETCDF
+# client application, this module also makes an effort to find tools that come
+# with the NETCDF distribution that may be useful for regression testing.
+#
+# This module will define the following variables:
+#  NETCDF_INCLUDE_DIRS - Location of the NETCDF includes
+#  NETCDF_INCLUDE_DIR - Location of the NETCDF includes (deprecated)
+#  NETCDF_DEFINITIONS - Required compiler definitions for NETCDF
+#  NETCDF_C_LIBRARIES - Required libraries for the NETCDF C bindings.
+#  NETCDF_CXX_LIBRARIES - Required libraries for the NETCDF C++ bindings
+#  NETCDF_FORTRAN_LIBRARIES - Required libraries for the NETCDF FORTRAN bindings
+#  NETCDF_F90_LIBRARIES - Required libraries for the NETCDF FORTRAN 90 bindings
+#  NETCDF_LIBRARIES - Required libraries for all requested bindings
+#  NETCDF_FOUND - true if NETCDF was found on the system
+#  NETCDF_LIBRARY_DIRS - the full set of library directories
+#  NETCDF_IS_PARALLEL - Whether or not NETCDF was found with parallel IO support
+#  NETCDF_CONFIG_EXECUTABLE - the path to the NC-CONFIG tool
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+# This module is maintained by Thijs Heus <thijs.heus at zmaw.de>.
+
+include(SelectLibraryConfigurations)
+include(FindPackageHandleStandardArgs)
+
+# List of the valid NETCDF components
+set( NETCDF_VALID_COMPONENTS
+    FORTRAN
+    F90
+    CXX
+    C
+)
+
+# Invoke the NETCDF wrapper compiler.  The compiler return value is stored to the
+# return_value argument, the text output is stored to the output variable.
+macro( _NETCDF_CONFIG flag output return_value )
+    if( NETCDF_CONFIG_EXECUTABLE )
+        exec_program( ${NETCDF_CONFIG_EXECUTABLE}
+            ARGS ${flag}
+            OUTPUT_VARIABLE ${output}
+            RETURN_VALUE ${return_value}
+        )
+        if( ${${return_value}} EQUAL 0 )
+            # do nothing
+        else()
+            message( STATUS
+              "Unable to determine ${flag} from NC-CONFIG." )
+        endif()
+    endif()
+endmacro()
+#
+# try to find the NETCDF wrapper compilers
+find_program( NETCDF_CONFIG_EXECUTABLE
+    NAMES nc-config
+    HINTS ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+          ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+    PATH_SUFFIXES bin Bin
+    DOC "NETCDF CONFIG PROGRAM.  Used only to detect NETCDF compile flags." )
+mark_as_advanced( NETCDF_CONFIG_EXECUTABLE )
+ecbuild_debug("FindNetCDF4: nc-config executable = ${NETCDF_CONFIG_EXECUTABLE}")
+
+set(output "no")
+_NETCDF_CONFIG (--has-hdf5 output return)
+set(HAS_HDF5 FALSE)
+
+if(${output} STREQUAL yes)
+  set(HAS_HDF5 TRUE)
+  set(HDF5_FIND_QUIETLY ${NETCDF_FIND_QUIETLY})
+  set(HDF5_FIND_REQUIRED ${NETCDF_FIND_REQUIRED})
+  find_package(HDF5)
+#        list( APPEND NETCDF_LIBRARIES_DEBUG
+#            ${HDF5_LIBRARIES_DEBUG} )
+#        list( APPEND NETCDF_LIBRARIES_RELEASE
+#            ${HDF5_LIBRARIES_RELEASE} )
+  set (NETCDF_IS_PARALLEL ${HDF5_IS_PARALLEL})
+endif()
+_NETCDF_CONFIG (--has-pnetcdf output return)
+if(${output} STREQUAL yes)
+  set (NETCDF_IS_PARALLEL TRUE)
+else()
+#   set(NETCDF_IS_PARALLEL FALSE)
+endif()
+set( NETCDF_IS_PARALLEL TRUE CACHE BOOL
+    "NETCDF library compiled with parallel IO support" )
+
+
+if( NETCDF_INCLUDE_DIRS AND NETCDF_LIBRARIES )
+    # Do nothing: we already have NETCDF_INCLUDE_PATH and NETCDF_LIBRARIES in the
+    # cache, it would be a shame to override them
+else()
+    if( NOT NETCDF_FIND_COMPONENTS )
+        set( NETCDF_LANGUAGE_BINDINGS "C" )
+    else()
+        # add the extra specified components, ensuring that they are valid.
+        foreach( component ${NETCDF_FIND_COMPONENTS} )
+            list( FIND NETCDF_VALID_COMPONENTS ${component} component_location )
+            if( ${component_location} EQUAL -1 )
+                message( FATAL_ERROR
+                    "\"${component}\" is not a valid NETCDF component." )
+            else()
+                list( APPEND NETCDF_LANGUAGE_BINDINGS ${component} )
+            endif()
+        endforeach()
+    endif()
+
+    # seed the initial lists of libraries to find with items we know we need
+    set( NETCDF_C_INCLUDE_NAMES netcdf.h )
+    set( NETCDF_CXX_INCLUDE_NAMES netcdfcpp.h ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_FORTRAN_INCLUDE_NAMES ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_F90_INCLUDE_NAMES netcdf.mod typesizes.mod ${NETCDF_C_INCLUDE_NAMES} )
+
+    set( NETCDF_C_LIBRARY_NAMES netcdf)
+    set( NETCDF_CXX_LIBRARY_NAMES netcdf_c++ netcdf_c++4 ${NETCDF_C_LIBRARY_NAMES} )
+    set( NETCDF_FORTRAN_LIBRARY_NAMES netcdff ${NETCDF_C_LIBRARY_NAMES})
+    set( NETCDF_F90_LIBRARY_NAMES ${NETCDF_FORTRAN_LIBRARY_NAMES} )
+
+    set( NETCDF_REQUIRED netcdf.h netcdfcpp.h netcdf.mod typesizes.mod netcdf netcdff netcdf_c++ netcdf_c++4)
+
+    foreach( LANGUAGE ${NETCDF_LANGUAGE_BINDINGS} )
+        ecbuild_debug("FindNetCDF4: looking for ${LANGUAGE} language bindings")
+
+        set( NETCDF_${LANGUAGE}_FOUND 1 ) # disable this in following if necessary
+
+        # find the NETCDF includes
+        foreach( INC ${NETCDF_${LANGUAGE}_INCLUDE_NAMES} )
+          #ecbuild_debug( "FindNetCDF4: looking for include file ${INC}")
+
+          find_path( NETCDF_${INC}_INCLUDE_DIR ${INC}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+          )
+          if( NOT NETCDF_${INC}_INCLUDE_DIR )
+            #ecbuild_debug( "FindNetCDF4: ${INC} not found" )
+            GET_FILENAME_COMPONENT( _basename ${INC} NAME_WE )
+            GET_FILENAME_COMPONENT( _ext ${INC} EXT )
+            string( TOUPPER ${_basename} _BASENAME )
+            set( INC_MOD "${_BASENAME}${_ext}")
+            #ecbuild_debug( "FindNetCDF4:     try ${INC_MOD}" )
+            find_path( NETCDF_${INC}_INCLUDE_DIR ${INC_MOD}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+            )
+          endif()
+
+          mark_as_advanced( NETCDF_${INC}_INCLUDE_DIR )
+          #ecbuild_debug_var( NETCDF_${INC}_INCLUDE_DIR)
+          if (NETCDF_${INC}_INCLUDE_DIR)
+            list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_${INC}_INCLUDE_DIR} )
+          else()
+            list( FIND NETCDF_REQUIRED ${INC} location )
+            if( ${location} EQUAL -1 )
+              else()
+              if(NETCDF_FIND_REQUIRED)
+                ecbuild_error( "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              endif()
+              set( NETCDF_${LANGUAGE}_FOUND 0 )
+            else()
+            endif()
+          endif()
+        endforeach()
+        # find the NETCDF libraries
+        foreach( LIB ${NETCDF_${LANGUAGE}_LIBRARY_NAMES} )
+            if( UNIX AND NETCDF_USE_STATIC_LIBRARIES )
+                # According to bug 1643 on the CMake bug tracker, this is the
+                # preferred method for searching for a static library.
+                # See http://www.cmake.org/Bug/view.php?id=1643.  We search
+                # first for the full static library name, but fall back to a
+                # generic search on the name if the static search fails.
+                set( THIS_LIBRARY_SEARCH_DEBUG lib${LIB}d.a ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} )
+            else()
+                set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} )
+            endif()
+            find_library( NETCDF_${LIB}_LIBRARY_DEBUG
+                NAMES ${THIS_LIBRARY_SEARCH_DEBUG}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib)
+            find_library( NETCDF_${LIB}_LIBRARY_RELEASE
+                NAMES ${THIS_LIBRARY_SEARCH_RELEASE}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib )
+            select_library_configurations( NETCDF_${LIB} )
+            # even though we adjusted the individual library names in
+            # select_library_configurations, we still need to distinguish
+            # between debug and release variants because NETCDF_LIBRARIES will
+            # need to specify different lists for debug and optimized builds.
+            # We can't just use the NETCDF_${LIB}_LIBRARY variable (which was set
+            # up by the selection macro above) because it may specify debug and
+            # optimized variants for a particular library, but a list of
+            # libraries is allowed to specify debug and optimized only once.
+          if (NETCDF_${LIB}_LIBRARY_RELEASE)
+            list( APPEND NETCDF_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_DEBUG)
+            list( APPEND NETCDF_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_RELEASE OR NETCDF_${LIB}_LIBRARY_DEBUG )
+          else()
+            list( FIND NETCDF_REQUIRED ${LIB} location )
+            if( ${location} EQUAL -1 )
+            else()
+              if(NETCDF_FIND_REQUIRED)
+                message( SEND_ERROR "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              else()
+                set( NETCDF_${LANGUAGE}_FOUND 0 )
+              endif()
+           endif()
+          endif()
+        endforeach()
+        list( APPEND NETCDF_LIBRARY_DIRS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS} )
+
+        # Append the libraries for this language binding to the list of all
+        # required libraries.
+
+        if( NETCDF_${LANGUAGE}_FOUND )
+            ecbuild_debug( "FindNetCDF4: ${LANGUAGE} language bindings found" )
+            if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    debug ${NETCDF_${LANGUAGE}_LIBRARIES_DEBUG}
+                    optimized ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            else()
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            endif()
+        endif()
+        # ecbuild_debug_var( NETCDF_${LANGUAGE}_LIBRARIES )
+        list( APPEND NETCDF_FOUND_REQUIRED_VARS NETCDF_${LANGUAGE}_FOUND )
+    endforeach()
+
+    # We may have picked up some duplicates in various lists during the above
+    # process for the language bindings (both the C and C++ bindings depend on
+    # libz for example).  Remove the duplicates.
+    if( NETCDF_INCLUDE_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+    endif()
+    if( NETCDF_LIBRARIES_DEBUG )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_DEBUG )
+    endif()
+    if( NETCDF_LIBRARIES_RELEASE )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_RELEASE )
+    endif()
+    if( NETCDF_LIBRARY_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARY_DIRS )
+    endif()
+
+    # Construct the complete list of NETCDF libraries with debug and optimized
+    # variants when the generator supports them.
+    if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+        set( NETCDF_LIBRARIES
+            debug ${NETCDF_LIBRARIES_DEBUG}
+            optimized ${NETCDF_LIBRARIES_RELEASE} )
+    else()
+        set( NETCDF_LIBRARIES ${NETCDF_LIBRARIES_RELEASE} )
+    endif()
+endif()
+
+set( NETCDF4_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+set( NETCDF4_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+# handle the QUIET and REQUIRED arguments and set NETCDF4_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF4 DEFAULT_MSG
+    ${NETCDF_FOUND_REQUIRED_VARS}
+    NETCDF_LIBRARIES
+    NETCDF_INCLUDE_DIRS
+)
+
+mark_as_advanced(
+    NETCDF_INCLUDE_DIRS
+    NETCDF_LIBRARIES
+    NETCDF_LIBRARY_DIRS
+)
+
+set( NETCDF_FOUND ${NETCDF4_FOUND} )
+
+# For backwards compatibility we set NETCDF_INCLUDE_DIR to the value of
+# NETCDF_INCLUDE_DIRS
+set( NETCDF_INCLUDE_DIR "${NETCDF_INCLUDE_DIRS}" )
+
diff --git a/cmake/contrib/FindNumPy.cmake b/cmake/contrib/FindNumPy.cmake
new file mode 100644
index 0000000..ba02cec
--- /dev/null
+++ b/cmake/contrib/FindNumPy.cmake
@@ -0,0 +1,101 @@
+# - Find the NumPy libraries
+# This module finds if NumPy is installed, and sets the following variables
+# indicating where it is.
+#
+# TODO: Update to provide the libraries and paths for linking npymath lib.
+#
+#  NUMPY_FOUND               - was NumPy found
+#  NUMPY_VERSION             - the version of NumPy found as a string
+#  NUMPY_VERSION_MAJOR       - the major version number of NumPy
+#  NUMPY_VERSION_MINOR       - the minor version number of NumPy
+#  NUMPY_VERSION_PATCH       - the patch version number of NumPy
+#  NUMPY_VERSION_DECIMAL     - e.g. version 1.6.1 is 10601
+#  NUMPY_INCLUDE_DIRS        - path to the NumPy include files
+
+#============================================================================
+# Copyright 2012 Continuum Analytics, Inc.
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+#============================================================================
+
+# Finding NumPy involves calling the Python interpreter
+if(NumPy_FIND_REQUIRED)
+    find_package(PythonInterp REQUIRED)
+else()
+    find_package(PythonInterp)
+endif()
+
+if(NOT PYTHONINTERP_FOUND)
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+    "import numpy as n; print(n.__version__); print(n.get_include());"
+    RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS
+    OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT
+    ERROR_VARIABLE _NUMPY_ERROR_VALUE
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0)
+    if(NumPy_FIND_REQUIRED)
+        message(FATAL_ERROR
+            "NumPy import failure:\n${_NUMPY_ERROR_VALUE}")
+    endif()
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+# Convert the process output into a list
+string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT})
+string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES})
+# Just in case there is unexpected output from the Python command.
+list(GET _NUMPY_VALUES -2 NUMPY_VERSION)
+list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS)
+
+string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}")
+if("${_VER_CHECK}" STREQUAL "")
+    # The output from Python was unexpected. Raise an error always
+    # here, because we found NumPy, but it appears to be corrupted somehow.
+    message(FATAL_ERROR
+        "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n")
+    return()
+endif()
+
+# Make sure all directory separators are '/'
+string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS})
+
+# Get the major and minor version numbers
+string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION})
+list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR)
+list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR)
+list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH)
+string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH})
+math(EXPR NUMPY_VERSION_DECIMAL
+    "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}")
+
+find_package_message(NUMPY
+    "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}"
+    "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}")
+
+set(NUMPY_FOUND TRUE)
\ No newline at end of file
diff --git a/cmake/contrib/GetGitRevisionDescription.cmake b/cmake/contrib/GetGitRevisionDescription.cmake
new file mode 100644
index 0000000..ea1da07
--- /dev/null
+++ b/cmake/contrib/GetGitRevisionDescription.cmake
@@ -0,0 +1,123 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+#  get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+#  git_describe(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+#  git_get_exact_tag(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+    return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+    set(GIT_PARENT_DIR "${PROJECT_SOURCE_DIR}")
+    set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    while(NOT EXISTS "${GIT_DIR}")  # .git dir not found, search parent directories
+        set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+        get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+        if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+            # We have reached the root directory, we are not in git
+            set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            return()
+        endif()
+        set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    endwhile()
+    set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+    if(NOT EXISTS "${GIT_DATA}")
+        file(MAKE_DIRECTORY "${GIT_DATA}")
+    endif()
+
+    if(NOT EXISTS "${GIT_DIR}/HEAD")
+        return()
+    endif()
+    set(HEAD_FILE "${GIT_DATA}/HEAD")
+    configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+    configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+        "${GIT_DATA}/grabRef.cmake"
+        @ONLY)
+    include("${GIT_DATA}/grabRef.cmake")
+
+    set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+    set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+    if(NOT GIT_FOUND)
+        find_package(Git QUIET)
+    endif()
+    get_git_head_revision(refspec hash)
+    if(NOT GIT_FOUND)
+        set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+    if(NOT hash)
+        set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+
+    # TODO sanitize
+    #if((${ARGN}" MATCHES "&&") OR
+    #   (ARGN MATCHES "||") OR
+    #   (ARGN MATCHES "\\;"))
+    #   message("Please report the following error to the project!")
+    #   message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+    #endif()
+
+    #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+    execute_process(COMMAND
+        "${GIT_EXECUTABLE}"
+        describe
+        ${hash}
+        ${ARGN}
+        WORKING_DIRECTORY
+        "${CMAKE_SOURCE_DIR}"
+        RESULT_VARIABLE
+        res
+        OUTPUT_VARIABLE
+        out
+        ERROR_QUIET
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if(NOT res EQUAL 0)
+        set(out "${out}-${res}-NOTFOUND")
+    endif()
+
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+    git_describe(out --exact-match ${ARGN})
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/contrib/GetGitRevisionDescription.cmake.in b/cmake/contrib/GetGitRevisionDescription.cmake.in
new file mode 100644
index 0000000..9fd3e64
--- /dev/null
+++ b/cmake/contrib/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,42 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+    # named branch
+    string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+    if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+        configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+    elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
+        configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+        set(HEAD_HASH "${HEAD_REF}")
+    endif()
+else()
+    # detached HEAD
+    configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+  if(EXISTS "@GIT_DATA@/head-ref")
+    file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+    string(STRIP "${HEAD_HASH}" HEAD_HASH)
+  else()
+    set(HEAD_HASH "unknown")
+  endif()
+endif()
diff --git a/cmake/contrib/GreatCMakeCookOff/.gitattributes b/cmake/contrib/GreatCMakeCookOff/.gitattributes
new file mode 100644
index 0000000..0b6acf1
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc  diff=astextplain
+*.DOC  diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF  diff=astextplain
+*.rtf  diff=astextplain
+*.RTF  diff=astextplain
diff --git a/cmake/contrib/GreatCMakeCookOff/.gitignore b/cmake/contrib/GreatCMakeCookOff/.gitignore
new file mode 100644
index 0000000..8a94597
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/.gitignore
@@ -0,0 +1,6 @@
+build/
+.*.swp
+.*.swo
+*.pyc
+wiki
+*.*~
diff --git a/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake b/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
new file mode 100644
index 0000000..ac7f456
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
@@ -0,0 +1,59 @@
+include(CheckCXXCompilerFlag)
+
+#�On older cmake versions + newer compilers, 
+#�the given version of CheckCXXCompilerFlags does not quite work.
+if(CMAKE_VERSION VERSION_LESS 2.8.9)
+  macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+     set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+     set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+     CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+       # Some compilers do not fail with a bad flag
+       FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+       FAIL_REGEX "unrecognized .*option"                     # GNU
+       FAIL_REGEX "unknown .*option"                          # Clang
+       FAIL_REGEX "ignoring unknown option"                   # MSVC
+       FAIL_REGEX "warning D9002"                             # MSVC, any lang
+       FAIL_REGEX "option.*not supported"                     # Intel
+       FAIL_REGEX "invalid argument .*option"                 # Intel
+       FAIL_REGEX "ignoring option .*argument required"       # Intel
+       FAIL_REGEX "[Uu]nknown option"                         # HP
+       FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+       FAIL_REGEX "command option .* is not recognized"       # XL
+       FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+       FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+       FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+       )
+     set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+  endmacro ()
+endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+if(MINGW) 
+  check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+  check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+endif(MINGW)
+if(has_std_gnupp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
+elseif(has_std_gnupp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
+elseif(has_std_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+elseif(has_std_cpp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+elseif(has_hstd_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -hstd=c++11")
+endif(has_std_gnupp11)
+
+if(MSVC) 
+  set(MSWINDOBE TRUE)
+  add_definitions(/EHsc)
+  # Wd4251 stops MSCrapWare from issuing meaningless warnings. Seems Microsoft engineers don't grok
+  # dynamic libraries yet. Or templates. Or both acting alone or together. In any case, issuing
+  # warning sure is easier on them than fixing  their OS.
+  # Unfortunately, it does disable warnings that may be of interest. Possibly. 
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_VARIADIC_MAX=10 /wd4251")
+endif(MSVC)
+
+set(PROJECT_USES_CPP11 True CACHE INTERNAL "Uses c++11.")
diff --git a/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake b/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
new file mode 100644
index 0000000..7db9357
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
@@ -0,0 +1,72 @@
+# CMake arguments for gtest.
+set(GTEST_CMAKE_ARGS 
+      -DBUILD_SHARED_LIBS=OFF
+      -Dgtest_force_shared_crt=ON
+      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+      -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
+      -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+      -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+      -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+      -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+      -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+      -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL})
+if(MINGW)
+  list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON)
+else(MINGW)
+  find_package(Threads)
+endif(MINGW)
+
+
+# Add gtest
+if(NOT EXTERNAL_ROOT)
+  set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+endif(NOT EXTERNAL_ROOT)
+include(ExternalProject)
+ExternalProject_Add(
+    googletest
+    PREFIX ${EXTERNAL_ROOT}
+    SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
+    TIMEOUT 10
+    # Force separate output paths for debug and release builds to allow easy
+    # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
+    CMAKE_ARGS ${GTEST_CMAKE_ARGS}
+    # Disable install step
+    INSTALL_COMMAND ""
+    # Wrap download, configure and build steps in a script to log output
+    LOG_DOWNLOAD ON
+    LOG_CONFIGURE ON
+    LOG_BUILD ON)
+
+if(PROJECT_USES_CPP11)
+  add_definitions(-DGTEST_LANG_CXX11)
+endif(PROJECT_USES_CPP11)
+
+macro(add_gtest name source)
+
+  ExternalProject_Get_Property(googletest source_dir)
+  include_directories(${source_dir}/include)
+  # Better, but only works on CMake 2.8.6?
+  # get_target_property(THISTEST_INCLUDE test_${name} INCLUDE_DIRECTORIES)
+  # set_target_properties(test_${name} PROPERTIES INCLUDE_DIRECTORIES
+  #                       "${source_dir}/include;${THISTEST_INCLUDE}") 
+
+  add_executable(test_${name} ${source})
+  ExternalProject_Get_Property(googletest binary_dir)
+  if(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/${CMAKE_CFG_INTDIR}/gtest.lib)
+  else(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/libgtest.a)
+  endif(MSVC)
+  if(CMAKE_THREAD_LIBS_INIT)
+    target_link_libraries(test_${name} ${CMAKE_THREAD_LIBS_INIT})
+  endif(CMAKE_THREAD_LIBS_INIT)
+
+  add_dependencies(test_${name} googletest)
+  if(NOT "${ARGN}" STREQUAL "")
+    target_link_libraries(test_${name} ${ARGN})
+  endif(NOT "${ARGN}" STREQUAL "")
+
+  add_test(cxx_${name} ${EXECUTABLE_OUTPUT_PATH}/test_${name}
+              --gtest_output=xml:${CMAKE_BINARY_DIR}/test-results/test_${name}.xml)
+  set_tests_properties(cxx_${name} PROPERTIES LABELS "gtest")
+endmacro()
diff --git a/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake b/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
new file mode 100644
index 0000000..593b62f
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
@@ -0,0 +1,198 @@
+# Checks for C++11 features
+#
+# USAGE: There are two functions
+#
+# cxx11_find_all_features(OUTPUT_VARIABLE)
+# This function returns a variable with all possible features.
+#
+# cxx11_feature_check([feature feature] [REQUIRED [feature feature]])
+# If no arguments are provided, then checks all available features
+# Features appeacing before REQUIRED are optional.
+# If arguments are provided and those features are available, sets
+# the variable HAS_CXX11_FEATURENAME, where FEATURENAME is the input in capital letters.
+# Fails if required feature are not available
+#
+# For possible features, please print out the result from the first function.
+#
+# Original script by Rolf Eike Beer
+# Modifications by Andreas Weis
+# Further Modifications by RSDT at UCL
+# Adapted to ecBuild by Florian Rathgeber <florian.rathgeber at ecmwf.int>
+
+set(CPP11_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp11 CACHE INTERNAL "c++11 file directory")
+
+MACRO(cxx11_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/cxx11_feature_tests/cxx11_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking C++11 support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.cpp")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(cxx11_check_single_feature)
+
+# Find list of all features
+function(cxx11_find_all_features outvar)
+  FILE(GLOB ALL_CPP11_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/*.cpp")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_CPP11_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_cxx11_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/${current_feature}*.cpp")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[c++11] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+  cxx11_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_cxx11_feature)
+
+function(cxx11_feature_check)
+
+  # find all features
+  cxx11_find_all_features(ALL_CPP11_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_CPP11_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[c++11] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # MinGW has not implemented std::random_device fully yet. Unfortunately, this can only be detected
+  # by running a program which tries to call std::random_device. However that generates an error that
+  # is *not* caught by CMake's try_run.
+  if(MSYS)
+    list(REMOVE_ITEM OPTIONALS "random_device")
+    list(FIND REQUIRED "random_device" feature_was_found)
+    if(NOT feature_was_found EQUAL "-1")
+      ecbuild_critical("[c++1] MSYS does not implement Random devices fully.\n"
+                       "       It cannot be required on this system.")
+    endif()
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_cxx11_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_cxx11_feature(${current_feature})
+    set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[c++11] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(cxx11_feature_check)
diff --git a/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake b/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
new file mode 100644
index 0000000..bc74e08
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
@@ -0,0 +1,53 @@
+# Tries and find which isnan to use
+# defines ISNAN_VARIATION, which should be included in a configuration files somewher as
+#   @ISNAN_VARIATION@
+if(ISNAN_VARIATION)
+  return()
+endif(ISNAN_VARIATION)
+
+include(CheckCXXSourceCompiles)
+CHECK_CXX_SOURCE_COMPILES(
+  "#include <cmath>\nint main() { bool a = std::isnan(0e0); return 0; }\n" 
+  CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = isnan(0e0); return 0; }\n" 
+    CXX_HAS_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS___ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "# include <float.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS_FLOAT_H_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+  message(FATAL_ERROR "[isnan] could not find standard function on this OS.")
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+
+if(CXX_HAS_STD_ISNAN)
+  set(ISNAN_HEADERS "#include <cmath>")
+  set(ISNAN_VARIATION "std::isnan")
+elseif(CXX_HAS_ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "::isnan")
+elseif(CXX_HAS___ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "__isnan")
+elseif(CXX_HAS_FLOAT_H_ISNAN)
+  set(ISNAN_HEADERS "#include <float.h>")
+  set(ISNAN_VARIATION "_isnan")
+else()
+  message(FATAL_ERROR "AM HERE")
+endif()
+if(ISNAN_VARIATION)
+  set(ISNAN_VARIATION ${ISNAN_VARIATION} CACHE INTERNAL "Definition for isnan\n")
+  set(ISNAN_HEADERS ${ISNAN_HEADERS} CACHE INTERNAL "Headers containing isnan definition\n")
+endif(ISNAN_VARIATION)
diff --git a/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake b/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
new file mode 100644
index 0000000..646d5dd
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
@@ -0,0 +1,131 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+# Modified by RSDT at UCL 
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  endif(NOT EIGEN3_VERSION_OK)
+endmacro(_eigen3_check_version)
+
+if(NOT EIGEN3_INCLUDE_DIR)
+  if(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+    set(EIGEN3_INCLUDE_DIR $ENV{EIGEN3_INCLUDE_DIR})
+  endif(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+endif(NOT EIGEN3_INCLUDE_DIR)
+if (EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else (EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      $ENV{HOME}/usr/include
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      /usr/include
+      /usr/local/include
+      ${EXTERNAL_ROOT}/include
+      PATH_SUFFIXES eigen3 eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+if(NOT EIGEN3_FOUND)
+  if(CMAKE_VERSION VERSION_LESS 2.8.10)
+    # Doesn't have Hg download prior to 2.8.10
+    message(FATAL_ERROR "Please install eigen.")
+  else(CMAKE_VERSION VERSION_LESS 2.8.10)
+    if(NOT EXTERNAL_ROOT)
+      set(EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/external)
+    endif(NOT EXTERNAL_ROOT)
+    find_package(Hg)
+    if(HG_FOUND)
+    
+      message(STATUS "Eigen3 not found. Will attempt to download it.")
+      include(ExternalProject)
+      ExternalProject_Add(
+          eigen
+          PREFIX ${EXTERNAL_ROOT}
+          HG_REPOSITORY https://bitbucket.org/eigen/eigen/
+          HG_TAG 3.2.0
+          TIMEOUT 10
+          CMAKE_ARGS 
+            -DCMAKE_INSTALL_PREFIX=${EXTERNAL_ROOT}
+            -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+            -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+            -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+            -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+            -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+            -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL}
+          # Wrap download, configure and build steps in a script to log output
+          LOG_DOWNLOAD ON
+          LOG_CONFIGURE ON
+          LOG_BUILD ON)
+      set(EIGEN3_INCLUDE_DIR ${EXTERNAL_ROOT}/include/eigen3)
+  
+    else(HG_FOUND)
+   
+      message(FATAL_ERROR "Hg not found, and eigen not found.\nNeed one or the other.")
+   
+    endif(HG_FOUND)
+  endif(CMAKE_VERSION VERSION_LESS 2.8.10)
+endif(NOT EIGEN3_FOUND)
diff --git a/cmake/contrib/GreatCMakeCookOff/LICENSE b/cmake/contrib/GreatCMakeCookOff/LICENSE
new file mode 100644
index 0000000..8dbb759
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 University College London
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cmake/contrib/GreatCMakeCookOff/README.md b/cmake/contrib/GreatCMakeCookOff/README.md
new file mode 100644
index 0000000..a0a6947
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/README.md
@@ -0,0 +1,176 @@
+The Great CMake CookOff
+=======================
+
+
+This is a repository of usefull and less than usefull cmake recipes.  It is distributed under the
+[MIT License](http://opensource.org/licenses/MIT)
+
+
+Adding [Eigen](http://eigen.tuxfamily.org/) to a project
+========================================================
+
+Looks for the Eigen installed on system. If not found, then uses external project to download it.
+Usage is as follows:
+
+```cmake
+# Tell cmake to look into GreatCMakeCookOff for recipes
+set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/GreatCMakeCookOff) 
+
+# Optionally, tell cmake where to download eigen, if needed.
+# Defaults to value below.
+set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+
+# Now look for cmake.
+find_package(Eigen)
+```
+
+**NOTE:** After building the first time, run cmake again. It will find the eigen it downloaded
+previously, and it will stop checking for updates. 
+
+Adding [GTest](https://code.google.com/p/googletest/) to a project
+==================================================================
+
+For googly reasons, whether valid or 404, GTest prefers to be compiled for each an every project. 
+This script does two things:
+
+- it adds GTest as an external project
+- it provides a function to add gtests to ctest
+
+This implies that GTest is downloaded the first time that make runs. Furthermore, it will be
+checked each and every time that makes runs. So, make now requires a working internet connection.
+Unlike Eigen above, there is currently no option avoid checking for updates.
+
+The CMakeLists.txt file could look like this:
+
+```cmake
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  find_package(GTest)
+  enable_testing()
+endif(tests)
+```
+
+And adding a test comes down to
+
+```cmake
+if(tests)
+
+  add_gtest(testme testme.cc mylib)
+
+endif(tests)
+```
+
+- first argument: name of the test
+- second argument: list of source files
+- other arguments: additional libraries to add during linking
+
+The test do expect an explicit main function. See the test generated in ``tests/addgtest.cmake``.
+
+**NOTE:** When using c++11, it is recommended to first include the c++11 flag script
+``AddCPP11Flags.cmake`` (see below) so that the gtest can be compiled with ``GTEST_LANG_CXX11``. 
+
+C++11
+=====
+
+Checking for specific features
+------------------------------
+
+Look for some c++11 features. Uses a script modified from [here](http://pageant.ghulbus.eu/?p=664).
+Usage is given below.
+
+```cmake
+# First need to enable c++
+enable_language(CXX)
+
+# The following will print out all available features.
+cxx11_find_all_features(ALL_FEATURES)
+message(STATUS "[c++11] features we can check for: ${ALL_FEATURES}")
+
+# The following checks for all features
+cxx11_feature_check()
+
+# An internal value is set if a particular feature exists.
+if(HAS_CXX11_AUTO)
+  message(STATUS "[c++11] has auto.")
+endif()
+if(HAS_CXX11_LAMBDA)
+  message(STATUS "[c++11] has lambda.")
+endif()
+```
+
+Alternatively, only a subset of features can be checked for, and some can be required:
+```cmake
+cxx11_feature_check(auto lambda REQUIRED long_long share_ptr variadic_templates)
+```
+The previous statement will fail if ``long long``, ``std::shared_ptr<...>``, and variadic templates
+are not available. It will also check for the availability of ``auto`` and ``lambda``, but without
+failing.
+
+Figuring out flags for some compilers
+-------------------------------------
+
+The script checks the existence of a few flags to enable c++11 features on different compilers.
+The output is somewhat verbose, but it seems to do the job for gcc, darwin-gcc, and microsoft visual
+studio. In addition, the intel compilers have to be told to use an external c++11 standard library.
+This script cannot figure where this library would be (hint: g++ provides it), so that is left up to
+the user. The script can be activated with a one liner.
+
+```cmake
+include("path/to/cookoff/AddCPP11Flags.cmake")
+```
+
+**NOTE:** On windows + visual studio, disables warnings 4251 and ups fake variadic templates to 10.
+
+
+Figure out ``isnan``
+====================
+
+Each and every vendor provides a different ``isnan``. There is a script to help define a portable
+c++ macro. It is meant to be used within a configuration file as follows:
+
+```cmake
+include("path/to/cookoff/CheckIsNaN.cmake")
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+configure_file(/path/to/config.h.in /path/to/config.h)
+```
+
+Two cmake variables are defined:
+
+- ISNAN_HEADERS will the header(s) relevant to the local ``isnan`` definition
+- ISNAN_VARIATIOPN is the fully qualified name to the local ``isnan`` definition
+
+They can be used as follows in a the configuration file ``config.h.in``:
+
+``cpp
+ at ISNAN_HEADERS@
+
+#define not_a_number(X) @ISNAN_VARIATION@
+``
+
+One should then use the macro ``not_a_number`` in-place of any ``isnan`` flavour.
+
+In c++11, it is also possible to define a function that takes only arithmetic type, thus obviating
+the need for a macro:
+
+```cpp
+ at ISNAN_HEADERS@
+#include <type_traits>
+
+template<class T>
+  typename std::enable_if<std::is_arithmetic<T>::value, bool> :: type 
+    not_a_number(T const &_in) { return @ISNAN_VARIATION@(_in); }
+```
+
+Testing CMake scripts
+=====================
+
+The file ``TestCMake.cmake`` contains a function to test cmake scripts. It converts an input cmake
+file into a project which is then configured, built, and run using ``ctest``. Unless an optional
+"SOURCE" is provided as argument, the test program is an empty ``main`` function returning 0. If the
+keyword is provided, then a ``main.cc`` or ``main.c`` file should provided the cmake script.
+
+For examples, look at the tests in this package.
diff --git a/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake b/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
new file mode 100644
index 0000000..4fc877a
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
@@ -0,0 +1,58 @@
+function(_args_have_option outvar optionname arglist)
+  list(FIND ${arglist} "${optionname}" HASOPTION)
+  if(HASOPTION EQUAL -1)
+    set(${outvar} False PARENT_SCOPE)
+  else()
+    set(${outvar} True PARENT_SCOPE)
+    list(REMOVE_ITEM ${arglist} ${optionname})
+    set(${arglist} ${${arglist}} PARENT_SCOPE)
+  endif()
+endfunction(_args_have_option)
+
+function(cmake_test testname)
+
+  # Parse further arguments
+  # Let caller create a source file
+  set(ALL_OPTIONS ${ARGN})
+  _args_have_option(SOURCE "SOURCE" ALL_OPTIONS)
+  # Let caller create executable to run
+  # Should be used with test-command
+  _args_have_option(NOEXEC "NOEXEC" ALL_OPTIONS)
+
+  # set source and build dir.
+  set(FAKE_PROJECT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${testname})
+  set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/fake_project_builds/${testname})
+
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${testname}.cmake 
+                 ${FAKE_PROJECT_DIR}/CMakeData.cmake @ONLY)
+  message(STATUS "[${testname}] project in ${FAKE_PROJECT_DIR}")
+
+  if(NOT SOURCE)
+    file(WRITE ${FAKE_PROJECT_DIR}/main.c "int main() { return 0; }" )
+  endif(NOT SOURCE)
+  file(WRITE ${FAKE_PROJECT_DIR}/CMakeLists.txt
+       "cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)\n"
+       "project(allfeatures)\n"
+       "include(\"${FAKE_PROJECT_DIR}/CMakeData.cmake\")\n"
+       "enable_language(C)\n"
+       "if(NOT ${NOEXEC})\n"
+       "  file(GLOB ALLFILES \${PROJECT_SOURCE_DIR}/*.c \${PROJECT_SOURCE_DIR}/*.cc)\n"
+       "  add_executable(${testname} \${ALLFILES})\n"
+       "endif(NOT ${NOEXEC})\n")
+  
+  
+  if(EXISTS ${BUILD_DIR})
+    file(REMOVE_RECURSE ${BUILD_DIR})
+  endif(EXISTS ${BUILD_DIR})
+  
+  file(MAKE_DIRECTORY ${BUILD_DIR})
+  
+  add_test(cmake_test_${testname}
+             ${CMAKE_CTEST_COMMAND} --build-and-test ${FAKE_PROJECT_DIR} ${BUILD_DIR}
+                                    --build-generator ${CMAKE_GENERATOR}
+                                    --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+                                    --build-project ${testname}
+                                    --build-options -Dcookoff_path=${CMAKE_SOURCE_DIR}/..
+                                    ${ALL_OPTIONS})
+
+endfunction(cmake_test)
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
new file mode 100644
index 0000000..d961df8
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
@@ -0,0 +1,8 @@
+#include <cstring>
+
+int main()
+{
+	if (!__func__) { return 1; }
+	if(std::strlen(__func__) <= 0) { return 1; }
+	return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
new file mode 100644
index 0000000..948648e
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
@@ -0,0 +1,12 @@
+
+int main()
+{
+	auto i = 5;
+	auto f = 3.14159f;
+	auto d = 3.14159;
+	bool ret = (
+		(sizeof(f) < sizeof(d)) &&
+		(sizeof(i) == sizeof(int))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc b/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
new file mode 100644
index 0000000..870ba92
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
@@ -0,0 +1,12 @@
+#include <iterator>
+#include <vector>
+#include <exception>
+
+int main() {
+  std::vector<int> vector(2, 1);
+  auto i_first = std::begin(vector);
+  auto i_end = std::end(vector);
+  if(i_first + 2 != i_end) return 1;
+  if(*i_first != 1) return 2;
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
new file mode 100644
index 0000000..ed62451
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
@@ -0,0 +1,19 @@
+constexpr int square(int x)
+{
+	return x*x;
+}
+
+constexpr int the_answer()
+{
+	return 42;
+}
+
+int main()
+{
+	int test_arr[square(3)];
+	bool ret = (
+		(square(the_answer()) == 1764) &&
+		(sizeof(test_arr)/sizeof(test_arr[0]) == 9)
+	);
+	return ret ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
new file mode 100644
index 0000000..4934d83
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
@@ -0,0 +1,32 @@
+#include <cmath>
+#include <assert.h>
+
+class A {
+  public:
+    int a;
+    double b;
+
+    A() : A(0, 0) {}
+    explicit A(int _a) : A(_a, 0) {}
+    explicit A(double _b) : A(0, _b) {}
+    A(int _a, double _b) : a(_a), b(_b) {}
+};
+
+int main() {
+  A instance(1, 1.5e0);
+  assert(instance.a == 1);
+  assert(std::abs(instance.b - 1.5) < 1e-8);
+
+  A instance1(1);
+  assert(instance1.a == 1);
+  assert(std::abs(instance1.b) < 1e-8);
+
+  A instance2(1.5);
+  assert(instance2.a == 0);
+  assert(std::abs(instance2.b - 1.5) < 1e-8);
+
+  A instance3;
+  assert(instance3.a == 0);
+  assert(std::abs(instance3.b) < 1e-8);
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
new file mode 100644
index 0000000..be2878f
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
@@ -0,0 +1,10 @@
+#include <cstdint>
+int main()
+{
+	bool test = 
+		(sizeof(int8_t) == 1) &&
+		(sizeof(int16_t) == 2) &&
+		(sizeof(int32_t) == 4) &&
+		(sizeof(int64_t) == 8);
+	return test ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
new file mode 100644
index 0000000..843f83a
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
@@ -0,0 +1,11 @@
+
+bool check_size(int i)
+{
+	return sizeof(int) == sizeof(decltype(i));
+}
+
+int main()
+{
+	bool ret = check_size(42);
+	return ret ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
new file mode 100644
index 0000000..565da27
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
@@ -0,0 +1,10 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
new file mode 100644
index 0000000..a71d565
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
@@ -0,0 +1,11 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  A second(first);
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
new file mode 100644
index 0000000..4eed94b
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
@@ -0,0 +1,8 @@
+#include <type_traits>
+
+template<int N>
+  typename std::enable_if<N==2, int>::type testme() { return 0; }
+
+int main() {
+  return testme<2>();
+};
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
new file mode 100644
index 0000000..37c97f9
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
@@ -0,0 +1,13 @@
+class A {
+  public:
+   A(int a = 5) : a_(a) {};
+   explicit operator bool() const { return a_ == 2; }
+  protected:
+   int a_;
+};
+
+int main () {
+  A a(6);
+  A const b(2);
+  return (static_cast<bool>(a) == false and static_cast<bool>(b) == true) ? 0: 1;  
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
new file mode 100644
index 0000000..462575f
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
@@ -0,0 +1,7 @@
+#include <vector>
+
+int main() {
+  std::vector<double> a{0, 1, 2, 3};
+  std::vector< std::vector<double> > b{ {5, 6, 7}, {8, 9, 10, 11} };
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
new file mode 100644
index 0000000..4c33ed5
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int ret = 0;
+	return ([&ret]() -> int { return ret; })();
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
new file mode 100644
index 0000000..8f005ee
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return sizeof(long double) > sizeof(double) ? 0: 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
new file mode 100644
index 0000000..0911127
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
@@ -0,0 +1,7 @@
+int main(void)
+{
+	long long l;
+	unsigned long long ul;
+
+	return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
new file mode 100644
index 0000000..0ea69d4
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
@@ -0,0 +1,9 @@
+class A {
+    public:
+    A() noexcept {};
+};
+int main()
+{
+   A a = A();
+   return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
new file mode 100644
index 0000000..c78fac4
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int* test = nullptr;
+	return test ? 1 : 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
new file mode 100644
index 0000000..7ab77a2
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int i = nullptr;
+	return 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
new file mode 100644
index 0000000..d8d4e0d
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    virtual void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
new file mode 100644
index 0000000..19f43c9
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
new file mode 100644
index 0000000..a777421
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
@@ -0,0 +1,13 @@
+#include <random>
+#include <sstream>
+
+int main()
+{
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<> dis(1, 6);
+    std::ostringstream sstr;
+    for(int n=0; n<10; ++n) sstr << dis(gen) << ' ';
+    return 0;
+}
+
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
new file mode 100644
index 0000000..75fb555
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
@@ -0,0 +1,15 @@
+int foo(int& lvalue)
+{
+	return 123;
+}
+
+int foo(int&& rvalue)
+{
+	return 321;
+}
+
+int main()
+{
+	int i = 42;
+	return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
new file mode 100644
index 0000000..4a4ff82
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::shared_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
new file mode 100644
index 0000000..a55fc09
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
@@ -0,0 +1,14 @@
+struct foo {
+	char bar;
+	int baz;
+};
+
+int main(void)
+{
+	bool ret = (
+		(sizeof(foo::bar) == 1) &&
+		(sizeof(foo::baz) >= sizeof(foo::bar)) &&
+		(sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
new file mode 100644
index 0000000..c3d74ca
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(0 < 1, "your ordering of integers is screwed");
+	return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
new file mode 100644
index 0000000..4cb1183
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(1 < 0, "this should fail");
+	return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
new file mode 100644
index 0000000..52881a9
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
@@ -0,0 +1,8 @@
+template<class A, class B> struct AClass {
+  typedef A t_first;
+  typedef B t_second;
+};
+
+template<class B> using Specialized = AClass<int, B>;
+
+int main() { return 0; }
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
new file mode 100644
index 0000000..8cf8319
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_trivially_move_constructible<int>::value;
+  bool const b = std::is_trivially_move_assignable<int>::value;
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
new file mode 100644
index 0000000..4344f76
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_move_constructible<int>::value;
+  bool const b = std::is_move_assignable<int>::value;
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
new file mode 100644
index 0000000..159591d
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::unique_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp b/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
new file mode 100644
index 0000000..4518e88
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
@@ -0,0 +1,23 @@
+int Accumulate()
+{
+	return 0;
+}
+
+template<typename T, typename... Ts>
+int Accumulate(T v, Ts... vs)
+{
+	return v + Accumulate(vs...);
+}
+
+template<int... Is>
+int CountElements()
+{
+	return sizeof...(Is);
+}
+
+int main()
+{
+	int acc = Accumulate(1, 2, 3, 4, -5);
+	int count = CountElements<1,2,3,4,5>();
+	return ((acc == 5) && (count == 5)) ? 0 : 1;
+}
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt b/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
new file mode 100644
index 0000000..f2eb4d0
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)
+project(COOKOFF_TEST)
+
+enable_testing()
+
+include(${PROJECT_SOURCE_DIR}/../TestCMake.cmake)
+
+cmake_test(checkisnan SOURCE)
+cmake_test(checkcpp11flags)
+cmake_test(addgtest NOEXEC SOURCE --test-command ${CMAKE_MAKE_PROGRAM} test)
+
+add_subdirectory(cpp11)
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake b/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
new file mode 100644
index 0000000..b1139d9
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
@@ -0,0 +1,20 @@
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  include(${cookoff_path}/AddGTest.cmake)
+  enable_testing()
+endif(tests)
+
+file(WRITE ${CMAKE_SOURCE_DIR}/mytest.cc 
+     "#include <gtest/gtest.h>\n\n"
+     "class TestMe : public ::testing::Test {};\n\n"
+     "TEST_F(TestMe, TestThis) {\n"
+     "  EXPECT_TRUE(true); \n"
+     "}\n\n"
+     "int main(int argc, char **argv) {\n"
+     "  ::testing::InitGoogleTest(&argc, argv);\n"
+     "  return RUN_ALL_TESTS();\n"
+     "}\n")
+
+
+add_gtest(mytest mytest.cc)
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake b/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
new file mode 100644
index 0000000..1f7e8a4
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
@@ -0,0 +1 @@
+include(${cookoff_path}/AddCPP11Flags.cmake)
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake b/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
new file mode 100644
index 0000000..e9cbdaa
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
@@ -0,0 +1,19 @@
+include(${cookoff_path}/CheckIsNaN.cmake)
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+enable_language(CXX)
+file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in
+     "\@ISNAN_HEADERS\@\n"
+     "#include <limits>\n"
+     "#include <exception>\n"
+     "#define not_a_number(X) \@ISNAN_VARIATION\@(X)\n"
+     "int main() {\n"
+     "  if(not_a_number(0.5e0)) throw std::exception(); \n"
+     "  if(not_a_number(2l)) throw std::exception(); \n"
+     "  if(not not_a_number(std::numeric_limits<double>::quiet_NaN())) throw std::exception(); \n"
+     "  return 0;\n"
+     "}\n")
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
new file mode 100644
index 0000000..45d20a5
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_test(allfeatures)
+cmake_test(parse_input_features)
+cmake_test(check_features)
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
new file mode 100644
index 0000000..f4dec87
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
@@ -0,0 +1,7 @@
+enable_language(CXX)
+include(${cookoff_path}/CheckCXX11Features.cmake)
+cxx11_find_all_features(ALL_CPP11_FEATURES)
+LIST(LENGTH ALL_CPP11_FEATURES LIST_LENGTH)
+if(${LIST_LENGTH} EQUAL 0)
+  message(FATAL_ERROR "No c++11 features found")
+endif()
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
new file mode 100644
index 0000000..1ea2fdb
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
@@ -0,0 +1,9 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function to test.
+cxx11_feature_check("auto")
+cxx11_feature_check()
diff --git a/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
new file mode 100644
index 0000000..ca88de0
--- /dev/null
+++ b/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
@@ -0,0 +1,68 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function with fake input
+set(ALL_FEATURES "first;second;third")
+
+# No input OPTIONALS == ALL_FEATURES
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "")
+if(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+  message(FATAL_ERROR "OPTIONALS results should contain everything.") 
+endif(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif(NOT "${REQUIRED}" STREQUAL "")
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif(NOT "${ERRORS}" STREQUAL "")
+
+# Single input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "second")
+if(NOT "${OPTIONALS}" STREQUAL "second")
+  message(FATAL_ERROR "OPTIONALS results should second.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# Single error input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "none")
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
+
+# Single valid input with REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS REQUIRED first)
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# one of each
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS second third REQUIRED first none)
+if(NOT "${OPTIONALS}" STREQUAL "second;third")
+  message(FATAL_ERROR "OPTIONALS results should be second;optional.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
diff --git a/cmake/ecbuild_add_c_flags.cmake b/cmake/ecbuild_add_c_flags.cmake
new file mode 100644
index 0000000..138e4d8
--- /dev/null
+++ b/cmake/ecbuild_add_c_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_c_flags
+# ===================
+#
+# Add C compiler flags to CMAKE_C_FLAGS only if supported by the compiler. ::
+#
+#   ecbuild_add_c_flags( <flag1> [ <flag2> ... ]
+#                        [ BUILD <build> ]
+#                        [ NAME <name> ]
+#                        [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_C_FLAGS_<build>`` instead of ``CMAKE_C_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_c_flags m_c_flags )
+
+  set( _flags ${m_c_flags} )
+
+  if( _flags AND CMAKE_C_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+      if( NOT DEFINED N_CFLAG )
+        set( N_CFLAG 0 )
+      endif()
+
+      math( EXPR N_CFLAG '${N_CFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_c_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_c_compiler_flag( ${_flags} C_FLAG_TEST_${N_CFLAG} )
+          set( _flag_ok ${C_FLAG_TEST_${N_CFLAG}} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_C_FLAGS_${_PAR_BUILD} "${CMAKE_C_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised C flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised C flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_c_flags m_c_flags )
+  ecbuild_deprecate( " cmake_add_c_flags is deprecated, use ecbuild_add_c_flags instead." )
+  ecbuild_add_c_flags( ${m_c_flags} )
+endmacro()
diff --git a/cmake/ecbuild_add_cxx11_flags.cmake b/cmake/ecbuild_add_cxx11_flags.cmake
new file mode 100644
index 0000000..6c5c861
--- /dev/null
+++ b/cmake/ecbuild_add_cxx11_flags.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx11_flags
+# =======================
+#
+# Add cxx11 flags to CXX compilation flags. ::
+#
+#   ecbuild_add_cxx11_flags()
+#
+# This macro uses macros from http://github.com/UCL/GreatCMakeCookOff.
+#
+##############################################################################
+
+macro( ecbuild_add_cxx11_flags )
+
+	# if( CMAKE_COMPILER_IS_GNUCXX )
+	# 	if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7 )
+	# 		ecbuild_add_cxx_flags("-std=c++0x")
+	# 	else()
+	# 		ecbuild_add_cxx_flags("-std=c++11")
+	# 	endif()
+	# endif()
+
+	include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake )
+
+endmacro( ecbuild_add_cxx11_flags )
diff --git a/cmake/ecbuild_add_cxx_flags.cmake b/cmake/ecbuild_add_cxx_flags.cmake
new file mode 100644
index 0000000..699c50a
--- /dev/null
+++ b/cmake/ecbuild_add_cxx_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx_flags
+# =====================
+#
+# Add C++ compiler flags to CMAKE_CXX_FLAGS only if supported by compiler. ::
+#
+#   ecbuild_add_cxx_flags( <flag1> [ <flag2> ... ]
+#                          [ BUILD <build> ]
+#                          [ NAME <name> ]
+#                          [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_CXX_FLAGS_<build>`` instead of ``CMAKE_CXX_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_cxx_flags m_cxx_flags )
+
+  set( _flags ${m_cxx_flags} )
+  if( _flags AND CMAKE_CXX_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+
+      if( NOT DEFINED N_CXXFLAG )
+        set( N_CXXFLAG 0 )
+      endif()
+
+      math( EXPR N_CXXFLAG '${N_CXXFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_cxx_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_cxx_compiler_flag( ${_flags} CXX_FLAG_TEST_${N_CXXFLAG} )
+          set( _flag_ok CXX_FLAG_TEST_${N_CXXFLAG} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_CXX_FLAGS_${_PAR_BUILD} "${CMAKE_CXX_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised CXX flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised CXX flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_cxx_flags m_cxx_flags )
+  ecbuild_deprecate( " cmake_add_cxx_flags is deprecated, use ecbuild_add_cxx_flags instead." )
+  ecbuild_add_cxx_flags( ${m_cxx_flags} )
+endmacro()
diff --git a/cmake/ecbuild_add_executable.cmake b/cmake/ecbuild_add_executable.cmake
new file mode 100644
index 0000000..f6cb128
--- /dev/null
+++ b/cmake/ecbuild_add_executable.cmake
@@ -0,0 +1,346 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_executable
+# ======================
+#
+# Add an executable with a given list of source files. ::
+#
+#   ecbuild_add_executable( TARGET <name>
+#                           SOURCES <source1> [<source2> ...]
+#                           [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                           [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                           [ OBJECTS <obj1> [<obj2> ...] ]
+#                           [ TEMPLATES <template1> [<template2> ...] ]
+#                           [ LIBS <library1> [<library2> ...] ]
+#                           [ INCLUDES <path1> [<path2> ...] ]
+#                           [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                           [ PERSISTENT <file1> [<file2> ...] ]
+#                           [ GENERATED <file1> [<file2> ...] ]
+#                           [ DEPENDS <target1> [<target2> ...] ]
+#                           [ CONDITION <condition> ]
+#                           [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                           [ NOINSTALL ]
+#                           [ VERSION <version> | AUTO_VERSION ]
+#                           [ CFLAGS <flag1> [<flag2> ...] ]
+#                           [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                           [ FFLAGS <flag1> [<flag2> ...] ]
+#                           [ LINKER_LANGUAGE <lang> ]
+#                           [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the executable
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   version to use as executable version
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   automatically version the executable with the package version
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_executable )
+
+  set( options NOINSTALL AUTO_VERSION )
+  set( single_value_args TARGET COMPONENT LINKER_LANGUAGE VERSION OUTPUT_NAME )
+  set( multi_value_args SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                        TEMPLATES LIBS INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                        CFLAGS CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_executable() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_executable() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+        ecbuild_error("ecbuild_add_executable(${_PAR_TARGET}): CUDA source files detected"
+                      "but CUDA was not found.")
+      endif()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): CUDA sources detected."
+                    "Building executable with ecbuild_add_executable() rather than intrinsic"
+                    "add_executable().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+    else()
+      cuda_add_executable( ${_PAR_TARGET} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list(REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION )
+        ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      endif()
+    endif()
+
+    # installation
+
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): installing to ${INSTALL_BIN_DIR}")
+
+      # add installation paths and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+               EXPORT  ${PROJECT_NAME}-targets
+               RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+               LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+               ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #        COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous command)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    else()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): not installing")
+      # NOINSTALL targets are always built the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_exe( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_EXES ${${PROJECT_NAME}_ALL_EXES} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endmacro( ecbuild_add_executable  )
diff --git a/cmake/ecbuild_add_extra_search_paths.cmake b/cmake/ecbuild_add_extra_search_paths.cmake
new file mode 100644
index 0000000..690cde6
--- /dev/null
+++ b/cmake/ecbuild_add_extra_search_paths.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+#
+# macro for adding search paths to CMAKE_PREFIX_PATH
+# for example the ECMWF /usr/local/apps paths
+#
+# usage: ecbuild_add_extra_search_paths( netcdf4 )
+
+function( ecbuild_add_extra_search_paths pkg )
+
+  ecbuild_deprecate( " ecbuild_add_extra_search_paths modifies CMAKE_PREFIX_PATH,"
+                     " which can affect future package discovery if not undone by the caller."
+                     " The current CMAKE_PREFIX_PATH is being backed up as _CMAKE_PREFIX_PATH"
+                     " so it can later be restored." )
+
+  # Back up current CMAKE_PREFIX_PATH so the caller can reset it
+  set( _CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+
+  string( TOUPPER ${pkg} _PKG )
+
+  ecbuild_list_extra_search_paths( ${pkg} CMAKE_PREFIX_PATH )
+
+  set( CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+  # ecbuild_debug_var( CMAKE_PREFIX_PATH )
+
+endfunction()
diff --git a/cmake/ecbuild_add_fortran_flags.cmake b/cmake/ecbuild_add_fortran_flags.cmake
new file mode 100644
index 0000000..2ac89e8
--- /dev/null
+++ b/cmake/ecbuild_add_fortran_flags.cmake
@@ -0,0 +1,109 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_fortran_flags
+# =========================
+#
+# Add Fortran compiler flags to CMAKE_Fortran_FLAGS only if supported by the
+# compiler. ::
+#
+#   ecbuild_add_fortran_flags( <flag1> [ <flag2> ... ]
+#                              [ BUILD <build> ]
+#                              [ NAME <name> ]
+#                              [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_add_fortran_flags m_fortran_flags )
+
+  set( _flags ${m_fortran_flags} )
+
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+
+    if( _try_add_flag )
+      if( NOT DEFINED N_FortranFLAG )
+        set( N_FortranFLAG 0 )
+      endif()
+
+      math( EXPR N_FortranFLAG '${N_FortranFLAG}+1' )
+
+      if( ECBUILD_TRUST_FLAGS )
+        set( _flag_ok 1 )
+      # Due to a bug in CMake < 3.0, check_fortran_compiler_flag ALWAYS fails with ifort
+      # see https://cmake.org/Bug/view.php?id=14507
+      elseif( CMAKE_MAJOR_VERSION LESS 3 AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+        set( _flag_ok 1 )
+        ecbuild_warn( "Not testing Fortran flags due to a bug in CMake < 3.0 with ifort" )
+      else()
+        if( DEFINED _PAR_NAME )
+          check_fortran_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_fortran_compiler_flag( ${_flags} Fortran_FLAG_TEST_${N_FortranFLAG} )
+          set( _flag_ok ${Fortran_FLAG_TEST_${N_FortranFLAG}} )
+        endif()
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_Fortran_FLAGS_${_PAR_BUILD} "${CMAKE_Fortran_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised Fortran flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised Fortran flag [${_flags}]" )
+      endif()
+    endif()
+
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_fortran_flags m_fortran_flags )
+  ecbuild_deprecate( " cmake_add_fortran_flags is deprecated, use ecbuild_add_fortran_flags instead." )
+  ecbuild_add_fortran_flags( ${m_fortran_flags} )
+endmacro()
diff --git a/cmake/ecbuild_add_library.cmake b/cmake/ecbuild_add_library.cmake
new file mode 100644
index 0000000..f9f567f
--- /dev/null
+++ b/cmake/ecbuild_add_library.cmake
@@ -0,0 +1,578 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_library
+# ===================
+#
+# Add a library with a given list of source files. ::
+#
+#   ecbuild_add_library( TARGET <name>
+#                        SOURCES <source1> [<source2> ...]
+#                        [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                        [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                        [ TYPE SHARED|STATIC|MODULE|OBJECT ]
+#                        [ OBJECTS <obj1> [<obj2> ...] ]
+#                        [ TEMPLATES <template1> [<template2> ...] ]
+#                        [ LIBS <library1> [<library2> ...] ]
+#                        [ INCLUDES <path1> [<path2> ...] ]
+#                        [ PRIVATE_INCLUDES <path1> [<path2> ...] ]
+#                        [ PUBLIC_INCLUDES <path1> [<path2> ...] ]
+#                        [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                        [ PERSISTENT <file1> [<file2> ...] ]
+#                        [ GENERATED <file1> [<file2> ...] ]
+#                        [ DEPENDS <target1> [<target2> ...] ]
+#                        [ CONDITION <condition> ]
+#                        [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                        [ NOINSTALL ]
+#                        [ HEADER_DESTINATION <path> ]
+#                        [ INSTALL_HEADERS LISTED|ALL ]
+#                        [ INSTALL_HEADERS_LIST <header1> [<header2> ...] ]
+#                        [ INSTALL_HEADERS_REGEX <pattern> ]
+#                        [ VERSION <version> | AUTO_VERSION ]
+#                        [ SOVERSION <soversion> | AUTO_SOVERSION ]
+#                        [ CFLAGS <flag1> [<flag2> ...] ]
+#                        [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                        [ FFLAGS <flag1> [<flag2> ...] ]
+#                        [ LINKER_LANGUAGE <lang> ]
+#                        [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# TYPE : optional
+#   library type, one of:
+#
+#   :SHARED: libraries are linked dynamically and loaded at runtime
+#   :STATIC: archives of object files for use when linking other targets.
+#   :MODULE: plugins that are not linked into other targets but may be loaded
+#            dynamically at runtime using dlopen-like functionality
+#   :OBJECT: files are just compiled into objects
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : (DEPRECATED) optional
+#   list of paths to add to include directories, behaves as PUBLIC_INCLUDES if CMake >= 2.8.11
+#   and reverts to include_directories() for CMake < 2.8.11
+#
+# PUBLIC_INCLUDES : optional
+#   list of paths to add to include directories which will be publicly exported to other projects
+#
+# PRIVATE_INCLUDES : optional
+#   list of paths to add to include directories which won't be exported to other projects,
+#   equivalent to using a include_directories() before calling this macro
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the library
+#
+# HEADER_DESTINATION
+#   directory to install headers (if not specified, INSTALL_INCLUDE_DIR is used)
+#
+# INSTALL_HEADERS : optional
+#   specify which header files to install:
+#
+#   :LISTED: install header files listed as SOURCES
+#   :ALL:    install all header files ending in .h, .hh, .hpp, .H
+#
+# INSTALL_HEADERS_LIST : optional
+#   list of extra headers to install
+#
+# INSTALL_HEADERS_REGEX : optional
+#   regular expression to match extra headers to install
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   build version of the library
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   use MAJOR.MINOR package version as build version of the library
+#
+# SOVERSION : optional, AUTO_SOVERSION or LIBS_SOVERSION is used if not specified
+#   ABI version of the library
+#
+# AUTO_SOVERSION : optional, ignored if SOVERSION is specified
+#   use MAJOR package version as ABI version of the library
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+function( ecbuild_add_library_impl )
+
+  set( options NOINSTALL AUTO_VERSION AUTO_SOVERSION )
+  set( single_value_args TARGET TYPE COMPONENT INSTALL_HEADERS
+                         INSTALL_HEADERS_REGEX LINKER_LANGUAGE
+                         HEADER_DESTINATION VERSION SOVERSION OUTPUT_NAME )
+  set( multi_value_args  SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                         TEMPLATES LIBS INCLUDES PRIVATE_INCLUDES
+                         PUBLIC_INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                         INSTALL_HEADERS_LIST CFLAGS CXXFLAGS FFLAGS GENERATED
+                         CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_library(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_library() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # defines the type of library
+    if( DEFINED _PAR_TYPE )
+      # checks that is either SHARED or STATIC or MODULE
+      if( NOT _PAR_TYPE MATCHES "STATIC" AND
+          NOT _PAR_TYPE MATCHES "SHARED" AND
+          NOT _PAR_TYPE MATCHES "OBJECT" AND
+          NOT _PAR_TYPE MATCHES "MODULE" )
+        ecbuild_critical( "library type must be one of [ STATIC | SHARED | MODULE | OBJECT ]" )
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): library type is ${_PAR_TYPE}")
+    endif()
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CUDA was not found.")
+      endif()
+      if( _PAR_TYPE MATCHES "OBJECT" )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CMake OBJECT libraries with CUDA are not supported.")
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): CUDA sources detected."
+                    "Building library with cuda_add_library() rather than intrinsic"
+                    "add_library().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    else()
+      cuda_add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list( REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" OR ECBUILD_USE_INCLUDE_DIRECTORIES )
+            include_directories( ${path} )
+          else()
+            target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+          endif()
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add private include dirs if defined
+    if( DEFINED _PAR_PRIVATE_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PRIVATE_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PRIVATE_INCLUDES )
+      foreach( path ${_PAR_PRIVATE_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add public include dirs if defined
+    if( DEFINED _PAR_PUBLIC_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PUBLIC_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PUBLIC_INCLUDES )
+      foreach( path ${_PAR_PUBLIC_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # FIX: Cray compiler PIC option is not detected by CMake
+
+    get_property( _target_pic TARGET ${_PAR_TARGET} PROPERTY POSITION_INDEPENDENT_CODE )
+    if( _target_pic )
+      if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CFLAGS "-fPIC -h PIC ${_PAR_CFLAGS}" )
+      endif()
+      if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CXXFLAGS "-fPIC -h PIC ${_PAR_CXXFLAGS}" )
+      endif()
+      if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_FFLAGS "-fPIC -h PIC ${_PAR_FFLAGS}" )
+      endif()
+    endif()
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION OR LIBS_VERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      elseif( DEFINED LIBS_VERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${LIBS_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${LIBS_VERSION}" )
+      endif()
+    endif()
+
+    # define SOVERSION if requested
+    if( DEFINED _PAR_SOVERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${_PAR_SOVERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${_PAR_SOVERSION}" )
+    else()
+      if( _PAR_AUTO_SOVERSION OR LIBS_SOVERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${${PNAME}_MAJOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${${PNAME}_MAJOR_VERSION}" )
+      elseif( DEFINED LIBS_SOVERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${LIBS_SOVERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${LIBS_SOVERSION}" )
+      endif()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    if( DEFINED _PAR_GENERATED )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+      set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # installation (except for OBJECT libraries)
+
+    if( NOT _PAR_NOINSTALL AND NOT _PAR_TYPE MATCHES "OBJECT" )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): installing to ${INSTALL_LIB_DIR}")
+
+      # and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+        EXPORT  ${PROJECT_NAME}-targets
+        RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+        LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+        ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #              COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # install headers
+      if( _PAR_HEADER_DESTINATION )
+        set( _h_destination "${_PAR_HEADER_DESTINATION}" )
+      else()
+        set( _h_destination "${INSTALL_INCLUDE_DIR}" )
+      endif()
+
+      if( _PAR_INSTALL_HEADERS )
+        if( _PAR_INSTALL_HEADERS MATCHES "LISTED" )
+          foreach( file ${${_PAR_TARGET}_h_srcs} )
+            get_filename_component( _file_dir ${file} PATH )
+            install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+          endforeach()
+          if( DEFINED _PAR_TEMPLATES )
+            foreach( file ${_PAR_TEMPLATES} )
+              get_filename_component( _file_dir ${file} PATH )
+              install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+          if( DEFINED _PAR_PERSISTENT )
+            foreach( file ${_PAR_PERSISTENT} )
+              get_filename_component( _file_dir ${file} PATH )
+              get_filename_component( _file_we  ${file} NAME_WE )
+              set( pfile "${CMAKE_CURRENT_BINARY_DIR}/${_file_dir}/${_file_we}.b" )
+              install( FILES ${pfile} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+        endif()
+        if( _PAR_INSTALL_HEADERS MATCHES "ALL" ) # "(\\.h|\\.b|\\.hxx|\\.hh|\\.hpp|\\.H)" ????
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.h" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hh" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hpp" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.H" )
+        endif()
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_LIST )
+        install( FILES ${_PAR_INSTALL_HEADERS_LIST} DESTINATION ${_h_destination} )
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_REGEX )
+        install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "${_PAR_INSTALL_HEADERS_REGEX}")
+      endif()
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous 2 commands)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    if( NOT _PAR_TYPE MATCHES "OBJECT" )
+      add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+    endif()
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_lib( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_LIBS ${${PROJECT_NAME}_ALL_LIBS} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endfunction( ecbuild_add_library_impl  )
+
+##############################################################################
+# auxiliary macro for adding a library
+##############################################################################
+
+macro( ecbuild_add_library )
+
+  set( options  )
+  set( single_value_args TARGET TYPE )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _p_TYPE ) # don't do anything if TYPE was specified
+
+    if( _p_TYPE MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} OUTPUT_NAME ${_p_TARGET} DEPENDS ${_p_TARGET} )
+
+    else()
+
+      ecbuild_add_library_impl( ${ARGV} )
+
+    endif()
+
+  else()
+
+    if( NOT DEFINED _p_TARGET )
+      ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+    else()
+
+      if( BUILD_SHARED_LIBS MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} DEPENDS ${_p_TARGET} )
+
+        # If the library is built conditionally the target might not exist
+        if ( TARGET ${_p_TARGET}-static )
+          set_target_properties( ${_p_TARGET}-static PROPERTIES OUTPUT_NAME ${_p_TARGET} )
+        endif()
+
+      else()
+
+        ecbuild_add_library_impl( ${ARGV} )
+
+      endif()
+
+    endif()
+
+  endif()
+
+endmacro( ecbuild_add_library )
diff --git a/cmake/ecbuild_add_option.cmake b/cmake/ecbuild_add_option.cmake
new file mode 100644
index 0000000..6d5363f
--- /dev/null
+++ b/cmake/ecbuild_add_option.cmake
@@ -0,0 +1,348 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_option
+# ==================
+#
+# Add a CMake configuration option, which may depend on a list of packages. ::
+#
+#   ecbuild_add_option( FEATURE <name>
+#                       [ DEFAULT ON|OFF ]
+#                       [ DESCRIPTION <description> ]
+#                       [ REQUIRED_PACKAGES <package1> [<package2> ...] ]
+#                       [ CONDITION <condition> ]
+#                       [ ADVANCED ] [ NO_TPL ] )
+#
+# Options
+# -------
+#
+# FEATURE : required
+#   name of the feature / option
+#
+# DEFAULT : optional, defaults to ON
+#   if set to ON, the feature is enabled even if not explicitly requested
+#
+# DESCRIPTION : optional
+#   string describing the feature (shown in summary and stored in the cache)
+#
+# REQUIRED_PACKAGES : optional
+#   list of packages required to be found for this feature to be enabled
+#
+#   The package specification can have one of two forms. Either ::
+#
+#     "<package> [ <version> ... ]"
+#
+#   to search for a given package using the CMake ``find_package`` mechanism.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``find_package`` are supported.
+#
+#   The other specification must start with ``PROJECT`` like this ::
+#
+#     "PROJECT <name> [ VERSION <version> ... ]"
+#
+#   and is used to search for an ecBuild project via ``ecbuild_use_package``.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``ecbuild_use_package`` are supported.
+#
+#   .. note::
+#
+#     Arguments inside the package string that require quoting need to use the
+#     `bracket argument syntax`_ introduced in CMake 3.0 since
+#     regular quotes even when escaped are swallowed by the CMake parser.
+#
+#     Alternatively, the name of a CMake variable containing the string can be
+#     passed, which will be expanded by ``ecbuild_find_package``: ::
+#
+#       set( ECCODES_FAIL_MSG
+#            "grib_api can be used instead (select with -DENABLE_ECCODES=OFF)" )
+#       ecbuild_add_option( FEATURE ECCODES
+#                           DESCRIPTION "Use eccodes instead of grib_api"
+#                           REQUIRED_PACKAGES "PROJECT eccodes REQUIRED FAILURE_MSG ECCODES_FAIL_MSG"
+#                           DEFAULT ON )
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this option to be
+#   enabled (must be valid in a CMake ``if`` statement)
+#
+# ADVANCED : optional
+#   mark the feature as advanced
+#
+# NO_TPL : optional
+#   do not add any ``REQUIRED_PACKAGES`` to the list of third party libraries
+#
+# Usage
+# -----
+#
+# Features with ``DEFAULT OFF`` need to be explcitly enabled by the user with
+# ``-DENABLE_<FEATURE>=ON``. If a feature is enabled, all ``REQUIRED_PACKAGES``
+# are found and ``CONDITION`` is met, ecBuild sets the variable
+# ``HAVE_<FEATURE>`` to ``ON``. This is the variable to use to check for the
+# availability of the feature.
+#
+# If a feature is explicitly enabled but the required packages are not found,
+# configuration fails. This only applies when configuring from *clean cache*.
+# With an already populated cache, use ``-DENABLE_<FEATURE>=REQUIRE`` to make
+# the feature a required feature (this cannot be done via the CMake GUI).
+#
+# .. _bracket argument syntax: https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument
+#
+##############################################################################
+
+macro( ecbuild_add_option )
+
+  set( options ADVANCED NO_TPL )
+  set( single_value_args FEATURE DEFAULT DESCRIPTION TYPE PURPOSE )
+  set( multi_value_args  REQUIRED_PACKAGES CONDITION )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( _p_UNPARSED_ARGUMENTS )
+    ecbuild_critical("Unknown keywords given to ecbuild_add_option(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # check FEATURE parameter
+
+  if( NOT _p_FEATURE  )
+    ecbuild_critical("The call to ecbuild_add_option() doesn't specify the FEATURE.")
+  endif()
+
+  # check DEFAULT parameter
+
+  if( NOT DEFINED _p_DEFAULT )
+    set( _p_DEFAULT ON )
+  else()
+    if( NOT _p_DEFAULT MATCHES "[Oo][Nn]" AND NOT _p_DEFAULT MATCHES "[Oo][Ff][Ff]" )
+      ecbuild_critical("In macro ecbuild_add_option(), DEFAULT is either ON or OFF: \"${_p_DEFAULT}\"")
+    endif()
+  endif()
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defaults to ${_p_DEFAULT}")
+
+  if( _p_PURPOSE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument PURPOSE is ignored and will be removed in a future release." )
+  endif()
+  if( _p_TYPE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument TYPE is ignored and will be removed in a future release." )
+  endif()
+
+  # check CONDITION parameter
+  if( DEFINED _p_CONDITION )
+    set(_feature_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_p_FEATURE}_condition.cmake")
+    file( WRITE  ${_feature_condition_file} "  if( ")
+    foreach( term ${_p_CONDITION} )
+      file( APPEND ${_feature_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_feature_condition_file} " )\n    set(_${_p_FEATURE}_condition TRUE)\n  else()\n    set(_${_p_FEATURE}_condition FALSE)\n  endif()\n")
+    include( ${_feature_condition_file} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): checking condition ${_p_CONDITION} -> ${_${_p_FEATURE}_condition}")
+  else()
+    set( _${_p_FEATURE}_condition TRUE )
+  endif()
+
+  # Check if user explicitly enabled/disabled the feature in cache
+  get_property( _in_cache CACHE ENABLE_${_p_FEATURE} PROPERTY VALUE SET )
+
+  # A feature set to REQUIRE is always treated as explicitly enabled
+  if( ENABLE_${_p_FEATURE} MATCHES "REQUIRE" )
+    set( ENABLE_${_p_FEATURE} ON CACHE BOOL "" FORCE )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} was required")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" FORCE )
+  elseif( NOT ENABLE_${_p_FEATURE} STREQUAL "" AND _in_cache )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}} was found in cache")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" )
+  else()
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} not found in cache")
+    set( ${_p_FEATURE}_user_provided_input 0 CACHE BOOL "" )
+  endif()
+
+  mark_as_advanced( ${_p_FEATURE}_user_provided_input )
+
+
+  # define the option -- for cmake GUI
+
+  option( ENABLE_${_p_FEATURE} "${_p_DESCRIPTION}" ${_p_DEFAULT} )
+  get_property( _feature_desc GLOBAL PROPERTY _CMAKE_${_p_FEATURE}_DESCRIPTION )
+  if( _feature_desc )
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${_feature_desc}, ${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  else()
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  endif()
+
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defining option ENABLE_${_p_FEATURE} '${_p_DESCRIPTION}' ${_p_DEFAULT}")
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}}")
+
+  if( ENABLE_${_p_FEATURE} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature enabled")
+
+    set( HAVE_${_p_FEATURE} 1 )
+
+    if( _${_p_FEATURE}_condition )
+
+      ### search for dependent packages
+
+      set( _failed_to_find_packages )  # clear variable
+      foreach( pkg ${_p_REQUIRED_PACKAGES} )
+        ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for dependent package ${pkg}")
+
+        string(REPLACE " " ";" pkglist ${pkg}) # string to list
+
+        list( GET pkglist 0 pkgname )
+
+        if( pkgname STREQUAL "PROJECT" )  # if 1st entry is PROJECT, then we are looking for a ecbuild project
+          set( pkgproject 1 )
+          list( GET pkglist 1 pkgname )
+          # Use feature description as package description if there is none
+          list( FIND pkglist DESCRIPTION __description )
+          if( __description LESS 0 )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): no description for ${pkgname}, using feature description '${_p_DESCRIPTION}'")
+            list( APPEND pkglist DESCRIPTION "${_p_DESCRIPTION}" )
+          endif()
+        else()                            # else 1st entry is package name
+          set( pkgproject 0 )
+        endif()
+
+        # ecbuild_debug_var( pkg )
+        # ecbuild_debug_var( pkglist )
+        # ecbuild_debug_var( pkgname )
+
+        string( TOUPPER ${pkgname} pkgUPPER )
+        string( TOLOWER ${pkgname} pkgLOWER )
+
+        set( __help_msg "Provide ${pkgname} location with -D${pkgUPPER}_PATH=/..." )
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+
+          ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ${pkgname} has already been found")
+          set( ${pkgname}_already_found 1 )
+
+        else()
+
+          if( pkgproject )
+
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for ecbuild project ${pkgname} - ecbuild_use_package( ${pkglist} )")
+            ecbuild_use_package( ${pkglist} )
+
+          else()
+
+            if( pkgname STREQUAL "LAPACK" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for LAPACK - ecbuild_find_package( NAME ${pkglist} )")
+              ecbuild_find_package( NAME ${pkglist} )
+              if( HAVE_LAPACK AND TARGET lapack )
+                ecbuild_debug( "LAPACK found as CMake target lapack" )
+                set( LAPACK_LIBRARIES lapack )
+              endif()
+            elseif( pkgname STREQUAL "MPI" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "MPI" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for MPI - ecbuild_find_mpi( ${_find_args} )")
+              ecbuild_find_mpi( ${_find_args} )
+            elseif( pkgname STREQUAL "OMP" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "OMP" )
+              if( NOT ENABLE_${_p_FEATURE} )
+                list( APPEND _find_args STUBS )
+              endif()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for OpenMP - ecbuild_find_omp( ${_find_args} )")
+              ecbuild_find_omp( ${_find_args} )
+            elseif( pkgname STREQUAL "Python" OR pkgname STREQUAL "PYTHON" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for Python - ecbuild_find_python( ${_find_args} )")
+              ecbuild_find_python( ${_find_args} )
+              set( __help_msg "Specify the location of the Python interpreter with -DPYTHON_EXECUTABLE=/..." )
+            elseif( pkgname STREQUAL "LEXYACC" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for lex-yacc - ecbuild_find_lexyacc( ${_find_args} )")
+              ecbuild_find_lexyacc( ${_find_args} )
+            else()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for package ${pkgname} - find_package( ${pkglist} )")
+              find_package( ${pkglist} )
+            endif()
+
+          endif()
+
+        endif()
+
+        # ecbuild_debug_var( ${pkgname}_FOUND  )
+        # ecbuild_debug_var( ${pkgLOWER}_FOUND )
+        # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+
+        # we have feature if all required packages were FOUND
+
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+          ecbuild_info( "Found package ${pkgname} required for feature ${_p_FEATURE}" )
+
+          # append to list of third-party libraries (to be forward to other packages )
+          # unless the NO_TPL option was given
+          if( NOT _p_NO_TPL )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): appending ${pkgname} to ${PROJECT_NAME_CAPS}_TPLS")
+            list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${pkgname} )
+            list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+          endif()
+
+        else()
+          ecbuild_info( "Could NOT find package ${pkgname} required for feature ${_p_FEATURE} -- ${__help_msg}" )
+          set( HAVE_${_p_FEATURE} 0 )
+          list( APPEND _failed_to_find_packages ${pkgname} )
+        endif()
+
+      endforeach()
+    else( _${_p_FEATURE}_condition )
+      set( HAVE_${_p_FEATURE} 0 )
+    endif( _${_p_FEATURE}_condition )
+
+    # FINAL CHECK
+
+    if( HAVE_${_p_FEATURE} )
+
+      ecbuild_enable_feature( ${_p_FEATURE} )
+
+      ecbuild_info( "Feature ${_p_FEATURE} enabled" )
+
+    else() # if user provided input and we cannot satisfy FAIL otherwise WARN
+
+      ecbuild_disable_feature( ${_p_FEATURE} )
+
+      if( ${_p_FEATURE}_user_provided_input )
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+      else()
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+        set( ENABLE_${_p_FEATURE} OFF )
+        ecbuild_disable_feature( ${_p_FEATURE} )
+      endif()
+
+    endif()
+
+  else()
+
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature disabled")
+    set( HAVE_${_p_FEATURE} 0 )
+    ecbuild_disable_feature( ${_p_FEATURE} )
+
+  endif()
+
+
+  if( ${_p_ADVANCED} )
+    mark_as_advanced( ENABLE_${_p_FEATURE} )
+  endif()
+
+  set( ${PROJECT_NAME_CAPS}_HAVE_${_p_FEATURE} ${HAVE_${_p_FEATURE}} )
+
+endmacro( ecbuild_add_option  )
diff --git a/cmake/ecbuild_add_persistent.cmake b/cmake/ecbuild_add_persistent.cmake
new file mode 100644
index 0000000..e5a875b
--- /dev/null
+++ b/cmake/ecbuild_add_persistent.cmake
@@ -0,0 +1,85 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_persistent
+# ======================
+#
+# Add persistent layer object classes. ::
+#
+#   ecbuild_add_persistent( SRC_LIST <variable>
+#                           FILES <file1> [<file2> ...] ]
+#                           [ NAMESPACE <namespace> ] )
+#
+# Options
+# -------
+#
+# SRC_LIST : required
+#   CMake variable to append the generated persistent layer objects to
+#
+# FILES : required
+#   list of base names of files to build persistent class information for
+#
+#   The source file is expected to have a .h extension, the generated file
+#   gets a .b extension.
+#
+# NAMESPACE : optional
+#   C++ namespace to place the persistent class information in
+#
+##############################################################################
+
+# define the script to build the persistent class information
+set( sg_perl "${CMAKE_CURRENT_LIST_DIR}/sg.pl" CACHE INTERNAL "perl script to generate persistent objects" )
+
+macro( ecbuild_add_persistent )
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args SRC_LIST NAMESPACE )
+  set( multi_value_args  FILES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_persistent(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SRC_LIST  )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the SRC_LIST.")
+  endif()
+
+  if( NOT _PAR_FILES )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the FILES.")
+  endif()
+
+  foreach( file ${_PAR_FILES} )
+
+    get_filename_component( _file_dir    ${file} PATH )
+    get_filename_component( _file_we     ${file} NAME_WE )
+
+    set( file ${_file_we} )
+    if( _file_dir )
+      file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} )
+      set( file ${_file_dir}/${_file_we} )
+    endif()
+
+    # ecbuild_debug_var(file)
+
+    add_custom_command( OUTPUT  ${file}.b
+                        COMMAND ${PERL_EXECUTABLE} ${sg_perl} ${CMAKE_CURRENT_SOURCE_DIR}/${file}.h
+                                ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} ${_PAR_NAMESPACE}
+                        DEPENDS ${sg_perl} ${file}.h )
+    set_source_files_properties( ${file}.h PROPERTIES OBJECT_DEPENDS "${file}.b" )
+    list( APPEND ${_PAR_SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/${file}.b )
+
+  endforeach()
+
+endmacro( ecbuild_add_persistent  )
diff --git a/cmake/ecbuild_add_resources.cmake b/cmake/ecbuild_add_resources.cmake
new file mode 100644
index 0000000..2dfaa5e
--- /dev/null
+++ b/cmake/ecbuild_add_resources.cmake
@@ -0,0 +1,151 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_resources
+# =====================
+#
+# Add resources as project files but optionally exclude them from packaging. ::
+#
+#   ecbuild_add_resources( TARGET <name>
+#                          [ SOURCES <source1> [<source2> ...] ]
+#                          [ SOURCES_PACK <source1> [<source2> ...] ]
+#                          [ SOURCES_DONT_PACK <source1> [<source2> ...] ]
+#                          [ PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK_DIRS <directory1> [<directory2> ...] ]
+#                          [ DONT_PACK_REGEX <regex1> [<regex2> ...] ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name (target will only be created if there are any sources)
+#
+# SOURCES : optional, alias for SOURCES_PACK
+#   list of source files included when packaging
+#
+# SOURCES_PACK : optional, alias for SOURCES
+#   list of source files included when packaging
+#
+# SOURCES_DONT_PACK : optional
+#   list of source files excluded when packaging
+#
+# PACK : optional, priority over DONT_PACK, DONT_PACK_DIRS, DONT_PACK_REGEX
+#   list of files to include when packaging
+#
+# DONT_PACK : optional
+#   list of files to exclude when packaging
+#
+# DONT_PACK_DIRS : optional
+#   list of directories to exclude when packaging
+#
+# DONT_PACK_REGEX : optional
+#   list of regular expressions to match files and directories to exclude when
+#   packaging
+#
+##############################################################################
+
+macro( ecbuild_add_resources )
+
+    set( options )
+    set( single_value_args TARGET )
+    set( multi_value_args  SOURCES SOURCES_PACK SOURCES_DONT_PACK PACK DONT_PACK DONT_PACK_DIRS DONT_PACK_REGEX )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_add_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_TARGET  )
+      ecbuild_critical("The call to ecbuild_add_resources() doesn't specify the TARGET.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_DONT_PACK_REGEX )
+        foreach( exp ${_PAR_DONT_PACK_REGEX} )
+            file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${exp} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+        endforeach()
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DONT_PACK_DIRS )
+        foreach( dir ${_PAR_DONT_PACK_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_DONT_PACK} )
+    endif()
+
+    # now lets remove files that we want to pack from the list
+    # note that these have priority over the files not to pack
+    # so we can GLOB_RECURSE * -> DONT_PACK and then select only the ones we pack
+
+    # files to pack but are not project files
+    if( DEFINED _PAR_PACK )
+        foreach( file ${_PAR_PACK} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # define as project files, but dont pack them
+    if( DEFINED _PAR_SOURCES_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_SOURCES_DONT_PACK} )
+		foreach( sfile ${_PAR_SOURCES_DONT_PACK} )
+			set( _full_sfile "${CMAKE_CURRENT_SOURCE_DIR}/${sfile}" )
+	        if( EXISTS ${_full_sfile} )
+				list( APPEND ${_PAR_TARGET}_files ${_full_sfile} )
+			endif()
+		endforeach()
+    endif()
+
+    # define as project files and pack them
+    # SOURCES_PACK is alias to SOURCES
+    if( DEFINED _PAR_SOURCES_PACK )
+        list( APPEND _PAR_SOURCES ${_PAR_SOURCES_PACK} )
+    endif()
+    if( DEFINED _PAR_SOURCES )
+        list( APPEND ${_PAR_TARGET}_files ${_PAR_SOURCES} )
+        foreach( file ${_PAR_SOURCES} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # there are project files, so lets create the target
+    if( DEFINED ${_PAR_TARGET}_files )
+        add_custom_target( ${_PAR_TARGET} SOURCES ${${_PAR_TARGET}_files} )
+    endif()
+
+    # remove CMakeLists.txt
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        if( ${file} MATCHES "CMakeLists.txt" )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endif()
+    endforeach()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro( ecbuild_add_resources  )
diff --git a/cmake/ecbuild_add_test.cmake b/cmake/ecbuild_add_test.cmake
new file mode 100644
index 0000000..da0bd22
--- /dev/null
+++ b/cmake/ecbuild_add_test.cmake
@@ -0,0 +1,506 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_test
+# ================
+#
+# Add a test as a script or an executable with a given list of source files. ::
+#
+#   ecbuild_add_test( [ TARGET <name> ]
+#                     [ SOURCES <source1> [<source2> ...] ]
+#                     [ OBJECTS <obj1> [<obj2> ...] ]
+#                     [ COMMAND <executable> ]
+#                     [ TYPE EXE|SCRIPT|PYTHON ]
+#                     [ LABELS <label1> [<label2> ...] ]
+#                     [ ARGS <argument1> [<argument2> ...] ]
+#                     [ RESOURCES <file1> [<file2> ...] ]
+#                     [ TEST_DATA <file1> [<file2> ...] ]
+#                     [ BOOST ]
+#                     [ MPI <number-of-mpi-tasks> ]
+#                     [ OMP <number-of-threads-per-mpi-task> ]
+#                     [ ENABLED ON|OFF ]
+#                     [ LIBS <library1> [<library2> ...] ]
+#                     [ INCLUDES <path1> [<path2> ...] ]
+#                     [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                     [ PERSISTENT <file1> [<file2> ...] ]
+#                     [ GENERATED <file1> [<file2> ...] ]
+#                     [ DEPENDS <target1> [<target2> ...] ]
+#                     [ TEST_DEPENDS <target1> [<target2> ...] ]
+#                     [ CONDITION <condition> ]
+#                     [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                     [ ENVIRONMENT <variable1> [<variable2> ...] ]
+#                     [ WORKING_DIRECTORY <path> ]
+#                     [ CFLAGS <flag1> [<flag2> ...] ]
+#                     [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                     [ FFLAGS <flag1> [<flag2> ...] ]
+#                     [ LINKER_LANGUAGE <lang> ] )
+#
+# Options
+# -------
+#
+# TARGET : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   target name to be built
+#
+# SOURCES : required if TARGET is provided
+#   list of source files to be compiled
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# COMMAND : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   command or script to execute (no executable is built)
+#
+# TYPE : optional
+#   test type, one of:
+#
+#   :EXE:    run built executable, default if TARGET is provided
+#   :SCRIPT: run command or script, default if COMMAND is provided
+#   :PYTHON: run a Python script (requires the Python interpreter to be found)
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   The project name in lower case is always added as a label. Additional
+#   labels are assigned depending on the type of test:
+#
+#   :executable: for type ``EXE``
+#   :script:     for type ``SCRIPT``
+#   :python:     for type ``PYTHON``
+#   :boost:      uses Boost unit test
+#   :mpi:        if ``MPI`` is set
+#   :openmp:     if ``OMP`` is set
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# ARGS : optional
+#   list of arguments to pass to TARGET or COMMAND when running the test
+#
+# RESOURCES : optional
+#   list of files to copy from the test source directory to the test directory
+#
+# TEST_DATA : optional
+#   list of test data files to download
+#
+# BOOST : optional
+#   use the Boost Unit Test Framework
+#
+# MPI : optional
+#   Run with MPI using the given number of MPI tasks.
+#
+#   If greater than 1, and ``MPIEXEC`` is not available, the test is disabled.
+#
+# OMP : optional
+#   number of OpenMP threads per MPI task to use.
+#
+#   If set, the environment variable OMP_NUM_THREADS will set.
+#   Also, in case of launchers like aprun, the OMP_NUMTHREADS_FLAG will be used.
+#
+# ENABLED : optional
+#   if set to OFF, the test is built but not enabled as a test case
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# TEST_DEPENDS : optional
+#   list of tests to be run before this one
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# ENVIRONMENT : optional
+#   list of environment variables to set in the test environment
+#
+# WORKING_DIRECTORY : optional
+#   directory to switch to before running the test
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_test )
+
+  set( options           BOOST )
+  set( single_value_args TARGET ENABLED COMMAND TYPE LINKER_LANGUAGE MPI OMP WORKING_DIRECTORY )
+  set( multi_value_args  SOURCES OBJECTS LIBS INCLUDES TEST_DEPENDS DEPENDS LABELS ARGS
+                         PERSISTENT DEFINITIONS RESOURCES TEST_DATA CFLAGS
+                         CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES ENVIRONMENT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_test(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  set( _TEST_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+
+  # Undocumented flag for disabling all MPI tests for test environment without suitable MPI(EXEC)
+  if( _PAR_MPI AND ECBUILD_DISABLE_MPI_TESTS )
+    ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ECBUILD_DISABLE_MPI_TESTS set - disabling test")
+    set( _PAR_ENABLED 0 )
+  elseif( _PAR_MPI )
+    # Check for MPIEXEC if it not set
+    find_program( MPIEXEC NAMES mpiexec mpirun lamexec srun
+                  DOC "Executable for running MPI programs." )
+    if( MPIEXEC )
+      set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC")
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): Running using ${MPIEXEC} on ${_PAR_MPI} MPI rank(s)")
+      set( _PAR_LABELS mpi ${_PAR_LABELS} )
+    elseif( _PAR_MPI GREATER 1 )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${_PAR_MPI} MPI ranks requested but MPIEXEC not available - disabling test")
+      set( _PAR_ENABLED 0 )
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): 1 MPI rank requested but MPIEXEC not available - running sequentially")
+      set( _PAR_MPI 0 )
+    endif()
+  endif()
+
+  # Check for OMP
+  if( DEFINED _PAR_OMP )
+    set( _PAR_LABELS openmp ${_PAR_LABELS} )
+  else()
+    set( _PAR_OMP 1 )
+  endif()
+  list( APPEND _PAR_ENVIRONMENT "OMP_NUM_THREADS=${_PAR_OMP}" )
+
+
+  # default is enabled
+  if( NOT DEFINED _PAR_ENABLED )
+    set( _PAR_ENABLED 1 )
+  endif()
+
+
+  ### check test type
+
+  # command implies script
+  if( DEFINED _PAR_COMMAND )
+    set( _PAR_TYPE "SCRIPT" )
+    set( _PAR_LABELS script ${_PAR_LABELS} )
+  endif()
+
+  # default of TYPE
+  if( NOT _PAR_TYPE AND DEFINED _PAR_TARGET )
+    set( _PAR_TYPE "EXE" )
+    set( _PAR_LABELS executable ${_PAR_LABELS} )
+    if( NOT _PAR_SOURCES )
+      ecbuild_critical("The call to ecbuild_add_test() defines a TARGET without SOURCES.")
+    endif()
+  endif()
+
+  if( _PAR_TYPE MATCHES "PYTHON" )
+    if( PYTHONINTERP_FOUND )
+      set( _PAR_COMMAND ${PYTHON_EXECUTABLE} )
+      set( _PAR_LABELS python ${_PAR_LABELS} )
+    else()
+      ecbuild_warn( "Requested a python test but python interpreter not found - disabling test\nPYTHON_EXECUTABLE: [${PYTHON_EXECUTABLE}]" )
+      set( _PAR_ENABLED 0 )
+    endif()
+  endif()
+
+  ### further checks
+
+  if( _PAR_ENABLED AND NOT _PAR_TARGET AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a TARGET nor a COMMAND.")
+  endif()
+
+  if( _PAR_ENABLED AND NOT _PAR_COMMAND AND NOT _PAR_SOURCES )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a COMMAND nor SOURCES, so no test can be defined or built.")
+  endif()
+
+  if( _PAR_TYPE MATCHES "SCRIPT" AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines a 'script' but doesn't specify the COMMAND.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${_TEST_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  # boost unit test linking to unit_test lib ?
+
+  if( _PAR_BOOST AND ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    if( HAVE_BOOST_UNIT_TEST )
+      set( _PAR_LABELS boost ${_PAR_LABELS} )
+      if( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} )
+        include_directories( ${Boost_INCLUDE_DIRS}  ) # temporary until we ship Boost Unit Test with ecBuild
+      else()
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} ${Boost_INCLUDE_DIRS} )
+      endif()
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): boost unit test framework not available - not building test")
+      set( _${_PAR_TARGET}_condition FALSE )
+    endif()
+
+  endif()
+
+  ### enable the tests
+
+  if( ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    # add resources
+
+    if( DEFINED _PAR_RESOURCES )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): copying resources ${_PAR_RESOURCES}")
+      foreach( rfile ${_PAR_RESOURCES} )
+        execute_process( COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${rfile} ${_TEST_DIR} )
+      endforeach()
+    endif()
+
+    # build executable
+
+    if( DEFINED _PAR_SOURCES )
+
+      # add include dirs if defined
+      if( DEFINED _PAR_INCLUDES )
+        list(REMOVE_DUPLICATES _PAR_INCLUDES )
+        foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+          if( path )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add ${path} to include_directories")
+            include_directories( ${path} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+          endif()
+        endforeach()
+      endif()
+
+      # add persistent layer files
+      if( DEFINED _PAR_PERSISTENT )
+        if( DEFINED PERSISTENT_NAMESPACE )
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+        else()
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+        endif()
+      endif()
+
+      # insert already compiled objects (from OBJECT libraries)
+      unset( _all_objects )
+      foreach( _obj ${_PAR_OBJECTS} )
+        list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+      endforeach()
+
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+
+      # add extra dependencies
+      if( DEFINED _PAR_DEPENDS)
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+        add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+      endif()
+
+      # add the link libraries
+      if( DEFINED _PAR_LIBS )
+        list(REMOVE_DUPLICATES _PAR_LIBS )
+        list(REMOVE_ITEM _PAR_LIBS debug)
+        list(REMOVE_ITEM _PAR_LIBS optimized)
+        foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+          if( lib )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): linking with ${lib}")
+            target_link_libraries( ${_PAR_TARGET} ${lib} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${lib} not found - not linking")
+          endif()
+        endforeach()
+      endif()
+
+      # add test libraries
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_LINKED AND HAVE_BOOST_UNIT_TEST )
+        target_link_libraries( ${_PAR_TARGET} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_TEST_EXEC_MONITOR_LIBRARY} )
+      endif()
+
+      # filter sources
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+
+      # Override compilation flags on a per source file basis
+      ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+      if( DEFINED _PAR_GENERATED )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+        set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+      endif()
+
+      # modify definitions to compilation ( -D... )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+
+      if( DEFINED _PAR_DEFINITIONS )
+        list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      endif()
+
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        list( APPEND _target_defs BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+      endif()
+
+      if( _target_defs )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using definitions ${_target_defs}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+      endif()
+
+      # set build location to local build dir
+      # not the project base as defined for libs and execs
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${_TEST_DIR} )
+
+      # whatever project settings are, we always build tests with the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+
+      # set linker language
+      if( DEFINED _PAR_LINKER_LANGUAGE )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      endif()
+
+      # make sure target is removed before - some problems with AIX
+      get_target_property(EXE_FILENAME ${_PAR_TARGET} OUTPUT_NAME)
+      add_custom_command( TARGET ${_PAR_TARGET}
+                          PRE_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E remove ${EXE_FILENAME} )
+
+    endif() # _PAR_SOURCES
+
+    if( DEFINED _PAR_COMMAND AND NOT _PAR_TARGET ) # in the absence of target, we use the command as a name
+      set( _PAR_TARGET ${_PAR_COMMAND} )
+    endif()
+
+    # scripts dont have actual build targets
+    # we build a phony target to trigger the dependencies
+    if( DEFINED _PAR_COMMAND AND DEFINED _PAR_DEPENDS )
+
+      add_custom_target( ${_PAR_TARGET}.x ALL COMMAND ${CMAKE_COMMAND} -E touch ${_PAR_TARGET}.x )
+
+      add_dependencies( ${_PAR_TARGET}.x ${_PAR_DEPENDS} )
+
+    endif()
+
+
+    # define the arguments
+    set( TEST_ARGS "" )
+    # Boost Unit Test >= 1.60 requires arguments to be passed to the application to be separated by --
+    if( DEFINED _PAR_ARGS AND _PAR_BOOST )
+      list( APPEND TEST_ARGS "--" ${_PAR_ARGS} )
+    elseif( DEFINED _PAR_ARGS )
+      list( APPEND TEST_ARGS ${_PAR_ARGS} )
+    endif()
+
+    # Wrap with MPIEXEC
+    if( _PAR_MPI )
+
+      set( MPIEXEC_TASKS ${MPIEXEC_NUMPROC_FLAG} ${_PAR_MPI} )
+      if( DEFINED MPIEXEC_NUMTHREAD_FLAG )
+        set( MPIEXEC_THREADS ${MPIEXEC_NUMTHREAD_FLAG} ${_PAR_OMP} )
+      endif()
+
+      set( _LAUNCH ${MPIEXEC} ${MPIEXEC_TASKS} ${MPIEXEC_THREADS} )
+
+      if( DEFINED _PAR_COMMAND )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_PAR_COMMAND}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_PAR_COMMAND} )
+      else()
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET} )
+      endif()
+    endif()
+
+    ### define the test
+
+    if( _PAR_ENABLED ) # we can disable and still build it but not run it with 'make tests'
+
+      if( DEFINED _PAR_COMMAND )
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_COMMAND} ${TEST_ARGS} ${_working_dir} ) # run a command as test
+      else()
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_TARGET}  ${TEST_ARGS} ${_working_dir} ) # run the test that was generated
+      endif()
+
+      # Set custom properties
+      if( ${_PAR_PROPERTIES} )
+        set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+      endif()
+
+      # get test data
+
+      if( _PAR_TEST_DATA )
+
+        ecbuild_get_test_multidata( TARGET ${_PAR_TARGET}_data NAMES ${_PAR_TEST_DATA} )
+
+        list( APPEND _PAR_TEST_DEPENDS ${_PAR_TARGET}_data )
+
+      endif()
+
+      # Add lower case project name to custom test labels
+      set( _PAR_LABELS ${PROJECT_NAME_LOWCASE} ${_PAR_LABELS} )
+      list( REMOVE_DUPLICATES _PAR_LABELS )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): assign labels ${_PAR_LABELS}")
+      set_property( TEST ${_PAR_TARGET} APPEND PROPERTY LABELS "${_PAR_LABELS}" )
+
+      if( DEFINED _PAR_ENVIRONMENT )
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY ENVIRONMENT "${_PAR_ENVIRONMENT}" )
+      endif()
+
+      if( DEFINED _PAR_WORKING_DIRECTORY )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set working directory to ${_PAR_WORKING_DIRECTORY}")
+        set_tests_properties( ${_PAR_TARGET} PROPERTIES WORKING_DIRECTORY "${_PAR_WORKING_DIRECTORY}")
+      endif()
+
+      if( DEFINED _PAR_TEST_DEPENDS )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set test dependencies to ${_PAR_TEST_DEPENDS}")
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY DEPENDS "${_PAR_TEST_DEPENDS}" )
+      endif()
+
+    endif()
+
+    # add to the overall list of tests
+    list( APPEND ECBUILD_ALL_TESTS ${_PAR_TARGET} )
+    list( REMOVE_DUPLICATES ECBUILD_ALL_TESTS )
+    set( ECBUILD_ALL_TESTS ${ECBUILD_ALL_TESTS} CACHE INTERNAL "" )
+
+  endif() # _condition
+
+  # finally mark project files
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+
+endmacro( ecbuild_add_test )
diff --git a/cmake/ecbuild_append_to_rpath.cmake b/cmake/ecbuild_append_to_rpath.cmake
new file mode 100644
index 0000000..38ecbb2
--- /dev/null
+++ b/cmake/ecbuild_append_to_rpath.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_append_to_rpath
+# =======================
+#
+# Append paths to the rpath. ::
+#
+#   ecbuild_append_to_rpath( RPATH_DIRS )
+#
+# ``RPATH_DIRS`` is a list of directories to append to ``CMAKE_INSTALL_RPATH``.
+#
+# * If a directory is absolute, simply append it.
+# * If a directory is relative, build a platform-dependent relative path
+#   (using ``@loader_path`` on Mac OSX, ``$ORIGIN`` on Linux and Solaris)
+#   or fall back to making it absolute by prepending the install prefix.
+#
+##############################################################################
+
+function( _path_append var path )
+	if( "${${var}}" STREQUAL "" )
+		set( ${var} "${path}" PARENT_SCOPE )
+	else()
+		list( FIND ${var} ${path} _found )
+		if( _found EQUAL "-1" )
+			set( ${var} "${${var}}:${path}" PARENT_SCOPE )
+		endif()
+	endif()
+endfunction()
+
+macro( ecbuild_append_to_rpath RPATH_DIRS )
+   
+   if( NOT ${ARGC} EQUAL 1 )
+     ecbuild_error( "ecbuild_append_to_rpath takes 1 argument")
+   endif()
+
+   foreach( RPATH_DIR ${RPATH_DIRS} )
+     
+		if( NOT ${RPATH_DIR} STREQUAL "" )
+
+			file( TO_CMAKE_PATH ${RPATH_DIR} RPATH_DIR ) # sanitize the path
+
+			if( IS_ABSOLUTE ${RPATH_DIR} )
+
+				_path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+
+			else()
+
+				set( _done 0 )
+
+				if( EC_OS_NAME STREQUAL "macosx" )
+
+					if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS 3.0) # cmake < 3.0
+						set( CMAKE_INSTALL_NAME_DIR "@loader_path/${RPATH_DIR}" )
+					endif()
+					_path_append( CMAKE_INSTALL_RPATH "@loader_path/${RPATH_DIR}" )
+
+					set( _done 1 )
+
+				endif()
+
+                if( EC_OS_NAME STREQUAL "freebsd" )
+                    _path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+                if( EC_OS_NAME STREQUAL "linux" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+				if( EC_OS_NAME STREQUAL "solaris" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+                if( EC_OS_NAME STREQUAL "aix" ) # always relative to exectuable path
+                    _path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+				# fallback
+
+				if( NOT _done )
+					_path_append( CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${RPATH_DIR}" )
+				endif()
+
+			endif()
+
+     endif()
+
+   endforeach()
+
+endmacro( ecbuild_append_to_rpath )
diff --git a/cmake/ecbuild_bundle.cmake b/cmake/ecbuild_bundle.cmake
new file mode 100644
index 0000000..9778e27
--- /dev/null
+++ b/cmake/ecbuild_bundle.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+include(CMakeParseArguments)
+
+include(ecbuild_git)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_initialize
+# =========================
+#
+# Initialise the ecBuild environment for a bundle. *Must* be called *before*
+# any call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_initialize()
+#
+##############################################################################
+
+macro( ecbuild_bundle_initialize )
+
+  include( local-config.cmake OPTIONAL )
+
+  # ecmwf_stash( PROJECT ecbuild DIR ${PROJECT_SOURCE_DIR}/ecbuild STASH "ecsdk/ecbuild" BRANCH develop )
+
+  # set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/ecbuild/cmake;${CMAKE_MODULE_PATH}" )
+
+  include( ecbuild_system )
+
+  ecbuild_requires_macro_version( 1.6 )
+
+  ecbuild_declare_project()
+
+  file( GLOB local_config_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *local-config.cmake )
+
+  ecbuild_add_resources( TARGET ecbuild_bundle_dont_pack DONT_PACK "${local_config_files}" )
+
+  if( EXISTS "${PROJECT_SOURCE_DIR}/README.md" )
+    add_custom_target( ${PROJECT_NAME}_readme SOURCES "${PROJECT_SOURCE_DIR}/README.md" )
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle
+# ==============
+#
+# Declare a subproject to be built as part of this bundle. ::
+#
+#   ecbuild_bundle( PROJECT <name>
+#                   STASH <repository> | GIT <giturl> | SOURCE <path>
+#                   [ BRANCH <gitbranch> | TAG <gittag> ]
+#                   [ UPDATE | NOREMOTE ] )
+#                   [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# STASH : cannot be combined with GIT or SOURCE
+#   Stash repository in the form <project>/<repository>
+#
+# GIT : cannot be combined with STASH or SOURCE
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# SOURCE : cannot be combined with STASH or GIT
+#   Path to an existing local repository, which will be symlinked
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+# Usage
+# -----
+#
+# A bundle is used to build a number of projects together. Each subproject
+# needs to be declared with a call to ecbuild_bundle, where the order of
+# projects is important and needs to respect dependencies: if project B
+# depends on project A, A should be listed before B in the bundle.
+#
+# The first time a bundle is built, the sources of all subprojects are cloned
+# into directories named according to project in the *source* tree of the
+# bundle (which means these directories should be added to ``.gitignore``).
+# If the ``SOURCE`` option is used it must point to an existing local
+# repository on disk and no new repository is cloned. Be aware that using the
+# ``BRANCH`` or ``TAG`` option leads to the corresponding version being checked
+# out in that repository!
+#
+# Subprojects are configured and built in order. Due to being added as a
+# subproject, the usual project discovery mechanism (i.e. locating and
+# importing a ``<project>-config.cmake`` file) is not used. Also there are no
+# ``<project>-config.cmake`` files being generated for individual subprojects.
+# However there *are* package-config files being generated for each library.
+#
+# To switch off a subproject when building a bundle, set the CMake variable
+# ``BUNDLE_SKIP_<PNAME>`` where ``PNAME`` is the capitalised project name.
+#
+##############################################################################
+
+macro( ecbuild_bundle )
+
+  set( options )
+  set( single_value_args PROJECT STASH GIT SOURCE )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  string(TOUPPER "${_PAR_PROJECT}" PNAME)
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( BUNDLE_SKIP_${PNAME} )
+    ecbuild_info( "Skipping bundle project ${_PAR_PROJECT}" )
+  else()
+    ecbuild_info( "Adding bundle project ${_PAR_PROJECT}" )
+
+    if( _PAR_STASH )
+      ecmwf_stash( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} STASH ${_PAR_STASH} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_GIT )
+      ecbuild_git( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} URL ${_PAR_GIT} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_SOURCE )
+      if( DEFINED ${PNAME}_SOURCE )
+        ecbuild_critical( "ecbuild_bundle called with SOURCE for project ${_PAR_PROJECT} but ${PNAME}_SOURCE is defined" )
+      endif()
+      execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${_PAR_SOURCE} ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} )
+    endif()
+
+    if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT} OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}/CMakeLists.txt )
+      ecbuild_critical("Source directory '${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}' for subproject '${_PAR_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    # Do not descend into ecbuild if included in a bundle (ECBUILD-333)
+    if( NOT _PAR_PROJECT STREQUAL "ecbuild" )
+      ecbuild_use_package( PROJECT ${_PAR_PROJECT} )
+    endif()
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_finalize
+# =======================
+#
+# Finalise the ecBuild environment for a bundle. *Must* be called *after* the
+# last call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_finalize()
+#
+# Options
+# -------
+#
+# See documentation for ecbuild_install_project() since all arguments are
+# forwarded to an internal call to that macro.
+#
+# If no arguments are passed, then the default installation NAME is set to
+# the default project name ${CMAKE_PROJECT_NAME}
+#
+##############################################################################
+
+macro( ecbuild_bundle_finalize )
+
+  add_custom_target( update DEPENDS ${git_update_targets} )
+
+  ecbuild_info("---------------------------------------------------------")
+  ecbuild_info("Bundle ${CMAKE_PROJECT_NAME}")
+  ecbuild_info("---------------------------------------------------------")
+
+  if("${ARGV1}")
+      ecbuild_install_project( ${ARGV} )
+  else()
+      ecbuild_install_project( NAME ${CMAKE_PROJECT_NAME} )
+  endif()
+
+  ecbuild_print_summary()
+
+endmacro()
diff --git a/cmake/ecbuild_cache.cmake b/cmake/ecbuild_cache.cmake
new file mode 100644
index 0000000..d45b31a
--- /dev/null
+++ b/cmake/ecbuild_cache.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecBuild Cache
+# =============
+#
+# During initialisation, ecBuild introspects the compiler and operating system
+# and performs a number of checks. The result of these is written to a
+# dedicated ``ecbuild-cache.cmake`` file in the build tree. This cache may be
+# used to speed up subsequent *clean* builds i.e. those where no CMakeCache.txt
+# exists yet.
+#
+# To use the ecBuild cache, configure with ``-DECBUILD_CACHE=<cache-file>``,
+# where ``<cache-file>`` is the path to an existing ``ecbuild-cache.cmake``.
+#
+# .. note ::
+#
+#   The ecBuild cache is specific to compiler *and* operating system. Do *not*
+#   attempt to use a cache file created on a different machine or with a
+#   different compiler!
+#
+##############################################################################
+
+# Prepare the cache and clobber any existing ecbuild-cache.cmake
+macro( ecbuild_prepare_cache )
+    include( CheckSymbolExists )
+    include( CheckIncludeFiles )
+    include( CheckCSourceCompiles )
+    include( CheckCXXSourceCompiles )
+    include( CheckTypeSize )
+    set( ecbuild_cache_file ${CMAKE_BINARY_DIR}/ecbuild-cache.cmake )
+    file(WRITE ${ecbuild_cache_file} "# ecbuild cache file\n\n")    
+endmacro()
+
+# Buffer the CMake variable var to be written to the ecBuild cache
+function( ecbuild_cache_var var )
+  if( NOT ${var} )
+    set( ${var} 0 )
+  endif()
+  set( ECBUILD_CACHE_BUFFER "${ECBUILD_CACHE_BUFFER}set( ${var} ${${var}} )\n" CACHE INTERNAL "Cache buffer" )
+endfunction()
+
+# Call check_symbol_exists only if the output is not defined yet
+function( ecbuild_cache_check_symbol_exists symbol includes output )
+  if( NOT DEFINED ${output} )
+    check_symbol_exists( ${symbol} ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_include_files only if the output is not defined yet
+function( ecbuild_cache_check_include_files includes output )
+  if( NOT DEFINED ${output} )
+    check_include_files( ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_c_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_c_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_c_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_cxx_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_cxx_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_cxx_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_type_size only if the output is not defined yet
+function( ecbuild_cache_check_type_size type output )
+  if( NOT DEFINED ${output} )
+    check_type_size( "${type}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Flush the ecBuild cache to disk and reset the buffer
+function( ecbuild_flush_cache )
+  file( APPEND ${ecbuild_cache_file} "${ECBUILD_CACHE_BUFFER}" )
+  set( ECBUILD_CACHE_BUFFER "" CACHE INTERNAL "Cache buffer" )
+endfunction()
diff --git a/cmake/ecbuild_check_c_source_return.cmake b/cmake/ecbuild_check_c_source_return.cmake
new file mode 100644
index 0000000..38d89f7
--- /dev/null
+++ b/cmake/ecbuild_check_c_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_c_source_return
+# =============================
+#
+# Compile and run a given C source code and return its output. ::
+#
+#   ecbuild_check_c_source_return( <source>
+#                                  VAR <name>
+#                                  OUTPUT <name>
+#                                  [ INCLUDES <path1> [ <path2> ... ] ]
+#                                  [ LIBS <library1> [ <library2> ... ] ]
+#                                  [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .c file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_c_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_c_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_c_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+    
+        # write the source file
+    
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/cmake/ecbuild_check_compiler.cmake b/cmake/ecbuild_check_compiler.cmake
new file mode 100644
index 0000000..197f0b5
--- /dev/null
+++ b/cmake/ecbuild_check_compiler.cmake
@@ -0,0 +1,156 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###################################################################################################
+# enable C to use in system introspection
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+endif()
+
+############################################################################################
+# try to get compiler version if cmake did not
+
+if( NOT CMAKE_C_COMPILER_VERSION )
+
+    set( EC_COMPILER_VERSION "?.?" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Intel" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -dumpversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE "([0-9])\\.([0-9])(\\.([0-9]))?" "\\1.\\2"  EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "Clang" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} --version
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*clang version ([0-9])\\.([0-9])(\\.([0-9]))?.*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "SunPro" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -V
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -qversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*V([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+
+    endif()
+
+    if( NOT EC_COMPILER_VERSION STREQUAL "?.?" )
+        set(CMAKE_C_COMPILER_VERSION "${EC_COMPILER_VERSION}" )
+    endif()
+
+endif()
+
+############################################################################################
+# c compiler tests
+
+if( CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+	ecbuild_cache_check_c_source_compiles(
+		  " typedef int foo_t;
+			static inline foo_t static_foo(){return 0;}
+			foo_t foo(){return 0;}
+			int main(int argc, char *argv[]){return 0;}
+		  " EC_HAVE_C_INLINE )
+
+endif()
+
+############################################################################################
+# c++ compiler tests
+
+if( CMAKE_CXX_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+    # check for __FUNCTION__
+    ecbuild_cache_check_cxx_source_compiles( "#include <iostream>\nint main(int argc, char* argv[]) { std::cout << __FUNCTION__ << std::endl; }"
+      EC_HAVE_FUNCTION_DEF )
+
+    # check for c++ abi, usually present in GNU compilers
+    ecbuild_cache_check_cxx_source_compiles( "#include <cxxabi.h>\n int main() { char * type; int status; char * r = abi::__cxa_demangle(type, 0, 0, &status); }"
+    EC_HAVE_CXXABI_H )
+
+    # check for bool
+    ecbuild_cache_check_cxx_source_compiles( "int main() { bool aflag = true; }"
+	  EC_HAVE_CXX_BOOL )
+
+    # check for sstream
+    ecbuild_cache_check_cxx_source_compiles( "#include <sstream>\nint main() { std::stringstream s; }"
+	  EC_HAVE_CXX_SSTREAM )
+
+endif()
+
+############################################################################################
+# enable warnings
+
+if( CMAKE_COMPILER_IS_GNUCC )
+
+    ecbuild_add_c_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_c_flags("-Wall")
+        # ecbuild_add_c_flags("-pedantic")
+        # ecbuild_add_c_flags("-Wextra")
+    endif()
+
+endif()
+
+if( CMAKE_COMPILER_IS_GNUCXX )
+
+   ecbuild_add_cxx_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_cxx_flags("-Wall")
+        #    ecbuild_add_cxx_flags("-Wextra")
+    endif()
+
+endif()
+
+if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+  ecbuild_add_fortran_flags("-warn all")
+endif()
+
+############################################################################################
+# compiler dependent fixes
+
+# For Cray compilers add "-Wl,-Bdynamic" at very end of linker commands, in order to produce dynamic executables by default
+
+if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+  set(CMAKE_Fortran_LINK_EXECUTABLE "<CMAKE_Fortran_COMPILER> <CMAKE_Fortran_LINK_FLAGS> <LINK_FLAGS> <FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+############################################################################################
+# Fortran compiler specific flags
+# if( NOT HAVE_SINGLE_PRECISION )
+#  if(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
+#      ecbuild_add_fortran_flags("-r8")
+#  elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+#      # NOTE that if we add -fdefault-real-8 then we NEED -fdefault-double-8 to avoid quadmath
+#      ecbuild_add_fortran_flags("-fdefault-real-8 -fdefault-double-8")
+#  endif()
+# endif()
diff --git a/cmake/ecbuild_check_cxx11.cmake b/cmake/ecbuild_check_cxx11.cmake
new file mode 100644
index 0000000..d2f9629
--- /dev/null
+++ b/cmake/ecbuild_check_cxx11.cmake
@@ -0,0 +1,138 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx11
+# ===================
+#
+# Check for C++11 features. ::
+#
+#   ecbuild_check_cxx11( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                        [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                        [ PRINT ] )
+#
+# This function uses macros from http://github.com/UCL/GreatCMakeCookOff
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_cxx11 )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_cxx11(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake )
+
+  cxx11_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  # Save CXX flags
+  set( CXX_FLAGS_SNASHOT ${CMAKE_CXX_FLAGS} )
+
+  # Add C++11 flags
+  include( ${ECBUILD_MACROS_DIR}/ecbuild_get_cxx11_flags.cmake )
+  ecbuild_get_cxx11_flags( CXX11_FLAGS )
+  ecbuild_debug( "ecbuild_check_cxx11: detected C++11 flag as ${CXX11_FLAGS}" )
+  set( CMAKE_CXX_FLAGS "${CXX11_FLAGS} ${CMAKE_CXX_FLAGS}" )
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    cxx11_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      cxx11_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      cxx11_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  # Restore CXX flags
+  set( CMAKE_CXX_FLAGS ${CXX_FLAGS_SNAPSHOT} )
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( CXX11_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( CXX11_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${CXX11_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_CXX11_${FEAT} )
+       list( APPEND CXX11_SUPPORTED_FEATURES ${f} )
+    else()
+       list( APPEND CXX11_NOT_SUPPORTED_FEATURES ${f} )
+    endif()
+  endforeach()
+
+  if( CXX11_CHECKED_FEATURES )
+    list( SORT CXX11_CHECKED_FEATURES )
+  endif()
+  if( CXX11_SUPPORTED_FEATURES )
+    list( SORT CXX11_SUPPORTED_FEATURES )
+  endif()
+  if( CXX11_NOT_SUPPORTED_FEATURES )
+    list( SORT CXX11_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( CXX11_CHECKED_FEATURES       ${CXX11_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( CXX11_SUPPORTED_FEATURES     ${CXX11_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( CXX11_NOT_SUPPORTED_FEATURES ${CXX11_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( CXX11_CHECKED_FEATURES )
+      join( CXX11_CHECKED_FEATURES " " CXX11_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked C++11 features: ${CXX11_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no C++11 features" )
+    endif()
+    if( CXX11_SUPPORTED_FEATURES )
+      join( CXX11_SUPPORTED_FEATURES " " CXX11_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found C++11 features: ${CXX11_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no C++11 features" )
+    endif()
+    if( CXX11_NOT_SUPPORTED_FEATURES )
+      join( CXX11_NOT_SUPPORTED_FEATURES " " CXX11_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found C++11 features: ${CXX11_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked C++11 features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_cxx11 )
diff --git a/cmake/ecbuild_check_cxx_source_return.cmake b/cmake/ecbuild_check_cxx_source_return.cmake
new file mode 100644
index 0000000..7270540
--- /dev/null
+++ b/cmake/ecbuild_check_cxx_source_return.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx_source_return
+# ===============================
+#
+# Compile and run a given C++ code and return its output. ::
+#
+#   ecbuild_check_cxx_source_return( <source>
+#                                    VAR <name>
+#                                    OUTPUT <name>
+#                                    [ INCLUDES <path1> [ <path2> ... ] ]
+#                                    [ LIBS <library1> [ <library2> ... ] ]
+#                                    [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .cxx file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_cxx_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_cxx_source_return(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _p_VAR OR NOT _p_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_cxx_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+    set( _msg "Testing ${_p_VAR}:" )
+
+    if( NOT DEFINED ${_p_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_p_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES)
+        if(CMAKE_REQUIRED_LIBRARIES)
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _p_LIBS )
+            list( APPEND __add_libs ${_p_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES)
+        if(CMAKE_REQUIRED_INCLUDES)
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _p_INCLUDES )
+            list( APPEND __add_incs ${_p_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx" "${SOURCE}\n" )
+
+        ecbuild_debug( "${_msg}" )
+        try_run( ${_p_VAR}_EXITCODE ${_p_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+
+        # ecbuild_debug_var( ${_p_VAR}_COMPILED )
+        # ecbuild_debug_var( ${_p_VAR}_EXITCODE )
+
+        # if it did not compile make the return value fail code of 1
+
+        if( NOT ${_p_VAR}_COMPILED )
+          ecbuild_debug( "${_msg} failed to compile" )
+        endif()
+
+        if( "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN" )
+          ecbuild_debug( "${_msg} failed to run" )
+        endif()
+
+        # if the return value was 0 then it worked
+        if( ${_p_VAR}_COMPILED AND "${${_p_VAR}_EXITCODE}" EQUAL 0 )
+
+          ecbuild_debug("${_msg} Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_p_VAR}     1              CACHE INTERNAL "Test ${_p_VAR}")
+          set( ${_p_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_p_VAR} output")
+
+        else()
+
+          if(CMAKE_CROSSCOMPILING AND "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_p_VAR} "${${_p_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_p_VAR} "" CACHE INTERNAL "Test ${_p_VAR}")
+            set(${_p_OUTPUT} "" CACHE INTERNAL "Test ${_p_VAR} output")
+          endif()
+
+          ecbuild_debug("Test ${_p_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+
+    endif()
+
+endmacro()
diff --git a/cmake/ecbuild_check_fortran.cmake b/cmake/ecbuild_check_fortran.cmake
new file mode 100644
index 0000000..d7a63cf
--- /dev/null
+++ b/cmake/ecbuild_check_fortran.cmake
@@ -0,0 +1,126 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran
+# =====================
+#
+# Check for Fortran features. ::
+#
+#   ecbuild_check_fortran( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                          [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                          [ PRINT ] )
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for, fails if not detected
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_fortran )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/fortran_features/CheckFortranFeatures.cmake )
+
+  fortran_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    fortran_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      fortran_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      fortran_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( Fortran_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( Fortran_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${Fortran_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_Fortran_${FEAT} )
+       list( APPEND Fortran_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 1 PARENT_SCOPE )
+    else()
+       list( APPEND Fortran_NOT_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 0 PARENT_SCOPE )
+    endif()
+  endforeach()
+
+  if( Fortran_CHECKED_FEATURES )
+    list( SORT Fortran_CHECKED_FEATURES )
+  endif()
+  if( Fortran_SUPPORTED_FEATURES )
+    list( SORT Fortran_SUPPORTED_FEATURES )
+  endif()
+  if( Fortran_NOT_SUPPORTED_FEATURES )
+    list( SORT Fortran_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( Fortran_CHECKED_FEATURES       ${Fortran_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( Fortran_SUPPORTED_FEATURES     ${Fortran_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( Fortran_NOT_SUPPORTED_FEATURES ${Fortran_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( Fortran_CHECKED_FEATURES )
+      join( Fortran_CHECKED_FEATURES " " Fortran_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked Fortran features: ${Fortran_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no Fortran features" )
+    endif()
+    if( Fortran_SUPPORTED_FEATURES )
+      join( Fortran_SUPPORTED_FEATURES " " Fortran_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found Fortran features: ${Fortran_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no Fortran features" )
+    endif()
+    if( Fortran_NOT_SUPPORTED_FEATURES )
+      join( Fortran_NOT_SUPPORTED_FEATURES " " Fortran_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found Fortran features: ${Fortran_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked Fortran features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_fortran )
diff --git a/cmake/ecbuild_check_fortran_source_return.cmake b/cmake/ecbuild_check_fortran_source_return.cmake
new file mode 100644
index 0000000..92168b9
--- /dev/null
+++ b/cmake/ecbuild_check_fortran_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran_source_return
+# ===================================
+#
+# Compile and run a given Fortran code and return its output. ::
+#
+#   ecbuild_check_fortran_source_return( <source>
+#                                        VAR <name>
+#                                        OUTPUT <name>
+#                                        [ INCLUDES <path1> [ <path2> ... ] ]
+#                                        [ LIBS <library1> [ <library2> ... ] ]
+#                                        [ DEFINITIONS <def1> [ <def2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .f file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_fortran_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_fortran_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_fortran_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/cmake/ecbuild_check_functions.cmake b/cmake/ecbuild_check_functions.cmake
new file mode 100644
index 0000000..59fbd3a
--- /dev/null
+++ b/cmake/ecbuild_check_functions.cmake
@@ -0,0 +1,186 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# os capability checks
+
+if( ENABLE_OS_FUNCTIONS_TEST )
+
+    ### symbol checks ##################
+
+    ecbuild_cache_check_symbol_exists( fseek        "stdio.h"                         EC_HAVE_FSEEK  )
+    ecbuild_cache_check_symbol_exists( fseeko       "stdio.h"                         EC_HAVE_FSEEKO )
+    ecbuild_cache_check_symbol_exists( ftello       "stdio.h"                         EC_HAVE_FTELLO )
+    ecbuild_cache_check_symbol_exists( lseek        "sys/types.h;unistd.h"            EC_HAVE_LSEEK  )
+    ecbuild_cache_check_symbol_exists( ftruncate    "sys/types.h;unistd.h"            EC_HAVE_FTRUNCATE  )
+    ecbuild_cache_check_symbol_exists( open         "sys/types.h;sys/stat.h;fcntl.h"  EC_HAVE_OPEN   )
+    ecbuild_cache_check_symbol_exists( fopen        "stdio.h"                         EC_HAVE_FOPEN  )
+    ecbuild_cache_check_symbol_exists( fmemopen     "stdio.h"                         EC_HAVE_FMEMOPEN )
+    ecbuild_cache_check_symbol_exists( funopen      "stdio.h"                         EC_HAVE_FUNOPEN )
+    ecbuild_cache_check_symbol_exists( flock        "sys/file.h"                      EC_HAVE_FLOCK  )
+    ecbuild_cache_check_symbol_exists( mmap         "sys/mman.h"                      EC_HAVE_MMAP   )
+
+    ecbuild_cache_check_symbol_exists( posix_memalign "stdlib.h"                      EC_HAVE_POSIX_MEMALIGN )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK      "fcntl.h"                         EC_HAVE_F_GETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLK      "fcntl.h"                         EC_HAVE_F_SETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW     "fcntl.h"                         EC_HAVE_F_SETLKW  )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK64     "fcntl.h"                        EC_HAVE_F_GETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLK64     "fcntl.h"                        EC_HAVE_F_SETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW64    "fcntl.h"                        EC_HAVE_F_SETLKW64  )
+
+    ecbuild_cache_check_symbol_exists( MAP_ANONYMOUS "sys/mman.h"                     EC_HAVE_MAP_ANONYMOUS )
+    ecbuild_cache_check_symbol_exists( MAP_ANON      "sys/mman.h"                     EC_HAVE_MAP_ANON )
+
+    ### include files checks ##################
+
+    ecbuild_cache_check_include_files( assert.h       EC_HAVE_ASSERT_H      )
+    ecbuild_cache_check_include_files( stdlib.h       EC_HAVE_STDLIB_H      )
+    ecbuild_cache_check_include_files( unistd.h       EC_HAVE_UNISTD_H      )
+    ecbuild_cache_check_include_files( string.h       EC_HAVE_STRING_H      )
+    ecbuild_cache_check_include_files( strings.h      EC_HAVE_STRINGS_H     )
+    ecbuild_cache_check_include_files( sys/stat.h     EC_HAVE_SYS_STAT_H    )
+    ecbuild_cache_check_include_files( sys/time.h     EC_HAVE_SYS_TIME_H    )
+    ecbuild_cache_check_include_files( sys/types.h    EC_HAVE_SYS_TYPES_H   )
+    ecbuild_cache_check_include_files( malloc.h       EC_HAVE_MALLOC_H      )
+    ecbuild_cache_check_include_files( sys/malloc.h   EC_HAVE_SYS_MALLOC_H  )
+
+    ecbuild_cache_check_include_files( sys/param.h    EC_HAVE_SYS_PARAM_H   )
+    ecbuild_cache_check_include_files( sys/mount.h    EC_HAVE_SYS_MOUNT_H   )
+    ecbuild_cache_check_include_files( sys/vfs.h      EC_HAVE_SYS_VFS_H     )
+
+    ### capability checks ##################
+
+    # test off_t
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\nint main(){ off_t l=0; return 0;}\n" EC_HAVE_OFFT )
+    # test off64_t
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){ off64_t l=0; return 0;}\n" EC_HAVE_OFF64T  )
+    # test struct stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; return 0; }"   EC_HAVE_STRUCT_STAT )
+    # test struct stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; return 0; }" EC_HAVE_STRUCT_STAT64 )
+    # test stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s;	stat(\"\",&s); return 0; }"    EC_HAVE_STAT )
+    # test stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; stat64(\"\",&s); return 0; }"  EC_HAVE_STAT64 )
+    # test fstat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; fstat(1,&s); return 0; }" EC_HAVE_FSTAT )
+    # test fstat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; fstat64(1,&s); return 0; }" EC_HAVE_FSTAT64 )
+    # test fseeko64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l=0;fseeko64(file,l,SEEK_CUR);return 0;}\n" EC_HAVE_FSEEKO64 )
+
+    # test for ftello64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l = ftello64(file);return 0;}\n"  EC_HAVE_FTELLO64 )
+
+    # test for lseek64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/types.h>\n#include <unistd.h>\nint main(){off64_t h = lseek64(0,0,SEEK_SET);return 0;}\n"  EC_HAVE_LSEEK64 )
+    # test for open64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){int fd = open64(\"name\",O_RDWR|O_CREAT,0777);return 0;}\n" EC_HAVE_OPEN64 )
+    # test for fopen64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\nint main(){FILE* file = fopen64(\"name\",\"w\");return 0;}\n"  EC_HAVE_FOPEN64 )
+    # test for ftruncate64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <unistd.h>\n#include <sys/types.h>\nint main(){ftruncate64(0,(off64_t)0);return 0;}\n" EC_HAVE_FTRUNCATE64 )
+    # test for flock64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){struct flock64 l;return 0;}\n" EC_HAVE_FLOCK64 )
+    # test for mmap64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/mman.h>\nint main(){void* addr = mmap64(0,10,PROT_READ|PROT_WRITE,MAP_PRIVATE,10,0); return 0;}\n" EC_HAVE_MMAP64 )
+    # test for struct statvfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/statvfs.h>\nint main(){ struct statvfs v; }" EC_HAVE_STRUCT_STATVFS )
+    # test for struct statvfs64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/statvfs.h>\nint main(){ struct statvfs64 v; }" EC_HAVE_STRUCT_STATVFS64 )
+
+    # test for fopencookie
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <stdio.h>\nint main(){ void* cookie; const char* mode; cookie_io_functions_t iof; FILE* fopencookie(void *cookie, const char *mode, cookie_io_functions_t iof); }" EC_HAVE_FOPENCOOKIE )
+
+    # test for fsync
+    ecbuild_cache_check_symbol_exists(fsync "unistd.h" EC_HAVE_FSYNC)
+    # test for fdatasync
+    ecbuild_cache_check_symbol_exists(fdatasync "unistd.h" EC_HAVE_FDATASYNC)
+    # test for dirfd
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <dirent.h>\nint main(){ DIR *dirp; int i = dirfd(dirp); }\n" EC_HAVE_DIRFD )
+    # test for sys/proc.h
+    ecbuild_cache_check_c_source_compiles( "#include <sys/proc.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROC )
+    # test for procfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/procfs.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROCFS )
+    # test for backtrace
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <execinfo.h>\n int main(){ void ** buffer; int i = backtrace(buffer, 256); }\n" EC_HAVE_EXECINFO_BACKTRACE )
+
+    #### reentrant funtions support  #############
+
+    # test for gmtime_r
+    ecbuild_cache_check_c_source_compiles( "#include <time.h>\nint main(){ time_t now; time(&now); struct tm t; gmtime_r(&now,&t); }\n" EC_HAVE_GMTIME_R )
+    # test for getpwuid_r
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <sys/types.h>\n#include <pwd.h>\nint main(){ char buf[4096]; struct passwd pwbuf; struct passwd *pwbufp = 0; getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pwbufp); }\n" EC_HAVE_GETPWUID_R )
+    # test for getpwnam_r
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <pwd.h>\nint main(){ struct passwd p; char line[1024]; int n = getpwnam_r(\"user\",&p,line,sizeof(line),0); }\n" EC_HAVE_GETPWNAM_R )
+    # test for readdir_r
+    ecbuild_cache_check_c_source_compiles( "#include <dirent.h>\nint main(){ DIR *dirp; struct dirent *entry; struct dirent **result; int i = readdir_r(dirp, entry, result); }\n" EC_HAVE_READDIR_R )
+    # test for gethostbyname_r
+    ecbuild_cache_check_c_source_compiles( "#include <netdb.h>\nint main(){ const char *name; struct hostent *ret; char *buf; struct hostent **result; size_t buflen; int *h_errnop; int i = gethostbyname_r(name,ret,buf,buflen,result,h_errnop); }\n" EC_HAVE_GETHOSTBYNAME_R )
+
+    #### special compiler __atributes__  #############
+
+    # test for __attribute__ ((__constructor__)) -- usually present in GCC, Clang, Intel on Linux, Solaris, MacOSX; not present in AIX XLC
+    ecbuild_cache_check_c_source_compiles( "#include <stdio.h>\nstatic int argc_;static char** argv_;static char** envp_;\nint main(){printf(\"%d\", argc_);}\n__attribute__ ((__constructor__)) static void before_main(int argc, char* argv[], char* envp[]){argc_ = argc;argv_ = argv;envp_ = envp;}\n" EC_HAVE_ATTRIBUTE_CONSTRUCTOR )
+
+    if( NOT DEFINED EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+        ecbuild_check_c_source_return("
+            #include <stdio.h>
+            #include <string.h>
+            int main(){return 0;}
+            __attribute__ ((__constructor__))
+            static void before_main(int argc, char* argv[], char* envp[])
+            {
+                printf(\"%d:%d\",argc, strstr(argv[0],\"cmTryCompileExec\")?1:0);
+            }"
+            VAR    EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+            OUTPUT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT )
+
+        if( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV AND NOT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT STREQUAL "1:1" )
+          set(EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV 0 CACHE INTERNAL "ATTRIBUTE_CONSTRUCTOR doesnt init argv correctly")
+        endif()
+    endif()
+    ecbuild_cache_var( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+
+
+    #### check for some Linux stuff #############
+
+    if( NOT DEFINED EC_HAVE_PROCFS )
+        ecbuild_check_c_source_return("
+            #include <sys/types.h>
+            #include <dirent.h>
+            int main()
+            {
+                DIR* d = opendir(\"/proc\");
+                if(d)
+                    return 0;
+                else
+                    return -1;
+            }"
+            VAR    EC_HAVE_PROCFS
+            OUTPUT EC_HAVE_PROCFS_OUTPUT )
+    endif()
+    ecbuild_cache_var( EC_HAVE_PROCFS )
+
+#    ecbuild_debug_var(EC_HAVE_PROCFS)
+#    ecbuild_debug_var(EC_HAVE_PROCFS_OUTPUT)
+
+    #### check support for DL library #############
+
+    ecbuild_cache_check_include_files( dlfcn.h  EC_HAVE_DLFCN_H )
+
+    cmake_push_check_state(RESET)
+    set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS} )
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <dlfcn.h>\nint main(){ void* addr; Dl_info info; dladdr(addr, &info); }\n" EC_HAVE_DLADDR )
+    cmake_pop_check_state()
+
+endif()
+
+
diff --git a/cmake/ecbuild_check_os.cmake b/cmake/ecbuild_check_os.cmake
new file mode 100644
index 0000000..d2af403
--- /dev/null
+++ b/cmake/ecbuild_check_os.cmake
@@ -0,0 +1,536 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# check size of pointer
+
+# Re-check size of void pointer since for some compiler combinations this is not properly set
+ecbuild_cache_check_type_size( "void*" CMAKE_SIZEOF_VOID_P  )
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+
+endif()
+
+math( EXPR EC_OS_BITS "${CMAKE_SIZEOF_VOID_P} * 8" )
+
+# we only support 32 and 64 bit operating systems
+if( NOT EC_OS_BITS EQUAL "32" AND NOT EC_OS_BITS EQUAL "64" )
+  ecbuild_critical( "operating system ${CMAKE_SYSTEM} ${EC_OS_BITS} bits -- ecbuild only supports 32 or 64 bit OS's" )
+endif()
+
+############################################################################################
+# For 64 bit architectures enable PIC (position-independent code)
+
+# Allow overriding the position independent code setting (ECBUILD-220)
+if( DEFINED ECBUILD_POSITION_INDEPENDENT_CODE )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ${ECBUILD_POSITION_INDEPENDENT_CODE} )
+elseif( ${EC_OS_BITS} EQUAL 64 )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ON )
+endif()
+
+
+############################################################################################
+# check architecture
+
+if( ENABLE_OS_TYPES_TEST )
+
+  set( EC_SIZEOF_PTR ${CMAKE_SIZEOF_VOID_P} )
+  ecbuild_cache_var( EC_SIZEOF_PTR )
+  ecbuild_cache_check_type_size( char           EC_SIZEOF_CHAR        )
+  ecbuild_cache_check_type_size( short          EC_SIZEOF_SHORT       )
+  ecbuild_cache_check_type_size( int            EC_SIZEOF_INT         )
+  ecbuild_cache_check_type_size( long           EC_SIZEOF_LONG        )
+  ecbuild_cache_check_type_size( "long long"    EC_SIZEOF_LONG_LONG   )
+  ecbuild_cache_check_type_size( float          EC_SIZEOF_FLOAT       )
+  ecbuild_cache_check_type_size( double         EC_SIZEOF_DOUBLE      )
+  ecbuild_cache_check_type_size( "long double"  EC_SIZEOF_LONG_DOUBLE )
+  ecbuild_cache_check_type_size( size_t         EC_SIZEOF_SIZE_T      )
+  ecbuild_cache_check_type_size( ssize_t        EC_SIZEOF_SSIZE_T     )
+  ecbuild_cache_check_type_size( off_t          EC_SIZEOF_OFF_T       )
+
+  ecbuild_debug( "sizeof void*       [${EC_SIZEOF_PTR}]" )
+  ecbuild_debug( "sizeof char        [${EC_SIZEOF_CHAR}]" )
+  ecbuild_debug( "sizeof short       [${EC_SIZEOF_SHORT}]" )
+  ecbuild_debug( "sizeof int         [${EC_SIZEOF_INT}]" )
+  ecbuild_debug( "sizeof long        [${EC_SIZEOF_LONG}]" )
+  ecbuild_debug( "sizeof long long   [${EC_SIZEOF_LONG_LONG}]" )
+  ecbuild_debug( "sizeof float       [${EC_SIZEOF_FLOAT}]" )
+  ecbuild_debug( "sizeof double      [${EC_SIZEOF_DOUBLE}]" )
+  ecbuild_debug( "sizeof long double [${EC_SIZEOF_LONG_DOUBLE}]" )
+  ecbuild_debug( "sizeof size_t      [${EC_SIZEOF_SIZE_T}]" )
+  ecbuild_debug( "sizeof ssize_t     [${EC_SIZEOF_SSIZE_T}]" )
+  ecbuild_debug( "sizeof off_t       [${EC_SIZEOF_OFF_T}]" )
+
+endif()
+
+############################################################################################
+# check for large file support
+
+# ensure we use 64bit access to files even on 32bit os -- aka Large File Support
+# by making off_t 64bit and stat behave as stat64
+
+if( ENABLE_LARGE_FILE_SUPPORT )
+
+  ecbuild_cache_check_type_size( off_t EC_SIZEOF_OFF_T )
+
+  if( EC_SIZEOF_OFF_T LESS "8" )
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+      add_definitions( -D_FILE_OFFSET_BITS=64 )
+    endif()
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+      add_definitions( -D_LARGE_FILES=64 )
+    endif()
+
+    get_directory_property( __compile_defs COMPILE_DEFINITIONS )
+
+    if( __compile_defs )
+      foreach( def ${__compile_defs} )
+        list( APPEND CMAKE_REQUIRED_DEFINITIONS -D${def} )
+      endforeach()
+    endif()
+
+  endif()
+
+endif()
+
+############################################################################################
+# check endiness
+
+if( ENABLE_OS_ENDINESS_TEST )
+
+  if( NOT DEFINED EC_BIG_ENDIAN AND NOT DEFINED EC_LITTLE_ENDIAN )
+
+    test_big_endian( _BIG_ENDIAN )
+
+    if( _BIG_ENDIAN )
+        set( EC_BIG_ENDIAN    1 )
+        set( EC_LITTLE_ENDIAN 0 )
+    else()
+        set( EC_BIG_ENDIAN    0 )
+        set( EC_LITTLE_ENDIAN 1 )
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( EC_BIG_ENDIAN )
+  ecbuild_cache_var( EC_LITTLE_ENDIAN )
+
+  if( NOT DEFINED IEEE_BE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0x30,0x61,0xDE,0x80,0x93,0x67,0xCC,0xD9,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x05,0x83,0x48,0x22,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_BE )
+
+    if( "${IEEE_BE}" STREQUAL "" )
+      set( IEEE_BE 0 CACHE INTERNAL "Test IEEE_BE")
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( IEEE_BE )
+
+  if( EC_BIG_ENDIAN AND NOT IEEE_BE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Big-Endian but compiled code runs differently -- to ignore this pass -DIEEE_BE=0 to CMake/ecBuild")
+  endif()
+
+  if( NOT DEFINED IEEE_LE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0xD9,0xCC,0x67,0x93,0x80,0xDE,0x61,0x30,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x22,0x48,0x83,0x05,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_LE )
+
+    if( "${IEEE_LE}" STREQUAL "" )
+      set( IEEE_LE 0 CACHE INTERNAL "Test IEEE_LE")
+    endif()
+  endif()
+
+  ecbuild_cache_var( IEEE_LE )
+
+  if( EC_LITTLE_ENDIAN AND NOT IEEE_LE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Little-Endian but compiled code runs differently -- to ignore this pass -DIEEE_LE=0 to CMake/ecBuild")
+  endif()
+
+endif()
+
+############################################################################################
+# enable profiling via gprof
+
+if( ENABLE_PROFILING )
+  ecbuild_deprecate( "ENABLE_PROFILING is deprecated and ignored, use ENABLE_GPROF instead" )
+endif()
+
+if( ENABLE_GPROF )
+
+  # User defined profiling flag takes precedence
+  if( ECBUILD_GPROF_FLAG )
+
+    ecbuild_debug( "Enabling profiling with user defined flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p  Generate extra code to write profile information suitable for the analysis program
+  #     prof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # -pg Generate extra code to write profile information suitable for the analysis program
+  #     gprof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # --coverage
+  #      This option is used to compile and link code instrumented for coverage analysis.  The
+  #      option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov
+  #      (when linking).  See the documentation for those options for more details.
+  #
+  #      *   Compile the source files with -fprofile-arcs plus optimization and code generation
+  #          options.  For test coverage analysis, use the additional -ftest-coverage option.
+  #          You do not need to profile every source file in a program.
+  #
+  #      *   Link your object files with -lgcov or -fprofile-arcs (the latter implies the
+  #          former).
+  #
+  #      *   Run the program on a representative workload to generate the arc profile
+  #          information.  This may be repeated any number of times.  You can run concurrent
+  #          instances of your program, and provided that the file system supports locking, the
+  #          data files will be correctly updated.  Also "fork" calls are detected and correctly
+  #          handled (double counting will not happen).
+  #
+  #      *   For profile-directed optimizations, compile the source files again with the same
+  #          optimization and code generation options plus -fbranch-probabilities.
+  #
+  #      *   For test coverage analysis, use gcov to produce human readable information from the
+  #          .gcno and .gcda files.  Refer to the gcov documentation for further information.
+  #
+  #      With -fprofile-arcs, for each function of your program GCC creates a program flow
+  #      graph, then finds a spanning tree for the graph.  Only arcs that are not on the
+  #      spanning tree have to be instrumented: the compiler adds code to count the number of
+  #      times that these arcs are executed.  When an arc is the only exit or only entrance to a
+  #      block, the instrumentation code can be added to the block; otherwise, a new basic block
+  #      must be created to hold the instrumentation code.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+
+    set( ECBUILD_GPROF_FLAG "-pg --coverage" )
+    ecbuild_debug( "Enabling profiling with GNU flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p
+  #
+  #        Compiles and links for function profiling
+  #               with gprof(1).
+  #
+  #        Architecture  Restrictions:  Not  available  on  Intel(R)   64   architecture
+  #        targeting the
+  #               Intel(R)  Xeon  Phi(TM) coprocessor x100 product family (formerly code
+  #               name  Knights  Corner),  on  IA-32  architecture  targeting   Intel(R)
+  #               Graphics Technology, or on Intel(R) 64 architecture targeting Intel(R)
+  #               Graphics Technology
+  #
+  #        Arguments:
+  #
+  #        None
+  #
+  #        Default:
+  #
+  #        OFF               Files are compiled and linked without profiling.
+  #
+  #        Description:
+  #
+  #        This option compiles and links for function profiling with gprof(1).
+  #
+  #        When you specify this option, inlining is disabled. However, you can override
+  #        this  by  specifying  pragma forceinline, declspec forceinline (Windows* OS),
+  #        attribute always_inline (Linux* OS and OS X*), or a compiler option  such  as
+  #        [Q]inline-forceinline.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "Intel" )
+
+    set( ECBUILD_GPROF_FLAG "-p" )
+    ecbuild_debug( "Enabling profiling with Intel flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -Mprof[=option[,option,...]]
+  #        Set performance profiling options.  Use of these options will cause the resulting
+  #        executable to create a performance profile that can be viewed and analyzed with the
+  #        PGPROF performance profiler.  In the descriptions below, PGI-style profiling implies
+  #        compiler-generated source instrumentation.  MPICH-style profiling implies the use of
+  #        instrumented wrappers for MPI library routines.  The -Mprof options are:
+  #
+  #        ccff
+  #
+  #        dwarf     Generate limited DWARF symbol information sufficient for most performance
+  #                  profilers.
+  #
+  #        func      Perform PGI-style function level profiling.
+  #
+  #        hwcts     Generate a profile using event-based sampling of hardware counters via the
+  #                  PAPI interface (linux86-64 only, PAPI must be installed).
+  #
+  #        lines     Perform PGI-style line level profiling.
+  #
+  #        hpmpi     (PGI CDK only) Perform MPICH-style profiling for the HP Implies
+  #                  -Mmpi=hpmpi.
+  #
+  #        mpich1    (PGI CDK only) Perform MPICH-style profiling for MPICH-1.  Implies
+  #                  -Mmpi=mpich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mpich2    (PGI CDK only) Perform MPICH-style profiling for MPICH-2.  Implies
+  #                  -Mmpi=mpich2.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mvapich1  (PGI CDK only) Perform MPICH-style profiling for MVAPICH.  Implies
+  #                  -Mmpi=mvapich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag
+  #                  is no longer fully supported.
+  #
+  #        time      Generate a profile using time-based instruction-level statistical
+  #                  sampling. This is equivalent to -pg, except that the profile is saved in a
+  #                  file named pgprof.out instead of gmon.out.
+  #
+  #        On Linux systems that have OProfile installed, PGPROF supports collection of
+  #        performance data without recompilation. Use of -Mprof=dwarf is useful for this mode
+  #        of profiling.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "PGI" )
+
+    set( ECBUILD_GPROF_FLAG "-Mprof=dwarf,time" )
+    ecbuild_debug( "Enabling profiling with PGI flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # There is no equivalent to -pg for clang:
+  # http://lists.llvm.org/pipermail/cfe-dev/2010-September/011255.html
+  else()
+
+    ecbuild_warn( "Profiling enabled but ecbuild doesn't know how to enable for this particular compiler ${CMAKE_C_COMPILER_ID}")
+
+  endif()
+
+  set( CMAKE_EXE_LINKER_FLAGS    "${CMAKE_EXE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+
+  set( _trust_flags ${ECBUILD_TRUST_FLAGS} )
+  set( ECBUILD_TRUST_FLAGS ON )
+  ecbuild_add_c_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_cxx_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_fortran_flags( "${ECBUILD_GPROF_FLAG}" )
+  set( ECBUILD_TRUST_FLAGS ${_trust_flags} )
+  unset( _trust_flags )
+
+endif()
+
+############################################################################################
+# check operating system
+
+set( EC_OS_NAME "UNKNOWN" )
+
+### Unix's -- Proper operating systems
+
+if( UNIX )
+
+  ### APPLE ###
+
+  if( APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+    set( EC_OS_NAME "macosx" )
+  endif()
+
+  ### Linux ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" )
+
+    set( EC_OS_NAME "linux" )
+
+    # The following option allows enabling the new dtags linker option
+    # (when set to OFF). ONLY SET TO OFF IF YOU KNOW WHAT YOU ARE DOING AND
+    # NEVER WHEN BUILDING PRODUCTION SOFTWARE. YOU HAVE BEEN WARNED!
+    option( ECBUILD_DISABLE_NEW_DTAGS "Set the linker flag --disable-new-dtags" ON )
+    mark_as_advanced( ECBUILD_DISABLE_NEW_DTAGS )
+
+    if( ECBUILD_DISABLE_NEW_DTAGS )
+      # recent linkers default to --enable-new-dtags
+      # which then adds both RPATH and RUNPATH to executables
+      # thus invalidating RPATH setting, and making LD_LIBRARY_PATH take precedence
+      # to be sure, use tool 'readelf -a <exe> | grep PATH' to see what paths are built-in
+      # see:
+      #  * http://blog.qt.digia.com/blog/2011/10/28/rpath-and-runpath
+      #  * http://www.cmake.org/Wiki/CMake_RPATH_handling
+      #  * man ld
+      #  * http://blog.tremily.us/posts/rpath
+      #  * http://fwarmerdam.blogspot.co.uk/2010/12/rpath-runpath-and-ldlibrarypath.html
+      set(CMAKE_EXE_LINKER_FLAGS     "${CMAKE_EXE_LINKER_FLAGS}    -Wl,--disable-new-dtags")
+      set(CMAKE_SHARED_LINKER_FLAGS  "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-new-dtags")
+      set(CMAKE_MODULE_LINKER_FLAGS  "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--disable-new-dtags")
+    endif()
+
+  endif()
+
+  ### FreeBSD ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
+    set( EC_OS_NAME "freebsd" )
+  endif()
+
+  ### Solaris ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" )
+    set( EC_OS_NAME "solaris" )
+  endif()
+
+  ### AIX ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+
+    set( EC_OS_NAME "aix" )
+
+    set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -bbigtoc" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+      set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker" )
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCC )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_c_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCXX )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_cxx_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_c_flags("-qpic=large")
+#            ecbuild_add_c_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_c_flags("-qstrict")
+          ecbuild_add_c_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_c_flags("-qfullpath")
+          ecbuild_add_c_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_CXX_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_cxx_flags("-qpic=large")
+      ecbuild_add_cxx_flags("-bmaxdata:0x40000000")
+      ecbuild_add_cxx_flags("-qrtti")
+      ecbuild_add_cxx_flags("-qfuncsect")
+
+#           ecbuild_add_cxx_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_cxx_flags("-qstrict")
+          ecbuild_add_cxx_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_cxx_flags("-qfullpath")
+          ecbuild_add_cxx_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_fortran_flags("-qxflag=dealloc_cfptr")
+      ecbuild_add_fortran_flags("-qextname")
+      ecbuild_add_fortran_flags("-qdpc=e")
+      ecbuild_add_fortran_flags("-bmaxdata:0x40000000")
+      ecbuild_add_fortran_flags("-bloadmap:loadmap -bmap:loadmap")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_fortran_flags("-q32")
+      endif()
+    endif()
+
+  endif()
+
+endif()
+
+### Cygwin
+
+if( ${CMAKE_SYSTEM_NAME} MATCHES "CYGWIN" )
+
+  set( EC_OS_NAME "cygwin" )
+  ecbuild_warn( "Building on Cygwin should work but is untested" )
+
+endif()
+
+### final warning / error
+
+if( ${EC_OS_NAME} MATCHES "UNKNOWN" )
+
+  if( DISABLE_OS_CHECK )
+    ecbuild_warn( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                  " -- DISABLE_OS_CHECK is ON so proceeding at your own risk ..." )
+  else()
+    ecbuild_critical( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                      " -- refusing to continue. Disable this check with -DDISABLE_OS_CHECK=ON" )
+  endif()
+
+endif()
diff --git a/cmake/ecbuild_compiler_flags.cmake b/cmake/ecbuild_compiler_flags.cmake
new file mode 100644
index 0000000..139038b
--- /dev/null
+++ b/cmake/ecbuild_compiler_flags.cmake
@@ -0,0 +1,217 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_compiler_flags
+# ======================
+#
+# Set compiler specific default compilation flags for a given language. ::
+#
+#   ecbuild_compiler_flags( <lang> )
+#
+# The procedure is as follows:
+#
+# 1.  ecBuild does **not** set ``CMAKE_<lang>_FLAGS`` i.e. the user can set
+#     these via ``-D`` or the CMake cache and these will be the "base" flags.
+#
+# 2.  ecBuild **overwrites** ``CMAKE_<lang>_FLAGS_<btype>`` in the CMake cache
+#     for all build types with compiler specific defaults for the currently
+#     loaded compiler i.e. any value set by the user via ``-D`` or the CMake
+#     cache **has no effect**.
+#
+# 3.  Any value the user provides via ``ECBUILD_<lang>_FLAGS`` or
+#     ``ECBUILD_<lang>_FLAGS_<btype>`` **overrides** the corresponding
+#     ``CMAKE_<lang>_FLAGS`` or ``CMAKE_<lang>_FLAGS_<btype>`` **without being
+#     written to the CMake cache**.
+#
+##############################################################################
+
+macro( ecbuild_compiler_flags _lang )
+
+  # Set compiler and language specific default flags - OVERWRITES variables in CMake cache
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): try include ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake ")
+    include( ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake OPTIONAL )
+  endif()
+
+  # Apply user or toolchain specified compilation flag overrides (NOT written to cache)
+
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    if( DEFINED ECBUILD_${_lang}_FLAGS_${_btype} )
+      ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS_${_btype} with ${ECBUILD_${_lang}_FLAGS_${_btype}}")
+      set( CMAKE_${_lang}_FLAGS_${_btype} ${ECBUILD_${_lang}_FLAGS_${_btype}} )
+    endif()
+    mark_as_advanced( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+  if( DEFINED ECBUILD_${_lang}_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS with ${ECBUILD_${_lang}_FLAGS}")
+    set( CMAKE_${_lang}_FLAGS "${ECBUILD_${_lang}_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_FLAGS )
+
+  if( DEFINED ECBUILD_${_lang}_LINK_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_LINK_FLAGS with ${ECBUILD_${_lang}_LINK_FLAGS}")
+    set( CMAKE_${_lang}_LINK_FLAGS "${ECBUILD_${_lang}_LINK_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_LINK_FLAGS )
+
+  ecbuild_debug_var( CMAKE_${_lang}_FLAGS )
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    ecbuild_debug_var( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# Using custom compilation flags
+# ==============================
+#
+# If compilation flags need to be controlled on a per source file basis,
+# ecBuild supports defining custom rules in a CMake or JSON file.
+#
+# When using this approach, *default compilation flags are NOT loaded*!
+#
+# Overriding compilation flags on a per source file basis using CMake rules
+# -------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_COMPILE_FLAGS`` to the *full path* of a CMake file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_COMPILE_FLAGS``
+# takes precendence and ``ECBUILD_COMPILE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# Flags can be overridden in 3 different ways:
+#
+# 1.  By defining project specific flags for a language and (optionally)
+#     build type e.g. ::
+#
+#       set(<PNAME>_Fortran_FLAGS "...") # common flags for all build types
+#       set(<PNAME>_Fortran_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 2.  By defining source file specific flags which are *combined* with the
+#     project and target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES COMPILE_FLAGS "..."  # common flags for all build types
+#                    COMPILE_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 3.  By defining source file specific flags which *override* the project and
+#     target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES OVERRIDE_COMPILE_FLAGS "..."
+#                    OVERRIDE_COMPILE_FLAGS_DEBUG "...")
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+# Overriding compilation flags on a per source file basis using JSON rules
+# ------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_SOURCE_FLAGS`` to the *full path* of a JSON file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_SOURCE_FLAGS``
+# takes precendence and ``ECBUILD_SOURCE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# The JSON file lists shell glob patterns and the rule to apply to each source
+# file matching the pattern, defined as an array ``[op, flag1, ...]``
+# containing an operator followed by one or more flags. Valid operators are:
+#
+# :+: Add the flags to the default compilation flags for matching files
+# :=: Set the flags for matching files, disregarding default compilation flags
+# :/: Remove the flags from the default compilation flags for matching files
+#
+# Rules can be nested to e.g. only apply to a subdirectory by setting the rule
+# to a dictionary, which will only apply to source files matching its pattern.
+#
+# An example JSON file demonstrating different rule types is given below: ::
+#
+#   {
+#     "*"       : [ "+", "-g0" ],
+#     "*.cxx"   : [ "+", "-cxx11" ],
+#     "*.f90"   : [ "+", "-pipe" ],
+#     "foo.c"   : [ "+", "-O0" ],
+#     "foo.cc"  : [ "+", "-O2", "-pipe" ],
+#     "bar/*": {
+#       "*.f90" : [ "=", "-O1" ]
+#     },
+#     "baz/*": {
+#       "*.f90" : [ "/", "-pipe" ],
+#       "*.f90" : [ "/", "-O2" ],
+#       "*.f90" : [ "+", "-O3" ]
+#     }
+#   }
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+##############################################################################
+
+# Custom (project specific) compilation flags enabled?
+foreach( _flags COMPILE SOURCE )
+  if( ${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS )
+    if ( ECBUILD_${_flags}_FLAGS )
+      ecbuild_debug( "Override ECBUILD_${_flags}_FLAGS (${ECBUILD_${_flags}_FLAGS}) with ${PROJECT_NAME} specific flags (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    else()
+      ecbuild_debug( "Use ${PROJECT_NAME} specific ECBUILD_${_flags}_FLAGS (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    endif()
+    set( ECBUILD_${_flags}_FLAGS ${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS} )
+  endif()
+  # Ensure ECBUILD_${_flags}_FLAGS is a valid file path
+  if( DEFINED ECBUILD_${_flags}_FLAGS AND NOT EXISTS ${ECBUILD_${_flags}_FLAGS} )
+    ecbuild_warn( "ECBUILD_${_flags}_FLAGS points to non-existent file ${ECBUILD_${_flags}_FLAGS} and will be ignored" )
+    unset( ECBUILD_${_flags}_FLAGS )
+    unset( ECBUILD_${_flags}_FLAGS CACHE )
+  endif()
+endforeach()
+if( ECBUILD_COMPILE_FLAGS )
+  include( "${ECBUILD_COMPILE_FLAGS}" )
+endif()
+
+foreach( _lang C CXX Fortran )
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+
+    # Clear default compilation flags potentially inherited from parent scope
+    # when using custom compilation flags
+    if( ECBUILD_SOURCE_FLAGS OR ECBUILD_COMPILE_FLAGS )
+      set(CMAKE_${_lang}_FLAGS "")
+      foreach(_btype ALL RELEASE RELWITHDEBINFO PRODUCTION BIT DEBUG)
+        set(CMAKE_${_lang}_FLAGS_${_btype} "")
+      endforeach()
+    # Load default compilation flags only if custom compilation flags not enabled
+    else()
+      ecbuild_compiler_flags( ${_lang} )
+    endif()
+
+  endif()
+endforeach()
+
+# Apply user or toolchain specified linker flag overrides per object type (NOT written to cache)
+foreach( _obj EXE SHARED MODULE )
+  if( ECBUILD_${_obj}_LINKER_FLAGS )
+    set( CMAKE_${_obj}_LINKER_FLAGS ${ECBUILD_${_obj}_LINKER_FLAGS} )
+  endif()
+endforeach()
+
+foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+
+  foreach( _obj EXE SHARED MODULE )
+    if( ECBUILD_${_obj}_LINKER_FLAGS_${_btype} )
+      set( CMAKE_${_obj}_LINKER_FLAGS_${_btype} ${ECBUILD_${_obj}_LINKER_FLAGS_${_btype}} )
+    endif()
+  endforeach()
+
+endforeach()
diff --git a/cmake/ecbuild_config.h.in b/cmake/ecbuild_config.h.in
new file mode 100644
index 0000000..a7d7d75
--- /dev/null
+++ b/cmake/ecbuild_config.h.in
@@ -0,0 +1,201 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef @PROJECT_NAME at _ecbuild_config_h
+#define @PROJECT_NAME at _ecbuild_config_h
+
+/* ecbuild info */
+
+#ifndef ECBUILD_VERSION_STR
+#define ECBUILD_VERSION_STR "@ECBUILD_VERSION_STR@"
+#endif
+#ifndef ECBUILD_MACROS_DIR
+#define ECBUILD_MACROS_DIR  "@ECBUILD_MACROS_DIR@"
+#endif
+
+/* cpu arch info */
+
+#cmakedefine EC_BIG_ENDIAN       @EC_BIG_ENDIAN@
+#cmakedefine EC_LITTLE_ENDIAN    @EC_LITTLE_ENDIAN@
+
+/* compiler support */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+/* os capability checks */
+
+/* --- symbols --- */
+
+#cmakedefine EC_HAVE_FSEEK
+#cmakedefine EC_HAVE_FSEEKO
+#cmakedefine EC_HAVE_FTELLO
+#cmakedefine EC_HAVE_LSEEK
+#cmakedefine EC_HAVE_FTRUNCATE
+#cmakedefine EC_HAVE_OPEN
+#cmakedefine EC_HAVE_FOPEN
+#cmakedefine EC_HAVE_FMEMOPEN
+#cmakedefine EC_HAVE_FUNOPEN
+#cmakedefine EC_HAVE_FLOCK
+#cmakedefine EC_HAVE_MMAP
+#cmakedefine EC_HAVE_FOPENCOOKIE
+
+#cmakedefine EC_HAVE_POSIX_MEMALIGN
+
+#cmakedefine EC_HAVE_F_GETLK
+#cmakedefine EC_HAVE_F_SETLKW
+#cmakedefine EC_HAVE_F_SETLK
+
+#cmakedefine EC_HAVE_F_GETLK64
+#cmakedefine EC_HAVE_F_SETLKW64
+#cmakedefine EC_HAVE_F_SETLK64
+
+#cmakedefine EC_HAVE_MAP_ANONYMOUS
+#cmakedefine EC_HAVE_MAP_ANON
+
+/* --- include files --- */
+
+#cmakedefine EC_HAVE_ASSERT_H
+#cmakedefine EC_HAVE_STDLIB_H
+#cmakedefine EC_HAVE_UNISTD_H
+#cmakedefine EC_HAVE_STRING_H
+#cmakedefine EC_HAVE_STRINGS_H
+#cmakedefine EC_HAVE_SYS_STAT_H
+#cmakedefine EC_HAVE_SYS_TIME_H
+#cmakedefine EC_HAVE_SYS_TYPES_H
+
+#cmakedefine EC_HAVE_MALLOC_H
+#cmakedefine EC_HAVE_SYS_MALLOC_H
+
+#cmakedefine EC_HAVE_SYS_PARAM_H
+#cmakedefine EC_HAVE_SYS_MOUNT_H
+#cmakedefine EC_HAVE_SYS_VFS_H
+
+/* --- capabilities --- */
+
+#cmakedefine EC_HAVE_OFFT
+#cmakedefine EC_HAVE_OFF64T
+
+#cmakedefine EC_HAVE_STRUCT_STAT
+#cmakedefine EC_HAVE_STRUCT_STAT64
+#cmakedefine EC_HAVE_STAT
+#cmakedefine EC_HAVE_STAT64
+#cmakedefine EC_HAVE_FSTAT
+#cmakedefine EC_HAVE_FSTAT64
+
+#cmakedefine EC_HAVE_FSEEKO64
+#cmakedefine EC_HAVE_FTELLO64
+#cmakedefine EC_HAVE_LSEEK64
+#cmakedefine EC_HAVE_OPEN64
+#cmakedefine EC_HAVE_FOPEN64
+#cmakedefine EC_HAVE_FTRUNCATE64
+#cmakedefine EC_HAVE_FLOCK64
+#cmakedefine EC_HAVE_MMAP64
+
+#cmakedefine EC_HAVE_STRUCT_STATVFS
+#cmakedefine EC_HAVE_STRUCT_STATVFS64
+#cmakedefine EC_HAVE_STATVFS
+#cmakedefine EC_HAVE_STATVFS64
+
+#cmakedefine EC_HAVE_FSYNC
+#cmakedefine EC_HAVE_FDATASYNC
+#cmakedefine EC_HAVE_DIRFD
+#cmakedefine EC_HAVE_SYSPROC
+#cmakedefine EC_HAVE_SYSPROCFS
+
+#cmakedefine EC_HAVE_EXECINFO_BACKTRACE
+
+/* --- asynchronous IO support --- */
+
+#cmakedefine EC_HAVE_AIOCB
+#cmakedefine EC_HAVE_AIO64CB
+
+/* --- reentrant funtions support --- */
+
+#cmakedefine EC_HAVE_GMTIME_R
+#cmakedefine EC_HAVE_GETPWUID_R
+#cmakedefine EC_HAVE_GETPWNAM_R
+#cmakedefine EC_HAVE_READDIR_R
+#cmakedefine EC_HAVE_GETHOSTBYNAME_R
+
+/* --- compiler __attribute__ support --- */
+
+#cmakedefine EC_HAVE_ATTRIBUTE_CONSTRUCTOR
+#cmakedefine EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+#cmakedefine EC_HAVE_PROCFS
+
+
+/* --- dl library support --- */
+
+#cmakedefine EC_HAVE_DLFCN_H
+#cmakedefine EC_HAVE_DLADDR
+
+/* --- c compiler support --- */
+
+#cmakedefine EC_HAVE_C_INLINE
+
+/* --- c++ compiler support --- */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+#cmakedefine EC_HAVE_CXXABI_H
+#cmakedefine EC_HAVE_CXX_BOOL
+
+#cmakedefine EC_HAVE_CXX_SSTREAM
+
+/* config info */
+
+#define @PNAME at _OS_NAME          "@CMAKE_SYSTEM@"
+#define @PNAME at _OS_BITS          @EC_OS_BITS@
+#define @PNAME at _OS_BITS_STR      "@EC_OS_BITS@"
+#define @PNAME at _OS_STR           "@EC_OS_NAME at .@EC_OS_BITS@"
+#define @PNAME at _OS_VERSION       "@CMAKE_SYSTEM_VERSION@"
+#define @PNAME at _SYS_PROCESSOR    "@CMAKE_SYSTEM_PROCESSOR@"
+
+#define @PNAME at _BUILD_TIMESTAMP  "@EC_BUILD_TIMESTAMP@"
+#define @PNAME at _BUILD_TYPE       "@CMAKE_BUILD_TYPE@"
+
+#define @PNAME at _C_COMPILER_ID      "@CMAKE_C_COMPILER_ID@"
+#define @PNAME at _C_COMPILER_VERSION "@CMAKE_C_COMPILER_VERSION@"
+
+#define @PNAME at _CXX_COMPILER_ID      "@CMAKE_CXX_COMPILER_ID@"
+#define @PNAME at _CXX_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@"
+
+#define @PNAME at _C_COMPILER       "@CMAKE_C_COMPILER@"
+#define @PNAME at _C_FLAGS          "@EC_C_FLAGS@"
+
+#define @PNAME at _CXX_COMPILER     "@CMAKE_CXX_COMPILER@"
+#define @PNAME at _CXX_FLAGS        "@EC_CXX_FLAGS@"
+
+/* Needed for finding per package config files */
+
+#define @PNAME at _INSTALL_DIR       "@CMAKE_INSTALL_PREFIX@"
+#define @PNAME at _INSTALL_BIN_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_BIN_DIR@"
+#define @PNAME at _INSTALL_LIB_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB_DIR@"
+#define @PNAME at _INSTALL_DATA_DIR  "@CMAKE_INSTALL_PREFIX@/@INSTALL_DATA_DIR@"
+
+#define @PNAME at _DEVELOPER_SRC_DIR "@CMAKE_SOURCE_DIR@"
+#define @PNAME at _DEVELOPER_BIN_DIR "@CMAKE_BINARY_DIR@"
+
+#cmakedefine EC_HAVE_FORTRAN
+
+#ifdef EC_HAVE_FORTRAN
+
+#define @PNAME at _Fortran_COMPILER_ID      "@CMAKE_Fortran_COMPILER_ID@"
+#define @PNAME at _Fortran_COMPILER_VERSION "@CMAKE_Fortran_COMPILER_VERSION@"
+
+#define @PNAME at _Fortran_COMPILER "@CMAKE_Fortran_COMPILER@"
+#define @PNAME at _Fortran_FLAGS    "@EC_Fortran_FLAGS@"
+
+#endif
+
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_LINKED
+
+#endif /* @PROJECT_NAME at _ecbuild_config_h */
diff --git a/cmake/ecbuild_declare_project.cmake b/cmake/ecbuild_declare_project.cmake
new file mode 100644
index 0000000..b3f29e7
--- /dev/null
+++ b/cmake/ecbuild_declare_project.cmake
@@ -0,0 +1,198 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_declare_project
+# =======================
+#
+# Initialise an ecBuild project. A CMake project must have previously been
+# declared with ``project( <name> ... )``. ::
+#
+#   ecbuild_declare_project()
+#
+# Sets the following CMake variables
+# (where ``PNAME`` is the capitalised project name):
+#
+# :<PNAME>_GIT_SHA1:       Git revision (if project is a Git repo)
+# :<PNAME>_GIT_SHA1_SHORT: short Git revision (if project is a Git repo)
+# :<PNAME>_VERSION:        version in format ``MAJOR.MINOR.PATCH``
+# :<PNAME>_VERSION_STR:    version as given in ``VERSION.cmake`` or 0.0.0
+# :<PNAME>_MAJOR_VERSION:  major version number
+# :<PNAME>_MINOR_VERSION:  minor version number
+# :<PNAME>_PATCH_VERSION:  patch version number
+# :INSTALL_BIN_DIR:        relative install directory for executables
+# :INSTALL_LIB_DIR:        relative install directory for libraries
+# :INSTALL_INCLUDE_DIR:    relative install directory for include files
+# :INSTALL_DATA_DIR:       relative install directory for data
+# :INSTALL_CMAKE_DIR:      relative install directory for CMake files
+#
+# Customising install locations
+# -----------------------------
+#
+# The relative installation directories of components can be customised by
+# setting the following CMake variables on the command line or in cache:
+#
+# :INSTALL_BIN_DIR:        directory for installing executables
+#                          (default: ``bin``)
+# :INSTALL_LIB_DIR:        directory for installing libraries
+#                          (default: ``lib``)
+# :INSTALL_INCLUDE_DIR:    directory for installing include files
+#                          (default: ``include``)
+# :INSTALL_DATA_DIR:       directory for installing data
+#                          (default: ``share/<project_name>``)
+# :INSTALL_CMAKE_DIR:      directory for installing CMake files
+#                          (default: ``share/<project_name>/cmake``)
+#
+# Using *relative* paths is recommended, which are interpreted relative to the
+# ``CMAKE_INSTALL_PREFIX``. Using absolute paths makes the build
+# non-relocatable and may break the generation of relocatable binary packages.
+#
+##############################################################################
+
+macro( ecbuild_declare_project )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  # reset the lists of targets (executables, libs, tests & resources)
+
+  set( ${PROJECT_NAME}_ALL_EXES "" CACHE INTERNAL "" )
+  set( ${PROJECT_NAME}_ALL_LIBS "" CACHE INTERNAL "" )
+
+  # if git project get its HEAD SHA1
+  # leave it here so we may use ${PNAME}_GIT_SHA1 on the version file
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/.git )
+    get_git_head_revision( GIT_REFSPEC ${PNAME}_GIT_SHA1 )
+    if( ${PNAME}_GIT_SHA1 )
+      string( SUBSTRING "${${PNAME}_GIT_SHA1}" 0 7 ${PNAME}_GIT_SHA1_SHORT )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1 )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1_SHORT )
+    else()
+      ecbuild_debug( "Could not get git-sha1 for project ${PNAME}")
+    endif()
+  endif()
+
+  # read and parse project version file
+  if( EXISTS ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+    include( ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+  else()
+    set( ${PROJECT_NAME}_VERSION_STR "0.0.0" )
+  endif()
+
+  string( REPLACE "." " " _version_list ${${PROJECT_NAME}_VERSION_STR} ) # dots to spaces
+
+  separate_arguments( _version_list )
+
+  list( GET _version_list 0 ${PNAME}_MAJOR_VERSION )
+  list( GET _version_list 1 ${PNAME}_MINOR_VERSION )
+  list( GET _version_list 2 ${PNAME}_PATCH_VERSION )
+
+  # cleanup patch version of any extra qualifiers ( -dev -rc1 ... )
+
+  string( REGEX REPLACE "^([0-9]+).*" "\\1" ${PNAME}_PATCH_VERSION "${${PNAME}_PATCH_VERSION}" )
+
+  set( ${PNAME}_VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}.${${PNAME}_PATCH_VERSION}"
+       CACHE INTERNAL "package ${PNAME} version" )
+
+  set( ${PNAME}_VERSION_STR "${${PROJECT_NAME}_VERSION_STR}"
+       CACHE INTERNAL "package ${PNAME} version string" ) # ignore caps
+
+  #    ecbuild_debug_var( ${PNAME}_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_VERSION_STR )
+  #    ecbuild_debug_var( ${PNAME}_MAJOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_MINOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_PATCH_VERSION )
+
+  # install dirs for this project
+
+  # Use defaults unless values are already present in cache
+  if( NOT INSTALL_BIN_DIR )
+    set( INSTALL_BIN_DIR bin )
+  endif()
+  if( NOT INSTALL_LIB_DIR )
+    set( INSTALL_LIB_DIR lib )
+  endif()
+  if( NOT INSTALL_INCLUDE_DIR )
+    set( INSTALL_INCLUDE_DIR include )
+  endif()
+  # INSTALL_DATA_DIR is package specific and needs to be reset for subpackages
+  # in a bundle. Users *cannot* override this directory (ECBUILD-315)
+  set( INSTALL_DATA_DIR share/${PROJECT_NAME} )
+  # share/${PROJECT_NAME}/cmake is a convention - it makes no sense to override it
+  set( INSTALL_CMAKE_DIR share/${PROJECT_NAME}/cmake )
+
+  mark_as_advanced( INSTALL_BIN_DIR )
+  mark_as_advanced( INSTALL_LIB_DIR )
+  mark_as_advanced( INSTALL_INCLUDE_DIR )
+  mark_as_advanced( INSTALL_DATA_DIR )
+  mark_as_advanced( INSTALL_CMAKE_DIR )
+
+  # warnings for non-relocatable projects
+
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+    if( IS_ABSOLUTE ${INSTALL_${p}_DIR} )
+      ecbuild_warn( "Defining INSTALL_${p}_DIR as absolute path '${INSTALL_${p}_DIR}' makes this build non-relocatable, possibly breaking the installation of RPMS and DEB packages" )
+    endif()
+  endforeach()
+
+  # make relative paths absolute ( needed later on ) and cache them ...
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+
+    set( var INSTALL_${p}_DIR )
+
+    if( NOT IS_ABSOLUTE "${${var}}" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${CMAKE_INSTALL_PREFIX}/${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    else()
+      ecbuild_warn( "Setting an absolute path for ${VAR} in project ${PNAME}, breakes generation of relocatable binary packages (rpm,deb,...)" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    endif()
+
+    #        ecbuild_debug_var( ${PNAME}_FULL_INSTALL_${p}_DIR )
+
+  endforeach()
+
+  # correctly set CMAKE_INSTALL_RPATH
+
+  if( ENABLE_RPATHS )
+
+    if( ENABLE_RELATIVE_RPATHS )
+
+      file( RELATIVE_PATH relative_rpath ${${PNAME}_FULL_INSTALL_BIN_DIR} ${${PNAME}_FULL_INSTALL_LIB_DIR} )
+      # ecbuild_debug_var( relative_rpath )
+
+      ecbuild_append_to_rpath( ${relative_rpath} )
+
+    else() # make rpaths absolute
+
+      if( IS_ABSOLUTE ${INSTALL_LIB_DIR} )
+        ecbuild_append_to_rpath( "${INSTALL_LIB_DIR}" )
+      else()
+        ecbuild_append_to_rpath( "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" )
+      endif()
+
+    endif()
+
+  endif()
+
+  # ecbuild_debug_var( CMAKE_INSTALL_RPATH )
+
+  # print project header
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( ${PNAME}_GIT_SHA1_SHORT )
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR}) [${${PNAME}_GIT_SHA1_SHORT}]" )
+  else()
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR})" )
+  endif()
+
+endmacro( ecbuild_declare_project )
diff --git a/cmake/ecbuild_define_build_types.cmake b/cmake/ecbuild_define_build_types.cmake
new file mode 100644
index 0000000..7cfc591
--- /dev/null
+++ b/cmake/ecbuild_define_build_types.cmake
@@ -0,0 +1,59 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# define default build type
+
+set( _BUILD_TYPE_MSG "Build type options are: [ None | Debug | Bit | Production | Release | RelWithDebInfo ]" )
+
+if( NOT ECBUILD_DEFAULT_BUILD_TYPE )
+	set( ECBUILD_DEFAULT_BUILD_TYPE "RelWithDebInfo" )
+endif()
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE ${ECBUILD_DEFAULT_BUILD_TYPE} CACHE STRING  ${_BUILD_TYPE_MSG}  FORCE )
+endif()
+
+# capitalize the build type for easy use with conditionals
+string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+# correct capitatlization of the build type
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "NONE" )
+  set(CMAKE_BUILD_TYPE None CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "DEBUG" )
+  set(CMAKE_BUILD_TYPE Debug CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "BIT" )
+  set(CMAKE_BUILD_TYPE Bit CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "PRODUCTION" )
+  set(CMAKE_BUILD_TYPE Production CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELEASE" )
+  set(CMAKE_BUILD_TYPE Release CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELWITHDEBINFO" )
+  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+# fail if build type is not one of the defined ones
+if( NOT CMAKE_BUILD_TYPE MATCHES "None"  AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Debug" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Bit" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Production" AND
+    NOT CMAKE_BUILD_TYPE MATCHES "Release"  AND
+    NOT CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" )
+    ecbuild_critical( "CMAKE_BUILD_TYPE is not recognized. ${_BUILD_TYPE_MSG}" )
+endif()
diff --git a/cmake/ecbuild_define_libs_and_execs_target.cmake b/cmake/ecbuild_define_libs_and_execs_target.cmake
new file mode 100644
index 0000000..799eb7d
--- /dev/null
+++ b/cmake/ecbuild_define_libs_and_execs_target.cmake
@@ -0,0 +1,29 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+############################################################################################
+# define libs and execs targets
+
+macro( ecbuild_define_libs_and_execs_targets )
+
+  add_custom_target( libs )
+
+  if( EC_ALL_LIBS )
+    add_dependencies( libs ${EC_ALL_LIBS} )
+  endif()
+
+  add_custom_target( execs )
+
+  if( EC_ALL_EXECS )
+    add_dependencies( execs ${EC_ALL_EXES} )
+  endif()
+
+endmacro(ecbuild_define_libs_and_execs_targets)
diff --git a/cmake/ecbuild_define_links_target.cmake b/cmake/ecbuild_define_links_target.cmake
new file mode 100644
index 0000000..745288c
--- /dev/null
+++ b/cmake/ecbuild_define_links_target.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# macro for adding a link to library on a development system
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+macro( ecbuild_link_exe TARGET  FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/bin
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/bin/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/bin/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/bin/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_EXES ${EC_ALL_EXES} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_exe  )
+
+###############################################################################
+# macro for adding a link to library on a development system
+
+macro( ecbuild_link_lib  TARGET FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/lib
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/lib/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/lib/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/lib/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_LIBS ${EC_ALL_LIBS} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_lib  )
+
+############################################################################################
+# define make links target
+
+macro( ecbuild_define_links_target )
+
+  if( DEFINED EC_LINK_DIR )
+
+    foreach( lib ${EC_ALL_LIBS} )
+      list( APPEND ec_link_libs ${lib}_link )
+    endforeach()
+    foreach( exe ${EC_ALL_EXES} )
+      list( APPEND ec_link_exes ${exe}_link )
+    endforeach()
+
+    add_custom_target( links DEPENDS ${ec_link_libs} ${ec_link_exes} )
+
+     #   ecbuild_debug_var( EC_ALL_EXES )
+     #   ecbuild_debug_var( ec_link_exes )
+
+     #  ecbuild_debug_var( EC_ALL_LIBS )
+     #   ecbuild_debug_var( ec_link_libs )
+
+  endif()
+
+endmacro(ecbuild_define_links_target)
diff --git a/cmake/ecbuild_define_options.cmake b/cmake/ecbuild_define_options.cmake
new file mode 100644
index 0000000..fc6376e
--- /dev/null
+++ b/cmake/ecbuild_define_options.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# general options
+
+option( BUILD_SHARED_LIBS       "build shared libraries when possible"            ON  )
+
+option( ENABLE_RPATHS           "when installing insert RPATHS into binaries"     ON  )
+option( ENABLE_RELATIVE_RPATHS  "try to use relative RPATHS, including build dir" ON  )
+option( ENABLE_WARNINGS         "enable compiler warnings"                        OFF )
+
+option( ENABLE_LARGE_FILE_SUPPORT "build with large file support"   ON  )
+
+option( ENABLE_PROFILING        "build with profiling support" OFF )
+
+mark_as_advanced( ENABLE_LARGE_FILE_SUPPORT )
+
+option( ENABLE_OS_TESTS          "Run all OS tests" ON )
+
+mark_as_advanced( ENABLE_OS_TESTS )
+
+option( ENABLE_FORTRAN_C_INTERFACE "Enable Fortran/C Interface" OFF )
+mark_as_advanced( ENABLE_FORTRAN_C_INTERFACE )
+
+option( DEVELOPER_MODE           "activates developer mode"               OFF )
+option( CHECK_UNUSED_FILES       "check for unused project files (slow)"  OFF )
+
+mark_as_advanced( DEVELOPER_MODE  )
+mark_as_advanced( CHECK_UNUSED_FILES  )
+
+include( CMakeDependentOption ) # make options depend on one another
+
+cmake_dependent_option( ENABLE_OS_TYPES_TEST     "Run sizeof tests on C types" ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_ENDINESS_TEST  "Run OS endiness tests"       ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_FUNCTIONS_TEST "Run OS functions tests"      ON "ENABLE_OS_TESTS" OFF)
+
+mark_as_advanced( ENABLE_OS_TYPES_TEST ENABLE_OS_ENDINESS_TEST ENABLE_OS_FUNCTIONS_TEST  )
+
+option( ECBUILD_USE_INCLUDE_DIRECTORIES "Forces to use global include_directories() instead of target specific. Adverse effect on PkgConfig generation." OFF )
+
+mark_as_advanced( ECBUILD_USE_INCLUDE_DIRECTORIES )
+
+set( CMAKE_NO_SYSTEM_FROM_IMPORTED ON )
+
+# hide some CMake options from CMake UI
+
+mark_as_advanced( CMAKE_OSX_ARCHITECTURES CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OSX_SYSROOT )
\ No newline at end of file
diff --git a/cmake/ecbuild_define_paths.cmake b/cmake/ecbuild_define_paths.cmake
new file mode 100644
index 0000000..16d7b94
--- /dev/null
+++ b/cmake/ecbuild_define_paths.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# define project paths
+
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+#######################################################################################################
+
+# setup library building rpaths (both in build dir and then when installed)
+
+# add the automatic parts to RPATH which point to dirs outside build tree
+set( CMAKE_INSTALL_RPATH_USE_LINK_PATH   TRUE  )
+
+# use RPATHs for the build tree
+set( CMAKE_SKIP_BUILD_RPATH              FALSE  )
+
+# If INSTALL_LIB_DIR is set to anything other than lib, the relative install
+# RPATH is wrong in the build tree
+if( ENABLE_RELATIVE_RPATHS )
+  ecbuild_debug( "Relative RPATHS are enabled" )
+  if( INSTALL_LIB_DIR STREQUAL "lib" OR (NOT INSTALL_LIB_DIR) )
+    # when building, use the install RPATH immediately (we don't want to relink)
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      TRUE  )
+    ecbuild_debug( "Building with install RPATH" )
+  else()
+    # when building, don't use the install RPATH yet, but later on when installing
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      FALSE  )
+    ecbuild_debug( "Not building with install RPATH, need to relink when installing" )
+  endif()
+endif()
+
+# Always include srcdir and builddir in include path
+# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+# in about every subdir
+
+set( CMAKE_INCLUDE_CURRENT_DIR OFF )
+
+# put the include dirs which are in the source or build tree
+# before all other include dirs, so the headers in the sources
+# are prefered over the already installed ones (since cmake 2.4.1)
+
+set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
diff --git a/cmake/ecbuild_define_uninstall.cmake b/cmake/ecbuild_define_uninstall.cmake
new file mode 100644
index 0000000..cc6efa9
--- /dev/null
+++ b/cmake/ecbuild_define_uninstall.cmake
@@ -0,0 +1,7 @@
+### adds uninstall target ###############
+
+configure_file(
+  "${CMAKE_CURRENT_LIST_DIR}/ecbuild_uninstall.cmake.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake" IMMEDIATE @ONLY)
+
+add_custom_target( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake")
diff --git a/cmake/ecbuild_dont_pack.cmake b/cmake/ecbuild_dont_pack.cmake
new file mode 100644
index 0000000..9f9f4a4
--- /dev/null
+++ b/cmake/ecbuild_dont_pack.cmake
@@ -0,0 +1,82 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_dont_pack
+# =================
+#
+# Specify files and directories to exclude from packaging. ::
+#
+#   ecbuild_dont_pack( [ FILES <file1> [ <file2> ... ] ]
+#                      [ DIRS <dir1> [ <dir2> ... ] ]
+#                      [ REGEX <regex> ] )
+#
+# Options
+# -------
+#
+# FILES : optional, one of FILES, DIRS, REGEX required
+#   list of files to exclude from packaging
+#
+# DIRS : optional, one of FILES, DIRS, REGEX required
+#   list of directories to exclude from packaging
+#
+# REGEX : optional, one of FILES, DIRS, REGEX required
+#   regular expression to match files / directories to exclude from packaging
+#
+##############################################################################
+
+macro( ecbuild_dont_pack )
+
+    set( options )
+    set( single_value_args REGEX )
+    set( multi_value_args  FILES DIRS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_dont_pack(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT DEFINED _PAR_REGEX AND NOT  DEFINED _PAR_FILES AND NOT  DEFINED _PAR_DIRS )
+      ecbuild_critical("Call to ecbuild_dont_pack does not speficify any list to avoid packing.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_REGEX )
+        file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_PAR_REGEX} )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DIRS )
+        foreach( dir ${_PAR_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_FILES )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_FILES} )
+    endif()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro()
diff --git a/cmake/ecbuild_download_resource.cmake b/cmake/ecbuild_download_resource.cmake
new file mode 100644
index 0000000..e1e8eff
--- /dev/null
+++ b/cmake/ecbuild_download_resource.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_download_resource
+# =========================
+#
+# Download a file from a given URL and save to FILE at configure time. ::
+#
+#   ecbuild_download_resource( FILE URL )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+##############################################################################
+
+function( ecbuild_download_resource _p_OUT _p_URL )
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  if( NOT EXISTS ${_p_OUT} )
+
+    find_program( CURL_PROGRAM curl )
+    mark_as_advanced(CURL_PROGRAM)
+    if( CURL_PROGRAM )
+
+      execute_process( COMMAND ${CURL_PROGRAM} --silent --show-error --fail
+                               --retry ${ECBUILD_DOWNLOAD_RETRIES}
+                               --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+                               --output ${_p_OUT} ${_p_URL}
+                       WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+    else()
+
+      find_program( WGET_PROGRAM wget )
+
+      if( WGET_PROGRAM )
+
+        # wget takes the total number of tries, curl the number or retries
+        math( EXPR ECBUILD_DOWNLOAD_RETRIES ${ECBUILD_DOWNLOAD_RETRIES} + 1 )
+
+        execute_process( COMMAND ${WGET_PROGRAM} -nv -O ${_p_OUT}
+                                 -t ${ECBUILD_DOWNLOAD_RETRIES}
+                                 -T ${ECBUILD_DOWNLOAD_TIMEOUT} ${_p_URL}
+                         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+      else()
+        ecbuild_critical("Could not find curl or wget. Error downloading ${_p_URL}")
+      endif()
+
+    endif()
+
+    if(CMD_RESULT)
+      ecbuild_critical("Error downloading ${_p_URL}")
+    endif()
+
+  endif()
+
+endfunction()
diff --git a/cmake/ecbuild_echo_targets.cmake b/cmake/ecbuild_echo_targets.cmake
new file mode 100644
index 0000000..61250ee
--- /dev/null
+++ b/cmake/ecbuild_echo_targets.cmake
@@ -0,0 +1,233 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target_property
+# ============================
+#
+# Output a given property of a given target. ::
+#
+#   ecbuild_echo_target_property( <target> <property> )
+#
+##############################################################################
+
+function(ecbuild_echo_target_property tgt prop)
+
+  cmake_policy(PUSH)
+
+  if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD)
+  endif()
+
+  # v for value, d for defined, s for set
+  get_property(v TARGET ${tgt} PROPERTY ${prop})
+  get_property(d TARGET ${tgt} PROPERTY ${prop} DEFINED)
+  get_property(s TARGET ${tgt} PROPERTY ${prop} SET)
+
+  # only produce output for values that are set
+  if(s)
+    ecbuild_debug("tgt='${tgt}' prop='${prop}'")
+    ecbuild_debug("  value='${v}'")
+    ecbuild_debug("  defined='${d}'")
+    ecbuild_debug("  set='${s}'")
+    ecbuild_debug("")
+  endif()
+
+  cmake_policy(POP)
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target
+# ===================
+#
+# Output all possible target properties of a given target. ::
+#
+#   ecbuild_echo_target( <target> )
+#
+##############################################################################
+
+function(ecbuild_echo_target tgt)
+  if(NOT TARGET ${tgt})
+    ecbuild_debug("There is no target named '${tgt}'")
+    return()
+  endif()
+
+  set(props
+DEBUG_OUTPUT_NAME
+DEBUG_POSTFIX
+RELEASE_OUTPUT_NAME
+RELEASE_POSTFIX
+ARCHIVE_OUTPUT_DIRECTORY
+ARCHIVE_OUTPUT_DIRECTORY_DEBUG
+ARCHIVE_OUTPUT_DIRECTORY_RELEASE
+ARCHIVE_OUTPUT_NAME
+ARCHIVE_OUTPUT_NAME_DEBUG
+ARCHIVE_OUTPUT_NAME_RELEASE
+AUTOMOC
+AUTOMOC_MOC_OPTIONS
+BUILD_WITH_INSTALL_RPATH
+BUNDLE
+BUNDLE_EXTENSION
+COMPILE_DEFINITIONS
+COMPILE_DEFINITIONS_DEBUG
+COMPILE_DEFINITIONS_RELEASE
+COMPILE_FLAGS
+DEBUG_POSTFIX
+RELEASE_POSTFIX
+DEFINE_SYMBOL
+ENABLE_EXPORTS
+EXCLUDE_FROM_ALL
+EchoString
+FOLDER
+FRAMEWORK
+Fortran_FORMAT
+Fortran_MODULE_DIRECTORY
+GENERATOR_FILE_NAME
+GNUtoMS
+HAS_CXX
+IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
+IMPORTED
+IMPORTED_CONFIGURATIONS
+IMPORTED_IMPLIB
+IMPORTED_IMPLIB_DEBUG
+IMPORTED_IMPLIB_RELEASE
+IMPORTED_LINK_DEPENDENT_LIBRARIES
+IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG
+IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_LANGUAGES
+IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG
+IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE
+IMPORTED_LINK_INTERFACE_LIBRARIES
+IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG
+IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_MULTIPLICITY
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_DEBUG
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_RELEASE
+IMPORTED_LOCATION
+IMPORTED_LOCATION_DEBUG
+IMPORTED_LOCATION_RELEASE
+IMPORTED_NO_SONAME
+IMPORTED_NO_SONAME_DEBUG
+IMPORTED_NO_SONAME_RELEASE
+IMPORTED_SONAME
+IMPORTED_SONAME_DEBUG
+IMPORTED_SONAME_RELEASE
+IMPORT_PREFIX
+IMPORT_SUFFIX
+INCLUDE_DIRECTORIES
+INSTALL_NAME_DIR
+INSTALL_RPATH
+INSTALL_RPATH_USE_LINK_PATH
+INTERPROCEDURAL_OPTIMIZATION
+INTERPROCEDURAL_OPTIMIZATION_DEBUG
+INTERPROCEDURAL_OPTIMIZATION_RELEASE
+LABELS
+LIBRARY_OUTPUT_DIRECTORY
+LIBRARY_OUTPUT_DIRECTORY_DEBUG
+LIBRARY_OUTPUT_DIRECTORY_RELEASE
+LIBRARY_OUTPUT_NAME
+LIBRARY_OUTPUT_NAME_DEBUG
+LIBRARY_OUTPUT_NAME_RELEASE
+LINKER_LANGUAGE
+LINK_DEPENDS
+LINK_FLAGS
+LINK_FLAGS_DEBUG
+LINK_FLAGS_RELEASE
+LINK_INTERFACE_LIBRARIES
+LINK_INTERFACE_LIBRARIES_DEBUG
+LINK_INTERFACE_LIBRARIES_RELEASE
+LINK_INTERFACE_MULTIPLICITY
+LINK_INTERFACE_MULTIPLICITY_DEBUG
+LINK_INTERFACE_MULTIPLICITY_RELEASE
+LINK_SEARCH_END_STATIC
+LINK_SEARCH_START_STATIC
+LOCATION
+LOCATION_DEBUG
+LOCATION_RELEASE
+MACOSX_BUNDLE
+MACOSX_BUNDLE_INFO_PLIST
+MACOSX_FRAMEWORK_INFO_PLIST
+MAP_IMPORTED_CONFIG_DEBUG
+MAP_IMPORTED_CONFIG_RELEASE
+OSX_ARCHITECTURES
+OSX_ARCHITECTURES_DEBUG
+OSX_ARCHITECTURES_RELEASE
+OUTPUT_NAME
+OUTPUT_NAME_DEBUG
+OUTPUT_NAME_RELEASE
+POST_INSTALL_SCRIPT
+PREFIX
+PRE_INSTALL_SCRIPT
+PRIVATE_HEADER
+PROJECT_LABEL
+PUBLIC_HEADER
+RESOURCE
+RULE_LAUNCH_COMPILE
+RULE_LAUNCH_CUSTOM
+RULE_LAUNCH_LINK
+RUNTIME_OUTPUT_DIRECTORY
+RUNTIME_OUTPUT_DIRECTORY_DEBUG
+RUNTIME_OUTPUT_DIRECTORY_RELEASE
+RUNTIME_OUTPUT_NAME
+RUNTIME_OUTPUT_NAME_DEBUG
+RUNTIME_OUTPUT_NAME_RELEASE
+SKIP_BUILD_RPATH
+SOURCES
+SOVERSION
+STATIC_LIBRARY_FLAGS
+STATIC_LIBRARY_FLAGS_DEBUG
+STATIC_LIBRARY_FLAGS_RELEASE
+SUFFIX
+TYPE
+VERSION
+VS_DOTNET_REFERENCES
+VS_GLOBAL_WHATEVER
+VS_GLOBAL_KEYWORD
+VS_GLOBAL_PROJECT_TYPES
+VS_KEYWORD
+VS_SCC_AUXPATH
+VS_SCC_LOCALPATH
+VS_SCC_PROJECTNAME
+VS_SCC_PROVIDER
+VS_WINRT_EXTENSIONS
+VS_WINRT_REFERENCES
+WIN32_EXECUTABLE
+XCODE_ATTRIBUTE_WHATEVER
+)
+
+  ecbuild_debug("======================== ${tgt} ========================")
+  foreach(p ${props})
+    ecbuild_echo_target_property("${tgt}" "${p}")
+  endforeach()
+  ecbuild_debug("")
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_targets
+# ====================
+#
+# Output all possible target properties of the specified list-of-targets.
+# This is very useful for debugging. ::
+#
+#   ecbuild_echo_targets( <list-of-targets> )
+#
+##############################################################################
+
+function(ecbuild_echo_targets)
+  set(tgts ${ARGV})
+  foreach(t ${tgts})
+    ecbuild_echo_target("${t}")
+  endforeach()
+endfunction()
diff --git a/cmake/ecbuild_enable_fortran.cmake b/cmake/ecbuild_enable_fortran.cmake
new file mode 100644
index 0000000..9d86aa7
--- /dev/null
+++ b/cmake/ecbuild_enable_fortran.cmake
@@ -0,0 +1,89 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_fortran
+# ======================
+#
+# Enable the Fortran language. ::
+#
+#   ecbuild_enable_fortran( [ MODULE_DIRECTORY <directory> ] [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# MODULE_DIRECTORY : optional, defaults to ``${CMAKE_BINARY_DIR}/module``
+#   set the CMAKE_Fortran_MODULE_DIRECTORY
+#
+# REQUIRED : optional
+#   fail if no working Fortran compiler was detected
+#
+##############################################################################
+
+macro( ecbuild_enable_fortran )
+
+  set( options REQUIRED  )
+  set( single_value_args MODULE_DIRECTORY )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_enable_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT CMAKE_Fortran_COMPILER_LOADED )
+    enable_language( Fortran )
+    ecbuild_compiler_flags( Fortran )
+    if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+      set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -warn all" )
+      ecbuild_debug( "Fortran FLAG [-warn all] added" )
+    endif()
+  endif()
+
+  if( DEFINED _PAR_REQUIRED )
+    if( CMAKE_Fortran_COMPILER_FORCED )
+      set( CMAKE_Fortran_COMPILER_WORKS 1 )
+    endif()
+    if( NOT CMAKE_Fortran_COMPILER OR NOT CMAKE_Fortran_COMPILER_WORKS )
+      ecbuild_critical( "Fortran compiler required by project ${PROJECT_NAME} but does not seem to work" )
+    endif()
+  endif()
+
+  if( CMAKE_Fortran_COMPILER_LOADED )
+
+    include(CheckFortranFunctionExists)
+    if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+      include(FortranCInterface)
+    endif()
+    set( EC_HAVE_FORTRAN 1 )
+
+    # see issue ECBUILD-298
+    if( CMAKE_Fortran_COMPILER_ID MATCHES PGI )
+      unset( CMAKE_Fortran_COMPILE_OPTIONS_PIE )
+      unset( CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS )
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_MODULE_DIRECTORY )
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${_PAR_MODULE_DIRECTORY} )
+  else()
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module
+         CACHE PATH "directory for all fortran modules." )
+  endif()
+
+  file( MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  include_directories( ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  install( CODE "EXECUTE_PROCESS (COMMAND \"${CMAKE_COMMAND}\" -E copy_directory \"${CMAKE_Fortran_MODULE_DIRECTORY}/\${BUILD_TYPE}\" \"${INSTALL_INCLUDE_DIR}\")" )
+
+endmacro( ecbuild_enable_fortran )
diff --git a/cmake/ecbuild_features.cmake b/cmake/ecbuild_features.cmake
new file mode 100644
index 0000000..9406fd8
--- /dev/null
+++ b/cmake/ecbuild_features.cmake
@@ -0,0 +1,57 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Internal macros to handle CMake features
+
+include( FeatureSummary )
+
+# Write list of enabled features to CMake variable ${OUT}
+macro( ecbuild_enabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY ENABLED_FEATURES )
+endmacro()
+
+# Write list of disabled features to CMake variable ${OUT}
+macro( ecbuild_disabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY DISABLED_FEATURES )
+endmacro()
+
+# Enable the feature ${_name} (add to enabled features, remove from disabled)
+function( ecbuild_enable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _disabled_features )
+    list( REMOVE_ITEM _disabled_features ${_name} )
+  endif()
+
+  list( APPEND _enabled_features ${_name} )
+  list( REMOVE_DUPLICATES _enabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
+
+# Disable the feature ${_name} (add to disabled features, remove from enabled)
+function( ecbuild_disable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _enabled_features )
+    list( REMOVE_ITEM _enabled_features ${_name} )
+  endif()
+
+  list( APPEND _disabled_features ${_name} )
+  list( REMOVE_DUPLICATES _disabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
diff --git a/cmake/ecbuild_find_fortranlibs.cmake b/cmake/ecbuild_find_fortranlibs.cmake
new file mode 100644
index 0000000..905d020
--- /dev/null
+++ b/cmake/ecbuild_find_fortranlibs.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_fortranlibs
+# ========================
+#
+# Find the Fortran (static) link libraries. ::
+#
+#   ecbuild_find_fortranlibs( [ COMPILER gfortran|pgi|xlf|intel ]
+#                             [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPILER : optional, defaults to gfortran
+#   request a given Fortran compiler (``gfortran``, ``pgi``, ``xlf``, ``intel``)
+#
+# REQUIRED : optional
+#   fail if Fortran libraries were not found
+#
+##############################################################################
+
+macro( ecbuild_find_fortranlibs )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args COMPILER )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT FORTRANLIBS_FOUND ) # don't repeat search
+
+    if( _PAR_COMPILER )
+      set( __known_fcomp 0 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "gfortran" )
+      set( WITH_LIBGFORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "pgi" )
+      set( WITH_PGI_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "xlf" )
+      set( WITH_XL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "intel" )
+      set( WITH_INTEL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER AND NOT __known_fcomp )
+      ecbuild_critical( "unknown fortran compiler ${_PAR_COMPILER}" )
+    endif()
+
+    ### set path from environment variables
+
+    foreach( _fortran_lib PGI XLF LIBGFORTRAN INTEL )
+      if( NOT ${_fortran_lib}_PATH AND NOT "$ENV{${_fortran_lib}_PATH}" STREQUAL "" )
+        set( ${_fortran_lib}_PATH "$ENV{${_fortran_lib}_PATH}" )
+      endif()
+    endforeach()
+
+    set( _flibs_found 0 )
+
+    ### default is to search for gfortran
+
+    if( NOT (WITH_PGI_FORTRAN OR WITH_LIBGFORTRAN OR
+             WITH_XL_FORTRAN OR WITH_INTEL_FORTRAN)
+        AND NOT (DEFINED PGI_PATH OR DEFINED LIBGFORTRAN_PATH OR
+                 DEFINED XLF_PATH OR DEFINED INTEL_PATH) )
+      ecbuild_warn( "Finding fortran libs for unspecified Fortran compiler: default search [ gfortran ]" )
+      set( WITH_LIBGFORTRAN 1 )
+    endif()
+
+    ### actual search ...
+
+    if( WITH_PGI_FORTRAN OR DEFINED PGI_PATH )
+
+      find_package(PGIFortran)
+
+      if( LIBPGIFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "PGI" )
+      endif()
+
+    endif()
+
+    if( WITH_LIBGFORTRAN OR DEFINED LIBGFORTRAN_PATH )
+
+      find_package(LibGFortran)
+
+      if( LIBGFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${GFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "gfortran" )
+      endif()
+
+    endif()
+
+    if( WITH_XL_FORTRAN OR DEFINED XLF_PATH )
+
+      find_package(XLFortranLibs)
+
+      if( LIBXLFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${XLFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "XLF" )
+      endif()
+
+    endif()
+
+    if( WITH_INTEL_FORTRAN OR DEFINED INTEL_PATH )
+
+      find_package(LibIFort)
+
+      if( LIBIFORT_FOUND )
+        set( FORTRAN_LIBRARIES ${IFORT_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "Intel" )
+      endif()
+
+    endif()
+
+    ### set found
+
+    if( _flibs_found )
+      set( FORTRANLIBS_FOUND 1 CACHE INTERNAL "Fortran libraries found" )
+      set( FORTRANLIBS_NAME ${_flibs_txt}  CACHE INTERNAL "Fortran library name" )
+      set( FORTRAN_LIBRARIES ${FORTRAN_LIBRARIES} CACHE INTERNAL "Fortran libraries" )
+      ecbuild_info( "Found Fortran libraries: ${_flibs_txt}" )
+    else()
+      set( FORTRANLIBS_FOUND 0 )
+      if( _PAR_REQUIRED )
+        ecbuild_critical( "Failed to find Fortran libraries" )
+      else()
+        ecbuild_warn( "Failed to find Fortran libraries" )
+      endif()
+    endif()
+
+  endif( NOT FORTRANLIBS_FOUND )
+
+endmacro( ecbuild_find_fortranlibs )
diff --git a/cmake/ecbuild_find_lexyacc.cmake b/cmake/ecbuild_find_lexyacc.cmake
new file mode 100644
index 0000000..3dc8b61
--- /dev/null
+++ b/cmake/ecbuild_find_lexyacc.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_lexyacc
+# ====================
+#
+# Find flex and bison (preferred) or lex and yacc.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables can set to skip search for bison or yacc:
+#
+# :SKIP_BISON: do not search for flex and bison
+# :SKIP_YACC:  do not search for lex and yacc
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if flex and bison were found:
+#
+# :FLEX_FOUND:       flex was found
+# :BISON_FOUND:      bison was found
+# :FLEX_EXECUTABLE:  path to the flex executable
+# :BISON_EXECUTABLE: path to the bison executable
+#
+# The following CMake variables are set if lex and yacc were found:
+#
+# :LEXYACC_FOUND:   Found suitable combination of bison, lex, yacc, flex
+# :LEX_FOUND:       lex was found
+# :YACC_FOUND:      yacc was found
+# :LEX_EXECUTABLE:  path to the lex executable
+# :YACC_EXECUTABLE: path to the yacc executable
+#
+##############################################################################
+
+macro( ecbuild_find_lexyacc )
+
+  # find preferably bison or else yacc
+
+  if( NOT SKIP_BISON )
+
+    find_package( BISON )
+    if(BISON_FOUND AND BISON_VERSION VERSION_LESS 2.3 )
+        ecbuild_critical( "Bison found with version ${BISON_VERSION} is less than 2.3.\nPlease define BISON_EXECUTABLE to an appropriate version or define SKIP_BISON to try finding Yacc instead" )
+    endif()
+    find_package( FLEX )
+
+  endif()
+
+  if( NOT BISON_FOUND AND NOT SKIP_YACC )
+
+    find_package( YACC )
+    find_package( LEX  )
+
+  endif()
+
+  set( LEXYACC_FOUND 1 )
+
+  if( NOT YACC_FOUND AND NOT BISON_FOUND ) # neither bison nor yacc were found
+    ecbuild_debug( "Neither bison or yacc were found - at least one is required (together with its lexical analyser" )
+    set( LEXYACC_FOUND 0 )
+  endif()
+
+  if( NOT YACC_FOUND ) # check for both bison & flex together
+    if( BISON_FOUND AND NOT FLEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - flex not found" )
+    endif()
+    if( FLEX_FOUND AND NOT BISON_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - bison not found" )
+    endif()
+  endif()
+
+  if( NOT BISON_FOUND ) # check for both yacc & lex together
+    if( YACC_FOUND AND NOT LEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - lex not found" )
+    endif()
+    if( LEX_FOUND AND NOT YACC_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - yacc not found" )
+    endif()
+  endif()
+
+endmacro( ecbuild_find_lexyacc )
diff --git a/cmake/ecbuild_find_mpi.cmake b/cmake/ecbuild_find_mpi.cmake
new file mode 100644
index 0000000..85ae5e1
--- /dev/null
+++ b/cmake/ecbuild_find_mpi.cmake
@@ -0,0 +1,328 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_mpi
+# ================
+#
+# Find MPI and check if MPI compilers successfully compile C/C++/Fortran. ::
+#
+#   ecbuild_find_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+# Input variables
+# ---------------
+#
+# ECBUILD_FIND_MPI : optional, defaults to TRUE
+#   test C/C++/Fortran MPI compiler wrappers (assume working if FALSE)
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if MPI was found: ::
+#
+#   MPI_FOUND
+#   MPI_LIBRARY
+#   MPI_EXTRA_LIBRARY 
+#
+# The following CMake variables are set if C bindings were found: ::
+#
+#   MPI_C_FOUND
+#   MPI_C_COMPILER
+#   MPI_C_COMPILE_FLAGS
+#   MPI_C_INCLUDE_PATH
+#   MPI_C_LIBRARIES
+#   MPI_C_LINK_FLAGS
+#
+# The following CMake variables are set if C++ bindings were found: ::
+#
+#   MPI_CXX_FOUND
+#   MPI_CXX_COMPILER
+#   MPI_CXX_COMPILE_FLAGS
+#   MPI_CXX_INCLUDE_PATH
+#   MPI_CXX_LIBRARIES
+#   MPI_CXX_LINK_FLAGS
+#
+# The following CMake variables are set if Fortran bindings were found: ::
+#
+#   MPI_Fortran_FOUND
+#   MPI_Fortran_COMPILER
+#   MPI_Fortran_COMPILE_FLAGS
+#   MPI_Fortran_INCLUDE_PATH
+#   MPI_Fortran_LIBRARIES
+#   MPI_Fortran_LINK_FLAGS
+#
+##############################################################################
+
+macro( ecbuild_find_mpi )
+
+    # parse parameters
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    # if user defined compilers are MPI compliant, then we use them ...
+    if( NOT DEFINED ECBUILD_FIND_MPI )
+      set( ECBUILD_FIND_MPI TRUE )
+    endif()
+    if( ECBUILD_FIND_MPI )
+
+        # C compiler
+
+        if( CMAKE_C_COMPILER_LOADED AND NOT MPI_C_COMPILER )
+
+            include(CheckCSourceCompiles)
+
+            check_c_source_compiles("
+                #include <mpi.h>
+                int main(int argc, char* argv[])
+                {
+                int rank;
+                MPI_Init(&argc, &argv); 
+                MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
+                MPI_Finalize();
+                return 0;
+                }
+                "
+                C_COMPILER_SUPPORTS_MPI )
+
+            if( C_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C compiler supports MPI -- ${CMAKE_C_COMPILER}" )
+                set( MPI_C_COMPILER ${CMAKE_C_COMPILER} )
+            endif()
+
+        endif()
+
+        # CXX compiler
+
+        if( CMAKE_CXX_COMPILER_LOADED AND NOT MPI_CXX_COMPILER )
+
+            include(CheckCXXSourceCompiles)
+
+            check_cxx_source_compiles("
+                #include <mpi.h>
+                 #include <iostream>
+                 int main(int argc, char* argv[])
+                 {
+                   MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Finalize();
+                   return 0;
+                 }
+                 "
+                 CXX_COMPILER_SUPPORTS_MPI )
+
+            if( CXX_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C++ compiler supports MPI -- ${CMAKE_CXX_COMPILER}" )
+                set( MPI_CXX_COMPILER ${CMAKE_CXX_COMPILER} )
+            endif()
+
+        endif()
+
+        # Fortran compiler
+
+        if( CMAKE_Fortran_COMPILER_LOADED AND NOT MPI_Fortran_COMPILER )
+
+            include(CheckFortranSourceCompiles)
+
+            check_fortran_source_compiles("
+                program main
+                use MPI
+                integer ierr
+                call MPI_INIT( ierr )
+                call MPI_FINALIZE( ierr )
+                end
+                "
+            Fortran_COMPILER_SUPPORTS_MPI )
+
+            if( Fortran_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "Fortran compiler supports MPI (F90) -- ${CMAKE_Fortran_COMPILER}" )
+                set( MPI_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} )
+                set( MPI_Fortran_FOUND TRUE )
+            endif()
+
+        endif()
+
+        if( NOT _PAR_REQUIRED )
+            find_package( MPI QUIET )
+        else()
+            find_package( MPI QUIET REQUIRED )
+        endif()
+
+        if( C_COMPILER_SUPPORTS_MPI )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CXX_COMPILER_SUPPORTS_MPI )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( Fortran_COMPILER_SUPPORTS_MPI )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    else()
+
+        # find_package with Cray compiler did not send MPI_<lang>_FOUND
+        if( CMAKE_C_COMPILER_LOADED )
+            set( C_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CMAKE_CXX_COMPILER_LOADED )
+            set( CXX_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( CMAKE_Fortran_COMPILER_LOADED )
+            set( Fortran_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    endif( ECBUILD_FIND_MPI )
+
+    # hide these variables from UI
+
+    mark_as_advanced( MPI_LIBRARY MPI_EXTRA_LIBRARY )
+
+    if( NOT _PAR_COMPONENTS )
+      set( _PAR_COMPONENTS C )
+    endif()
+
+    set( MPI_FOUND TRUE )
+    foreach( _lang ${_PAR_COMPONENTS} )
+      if( NOT MPI_${_lang}_FOUND )
+        set( MPI_FOUND FALSE )
+      endif()
+    endforeach()
+
+endmacro( ecbuild_find_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_mpi
+# ==================
+#
+# Find MPI, add include directories and set compiler flags. ::
+#
+#   ecbuild_enable_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                       [ REQUIRED ] )
+#
+# For each MPI language binding found, set the corresponding compiler flags
+# and add the include directories.
+#
+# See ``ecbuild_find_mpi`` for input and output variables.
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+##############################################################################
+
+macro( ecbuild_enable_mpi )
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_COMPONENTS )
+      set (_PAR_COMPONENTS C )
+    endif()
+
+    if( NOT _PAR_REQUIRED )
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} )
+    else()
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} REQUIRED )
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_enable_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_include_mpi
+# ===================
+#
+# Add MPI include directories and set compiler flags, assuming MPI was found.
+#
+# For each MPI language binding found, set corresponding compiler flags and
+# add include directories. ``ecbuild_find_mpi`` must have been called before.
+#
+##############################################################################
+
+macro( ecbuild_include_mpi )
+
+    set( options )
+    set( single_value_args )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_c_source_return )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_cxx_source_return )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_include_mpi )
diff --git a/cmake/ecbuild_find_omp.cmake b/cmake/ecbuild_find_omp.cmake
new file mode 100644
index 0000000..3dc6fac
--- /dev/null
+++ b/cmake/ecbuild_find_omp.cmake
@@ -0,0 +1,259 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for looking for openmp flags
+
+macro( lookup_omp_flags )
+  set(_OMP_FLAG_GNU        "-fopenmp")
+  set(_OMPSTUBS_FLAG_GNU   "-fno-openmp")
+
+  set(_OMP_FLAG_Cray       "-homp")
+  set(_OMPSTUBS_FLAG_Cray  "-hnoomp")
+
+  set(_OMP_FLAG_XL         "-qsmp=omp")
+  set(_OMPSTUBS_FLAG_XL    "-qsmp=noomp")
+
+  set(_OMP_FLAG_Intel      "-qopenmp")
+  set(_OMPSTUBS_FLAG_Intel "-qopenmp-stubs")
+
+  # sample C openmp source code to test
+  set(_OMP_C_TEST_SOURCE
+  "
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    #pragma omp parallel
+    {
+      (void)omp_get_thread_num();
+    }
+    return 0;
+  #else
+    breaks_on_purpose
+  #endif
+  }
+  ")
+  set( _OMP_CXX_TEST_SOURCE ${_OMP_C_TEST_SOURCE} )
+
+
+  # sample C openmp source code to test
+  set(_OMPSTUBS_C_TEST_SOURCE
+  "
+  // Include must be found
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    breaks_on_purpose
+  #else
+    #pragma omp parallel
+    {
+      // This pragma should have passed compilation
+      (void)0;
+    }
+    return 0;
+  #endif
+  }
+  ")
+  set( _OMPSTUBS_CXX_TEST_SOURCE ${_OMPSTUBS_C_TEST_SOURCE} )
+
+
+  # sample Fortran openmp source code to test
+  set(_OMP_Fortran_TEST_SOURCE
+  "
+  program main
+    use omp_lib
+  end program
+  ")
+  set( _OMPSTUBS_Fortran_TEST_SOURCE ${_OMP_Fortran_TEST_SOURCE} )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_omp
+# ================
+#
+# Find OpenMP. ::
+#
+#   ecbuild_find_omp( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ]
+#                     [ STUBS ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if OpenMP was not found
+#
+# STUBS : optional
+#   search for OpenMP stubs
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if OpenMP was found:
+#
+# :OMP_FOUND: OpenMP was found
+#
+# For each language listed in COMPONENTS, the following variables are set:
+#
+# :OMP_<LANG>_FOUND: OpenMP bindings for LANG were found
+# :OMP_<LANG>_FLAGS: OpenMP compiler flags for LANG
+#
+# If the STUBS option was given, all variables are also set with the OMPSTUBS
+# instead of the OMP prefix.
+#
+##############################################################################
+
+macro( ecbuild_find_omp )
+
+  set( options REQUIRED STUBS )
+  set( single_value_args )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT _PAR_COMPONENTS )
+    ecbuild_critical( "No COMPONENTS were specified, looking for OMP.\n Please find with COMPONENTS C CXX Fortran " )
+  endif()
+
+  set( _STUBS "" )
+  if( _PAR_STUBS )
+    set( _STUBS "STUBS" )
+  endif()
+
+  lookup_omp_flags()
+
+  set( OMP${_STUBS}_FOUND TRUE )
+
+  foreach( _LANG ${_PAR_COMPONENTS} )
+
+    if( NOT OMP${_STUBS}_${_LANG}_FLAGS )
+
+      if( DEFINED _OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID} )
+        set( _OMP${_STUBS}_${_LANG}_FLAG "${_OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID}}" )
+      endif()
+      if( CMAKE_${_LANG}_COMPILER_LOADED AND _OMP${_STUBS}_${_LANG}_FLAG )
+        set(SAVE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+        set(CMAKE_REQUIRED_FLAGS "${_OMP${_STUBS}_${_LANG}_FLAG}")
+        include(Check${_LANG}SourceCompiles)
+        set( _SOURCE ${_OMP${_STUBS}_${_LANG}_TEST_SOURCE} )
+        set( _FLAG ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS})
+        if( _LANG STREQUAL "C" )
+          check_c_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "CXX" )
+          check_cxx_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "Fortran" )
+          check_fortran_source_compiles("${_SOURCE}" ${_FLAG} SRC_EXT f90)
+        endif()
+        set(CMAKE_REQUIRED_FLAGS "${SAVE_CMAKE_REQUIRED_FLAGS}")
+      endif()
+
+      if( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} )
+        set( OMP${_STUBS}_${_LANG}_FLAGS ${_OMP${_STUBS}_${_LANG}_FLAG} )
+      endif()
+
+    else()
+      set( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} TRUE )
+    endif()
+
+
+    set( OMP${_STUBS}_${_LANG}_FIND_QUIETLY TRUE )
+    find_package_handle_standard_args( OMP${_STUBS}_${_LANG} REQUIRED_VARS ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS}  )
+
+    if( OMP${_STUBS}_FORTRAN_FOUND )
+      set( OMP${_STUBS}_Fortran_FOUND TRUE )
+    endif()
+
+    if( NOT OMP${_STUBS}_${_LANG}_FOUND )
+      set( OMP${_STUBS}_FOUND FALSE )
+    endif()
+
+    if( _PAR_STUBS )
+      set( OMP_${_LANG}_FOUND ${OMPSTUBS_${_LANG}_FOUND} )
+      set( OMP_${_LANG}_FLAGS ${OMPSTUBS_${_LANG}_FLAGS} )
+    endif()
+
+  endforeach()
+
+  if( _PAR_STUBS )
+    set( OMP_FOUND ${OMPSTUBS_FOUND} )
+  endif()
+
+endmacro( ecbuild_find_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_omp
+# ==================
+#
+# Find OpenMP for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP support was detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_omp )
+
+  ecbuild_debug("ecbuild_enable_omp: Trying to enable OpenMP")
+  ecbuild_find_omp( COMPONENTS C CXX Fortran )
+
+  ecbuild_debug_var("OMP_C_FOUND")
+  if( OMP_C_FOUND )
+    ecbuild_debug("Adding ${OMP_C_FLAGS} to CMAKE_C_FLAGS")
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMP_C_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_CXX_FOUND")
+  if( OMP_CXX_FOUND )
+    ecbuild_debug("Adding ${OMP_CXX_FLAGS} to CMAKE_CXX_FLAGS")
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMP_CXX_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_Fortran_FOUND")
+  if( OMP_Fortran_FOUND )
+    ecbuild_debug("Adding ${OMP_Fortran_FLAGS} to CMAKE_Fortran_FLAGS")
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMP_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_ompstubs
+# =======================
+#
+# Find OpenMP stubs for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP stubs were detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_ompstubs )
+
+  ecbuild_find_omp( COMPONENTS C CXX Fortran STUBS )
+
+  if( OMPSTUBS_C_FOUND )
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMPSTUBS_C_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_CXX_FOUND )
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMPSTUBS_CXX_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_Fortran_FOUND )
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMPSTUBS_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_ompstubs )
diff --git a/cmake/ecbuild_find_package.cmake b/cmake/ecbuild_find_package.cmake
new file mode 100644
index 0000000..71be044
--- /dev/null
+++ b/cmake/ecbuild_find_package.cmake
@@ -0,0 +1,368 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_package
+# ====================
+#
+# Find a package and import its configuration. ::
+#
+#   ecbuild_find_package( NAME <name>
+#                         [ VERSION <version> [ EXACT ] ]
+#                         [ COMPONENTS <component1> [ <component2> ... ] ]
+#                         [ URL <url> ]
+#                         [ DESCRIPTION <description> ]
+#                         [ TYPE <type> ]
+#                         [ PURPOSE <purpose> ]
+#                         [ FAILURE_MSG <message> ]
+#                         [ REQUIRED ]
+#                         [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# COMPONENTS : optional
+#   list of package components to find (behaviour depends on the package)
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   literal string or name of CMake variable describing the package
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   literal string or name of CMake variable describing which functionality
+#   this package enables in the project
+#
+# FAILURE_MSG : optional
+#   literal string or name of CMake variable containing a message to be
+#   appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>`` is
+# the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :DEVELOPER_MODE: if enabled, discover projects parallel in the build tree
+# :<name>_PATH:    install prefix path of the package
+# :<NAME>_PATH:    install prefix path of the package
+# :<name>_DIR:     directory containing the ``<name>-config.cmake`` file
+#                  (usually ``<install-prefix>/share/<name>/cmake``)
+#
+# The environment variables ``<name>_PATH``, ``<NAME>_PATH``, ``<name>_DIR``
+# are taken into account only if the corresponding CMake variables are unset.
+#
+# Usage
+# -----
+#
+# The search proceeds as follows:
+#
+# 1.  If any paths have been specified by the user via CMake or environment
+#     variables as given above or a parallel build tree has been discovered in
+#     DEVELOPER_MODE:
+#
+#     * search for ``<name>-config.cmake`` in those paths only
+#     * search using ``Find<name>.cmake`` (which should respect those paths)
+#     * fail if the package was not found in any of those paths
+#
+# 2.  Search for ``<name>-config.cmake`` in the ``CMAKE_PREFIX_PATH`` and if
+#     DEVELOPER_MODE is enabled also in the user package registry.
+#
+# 3.  Search system paths for ``<name>-config.cmake``.
+#
+# 4.  Search system paths using ``Find<name>.cmake``.
+#
+# 5.  If the package was found, and a minimum version was requested, check if
+#     the version is acceptable and if not, unset ``<NAME>_FOUND``.
+#
+# 6.  Fail if the package was not found and is REQUIRED.
+#
+##############################################################################
+
+macro( ecbuild_find_package )
+
+  set( options REQUIRED QUIET EXACT )
+  set( single_value_args NAME VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_package(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_NAME  )
+    ecbuild_critical("The call to ecbuild_find_package() doesn't specify the NAME.")
+  endif()
+
+  if( _PAR_EXACT AND NOT _PAR_VERSION )
+    ecbuild_critical("Call to ecbuild_find_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _PAR_REQUIRED )
+    set( _PAR_TYPE REQUIRED )
+  endif()
+
+  # ecbuild_debug_var( _PAR_NAME )
+
+  string( TOUPPER ${_PAR_NAME} pkgUPPER )
+  string( TOLOWER ${_PAR_NAME} pkgLOWER )
+
+  set( _${pkgUPPER}_version "" )
+  if( _PAR_VERSION )
+    set( _${pkgUPPER}_version ${_PAR_VERSION} )
+    if( _PAR_EXACT )
+      set( _${pkgUPPER}_version ${_PAR_VERSION} EXACT )
+    endif()
+  endif()
+
+  # check developer mode (search in cmake cache )
+
+  if( NOT ${DEVELOPER_MODE} )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): Not in DEVELOPER_MODE - do not search package registry or recent GUI build paths")
+    set( NO_DEV_BUILD_DIRS NO_CMAKE_PACKAGE_REGISTRY NO_CMAKE_BUILDS_PATH )
+  endif()
+
+  # in DEVELOPER_MODE we give priority to projects parallel in the build tree
+  # so lets prepend a parallel build tree to the search path if we find it
+
+  if( DEVELOPER_MODE )
+    get_filename_component( _proj_bdir "${CMAKE_BINARY_DIR}/../${pkgLOWER}" ABSOLUTE )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - searching for ${pkgLOWER}-config.cmake in ${_proj_bdir}")
+    if( EXISTS ${_proj_bdir}/${pkgLOWER}-config.cmake )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - found parallel build tree in ${_proj_bdir}")
+      if( ${pkgUPPER}_PATH )
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - ${pkgUPPER}_PATH already set to ${${pkgUPPER}_PATH}, not modifying")
+      else()
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - setting ${pkgUPPER}_PATH to ${_proj_bdir}")
+        set( ${pkgUPPER}_PATH "${_proj_bdir}" )
+      endif()
+    endif()
+  endif()
+
+  # Read environment variables but ONLY if the corresponding CMake variables are unset
+
+  if( NOT DEFINED ${pkgUPPER}_PATH AND NOT "$ENV{${pkgUPPER}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${pkgUPPER}_PATH=${${pkgUPPER}_PATH} from environment")
+    set( ${pkgUPPER}_PATH "$ENV{${pkgUPPER}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_PATH AND NOT "$ENV{${_PAR_NAME}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH} from environment")
+    set( ${_PAR_NAME}_PATH "$ENV{${_PAR_NAME}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_DIR AND NOT "$ENV{${_PAR_NAME}_DIR}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR} from environment")
+    set( ${_PAR_NAME}_DIR "$ENV{${_PAR_NAME}_DIR}" )
+  endif()
+
+  # Find packages quietly unless in DEVELOPER_MODE or LOG_LEVEL is DEBUG
+
+  if( NOT DEVELOPER_MODE AND ( ECBUILD_LOG_LEVEL GREATER ${ECBUILD_DEBUG} ) )
+    set( _find_quiet QUIET )
+  endif()
+
+  # search user defined paths first
+
+  if( ${_PAR_NAME}_PATH OR ${pkgUPPER}_PATH OR ${_PAR_NAME}_DIR )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+
+    # 1) search using CONFIG mode -- try to locate a configuration file provided by the package (package-config.cmake)
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 1) search using CONFIG mode -- try to locate ${_PAR_NAME}-config.cmake")
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}):    using hints ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} NO_MODULE ${_find_quiet}
+        COMPONENTS ${_PAR_COMPONENTS}
+        HINTS ${${pkgUPPER}_PATH} ${${_PAR_NAME}_PATH} ${${_PAR_NAME}_DIR}
+        NO_DEFAULT_PATH )
+    endif()
+
+    # 2) search using a file Find<package>.cmake if it exists ( macro should itself take *_PATH into account )
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 2) search using a file Find${_PAR_NAME}.cmake if it exists")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} MODULE ${_find_quiet}
+                    COMPONENTS ${_PAR_COMPONENTS} )
+    endif()
+
+    # is <package>_PATH was given and we don't find anything then we FAIL
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      if( ${_PAR_NAME}_PATH )
+        ecbuild_critical( "${_PAR_NAME}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${_PAR_NAME}_PATH}'" )
+      endif()
+      if( ${pkgUPPER}_PATH )
+        ecbuild_critical( "${pkgUPPER}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${pkgUPPER}_PATH}'" )
+      endif()
+    endif()
+
+  endif()
+
+  # 3) search developer cache and recently configured packages in the CMake GUI if in DEVELOPER_MODE
+  #    otherwise only search CMAKE_PREFIX_PATH and <package>_PATH
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    if (NO_DEV_BUILD_DIRS)
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH")
+    else()
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH and package registry")
+    endif()
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      HINTS ENV ${pkgUPPER}_PATH
+      ${NO_DEV_BUILD_DIRS}
+      NO_CMAKE_ENVIRONMENT_PATH
+      NO_SYSTEM_ENVIRONMENT_PATH
+      NO_CMAKE_SYSTEM_PATH
+      NO_CMAKE_SYSTEM_PACKAGE_REGISTRY )
+
+  endif()
+
+  # 4) search system paths, for <package>-config.cmake
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 5) search system paths, for ${_PAR_NAME}-config.cmake")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      ${NO_DEV_BUILD_DIRS} )
+
+  endif()
+
+  # 5) search system paths, using Find<package>.cmake if it exists
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 6) search system paths, using Find${_PAR_NAME}.cmake if it exists")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} MODULE
+                  COMPONENTS ${_PAR_COMPONENTS} )
+
+  endif()
+
+  # check version found is acceptable
+
+  if( ${_PAR_NAME}_FOUND )
+    set( _version_acceptable 1 )
+    if( _PAR_VERSION )
+      if( ${_PAR_NAME}_VERSION )
+        if( _PAR_EXACT )
+          if( NOT ${_PAR_NAME}_VERSION VERSION_EQUAL _PAR_VERSION )
+            ecbuild_warn( "${PROJECT_NAME} requires (exactly) ${_PAR_NAME} = ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            set( _version_acceptable 0 )
+          endif()
+        else()
+          if( _PAR_VERSION VERSION_LESS ${_PAR_NAME}_VERSION OR _PAR_VERSION VERSION_EQUAL ${_PAR_NAME}_VERSION )
+            set( _version_acceptable 1 )
+          else()
+            if( NOT _PAR_QUIET )
+              ecbuild_warn( "${PROJECT_NAME} requires ${_PAR_NAME} >= ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            endif()
+            set( _version_acceptable 0 )
+          endif()
+        endif()
+      else()
+        if( NOT _PAR_QUIET )
+          ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but no version information, so cannot check if satisfies ${_PAR_VERSION}" )
+        endif()
+        set( _version_acceptable 0 )
+      endif()
+    endif()
+  endif()
+
+  if( ${_PAR_NAME}_FOUND )
+
+    if( _version_acceptable )
+      set( ${pkgUPPER}_FOUND ${${_PAR_NAME}_FOUND} )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but with unsuitable version" )
+      endif()
+      set( ${pkgUPPER}_FOUND 0 )
+      set( ${_PAR_NAME}_FOUND 0 )
+    endif()
+
+  endif()
+
+  ### final messages
+
+  if( ${_PAR_NAME}_FOUND OR ${pkgUPPER}_FOUND )
+
+    if( NOT _PAR_QUIET )
+      ecbuild_info( "[${_PAR_NAME}] (${${_PAR_NAME}_VERSION})" )
+      foreach( var in ITEMS INCLUDE_DIR INCLUDE_DIRS DEFINITIONS LIBRARY LIBRARIES )
+        if( ${pkgUPPER}_${var} )
+          ecbuild_info( "   ${pkgUPPER}_${var} : [${${pkgUPPER}_${var}}]" )
+        elseif( ${_PAR_NAME}_${var} )
+          ecbuild_info( "   ${_PAR_NAME}_${var} : [${${_PAR_NAME}_${var}}]" )
+        endif()
+      endforeach()
+    endif()
+
+    if( DEFINED ${_PAR_DESCRIPTION} )
+      set( _PAR_DESCRIPTION ${${_PAR_DESCRIPTION}} )
+    endif()
+    if( DEFINED ${_PAR_PURPOSE} )
+      set( _PAR_PURPOSE ${${_PAR_PURPOSE}} )
+    endif()
+    set_package_properties( ${_PAR_NAME} PROPERTIES
+                            URL "${_PAR_URL}"
+                            DESCRIPTION "${_PAR_DESCRIPTION}"
+                            TYPE "${_PAR_TYPE}"
+                            PURPOSE "${_PAR_PURPOSE}" )
+
+  else()
+
+    if( DEFINED ${_PAR_FAILURE_MSG} )
+      set( _PAR_FAILURE_MSG ${${_PAR_FAILURE_MSG}} )
+    endif()
+    set( _failed_message
+      "  ${PROJECT_NAME} FAILED to find package ${_PAR_NAME}\n"
+      "    Provide location with \"-D${pkgUPPER}_PATH=/...\" or \"-D${_PAR_NAME}_DIR=/...\" \n"
+      "    You may also export environment variables ${pkgUPPER}_PATH or ${_PAR_NAME}_DIR\n"
+      "  Values (note CAPITALISATION):\n"
+      "    ${pkgUPPER}_PATH should contain the path to the install prefix (as in <install>/bin <install>/lib <install>/include)\n"
+      "    ${_PAR_NAME}_DIR should be a directory containing a <package>-config.cmake file (usually <install>/share/<package>/cmake)\n"
+      )
+
+    if( _PAR_REQUIRED )
+      ecbuild_critical( "${_failed_message}!! ${PROJECT_NAME} requires package ${_PAR_NAME} !!\n${_PAR_FAILURE_MSG}" )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${_failed_message}\n${_PAR_FAILURE_MSG}" )
+      endif()
+    endif()
+
+  endif()
+
+endmacro()
diff --git a/cmake/ecbuild_find_perl.cmake b/cmake/ecbuild_find_perl.cmake
new file mode 100644
index 0000000..b6c1825
--- /dev/null
+++ b/cmake/ecbuild_find_perl.cmake
@@ -0,0 +1,73 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_perl
+# =================
+#
+# Find perl executable and its version. ::
+#
+#   ecbuild_find_perl( [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# REQUIRED : optional
+#   fail if perl was not found
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if perl was found:
+#
+# :PERL_FOUND:          perl was found
+# :PERL_EXECUTABLE:     path to the perl executable
+# :PERL_VERSION:        perl version
+# :PERL_VERSION_STRING: perl version (same as ``PERL_VERSION``)
+#
+##############################################################################
+
+macro( ecbuild_find_perl )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_perl(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  find_package( Perl )
+
+  if( NOT PERL_EXECUTABLE AND _p_REQUIRED )
+    ecbuild_critical( "Failed to find Perl (REQUIRED)" )
+  endif()
+
+  if( PERL_EXECUTABLE )
+
+    execute_process( COMMAND ${PERL_EXECUTABLE} -V:version OUTPUT_VARIABLE  perl_version_output_variable  RESULT_VARIABLE  perl_version_return )
+    if( NOT perl_version_return )
+      string(REGEX REPLACE "version='([^']+)'.*" "\\1" PERL_VERSION ${perl_version_output_variable})
+    endif()
+
+    # from cmake 2.8.8 onwards
+    if( NOT PERL_VERSION_STRING )
+      set( PERL_VERSION_STRING ${PERL_VERSION} )
+    endif()
+
+    ecbuild_debug("ecbuild_find_perl: found perl version ${PERL_VERSION_STRING} as ${PERL_EXECUTABLE}")
+
+  endif()
+
+endmacro( ecbuild_find_perl )
diff --git a/cmake/ecbuild_find_python.cmake b/cmake/ecbuild_find_python.cmake
new file mode 100644
index 0000000..0e9adaa
--- /dev/null
+++ b/cmake/ecbuild_find_python.cmake
@@ -0,0 +1,263 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_python
+# ===================
+#
+# Find Python interpreter, its version and the Python libraries. ::
+#
+#   ecbuild_find_python( [ VERSION <version> ] [ REQUIRED ] [ NO_LIBS ] )
+#
+# Options
+# -------
+#
+# VERSION : optional
+#   minimum required version
+#
+# REQUIRED : optional
+#   fail if Python was not found
+#
+# NO_LIBS : optional
+#   only search for the Python interpreter, not the libraries
+#
+# Unless ``NO_LIBS`` is set, the ``python-config`` utility, if found, is used
+# to determine the Python include directories, libraries and link line. Set the
+# CMake variable ``PYTHON_NO_CONFIG`` to use CMake's FindPythonLibs instead.
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if python was found:
+#
+# :PYTHONINTERP_FOUND:    Python interpreter was found
+# :PYTHONLIBS_FOUND:      Python libraries were found
+# :PYTHON_FOUND:          Python was found (both interpreter and libraries)
+# :PYTHON_EXECUTABLE:     Python executable
+# :PYTHON_VERSION_MAJOR:  major version number
+# :PYTHON_VERSION_MINOR:  minor version number
+# :PYTHON_VERSION_PATCH:  patch version number
+# :PYTHON_VERSION_STRING: Python version
+# :PYTHON_INCLUDE_DIRS:   Python include directories
+# :PYTHON_LIBRARIES:      Python libraries
+# :PYTHON_SITE_PACKAGES:  Python site packages directory
+#
+##############################################################################
+
+set( __test_python ${CMAKE_CURRENT_LIST_DIR}/pymain.c )
+
+function( ecbuild_find_python )
+
+    # parse parameters
+
+    set( options REQUIRED NO_LIBS )
+    set( single_value_args VERSION )
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+    if( _p_REQUIRED )
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter (required) ..." )
+      set( _p_REQUIRED REQUIRED )
+    else()
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter ..." )
+      unset( _p_REQUIRED )
+    endif()
+
+    # find python executable
+
+    # Search first without specifying the version, since doing so gives preference to the specified
+    # version even though a never version of the interpreter may be available
+    find_package( PythonInterp ${_p_REQUIRED} )
+
+    # If no suitable version was found, search again with the version specified
+    if( PYTHONINTERP_FOUND AND _p_VERSION )
+      if( _p_VERSION VERSION_GREATER "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}', however version '${_p_VERSION}' is required. Searching again..." )
+        unset( PYTHONINTERP_FOUND )
+        unset( PYTHON_EXECUTABLE )
+        unset( PYTHON_EXECUTABLE CACHE )
+        unset( PYTHON_VERSION_MAJOR )
+        unset( PYTHON_VERSION_MINOR )
+        unset( PYTHON_VERSION_PATCH )
+        unset( PYTHON_VERSION_STRING )
+        find_package( PythonInterp "${_p_VERSION}" ${_p_REQUIRED} )
+      endif()
+    endif()
+
+    set_package_properties( PythonInterp PROPERTIES
+                            URL http://python.org
+                            DESCRIPTION "Python interpreter" )
+
+    set( __required_vars PYTHONINTERP_FOUND )
+
+    if( PYTHONINTERP_FOUND )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}'" )
+
+        # find where python site-packages are ...
+
+        if( PYTHON_EXECUTABLE )
+            execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
+        endif()
+        ecbuild_debug( "ecbuild_find_python: PYTHON_SITE_PACKAGES=${PYTHON_SITE_PACKAGES}" )
+    else()
+        ecbuild_debug( "ecbuild_find_python: could NOT find Python interpreter!" )
+    endif()
+
+    if( PYTHONINTERP_FOUND AND _p_NO_LIBS )
+        ecbuild_debug( "ecbuild_find_python: NOT searching for Python libraries" )
+    elseif( PYTHONINTERP_FOUND )
+        list( APPEND __required_vars PYTHONLIBS_FOUND PYTHON_LIBS_WORKING )
+        ecbuild_debug( "ecbuild_find_python: Searching for Python libraries ..." )
+
+        # find python config
+
+        if( PYTHON_EXECUTABLE AND EXISTS ${PYTHON_EXECUTABLE}-config )
+            set(PYTHON_CONFIG_EXECUTABLE ${PYTHON_EXECUTABLE}-config CACHE PATH "" FORCE)
+        else()
+            get_filename_component( __python_bin_dir ${PYTHON_EXECUTABLE} PATH )
+            find_program( PYTHON_CONFIG_EXECUTABLE
+                          NO_CMAKE_PATH NO_CMAKE_SYSTEM_PATH
+                          NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH
+                          HINTS ${__python_bin_dir}
+                          NAMES python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}-config
+                                python${PYTHON_VERSION_MAJOR}-config
+                                python-config )
+        endif()
+
+        ecbuild_debug( "ecbuild_find_python: found python-config at '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+        # find python libs
+
+        # The OpenBSD python packages have python-config's
+        # that don't reliably report linking flags that will work.
+
+        if( PYTHON_CONFIG_EXECUTABLE AND NOT ( PYTHON_NO_CONFIG OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" ) )
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+            if( DEFINED PYTHON_LIBRARY )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY already set to '${PYTHON_LIBRARY}'" )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --prefix
+                              OUTPUT_VARIABLE PYTHON_PREFIX
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_PREFIX=${PYTHON_PREFIX}" )
+
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --ldflags
+                              OUTPUT_VARIABLE PYTHON_LIBRARY
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY=${PYTHON_LIBRARY}" )
+
+              # Prepend -L and and set the RPATH to the lib directory under the
+              # Python install prefix unless it is a standard system prefix path
+              if( PYTHON_LIBRARY AND PYTHON_PREFIX AND NOT CMAKE_SYSTEM_PREFIX_PATH MATCHES ${PYTHON_PREFIX} )
+                ecbuild_debug( "ecbuild_find_python: Python libraries not in CMAKE_SYSTEM_PREFIX_PATH, prepending PYTHON_PREFIX '${PYTHON_PREFIX}' to PYTHON_LIBRARY" )
+                set( PYTHON_LIBRARY "-L${PYTHON_PREFIX}/lib -Wl,-rpath,${PYTHON_PREFIX}/lib ${PYTHON_LIBRARY}" )
+              endif()
+            endif()
+
+            if( DEFINED PYTHON_INCLUDE_DIR )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR already set to '${PYTHON_INCLUDE_DIR}'" )
+            elseif(DEFINED PYTHON_INCLUDE_PATH AND NOT DEFINED PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_PATH already set to '${PYTHON_INCLUDE_PATH}'" )
+              ecbuild_deprecate( "ecbuild_find_python: PYTHON_INCLUDE_PATH is deprecated, use PYTHON_INCLUDE_DIR instead!" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_PATH}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --includes
+                              OUTPUT_VARIABLE PYTHON_INCLUDE_DIR
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+
+              string(REGEX REPLACE "^[-I]" "" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+              string(REGEX REPLACE "[ ]-I" " " PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+
+              separate_arguments(PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR}" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+
+            endif()
+
+            set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}")
+            set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}")
+
+            find_package_handle_standard_args( PythonLibs DEFAULT_MSG
+                                               PYTHON_INCLUDE_DIRS PYTHON_LIBRARIES )
+
+        else() # revert to finding pythonlibs the standard way (cmake macro)
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using find_package( PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH} ${_p_REQUIRED} )" )
+
+            find_package( PythonLibs "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" ${_p_REQUIRED} )
+
+            set_package_properties( PythonLibs PROPERTIES
+                                    URL http://python.org
+                                    DESCRIPTION "Python library and header" )
+
+        endif()
+
+        # Remove duplicate include directories
+        list(REMOVE_DUPLICATES PYTHON_INCLUDE_DIRS)
+
+        ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIRS=${PYTHON_INCLUDE_DIRS}" )
+        ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARIES=${PYTHON_LIBRARIES}" )
+
+        if( PYTHON_LIBRARIES AND PYTHON_INCLUDE_DIRS )
+            ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries ..." )
+            # Test if we can link against the Python libraries and include Python.h
+            try_compile( PYTHON_LIBS_WORKING ${CMAKE_CURRENT_BINARY_DIR}
+                         ${__test_python}
+                         CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PYTHON_INCLUDE_DIRS}"
+                         LINK_LIBRARIES ${PYTHON_LIBRARIES}
+                         OUTPUT_VARIABLE __try_compile_output )
+            if( PYTHON_LIBS_WORKING )
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries successful" )
+            else()
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries failed\n${__try_compile_output}" )
+            endif()
+
+        else()
+            ecbuild_debug( "ecbuild_find_python: Python library and include diretory not found" )
+        endif()
+
+    endif()
+
+    find_package_handle_standard_args( Python DEFAULT_MSG ${__required_vars} )
+
+    ecbuild_debug_var( PYTHONINTERP_FOUND )
+    ecbuild_debug_var( PYTHON_FOUND )
+    ecbuild_debug_var( PYTHON_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_CONFIG_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_VERSION_MAJOR )
+    ecbuild_debug_var( PYTHON_VERSION_MINOR )
+    ecbuild_debug_var( PYTHON_VERSION_PATCH )
+    ecbuild_debug_var( PYTHON_VERSION_STRING )
+    ecbuild_debug_var( PYTHON_INCLUDE_DIRS )
+    ecbuild_debug_var( PYTHON_LIBRARIES )
+    ecbuild_debug_var( PYTHON_SITE_PACKAGES )
+
+    set( PYTHONINTERP_FOUND    ${PYTHONINTERP_FOUND} PARENT_SCOPE )
+    set( PYTHONLIBS_FOUND      ${PYTHONLIBS_FOUND} PARENT_SCOPE )
+    set( PYTHON_FOUND          ${PYTHON_FOUND} PARENT_SCOPE )
+    set( PYTHON_EXECUTABLE     ${PYTHON_EXECUTABLE} PARENT_SCOPE )
+    set( PYTHON_VERSION_MAJOR  ${PYTHON_VERSION_MAJOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_MINOR  ${PYTHON_VERSION_MINOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_PATCH  ${PYTHON_VERSION_PATCH} PARENT_SCOPE )
+    set( PYTHON_VERSION_STRING ${PYTHON_VERSION_STRING} PARENT_SCOPE )
+    set( PYTHON_INCLUDE_DIRS   ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE )
+    set( PYTHON_LIBRARIES      ${PYTHON_LIBRARIES} PARENT_SCOPE )
+    set( PYTHON_SITE_PACKAGES  ${PYTHON_SITE_PACKAGES} PARENT_SCOPE )
+
+endfunction( ecbuild_find_python )
diff --git a/cmake/ecbuild_generate_config_headers.cmake b/cmake/ecbuild_generate_config_headers.cmake
new file mode 100644
index 0000000..8f2d44e
--- /dev/null
+++ b/cmake/ecbuild_generate_config_headers.cmake
@@ -0,0 +1,63 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_config_headers
+# ===============================
+#
+# Generates the ecBuild configuration header for the project with the system
+# introspection done by CMake. ::
+#
+#   ecbuild_generate_config_headers( [ DESTINATION <directory> ] )
+#
+# Options
+# -------
+#
+# DESTINATION : optional
+#   installation destination directory
+#
+##############################################################################
+
+function( ecbuild_generate_config_headers )
+
+  # parse parameters
+
+  set( options )
+  set( single_value_args DESTINATION )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_config_headers(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # generate list of compiler flags
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${langs} )
+    set( EC_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+  endforeach()
+
+  configure_file( ${ECBUILD_MACROS_DIR}/ecbuild_config.h.in  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h   )
+
+  # install ecbuild configuration
+
+  set( _destination ${INSTALL_INCLUDE_DIR} )
+  if( _p_DESTINATION )
+    set( _destination ${_p_DESTINATION} )
+  endif()
+
+  install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h DESTINATION ${_destination} )
+
+endfunction( ecbuild_generate_config_headers )
diff --git a/cmake/ecbuild_generate_fortran_interfaces.cmake b/cmake/ecbuild_generate_fortran_interfaces.cmake
new file mode 100644
index 0000000..a127315
--- /dev/null
+++ b/cmake/ecbuild_generate_fortran_interfaces.cmake
@@ -0,0 +1,143 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_fortran_interfaces
+# ===================================
+#
+# Generates interfaces from the Fortran source files. ::
+#
+#   ecbuild_generate_fortran_interfaces()
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+##############################################################################
+
+function( ecbuild_generate_fortran_interfaces )
+
+  find_program( FCM_EXECUTABLE fcm REQUIRED DOC "Fortran interface generator" )
+
+  if( NOT FCM_EXECUTABLE )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: fcm executable not found." )
+  endif()
+
+  set( options )
+  set( single_value_args TARGET DESTINATION PARALLEL INCLUDE_DIRS GENERATED SOURCE_DIR FCM_CONFIG_FILE )
+  set( multi_value_args DIRECTORIES )
+
+  cmake_parse_arguments( P "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT DEFINED P_TARGET )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: TARGET argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DESTINATION )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DESTINATION argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DIRECTORIES )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DIRECTORIES argument missing" )
+  endif()
+
+  if( NOT DEFINED P_PARALLEL OR (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") )
+    set( P_PARALLEL 1 )
+  endif()
+
+  ecbuild_debug_var( P_PARALLEL )
+
+  if( NOT DEFINED P_SOURCE_DIR )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: SOURCE_DIR argument missing")
+  endif()
+
+  if( DEFINED P_FCM_CONFIG_FILE )
+    set( FCM_CONFIG_FILE ${P_FCM_CONFIG_FILE} )
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( PROJECT_FCM_CONFIG_FILE "${PROJECT_SOURCE_DIR}/cmake/fcm-make-interfaces.cfg" )
+    if( EXISTS ${PROJECT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${PROJECT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${PROJECT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${PROJECT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( DEFAULT_FCM_CONFIG_FILE "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg" )
+    if( EXISTS ${DEFAULT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${DEFAULT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  ecbuild_debug_var( FCM_CONFIG_FILE )
+
+  if( NOT EXISTS ${FCM_CONFIG_FILE} )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: needs fcm configuration in ${FCM_CONFIG_FILE}" )
+  endif()
+
+  foreach( _srcdir ${P_DIRECTORIES} )
+    if( _srcdir MATCHES "/$" )
+      ecbuild_critical("ecbuild_generate_fortran_interfaces: directory ${_srcdir} must not end with /")
+    endif()
+    ecbuild_list_add_pattern( LIST fortran_files SOURCE_DIR ${P_SOURCE_DIR} GLOB ${_srcdir}/*.F* )
+  endforeach()
+
+  string( REPLACE ";" " " _srcdirs "${P_DIRECTORIES}" )
+
+  set( _cnt 0 )
+  foreach( file ${_fortran_files} )
+    if( ${${SRC}/file} IS_NEWER_THAN ${${SRC}/file} )
+      set( run_fcm 1 )
+    endif()
+  endforeach()
+
+  foreach( fortran_file ${fortran_files} )
+    #list( APPEND fullpath_fortran_files ${CMAKE_CURRENT_SOURCE_DIR}/${fortran_file} )
+      get_filename_component(base ${fortran_file} NAME_WE)
+      set( interface_file "${CMAKE_CURRENT_BINARY_DIR}/interfaces/include/${base}.intfb.h" )
+      list( APPEND interface_files ${interface_file} )
+      set_source_files_properties( ${interface_file} PROPERTIES GENERATED TRUE )
+      math(EXPR _cnt "${_cnt}+1")
+  endforeach()
+
+  ecbuild_info("Target ${P_TARGET} will generate ${_cnt} interface files using FCM")
+
+
+
+  if( DEFINED P_GENERATED )
+    set( ${P_GENERATED} ${interface_files} PARENT_SCOPE )
+  endif()
+
+  set( include_dir ${CMAKE_CURRENT_BINARY_DIR}/${P_DESTINATION}/interfaces/include )
+  set( ${P_INCLUDE_DIRS} ${include_dir} PARENT_SCOPE )
+
+  execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${include_dir}
+                   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+
+    add_custom_command(
+      OUTPUT  "${P_DESTINATION}/${P_TARGET}.timestamp"
+      COMMAND ${FCM_EXECUTABLE} make -j ${P_PARALLEL} --config-file=${FCM_CONFIG_FILE} interfaces.ns-incl=${_srcdirs} interfaces.source=${P_SOURCE_DIR}
+      COMMAND touch "${P_TARGET}.timestamp"
+      DEPENDS ${fortran_files}
+      COMMENT "Generating ${_cnt} interface files for target ${P_TARGET}"
+      WORKING_DIRECTORY ${P_DESTINATION} VERBATIM )
+
+    add_custom_target( ${P_TARGET} DEPENDS ${P_DESTINATION}/${P_TARGET}.timestamp )
+
+
+endfunction( ecbuild_generate_fortran_interfaces )
diff --git a/cmake/ecbuild_generate_rpc.cmake b/cmake/ecbuild_generate_rpc.cmake
new file mode 100644
index 0000000..2eb8605
--- /dev/null
+++ b/cmake/ecbuild_generate_rpc.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_rpc
+# ====================
+#
+# Process RPC (Remote Procedure Call) Language files using rpcgen. ::
+#
+#   ecbuild_generate_rpc( SOURCE <file>
+#                         [ TARGET_H <file> ]
+#                         [ TARGET_C <file> ]
+#                         [ DEPENDANT <file1> [ <file2> ... ] ] )
+#
+# Options
+# -------
+#
+# SOURCE : required
+#   RPC source file
+#
+# TARGET_H : optional (required if TARGET_C not given)
+#   name of header file to be generated
+#
+# TARGET_C : optional (required if TARGET_H not given)
+#   name of source file to be generated
+#
+# DEPENDANT : optional
+#  list of files which depend on the generated source and header files
+#
+##############################################################################
+
+macro( ecbuild_generate_rpc )
+
+  set( options )
+  set( single_value_args SOURCE TARGET_H TARGET_C )
+  set( multi_value_args DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_rpc(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SOURCE  )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the SOURCE file.")
+  endif()
+
+  # optional
+  #    if( NOT _PAR_DEPENDANT )
+  #      ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the DEPENDANT files.")
+  #    endif()
+
+  if( NOT DEFINED _PAR_TARGET_H AND NOT DEFINED _PAR_TARGET_C )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the _PAR_TARGET_H or _PAR_TARGET_C files.")
+  endif()
+
+  find_package( RPCGEN REQUIRED )
+
+  if( DEFINED _PAR_TARGET_H )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}
+      COMMAND ${RPCGEN_EXECUTABLE} -h -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}" )
+      endforeach()
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_TARGET_C )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}
+      COMMAND ${RPCGEN_EXECUTABLE} -c -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}" )
+      endforeach()
+    endif()
+
+  endif()
+
+endmacro( ecbuild_generate_rpc )
diff --git a/cmake/ecbuild_generate_yy.cmake b/cmake/ecbuild_generate_yy.cmake
new file mode 100644
index 0000000..ea7ec13
--- /dev/null
+++ b/cmake/ecbuild_generate_yy.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_yy
+# ===================
+#
+# Process lex/yacc files. ::
+#
+#   ecbuild_generate_yy( YYPREFIX <prefix>
+#                        YACC <file>
+#                        LEX <file>
+#                        DEPENDANT <file1> [ <file2> ... ]
+#                        [ SOURCE_DIR <dir> ]
+#                        [ OUTPUT_DIRECTORY <dir> ]
+#                        [ YACC_TARGET <file> ]
+#                        [ LEX_TARGET <file> ]
+#                        [ YACC_FLAGS <flags> ]
+#                        [ LEX_FLAGS <flags> ]
+#                        [ BISON_FLAGS <flags> ]
+#                        [ FLEX_FLAGS <flags> ] )
+#
+# Options
+# -------
+#
+# YYPREFIX : required
+#   prefix to use for file and function names
+#
+# YACC : required
+#   base name of the yacc source file (without .y extension)
+#
+# LEX : required
+#   base name of the lex source file (without .l extension)
+#
+# DEPENDANT : required
+#  list of files which depend on the generated lex and yacc target files
+#  At least one should be an existing source file (not generated itself).
+#
+# SOURCE_DIR : optional, defaults to CMAKE_CURRENT_SOURCE_DIR
+#   directory where yacc and lex source files are located
+#
+# OUTPUT_DIRECTORY : optional, defaults to CMAKE_CURRENT_BINARY_DIR
+#   output directory for yacc and lex target files
+#
+# YACC_TARGET : optional, defaults to YACC
+#   base name of the generated yacc target file (without .c extension)
+#
+# LEX_TARGET : optional, defaults to LEX
+#   base name of the generated lex target file (without .c extension)
+#
+# YACC_FLAGS : optional, defaults to -t
+#   flags to pass to yacc executable
+#
+# LEX_FLAGS : optional
+#   flags to pass to lex executable
+#
+# BISON_FLAGS : optional, defaults to -t
+#   flags to pass to bison executable
+#
+# FLEX_FLAGS : optional, defaults to -l
+#   flags to pass to flex executable
+#
+##############################################################################
+
+macro( ecbuild_generate_yy )
+
+  ecbuild_find_lexyacc() # find [ yacc|byson ] and [ lex|flex ]
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args YYPREFIX YACC LEX SOURCE_DIR OUTPUT_DIRECTORY YACC_TARGET LEX_TARGET LEX_FLAGS YACC_FLAGS FLEX_FLAGS BISON_FLAGS )
+  set( multi_value_args  DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_yy(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_YYPREFIX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YYPREFIX.")
+  endif()
+
+  if( NOT _PAR_YACC  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YACC file.")
+  endif()
+
+  if( NOT _PAR_LEX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the LEX file.")
+  endif()
+
+  if( NOT _PAR_DEPENDANT )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the DEPENDANT files.")
+  endif()
+
+  set( BASE ${_PAR_YYPREFIX}_${_PAR_YACC} )
+
+  ## default flags
+
+  if( NOT _PAR_LEX_FLAGS )
+    set( _PAR_LEX_FLAGS "" )
+  endif()
+
+  if( NOT _PAR_FLEX_FLAGS )
+    set( _PAR_FLEX_FLAGS "-l" )
+  endif()
+
+  if( NOT _PAR_YACC_FLAGS )
+    set( _PAR_YACC_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_BISON_FLAGS )
+    set( _PAR_BISON_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_YACC_TARGET )
+    set ( _PAR_YACC_TARGET ${_PAR_YACC} )
+  endif()
+
+  if ( NOT _PAR_LEX_TARGET )
+    set ( _PAR_LEX_TARGET ${_PAR_LEX} )
+  endif()
+
+  if( NOT _PAR_SOURCE_DIR )
+    set( _PAR_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
+  endif()
+
+  if( NOT _PAR_OUTPUT_DIRECTORY )
+    set( _PAR_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+  else()
+    file( MAKE_DIRECTORY ${_PAR_OUTPUT_DIRECTORY} )
+  endif()
+
+  set( ${BASE}yy_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.c )
+  set( ${BASE}yh_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.h )
+  set( ${BASE}yl_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.tmp.c )
+
+  set( ${BASE}yy_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.c )
+  set( ${BASE}yh_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.h )
+  set( ${BASE}yl_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.c )
+
+  if( BISON_FOUND )
+    bison_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_BISON_FLAGS}" )
+  else()
+    yacc_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_YACC_FLAGS}" )
+  endif()
+
+  if( FLEX_FOUND )
+    flex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_FLEX_FLAGS}" )
+    add_flex_bison_dependency(${BASE}_scanner ${BASE}_parser)
+  else()
+    lex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_LEX_FLAGS}" )
+    add_lex_yacc_dependency(${BASE}_scanner ${BASE}_parser)
+  endif()
+
+  set_source_files_properties(${${BASE}yy_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_tmp_target} GENERATED)
+
+  add_custom_command(OUTPUT  ${${BASE}yy_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yy_tmp_target} ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yy_target}
+    DEPENDS ${${BASE}yy_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yh_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yh_tmp_target} ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.h/\\.h/g' ${${BASE}yh_target}
+    DEPENDS ${${BASE}yh_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yl_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yl_tmp_target} ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yl_target}
+    DEPENDS ${${BASE}yl_tmp_target}
+    )
+
+  set_source_files_properties(${${BASE}yy_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_target} GENERATED)
+
+  foreach( file ${_PAR_DEPENDANT} )
+    if( NOT IS_ABSOLUTE ${file})
+      set( file ${_PAR_SOURCE_DIR}/${file} )
+    endif()
+    set_source_files_properties( ${file} PROPERTIES
+        OBJECT_DEPENDS "${${BASE}yy_target};${${BASE}yh_target};${${BASE}yl_target}" )
+  endforeach()
+
+endmacro( ecbuild_generate_yy )
diff --git a/cmake/ecbuild_get_cxx11_flags.cmake b/cmake/ecbuild_get_cxx11_flags.cmake
new file mode 100644
index 0000000..acb0b21
--- /dev/null
+++ b/cmake/ecbuild_get_cxx11_flags.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_cxx11_flags
+# =======================
+#
+# Set the CMake variable ``${CXX11_FLAGS}`` to the C++11 flags for the current
+# compiler (based on macros from https://github.com/UCL/GreatCMakeCookOff). ::
+#
+#   ecbuild_get_cxx11_flags( CXX11_FLAGS )
+#
+##############################################################################
+
+function( ecbuild_get_cxx11_flags CXX11_FLAGS )
+
+  include(CheckCXXCompilerFlag)
+
+  # On older cmake versions + newer compilers,
+  # the given version of CheckCXXCompilerFlags does not quite work.
+  if(CMAKE_VERSION VERSION_LESS 2.8.9)
+    macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+       set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+       set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+       CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+         # Some compilers do not fail with a bad flag
+         FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+         FAIL_REGEX "unrecognized .*option"                     # GNU
+         FAIL_REGEX "unknown .*option"                          # Clang
+         FAIL_REGEX "ignoring unknown option"                   # MSVC
+         FAIL_REGEX "warning D9002"                             # MSVC, any lang
+         FAIL_REGEX "option.*not supported"                     # Intel
+         FAIL_REGEX "invalid argument .*option"                 # Intel
+         FAIL_REGEX "ignoring option .*argument required"       # Intel
+         FAIL_REGEX "[Uu]nknown option"                         # HP
+         FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+         FAIL_REGEX "command option .* is not recognized"       # XL
+         FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+         FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+         FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+         )
+       set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+    endmacro ()
+  endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+  check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+  check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+  check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+  if(MINGW)
+    check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+    check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+  endif(MINGW)
+  if(has_std_gnupp11)
+    set(${CXX11_FLAGS} "-std=gnu++11" PARENT_SCOPE)
+  elseif(has_std_gnupp0x)
+    set(${CXX11_FLAGS} "-std=gnu++0x" PARENT_SCOPE)
+  elseif(has_hstd_cpp11)
+    set(${CXX11_FLAGS} "-hstd=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp11)
+    set(${CXX11_FLAGS} "-std=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp0x)
+    set(${CXX11_FLAGS} "-std=c++0x" PARENT_SCOPE)
+  else()
+    ecbuild_critical("Could not detect C++11 flags")
+  endif(has_std_gnupp11)
+
+endfunction()
diff --git a/cmake/ecbuild_get_date.cmake b/cmake/ecbuild_get_date.cmake
new file mode 100644
index 0000000..0f94d7d
--- /dev/null
+++ b/cmake/ecbuild_get_date.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_date
+# ================
+#
+# Set the CMake variable ``${DATE}`` to the current date in the form
+# YYYY.mm.DD. ::
+#
+#   ecbuild_get_date( DATE )
+#
+##############################################################################
+
+macro(ecbuild_get_date RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%d/%m/%Y" OUTPUT_VARIABLE ${RESULT})
+        string(REGEX REPLACE "(..)/(..)/(....).*" "\\3.\\2.\\1" ${RESULT} ${${RESULT}})
+    else()
+        ecbuild_error("date not implemented")
+    endif()
+endmacro(ecbuild_get_date)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_timestamp
+# =====================
+#
+# Set the CMake variable ``${TIMESTAMP}`` to the current date and time in the
+# form YYYYmmDDHHMMSS. ::
+#
+#   ecbuild_get_timestamp( TIMESTAMP )
+#
+##############################################################################
+
+macro(ecbuild_get_timestamp RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%Y/%m/%d/%H/%M/%S" OUTPUT_VARIABLE _timestamp)
+        string(REGEX REPLACE "(....)/(..)/(..)/(..)/(..)/(..).*" "\\1\\2\\3\\4\\5\\6" ${RESULT} ${_timestamp})
+    else()
+        ecbuild_warn("This is NOT UNIX - timestamp not implemented")
+    endif()
+endmacro(ecbuild_get_timestamp)
+
diff --git a/cmake/ecbuild_get_resources.cmake b/cmake/ecbuild_get_resources.cmake
new file mode 100644
index 0000000..d6c20e4
--- /dev/null
+++ b/cmake/ecbuild_get_resources.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for adding a test
+##############################################################################
+
+macro( ecbuild_get_resources )
+
+    set( options                )
+    set( single_value_args TO_DIR  )
+    set( multi_value_args  LIST )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_get_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_LIST )
+        ecbuild_critical( "Missing parameter LIST of resources in macro ecbuild_get_resources()" )
+    endif()
+
+    if( NOT _PAR_TO_DIR )
+		set( _PAR_TO_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+	endif()
+
+    list( LENGTH _PAR_LIST _rsize )
+    math( EXPR _max "${_rsize}-1" )
+    foreach( i RANGE 0 ${_max} 2 )
+
+        math( EXPR in "${i}+1" )
+
+        list( GET _PAR_LIST ${i}  r  )
+        list( GET _PAR_LIST ${in} rh )
+
+#        ecbuild_debug_var( r  )
+#        ecbuild_debug_var( rh )
+
+        get_filename_component( rf ${r} NAME )
+
+        file( DOWNLOAD ${r} ${_PAR_TO_DIR}/${rf} EXPECTED_HASH SHA1=${rh} )
+
+    endforeach()
+
+
+endmacro()
diff --git a/cmake/ecbuild_get_test_data.cmake b/cmake/ecbuild_get_test_data.cmake
new file mode 100644
index 0000000..4945b90
--- /dev/null
+++ b/cmake/ecbuild_get_test_data.cmake
@@ -0,0 +1,427 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# function for downloading test data
+
+function( _download_test_data _p_NAME _p_DIRNAME )
+
+  # TODO: make that 'at ecmwf'
+  #if(1)
+  #unset(ENV{no_proxy})
+  #unset(ENV{NO_PROXY})
+  #set(ENV{http_proxy} "http://proxy.ecmwf.int:3333")
+  #endif()
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  find_program( CURL_PROGRAM curl )
+  mark_as_advanced(CURL_PROGRAM)
+
+  if( CURL_PROGRAM )
+
+    add_custom_command( OUTPUT ${_p_NAME}
+      COMMENT "(curl) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+      COMMAND ${CURL_PROGRAM} --silent --show-error --fail --output ${_p_NAME}
+              --retry ${ECBUILD_DOWNLOAD_RETRIES}
+              --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+              http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+  else()
+
+    find_program( WGET_PROGRAM wget )
+
+    if( WGET_PROGRAM )
+
+      # wget takes the total number of tries, curl the number or retries
+      math( EXPR ECBUILD_DOWNLOAD_RETRIES "${ECBUILD_DOWNLOAD_RETRIES} + 1" )
+
+      add_custom_command( OUTPUT ${_p_NAME}
+        COMMENT "(wget) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+        COMMAND ${WGET_PROGRAM} -nv -O ${_p_NAME}
+                -t ${ECBUILD_DOWNLOAD_RETRIES} -T ${ECBUILD_DOWNLOAD_TIMEOUT}
+                http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+    else()
+
+      if( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+        ecbuild_warn( "Couldn't find curl neither wget -- cannot download test data from server.\nPlease obtain the test data by other means and pleace it in the build directory." )
+        set( WARNING_CANNOT_DOWNLOAD_TEST_DATA 1 CACHE INTERNAL "Couldn't find curl neither wget -- cannot download test data from server" )
+        mark_as_advanced( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+      endif()
+
+    endif()
+
+  endif()
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_data
+# =====================
+#
+# Download a test data set at build time. ::
+#
+#   ecbuild_get_test_data( NAME <name>
+#                          [ TARGET <target> ]
+#                          [ DIRNAME <dir> ]
+#                          [ MD5 <hash> ]
+#                          [ EXTRACT ]
+#                          [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAME : required
+#   name of the test data file
+#
+# TARGET : optional, defaults to test_data_<name>
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# MD5 : optional, ignored if NOCHECK is given
+#   md5 checksum of the data set to verify. If not given and NOCHECK is *not*
+#   set, download the md5 checksum and verify
+#
+# EXTRACT : optional
+#   extract the downloaded file (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>/<NAME>``
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, the downloaded file is verified against an md5 checksum, either
+# given as the ``MD5`` argument or downloaded from the server otherwise. Use
+# the argument ``NOCHECK`` to disable this check.
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+# Examples
+# --------
+#
+# Do not verify the checksum: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib NOCHECK )
+#
+# Checksum agains remote md5 file: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib MD5 f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_data )
+
+    set( options NOCHECK EXTRACT )
+    set( single_value_args TARGET URL NAME DIRNAME MD5 SHA1)
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    file( RELATIVE_PATH currdir ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} )
+
+    ### check parameters
+
+    if( NOT _p_NAME )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAME")
+    endif()
+
+    if( NOT _p_TARGET )
+      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "test_data_${_p_NAME}")
+#      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "${_p_NAME}")
+#      set( _p_TARGET ${_p_NAME} )
+    endif()
+
+    if( NOT _p_DIRNAME )
+      set( _p_DIRNAME ${PROJECT_NAME}/${currdir} )
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_URL )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    # download the data
+
+    _download_test_data( ${_p_NAME} ${_p_DIRNAME} )
+
+    # perform the checksum if requested
+
+    set( _deps ${_p_NAME} )
+
+    if( NOT _p_NOCHECK )
+
+        if( NOT _p_MD5 AND NOT _p_SHA1) # use remote md5
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            _download_test_data( ${_p_NAME}.md5 ${_p_DIRNAME} )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 ${_p_NAME}.md5 )
+
+            list( APPEND _deps  ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+        if( _p_MD5 )
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            configure_file( "${ECBUILD_MACROS_DIR}/md5.in" ${_p_NAME}.md5 @ONLY )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 )
+
+            list( APPEND _deps ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+#        if( _p_SHA1 )
+
+#            find_program( SHASUM NAMES sha1sum shasum )
+#            if( SHASUM )
+#                add_custom_command( OUTPUT ${_p_NAME}.localsha1
+#                                    COMMAND ${SHASUM} ${_p_NAME} > ${_p_NAME}.localsha1 )
+
+#                add_custom_command( OUTPUT ${_p_NAME}.ok
+#                                    COMMAND diff ${_p_NAME}.sha1 ${_p_NAME}.localsha1 && touch ${_p_NAME}.ok )
+
+#                configure_file( "${ECBUILD_MACROS_DIR}/sha1.in" ${_p_NAME}.sha1 @ONLY )
+
+#                list( APPEND _deps ${_p_NAME}.localsha1 ${_p_NAME}.ok )
+#            endif()
+
+#        endif()
+
+    endif()
+
+    add_custom_target( ${_p_TARGET} DEPENDS ${_deps} )
+
+    if( _p_EXTRACT )
+      ecbuild_debug("ecbuild_get_test_data: extracting ${_p_NAME} (post-build for target ${_p_TARGET}")
+      add_custom_command( TARGET ${_p_TARGET} POST_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E tar xv ${_p_NAME} )
+    endif()
+
+endfunction(ecbuild_get_test_data)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_multidata
+# ==========================
+#
+# Download multiple test data sets at build time. ::
+#
+#   ecbuild_get_test_multidata( NAMES <name1> [ <name2> ... ]
+#                               TARGET <target>
+#                               [ DIRNAME <dir> ]
+#                               [ LABELS <label1> [<label2> ...] ]
+#                               [ EXTRACT ]
+#                               [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAMES : required
+#   list of names of the test data files
+#
+# TARGET : optional
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   Lower case project name and ``download_data`` are always added as labels.
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# EXTRACT : optional
+#   extract downloaded files (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>``
+# for each name given in the list of ``NAMES``. Each name may contain a
+# relative path, which is appended to ``DIRNAME`` and may be followed by an
+# md5 checksum, separated with a ``:`` (the name must not contain spaces).
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, each downloaded file is verified against an md5 checksum, either
+# given as part of the name as described above or a remote checksum downloaded
+# from the server. Use the argument ``NOCHECK`` to disable this check.
+#
+# Examples
+# --------
+#
+# Do not verify checksums: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir NOCHECK )
+#
+# Checksums agains remote md5 file: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data DIRNAME test/data/dir
+#                               NAMES msl.grib:f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_multidata )
+
+    set( options EXTRACT NOCHECK )
+    set( single_value_args TARGET DIRNAME )
+    set( multi_value_args  NAMES LABELS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    ### check parameters
+
+    if( NOT _p_NAMES )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAMES")
+    endif()
+
+    if( NOT _p_TARGET )
+      ecbuild_critical("ecbuild_get_test_data() expects a TARGET")
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    if( _p_EXTRACT )
+        set( _extract EXTRACT )
+    endif()
+
+    if( _p_NOCHECK )
+        set( _nocheck NOCHECK )
+    endif()
+
+    ### prepare file
+
+    set( _script ${CMAKE_CURRENT_BINARY_DIR}/get_data_${_p_TARGET}.cmake )
+
+    file( WRITE ${_script} "
+function(EXEC_CHECK)
+     execute_process(COMMAND \${ARGV} RESULT_VARIABLE CMD_RESULT)
+     if(CMD_RESULT)
+           message(FATAL_ERROR \"Error running ${CMD}\")
+     endif()
+endfunction()\n\n" )
+
+    foreach( _d ${_p_NAMES} )
+
+        string( REGEX MATCH "[^:]+" _f "${_d}" )
+
+        get_filename_component( _file ${_f} NAME )
+        get_filename_component( _dir  ${_f} PATH )
+
+        list( APPEND _path_comps ${_p_DIRNAME} ${_dir} )
+        join( _path_comps "/" _dirname )
+        if( _dirname )
+            set( _dirname DIRNAME ${_dirname} )
+        endif()
+        unset( _path_comps )
+
+        string( REPLACE "." "_" _name "${_file}" )
+        string( REGEX MATCH ":.*"  _md5  "${_d}" )
+        string( REPLACE ":" "" _md5 "${_md5}" )
+
+        if( _md5 )
+            set( _md5 MD5 ${_md5} )
+        endif()
+
+        #ecbuild_debug_var(_f)
+        #ecbuild_debug_var(_file)
+        #ecbuild_debug_var(_dirname)
+        #ecbuild_debug_var(_name)
+        #ecbuild_debug_var(_md5)
+
+        ecbuild_get_test_data(
+            TARGET __get_data_${_p_TARGET}_${_name}
+            NAME ${_file} ${_dirname} ${_md5} ${_extract} ${_nocheck} )
+
+        # The option /fast disables dependency checking on a target, see
+        # https://cmake.org/Wiki/CMake_FAQ#Is_there_a_way_to_skip_checking_of_dependent_libraries_when_compiling.3F
+        if( WIN32 )
+          set( _fast "\fast" )
+        else()
+          set( _fast "/fast" )
+        endif()
+        file( APPEND ${_script}
+              "exec_check( \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target __get_data_${_p_TARGET}_${_name}${_fast} )\n" )
+
+    endforeach()
+
+    if( ENABLE_TESTS )
+      add_test(  NAME ${_p_TARGET} COMMAND ${CMAKE_COMMAND} -P ${_script} )
+      set( _p_LABELS ${PROJECT_NAME_LOWCASE} download_data ${_p_LABELS} )
+      list( REMOVE_DUPLICATES _p_LABELS )
+      set_property( TEST ${_p_TARGET} APPEND PROPERTY LABELS "${_p_LABELS}" )
+    endif()
+
+endfunction(ecbuild_get_test_multidata)
diff --git a/cmake/ecbuild_git.cmake b/cmake/ecbuild_git.cmake
new file mode 100644
index 0000000..18aab19
--- /dev/null
+++ b/cmake/ecbuild_git.cmake
@@ -0,0 +1,300 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( ECBUILD_GIT  ON  CACHE BOOL "Turn on/off ecbuild_git() function" )
+
+mark_as_advanced(ECBUILD_GIT)
+
+if( ECBUILD_GIT )
+
+  find_package(Git)
+
+  set( ECMWF_USER $ENV{USER} CACHE STRING "ECMWF git user" )
+  set( ECMWF_GIT  SSH        CACHE STRING "ECMWF git protocol" )
+
+  set( ECMWF_GIT_SSH   "ssh://git@software.ecmwf.int:7999"                  CACHE INTERNAL "ECMWF ssh address" )
+  set( ECMWF_GIT_HTTPS "https://${ECMWF_USER}@software.ecmwf.int/stash/scm" CACHE INTERNAL "ECMWF https address" )
+
+  if( ECMWF_GIT MATCHES "[Ss][Ss][Hh]" )
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_SSH} CACHE INTERNAL "" )
+  else()
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_HTTPS} CACHE INTERNAL "" )
+  endif()
+
+endif()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_git
+# ===========
+#
+# Manages an external Git repository. ::
+#
+#   ecbuild_git( PROJECT <name>
+#                DIR <directory>
+#                URL <giturl>
+#                [ BRANCH <gitbranch> | TAG <gittag> ]
+#                [ UPDATE | NOREMOTE ] )
+#                [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# URL : required
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecbuild_git )
+
+  set( options UPDATE NOREMOTE MANUAL )
+  set( single_value_args PROJECT DIR URL TAG BRANCH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _PAR_BRANCH AND DEFINED _PAR_TAG )
+    ecbuild_critical( "Cannot defined both BRANCH and TAG in macro ecbuild_git" )
+  endif()
+
+  if( _PAR_UPDATE AND _PAR_NOREMOTE )
+    ecbuild_critical( "Cannot pass both NOREMOTE and UPDATE in macro ecbuild_git" )
+  endif()
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_git(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( ECBUILD_GIT )
+
+    set( _needs_switch 0 )
+
+    get_filename_component( ABS_PAR_DIR "${_PAR_DIR}" ABSOLUTE )
+    get_filename_component( PARENT_DIR  "${_PAR_DIR}/.." ABSOLUTE )
+
+    ### clone if no directory
+
+    if( NOT EXISTS "${_PAR_DIR}" )
+
+      ecbuild_info( "Cloning ${_PAR_PROJECT} from ${_PAR_URL} into ${_PAR_DIR}...")
+      execute_process(
+        COMMAND ${GIT_EXECUTABLE} "clone" ${_PAR_URL} ${clone_args} ${_PAR_DIR} "-q"
+        RESULT_VARIABLE nok ERROR_VARIABLE error
+        WORKING_DIRECTORY "${PARENT_DIR}")
+      if(nok)
+        ecbuild_critical("${_PAR_DIR} git clone failed:\n  ${GIT_EXECUTABLE} clone ${_PAR_URL} ${clone_args} ${_PAR_DIR} -q\n  ${error}\n")
+      endif()
+      ecbuild_info( "${_PAR_DIR} retrieved.")
+      set( _needs_switch 1 )
+
+    endif()
+
+    ### check current tag and sha1
+
+    if( IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
+                       OUTPUT_VARIABLE _sha1 RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if(nok)
+        ecbuild_info("git rev-parse HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+                       OUTPUT_VARIABLE _current_branch RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if( nok OR _current_branch STREQUAL "" )
+        ecbuild_info("git rev-parse --abbrev-ref HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} describe --exact-match --abbrev=0
+                       OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE  ERROR_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+
+      if( error MATCHES "no tag exactly matches" OR error MATCHES "No names found" )
+        unset( _current_tag )
+      else()
+        if( nok )
+          ecbuild_info("git describe --exact-match --abbrev=0 on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+      if( NOT _current_tag ) # try nother method
+        execute_process( COMMAND ${GIT_EXECUTABLE} name-rev --tags --name-only ${_sha1}
+                         OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                         OUTPUT_STRIP_TRAILING_WHITESPACE
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+        if( nok OR _current_tag STREQUAL "" )
+          ecbuild_info("git name-rev --tags --name-only on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_BRANCH AND NOT "${_current_branch}" STREQUAL "${_PAR_BRANCH}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_TAG AND NOT "${_current_tag}" STREQUAL "${_PAR_TAG}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( DEFINED _PAR_BRANCH AND _PAR_UPDATE AND NOT _PAR_NOREMOTE )
+
+      add_custom_target( git_update_${_PAR_PROJECT}
+                         COMMAND "${GIT_EXECUTABLE}" pull -q
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}"
+                         COMMENT "git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR}" )
+
+      set( git_update_targets "git_update_${_PAR_PROJECT};${git_update_targets}" )
+
+    endif()
+
+    ### updates
+
+    if( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      if( DEFINED _PAR_BRANCH )
+        set ( _gitref ${_PAR_BRANCH} )
+        ecbuild_info("Updating ${_PAR_PROJECT} to head of BRANCH ${_PAR_BRANCH}...")
+      else()
+        ecbuild_info("Updating ${_PAR_PROJECT} to TAG ${_PAR_TAG}...")
+        set ( _gitref ${_PAR_TAG} )
+      endif()
+
+      # fetching latest tags and branches
+
+      if( NOT _PAR_NOREMOTE )
+
+        ecbuild_info("git fetch --all @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+        ecbuild_info("git fetch --all --tags @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all --tags -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all --tags in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      else()
+        ecbuild_info("${_PAR_DIR} marked NOREMOTE : Skipping git fetch")
+      endif()
+
+      # checking out gitref
+
+      ecbuild_info("git checkout ${_gitref} @ ${ABS_PAR_DIR}")
+      execute_process( COMMAND "${GIT_EXECUTABLE}" checkout -q "${_gitref}"
+                       RESULT_VARIABLE nok ERROR_VARIABLE error
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}")
+      if(nok)
+        ecbuild_critical("git checkout ${_gitref} on ${_PAR_DIR} failed:\n  ${GIT_EXECUTABLE} checkout -q ${_gitref}\n  ${error}")
+      endif()
+
+      if( DEFINED _PAR_BRANCH AND _PAR_UPDATE ) #############################################################################
+
+        # Use git pull --ff-only, we WANT this to fail on upstream rebase and
+        # we DON'T want merge commits here!
+        execute_process( COMMAND "${GIT_EXECUTABLE}" pull -q --ff-only
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_critical("git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      endif() ####################################################################################
+
+    endif( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+  endif( ECBUILD_GIT )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_stash
+# =============
+#
+# Manages an external Git repository on ECMWF Stash. ::
+#
+#   ecbuild_stash( PROJECT <name>
+#                  DIR <directory>
+#                  STASH <repository>
+#                  [ BRANCH <gitbranch> | TAG <gittag> ]
+#                  [ UPDATE | NOREMOTE ] )
+#                  [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# STASH : required
+#   Stash repository in the form <project>/<repository>
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecmwf_stash )
+
+  set( options )
+  set( single_value_args STASH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  ecbuild_git( URL "${ECMWF_GIT_ADDRESS}/${_PAR_STASH}.git" ${_PAR_UNPARSED_ARGUMENTS} )
+
+endmacro()
diff --git a/cmake/ecbuild_install_project.cmake b/cmake/ecbuild_install_project.cmake
new file mode 100644
index 0000000..113e642
--- /dev/null
+++ b/cmake/ecbuild_install_project.cmake
@@ -0,0 +1,437 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_install_project
+# =======================
+#
+# Set up packaging and export configuration. ::
+#
+#   ecbuild_install_project( NAME <name> [ DESCRIPTION <description> ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   project name used for packaging
+#
+# DESCRIPTION : optional
+#   project description used for packaging
+#
+# Usage
+# -----
+#
+# ``ecbuild_install_project`` should be called at the very end of any ecBuild
+# project (only followed by ``ecbuild_print_summary``), sets up packaging of
+# the project with cpack and exports the configuration and targets for other
+# projects to use.
+#
+# Unless ECBUILD_SKIP_<PNAME>_EXPORT is set, the following files are generated:
+#
+# :<project>-config.cmake:         default project configuration
+# :<project>-config-version.cmake: project version number
+# :<project>-import.cmake:         extra project configuration (optional)
+# :<project>-config.cmake.tpls:    3rd party project configurations
+# :<project>-targets.cmake:        exported targets
+#
+# For ``<project>-import.cmake`` to be exported to build and install tree,
+# ``<project>-import.cmake`` or ``<project>-import.cmake.in`` must exist in
+# the source tree. ``<project>-config.cmake.in`` and
+# ``<project>-config-version.cmake.in`` can be provided in the source tree to
+# override the default templates used to generate ``<project>-config.cmake``
+# and ``<project>-config-version.cmake``.
+#
+# In DEVELOPER_MODE, the build tree location is also added to the CMake user
+# package registry for top level projects.
+#
+# If the project is added as a subdirectory, the following CMake variables
+# are set in the parent scope:
+#
+# :<PROJECT>_FOUND:            set to ``TRUE``
+# :<project>_FOUND:            set to ``TRUE``
+# :<PROJECT>_VERSION:          version string
+# :<project>_VERSION:          version string
+# :<PROJECT>_INCLUDE_DIRS:     list of include directories
+# :<PROJECT>_LIBRARIES:        list of libraries
+# :<PROJECT>_DEFINITIONS:      list of compiler definitions
+# :<PROJECT>_TPLS:             list of 3rd party dependencies
+# :<PROJECT>_TPL_LIBRARIES:    libraries of 3rd party dependencies
+# :<PROJECT>_TPL_DEFINITIONS:  compiler definitions of 3rd party dependencies
+# :<PROJECT>_TPL_INCLUDE_DIRS: include directories of 3rd party dependencies
+# :<PROJECT>_FEATURES:         list of enabled features
+# :<PROJECT>_HAVE_<FEATURE>:   set to 1 for each enabled features
+#
+##############################################################################
+
+function( ecbuild_set_if_not_defined VAR VALUE )
+
+    if(NOT DEFINED ${VAR})
+        set( ${VAR} "${VALUE}" PARENT_SCOPE )
+        # ecbuild_info("Variable not defined, setting ${VAR} => ${VALUE}")
+    # else()
+        # ecbuild_info("${VAR} == ${${VAR}}")
+    endif()
+
+endfunction()
+
+macro( ecbuild_install_project )
+
+    set( options )
+    set( single_value_args NAME DESCRIPTION )
+    set( multi_value_args  COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_install_project(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_NAME  )
+      ecbuild_critical("The call to ecbuild_install_project() doesn't specify the NAME.")
+    endif()
+
+    ### EXTRA TARGETS #####################################################
+
+    # added here to avoid adding another macro call at the end of each project,
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        ecbuild_define_libs_and_execs_targets()
+        ecbuild_define_links_target()
+
+    endif()
+
+    ### PACKAGING ########################################################
+
+    set( PNAME ${PROJECT_NAME_CAPS} )
+    set( LNAME ${PROJECT_NAME_LOWCASE} )
+
+    # components
+
+    #    if( DEFINED _PAR_COMPONENTS )
+    #        set(CPACK_COMPONENTS_ALL   "${_PAR_COMPONENTS}")
+    #    else()
+    #        set(CPACK_COMPONENTS_ALL   "${PROJECT_NAME}")
+    #    endif()
+
+    # name, version, etc ...
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_NAME      "${_PAR_NAME}")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VERSION   "${${PNAME}_VERSION_STR}")
+    # Convert "/" to "-" for the case where the version string contains a "/"
+    string( REPLACE "/" "-" CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION} )
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_FILE_NAME   "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
+
+    #    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ECMWF") # required for DEB
+    #    set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON")
+    #    set(CPACK_RPM_COMPONENT_INSTALL "ON")
+
+    ecbuild_set_if_not_defined(CPACK_SOURCE_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VENDOR "ECMWF")
+
+    # short description
+
+    if( _PAR_DESCRIPTION )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_DESCRIPTION}" )
+    else()
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_NAME} misses a description" )
+    endif()
+
+    # long description
+
+    if( EXISTS ${PROJECT_SOURCE_DIR}/INSTALL )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/INSTALL")
+    endif()
+    if( EXISTS ${PROJECT_SOURCE_DIR}/LICENSE )
+        ecbuild_set_if_not_defined(CPACK_RESOURCE_FILE_LICENSE    "${PROJECT_SOURCE_DIR}/LICENSE")
+    endif()
+
+    # set(CPACK_PACKAGE_EXECUTABLES ${ECBUILD_ALL_EXES})
+
+    list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES
+          "${PROJECT_SOURCE_DIR}" "."
+          "${ECBUILD_MACROS_DIR}" "cmake/" )
+
+    # what to pack and not
+
+    set(CPACK_SOURCE_IGNORE_FILES
+        /build/
+        /\\\\.git/
+        /\\\\.svn/
+        CMakeLists.txt.user
+        \\\\.swp$
+        p4config
+    )
+
+    # skip the files that were declared as DONT_PACK
+
+    list( APPEND CPACK_SOURCE_IGNORE_FILES ${ECBUILD_DONT_PACK_FILES} )
+
+    # Find the ecbuild toolchain files and include in the source package if found
+    find_path( ECBUILD_TOOLCHAIN_DIR ecmwf-XC30-GNU.cmake
+               PATHS ${ECBUILD_MACROS_DIR}/../toolchains
+                     ${ECBUILD_MACROS_DIR}/../share/ecbuild/toolchains )
+
+    mark_as_advanced( ECBUILD_TOOLCHAIN_DIR )
+
+    if( ECBUILD_TOOLCHAIN_DIR )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_TOOLCHAIN_DIR}" "share/ecbuild/toolchains/" )
+    endif()
+
+    # Find the ecbuild bin directory and include in the source package if found
+    find_program( ECBUILD_SCRIPT ecbuild
+                  PATHS ${ECBUILD_MACROS_DIR}/../bin
+                        ${ECBUILD_MACROS_DIR}/../../../bin )
+
+    mark_as_advanced( ECBUILD_SCRIPT )
+
+    if( ECBUILD_SCRIPT )
+      get_filename_component( ECBUILD_BIN_DIR ${ECBUILD_SCRIPT} PATH )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_BIN_DIR}" "bin/" )
+    endif()
+
+    # cpack config file
+
+    # set(CPACK_INSTALL_CMAKE_PROJECTS "${${PROJECT_NAME}_BINARY_DIR}" "${PROJECT_NAME}" "${CPACK_COMPONENTS_ALL}" "*" )
+
+    include( CPack )
+
+    ### EXPORTS ########################################################
+
+    ecbuild_enabled_features( ${PROJECT_NAME_CAPS}_FEATURES )
+    foreach( _f ${${PNAME}_FEATURES} )
+        set( ${PNAME}_HAVE_${_f} 1 )
+    endforeach()
+
+    ecbuild_info( "${PROJECT_NAME_CAPS}_TPLS: ${${PROJECT_NAME_CAPS}_TPLS}" )
+
+    foreach( _tpl ${${PNAME}_TPLS} )
+        string( TOUPPER ${_tpl} _TPL )
+
+        if( ${_tpl}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIRS} )
+        elseif( ${_tpl}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIR} )
+        elseif( ${_TPL}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIRS} )
+        elseif( ${_TPL}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIR} )
+        endif()
+
+        if( ${_tpl}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARIES} )
+        elseif( ${_tpl}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARY} )
+        elseif( ${_TPL}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARIES} )
+        elseif( ${_TPL}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARY} )
+        endif()
+
+        if( ${_tpl}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_tpl}_DEFINITIONS} )
+        elseif( ${_TPL}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_TPL}_DEFINITIONS} )
+        endif()
+    endforeach()
+
+    # Deduplicate TPL includes, libs and definitions
+    # The same TPL may indirectly be pulled in multiple times!
+    if( ${PNAME}_TPL_INCLUDE_DIRS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_INCLUDE_DIRS )
+    endif()
+    if( ${PNAME}_TPL_LIBRARIES )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_LIBRARIES )
+    endif()
+    if( ${PNAME}_TPL_DEFINITIONS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_DEFINITIONS )
+    endif()
+
+    # Generate the project .cmake config files
+    # All variables here must be (sub)project specific in order to work within bundles
+    if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+        set( _template_config "${ECBUILD_MACROS_DIR}/project-config.cmake.in" )
+        if( EXISTS ${LNAME}-config.cmake.in )
+            set( _template_config "${LNAME}-config.cmake.in" )
+        endif()
+
+        set( _template_config_version "${ECBUILD_MACROS_DIR}/project-config-version.cmake.in" )
+        if( EXISTS ${LNAME}-config-version.cmake.in )
+            set( _template_config_version "${LNAME}-config-version.cmake.in" )
+        endif()
+
+        # project-config-version.cmake -- format ([0-9]+).([0-9]+).([0-9]+)
+
+        set( PACKAGE_VERSION        "${${PNAME}_VERSION}" )
+        set( PACKAGE_GIT_SHA1       "${${PNAME}_GIT_SHA1}" )
+        set( PACKAGE_GIT_SHA1_SHORT "${${PNAME}_GIT_SHA1_SHORT}" )
+
+        configure_file( "${_template_config_version}" "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" @ONLY )
+
+        install( FILES "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # prepare imutable variables (don't depend on install path)
+
+        if( ${PNAME}_FEATURES )
+          set( CONF_FEATURES ${${PNAME}_FEATURES} )
+        endif()
+
+        set( CONF_LIBRARIES ${${PROJECT_NAME}_ALL_LIBS} )
+        if( ${PNAME}_LIBRARIES )
+            set( CONF_LIBRARIES ${${PNAME}_LIBRARIES} )
+        endif()
+
+        set( CONF_DEFINITIONS "" )
+        if( ${PNAME}_DEFINITIONS )
+           set( CONF_DEFINITIONS ${${PNAME}_DEFINITIONS} )
+        endif()
+
+        set( CONF_TPL_LIBRARIES   "" )
+        if( ${PNAME}_TPL_LIBRARIES )
+           set( CONF_TPL_LIBRARIES ${${PNAME}_TPL_LIBRARIES} )
+        endif()
+
+        set( CONF_TPLS ${${PNAME}_TPLS} )
+
+        set( CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" )
+        if( ${PNAME}_INCLUDE_DIRS )
+            set( CONF_INCLUDE_DIRS ${${PNAME}_INCLUDE_DIRS} )
+        endif()
+
+        set( CONF_TPL_INCLUDE_DIRS "" )
+        if( ${PNAME}_TPL_INCLUDE_DIRS )
+            set( CONF_TPL_INCLUDE_DIRS ${${PNAME}_TPL_INCLUDE_DIRS} )
+        endif()
+
+        # Generate <project>-import.cmake (if it exists)
+
+        set( CONF_IMPORT_FILE "${LNAME}-import.cmake" )
+
+        # If <project>-import.cmake.in exist in source tree, configure it to
+        # the build tree and install the configured version
+        if( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in - configuring to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" @ONLY )
+          install( FILES "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        # Otherwise, if <project>-import.cmake exist in source tree, copy it to
+        # the build tree and install it
+        elseif( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE} - copying to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" COPYONLY )
+          install( FILES "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        else()
+          ecbuild_debug( "No ${CONF_IMPORT_FILE} found in ${PROJECT_SOURCE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use from the build tree
+
+        set( _lname_config "${PROJECT_BINARY_DIR}/${LNAME}-config.cmake")
+
+        # Include directories (may) reference source and build tree and the
+        # config file is marked as coming from a build tree
+        set( _is_build_dir_export ON )
+        configure_file( "${_template_config}" "${_lname_config}" @ONLY )
+
+        # Generate <project>-config.cmake.tpls (if there are any TPLs)
+
+        file( REMOVE ${_lname_config}.tpls.in )
+
+        foreach( _tpl ${${PNAME}_TPLS} )
+
+            string( TOUPPER ${_tpl} TPL )
+
+            if( ${TPL}_IMPORT_FILE ) # ecBuild packages should trigger this if they export themselves
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_IMPORT_FILE}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_IMPORT_FILE )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            elseif( ${TPL}_CONFIG ) # cmake built packages (e.g. CGAL) may have exported their targets
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_CONFIG}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_CONFIG )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    set( ${TPL}_CONFIG \"${__import_file}\" )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            endif()
+
+        endforeach()
+
+        if( EXISTS "${_lname_config}.tpls.in" )
+            configure_file( "${_lname_config}.tpls.in" "${_lname_config}.tpls" @ONLY )
+            install( FILES "${_lname_config}.tpls" DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use in the install tree
+
+        # Compute path to the include dir relative to the project's CMake dir
+        # where <project>-config.cmake is installed to
+        file( RELATIVE_PATH REL_INCLUDE_DIR "${${PNAME}_FULL_INSTALL_CMAKE_DIR}" "${${PNAME}_FULL_INSTALL_INCLUDE_DIR}" )
+        set( CONF_INCLUDE_DIRS "\${${PNAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" )
+
+        set( _is_build_dir_export OFF )
+        configure_file( "${_template_config}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" @ONLY )
+        install( FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # install the export
+
+        if( ${PROJECT_NAME}_ALL_EXES OR ${PROJECT_NAME}_ALL_LIBS )
+            install( EXPORT ${PROJECT_NAME}-targets
+                     DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+    endif()  # if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+    # exports the package for use from the build-tree but only in DEVELOPER_MODE
+    # inserts <package> into the CMake user package registry
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        if( DEVELOPER_MODE )
+            export( PACKAGE ${PROJECT_NAME} )
+        endif()
+
+    else()
+
+    # export variables for upper projects
+
+        set( ${PNAME}_FOUND             TRUE                          PARENT_SCOPE )
+        set( ${PROJECT_NAME}_FOUND      TRUE                          PARENT_SCOPE )
+        set( ${PNAME}_VERSION           ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1          ${${PNAME}_GIT_SHA1}          PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1_SHORT    ${${PNAME}_GIT_SHA1_SHORT}    PARENT_SCOPE )
+        set( ${PROJECT_NAME}_VERSION    ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_INCLUDE_DIRS      ${${PNAME}_INCLUDE_DIRS}      PARENT_SCOPE )
+        set( ${PNAME}_LIBRARIES         ${${PNAME}_LIBRARIES}         PARENT_SCOPE )
+        set( ${PNAME}_DEFINITIONS       ${${PNAME}_DEFINITIONS}       PARENT_SCOPE )
+        set( ${PNAME}_PACKAGES          ${${PNAME}_PACKAGES}          PARENT_SCOPE )
+        set( ${PNAME}_TPLS              ${${PNAME}_TPLS}              PARENT_SCOPE )
+        set( ${PNAME}_TPL_LIBRARIES     ${${PNAME}_TPL_LIBRARIES}     PARENT_SCOPE )
+        set( ${PNAME}_TPL_DEFINITIONS   ${${PNAME}_TPL_DEFINITIONS}   PARENT_SCOPE )
+        set( ${PNAME}_TPL_INCLUDE_DIRS  ${${PNAME}_TPL_INCLUDE_DIRS}  PARENT_SCOPE )
+        set( ${PNAME}_FEATURES          ${${PNAME}_FEATURES}          PARENT_SCOPE )
+        foreach( _f ${${PNAME}_FEATURES} )
+            set( ${PNAME}_HAVE_${_f} ${${PNAME}_HAVE_${_f}} PARENT_SCOPE )
+        endforeach()
+
+    endif()
+
+endmacro( ecbuild_install_project )
diff --git a/cmake/ecbuild_list_add_pattern.cmake b/cmake/ecbuild_list_add_pattern.cmake
new file mode 100644
index 0000000..a0f9f34
--- /dev/null
+++ b/cmake/ecbuild_list_add_pattern.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_add_pattern
+# ========================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_add_pattern( LIST <input_list>
+#                             GLOB <pattern1> [ <pattern2> ... ]
+#                             [ SOURCE_DIR <source_dir> ]
+#                             [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be appended to
+#
+# GLOB : required
+#   Regex pattern of exclusion
+#
+# SOURCE_DIR : optional
+#   Directory from where to start search
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_add_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST SOURCE_DIR )
+  set( multi_value_args  GLOB )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_add_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_GLOB )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the GLOB.")
+  endif()
+
+  #####
+
+  set( input_list ${${_p_LIST}} )
+  unset( matched_files )
+
+  foreach( pattern ${_p_GLOB} )
+
+    if( IS_ABSOLUTE ${pattern} )
+      ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern}" )
+      file( GLOB_RECURSE matched_files ${pattern} )
+    else()
+
+      if(_p_SOURCE_DIR)
+        if( IS_ABSOLUTE ${_p_SOURCE_DIR} )
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files ${_p_SOURCE_DIR}/${pattern} )
+        else()
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_p_SOURCE_DIR}/${pattern} )
+        endif()
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern} ")
+        file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${pattern} )
+      endif()
+
+    endif()
+
+    if(matched_files)
+      ecbuild_debug( "ecbuild_list_add_pattern: Found ${matched_files}" )
+      list( APPEND input_list ${matched_files} )
+      list( REMOVE_DUPLICATES input_list )
+      set( ${_p_LIST} ${input_list} PARENT_SCOPE )
+    else()
+      if(NOT _p_QUIET)
+        ecbuild_warn( "ecbuild_list_add_pattern: no matches found for patterns ${pattern}" )
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern:no matches found for patterns ${pattern}" )
+      endif()
+    endif()
+
+  endforeach()
+
+
+endfunction(ecbuild_list_add_pattern)
diff --git a/cmake/ecbuild_list_exclude_pattern.cmake b/cmake/ecbuild_list_exclude_pattern.cmake
new file mode 100644
index 0000000..bfb6ee8
--- /dev/null
+++ b/cmake/ecbuild_list_exclude_pattern.cmake
@@ -0,0 +1,88 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_exclude_pattern
+# ============================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_exclude_pattern( LIST <input_list>
+#                                 REGEX <regex1> [ <regex2> ... ]
+#                                 [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be cleaned
+#
+# REGEX : required
+#   Regex pattern of exclusions
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_exclude_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST )
+  set( multi_value_args  REGEX )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_exclude_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_REGEX )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the REGEX.")
+  endif()
+
+  #####
+
+  set( result "" )
+  set( matches_found 0 )
+
+  # ecbuild_debug_var(_p_REGEX)
+
+  foreach( item ${${_p_LIST}} )
+
+    set( _keep 1 )
+
+    foreach( pattern ${_p_REGEX} )
+        if( ${item} MATCHES ${pattern} )
+            set( _keep 0 )
+            set( matches_found 1 )
+        endif()
+    endforeach()
+    if( _keep )
+        list( APPEND result ${item} )
+#    else()
+#      ecbuild_warn( "removing ${item}" )
+    endif()
+
+  endforeach()
+
+  if( matches_found )
+      set( ${_p_LIST} ${result} PARENT_SCOPE )
+  else()
+    if( NOT _p_QUIET )
+        ecbuild_warn( "ecbuild_list_exclude_pattern: no matches found for patterns ${_p_REGEX} in ${_p_LIST}" )
+    endif()
+  endif()
+
+endfunction(ecbuild_list_exclude_pattern)
diff --git a/cmake/ecbuild_list_extra_search_paths.cmake b/cmake/ecbuild_list_extra_search_paths.cmake
new file mode 100644
index 0000000..1ba31bc
--- /dev/null
+++ b/cmake/ecbuild_list_extra_search_paths.cmake
@@ -0,0 +1,81 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+#
+# macro for adding search paths for a package to a given CMake variable
+#
+# usage: ecbuild_list_extra_search_paths( netcdf4 VARIABLE )
+
+function( ecbuild_list_extra_search_paths pkg var )
+
+  ecbuild_deprecate( " ecbuild_list_extra_search_paths should no longer be"
+                     " used and is going to be removed in a future version of ecBuild." )
+
+	# ecbuild_debug_var( pkg )
+	# ecbuild_debug_var( var )
+
+	string( TOUPPER ${pkg} _PKG )
+
+	# PKG_PATH (upper case)
+
+	if( DEFINED ${_PKG}_PATH AND EXISTS ${${_PKG}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${_PKG}_PATH = ${${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} ${${_PKG}_PATH} )
+	endif()
+
+	# ENV PKG_PATH (upper case)
+
+	if( DEFINED ENV{${_PKG}_PATH} AND EXISTS $ENV{${_PKG}_PATH}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_PATH = $ENV{${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_PATH} )
+	endif()
+
+	# pkg_PATH (lower case)
+
+	if( DEFINED ${pkg}_PATH AND EXISTS ${${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${pkg}_PATH = ${${pkg}_PATH} to ${var}")
+		list( APPEND ${var} ${${pkg}_PATH} )
+	endif()
+
+	# ENV pkg_PATH (lower case)
+
+  if( DEFINED ${pkg}_PATH AND EXISTS $ENV{${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_PATH = $ENV{${pkg}_PATH} to ${var}")
+    list( APPEND ${var} $ENV{${pkg}_PATH} )
+	endif()
+
+	# ENV PKG_DIR (upper case)
+
+	if( DEFINED ENV{${_PKG}_DIR} AND EXISTS $ENV{${_PKG}_DIR}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_DIR = $ENV{${_PKG}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_DIR} )
+	endif()
+
+	# ENV pkg_DIR (lower case)
+
+	if( DEFINED ENV{${pkg}_DIR} AND EXISTS $ENV{${pkg}_DIR} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_DIR = $ENV{${pkg}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${pkg}_DIR} )
+	endif()
+
+	# sanitize the list
+
+	if( ${var} )
+		list( REMOVE_DUPLICATES ${var} )
+	endif()
+
+	# define it out of the function
+
+  ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): setting ${var} to ${${var}}")
+	set( ${var} ${${var}} PARENT_SCOPE )
+
+# ecbuild_debug_var( ${var} )
+
+endfunction()
+
diff --git a/cmake/ecbuild_list_macros.cmake b/cmake/ecbuild_list_macros.cmake
new file mode 100644
index 0000000..232e5e8
--- /dev/null
+++ b/cmake/ecbuild_list_macros.cmake
@@ -0,0 +1,58 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# function for concatenating list into a string
+#
+# examples:
+#
+#   set( _paths "foo" "bar" )
+#   join( _paths "/" _mypath )
+#
+#   message( "${_mpath}" ) #  produces "foo/bar"
+
+function( JOIN _listname _glue _output )
+
+	set( _ret "" )
+
+	foreach( _v ${${_listname}} )
+		if( _ret )
+			set(_ret "${_ret}${_glue}${_v}") # append
+		else()
+			set(_ret "${_v}") # init
+		endif()
+	endforeach()
+
+	set(${_output} "${_ret}" PARENT_SCOPE)
+
+endfunction()
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_insert( "mymap" "foo" "bar" )
+#
+
+function( MAP_INSERT _map _key _value )
+	set( "_${_map}_${_key}" "${_value}" PARENT_SCOPE )
+endfunction(MAP_INSERT)
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_get( "mymap" "foo" VAR )
+#
+
+function( MAP_GET _map _key _var )
+	set( ${_var} "${_${_map}_${_key}}" PARENT_SCOPE )
+endfunction(MAP_GET)
+
diff --git a/cmake/ecbuild_log.cmake b/cmake/ecbuild_log.cmake
new file mode 100644
index 0000000..57ebf0d
--- /dev/null
+++ b/cmake/ecbuild_log.cmake
@@ -0,0 +1,249 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# Logging
+# =======
+#
+# ecBuild provides functions for logging based on a log level set by the user,
+# similar to the Python logging module:
+#
+# :ecbuild_debug:     logs a ``STATUS`` message if log level <= ``DEBUG``
+# :ecbuild_info:      logs a ``STATUS`` message if log level <= ``INFO``
+# :ecbuild_warn:      logs a ``WARNING`` message if log level <= ``WARN``
+# :ecbuild_error:     logs a ``SEND_ERROR`` message if log level <= ``ERROR``
+# :ecbuild_critical:  logs a ``FATAL_ERROR`` message if log level <= ``CRITICAL``
+# :ecbuild_deprecate: logs a ``DEPRECATION`` message as a warning
+#                     enable CMAKE_ERROR_DEPRECATED to raise an error instead
+#                     disable CMAKE_WARN_DEPRECATED to hide deprecations
+#
+# Furthermore there are auxilliary functions for outputting CMake variables,
+# CMake lists and environment variables if the log level is ``DEBUG``:
+#
+# :ecbuild_debug_var:      logs given CMake variables if log level <= ``DEBUG``
+# :ecbuild_debug_list:     logs given CMake lists if log level <= ``DEBUG``
+# :ecbuild_debug_env_var:  logs given environment variables if log level <= ``DEBUG``
+# :ecbuild_debug_property: logs given global CMake property if log level <= ``DEBUG``
+#
+# To log a message to the ecBuild log file only at a given log level, use ::
+#
+#   ecbuild_log( <level> <msg> )
+#
+# Input variables
+# ---------------
+#
+# CMake variables controlling logging behaviour:
+#
+# ECBUILD_LOG_FILE : path
+#   set the log file, defaults to ``${CMAKE_BINARY_DIR}/ecbuild.log``
+#
+#   All ecBuild log functions write their messages to this log file with a time
+#   stamp. Messages emitted by CMake directly cannot be logged to file.
+#
+# ECBUILD_LOG_LEVEL : string, one of DEBUG, INFO, WARN, ERROR, CRITICAL, OFF
+#   desired log level, defaults to ``INFO``, ``OFF`` to disable logging
+#
+# ECBUILD_NO_COLOUR : bool
+#   if set, does not colour log output (by default log output is coloured)
+#
+# Usage
+# -----
+#
+# The functions ``ecbuild_debug`` and ``ecbuild_info`` can be used to output
+# messages which are not printed by default. Many ecBuild macros use this
+# facility to log debugging hints. When debugging a CMake run, users can use
+# ``-DECBUILD_LOG_LEVEL=DEBUG`` to get detailed diagnostics.
+#
+##############################################################################
+
+# Define colour escape sequences (not available on Windows)
+if(NOT (WIN32 OR ECBUILD_NO_COLOUR))
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
+
+set(ECBUILD_DEBUG    10)
+set(ECBUILD_INFO     20)
+set(ECBUILD_WARN     30)
+set(ECBUILD_ERROR    40)
+set(ECBUILD_CRITICAL 50)
+
+if( NOT DEFINED ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( NOT ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL 60)
+elseif( ECBUILD_LOG_LEVEL STREQUAL "DEBUG" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_DEBUG})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "INFO" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "WARN" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "ERROR" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_ERROR})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "CRITICAL" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_CRITICAL})
+else()
+  message(WARNING "Unknown log level ${ECBUILD_LOG_LEVEL} (valid are DEBUG, INFO, WARN, ERROR, CRITICAL) - using WARN")
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+endif()
+
+if( NOT DEFINED ECBUILD_LOG_FILE )
+  set( ECBUILD_LOG_FILE ${CMAKE_BINARY_DIR}/ecbuild.log )
+endif()
+if( NOT DEFINED CMAKE_ERROR_DEPRECATED AND NOT DEFINED CMAKE_WARN_DEPRECATED )
+  set( CMAKE_WARN_DEPRECATED ON )
+endif()
+
+##############################################################################
+
+function( ecbuild_log LEVEL )
+  string( REPLACE ";" " " MSG "${ARGN}" )
+  string( TIMESTAMP _time )
+  file( APPEND ${ECBUILD_LOG_FILE} "${_time} - ${PROJECT_NAME} - ${LEVEL} - ${MSG}\n" )
+endfunction( ecbuild_log )
+
+##############################################################################
+
+function( ecbuild_debug )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(DEBUG "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    message(STATUS "${Blue}DEBUG - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_debug )
+
+##############################################################################
+
+function( ecbuild_info )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(INFO "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 21)
+    message(STATUS "${MSG}")
+  endif()
+endfunction( ecbuild_info )
+
+##############################################################################
+
+function( ecbuild_warn )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(WARNING "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 31)
+    message(WARNING "${Yellow}WARN - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_warn )
+
+##############################################################################
+
+function( ecbuild_error )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 41)
+    message(SEND_ERROR "${BoldRed}ERROR - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_error )
+
+##############################################################################
+
+function( ecbuild_deprecate )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(DEPRECATION "${MSG}")
+  # DEPRECATION message type was only introduced in CMake 3.0, provide
+  # consistent behaviour for CMake < 3.0
+  if( CMAKE_VERSION VERSION_LESS 3.0 )
+    if( CMAKE_ERROR_DEPRECATED )
+      message(FATAL_ERROR "${BoldRed}DEPRECATION - ${MSG}${ColourReset}")
+    elseif( CMAKE_WARN_DEPRECATED )
+      message(WARNING "${Yellow}DEPRECATION - ${MSG}${ColourReset}")
+    endif()
+  else()
+    message(DEPRECATION "${BoldRed}${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_deprecate )
+
+##############################################################################
+
+function( ecbuild_critical )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(FATAL_ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 51)
+    message(FATAL_ERROR "${BoldMagenta}CRITICAL - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_critical )
+
+##############################################################################
+# function for debugging CMake variables
+
+function( ecbuild_debug_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ${VAR} : ${${VAR}}${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging CMake lists
+
+function( ecbuild_debug_list )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    foreach( _elem ${${VAR}} )
+      ecbuild_log( DEBUG "  ${_elem}" )
+    endforeach()
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message( STATUS "${Blue}DEBUG - ${VAR}" )
+      foreach( _elem ${${VAR}} )
+        message( STATUS "  ${_elem}" )
+      endforeach()
+      message(STATUS "${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging environment variables
+
+function( ecbuild_debug_env_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "ENV ${VAR} : $ENV{${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ENV ${VAR} [$ENV{${VAR}}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging a CMake global property
+
+function( ecbuild_debug_property )
+  foreach( VAR ${ARGV} )
+    get_property( __prop GLOBAL PROPERTY ${VAR} )
+    ecbuild_log(DEBUG "PROPERTY ${VAR} : ${__prop}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - PROPERTY ${VAR} [${__prop}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
diff --git a/cmake/ecbuild_pkgconfig.cmake b/cmake/ecbuild_pkgconfig.cmake
new file mode 100644
index 0000000..5ae273e
--- /dev/null
+++ b/cmake/ecbuild_pkgconfig.cmake
@@ -0,0 +1,421 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# Write transitive list of library dependencies of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_library_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    unset( _location )
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      set( _imported 0 )
+      get_property( _imported TARGET ${_lib} PROPERTY IMPORTED )
+
+      unset( _deps )
+
+      if( _imported )
+
+        get_property( _location TARGET ${_lib} PROPERTY LOCATION )
+        get_property( _configs   TARGET ${_lib} PROPERTY IMPORTED_CONFIGURATIONS )
+        list( REVERSE _configs )
+        list( GET _configs 0 _config)
+        get_property( _deps     TARGET ${_lib} PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES_${_config} )
+        get_property( _locimp   TARGET ${_lib} PROPERTY IMPORTED_LOCATION_${_config} )
+
+      else()
+
+        list( APPEND _location ${_lib} )
+        get_property( _deps TARGET ${_lib} PROPERTY LINK_LIBRARIES )
+
+      endif()
+
+      ecbuild_library_dependencies( _deps_location _deps )
+      list( APPEND _location ${_deps_location} )
+
+    else()
+
+      set( _location ${_lib} )
+
+    endif()
+
+    list( APPEND _dependencies ${_location} )
+
+  endforeach()
+
+  if( _dependencies )
+    list( REVERSE           _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    list( REVERSE           _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_library_dependencies)
+
+##############################################################################
+
+# Write list of include directories of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_include_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      get_property( _include_dirs TARGET ${_lib} PROPERTY INCLUDE_DIRECTORIES )
+      list( APPEND _dependencies ${_include_dirs} )
+
+    endif()
+
+  endforeach()
+
+  if( _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_include_dependencies)
+
+##############################################################################
+
+# Transform list of libraries in ${libraries}, ignoring any in ${ignore_libs},
+# and write pkg-config compatible string to CMake variable ${pkgconfig_libs}
+function( ecbuild_pkgconfig_libs pkgconfig_libs libraries ignore_libs )
+
+  set( _libraries ${${libraries}} )
+  set( _ignore_libs ${${ignore_libs}} )
+
+  foreach( _lib ${_libraries} )
+
+    unset( _name )
+    unset( _dir  )
+
+    if( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+      get_filename_component( _name ${_lib} NAME_WE )
+      list( APPEND _pkgconfig_libs "-framework ${_name}" )
+
+    else()
+
+      if( ${_lib} MATCHES "-l.+" )
+
+        string( REGEX REPLACE "^-l" "" _name ${_lib} )
+
+      else()
+
+
+        get_filename_component( _name ${_lib} NAME_WE )
+        get_filename_component( _dir  ${_lib} PATH )
+
+        if( TARGET ${_lib} )
+          get_target_property( _name ${_lib} OUTPUT_NAME )
+        endif()
+        if( NOT _name )
+          set( _name ${_lib} )
+        endif()
+
+        string( REGEX REPLACE "^lib" "" _name ${_name} )
+
+        if( "${_dir}" STREQUAL "/usr/lib" )
+          unset( _dir )
+        endif()
+        if( "${_dir}" STREQUAL "/usr/lib64" )
+          unset( _dir )
+        endif()
+
+      endif()
+
+      set( _set_append TRUE )
+        foreach( _ignore ${_ignore_libs} )
+          if( "${_name}" STREQUAL "${_ignore}" )
+            set( _set_append FALSE )
+          endif()
+      endforeach()
+
+      if( _set_append )
+
+        if( _dir )
+          list( APPEND _pkgconfig_libs "-L${_dir}" "-l${_name}" )
+        else()
+          list( APPEND _pkgconfig_libs "-l${_name}" )
+        endif()
+
+      endif()
+
+    endif( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+  endforeach( _lib ${_libraries} )
+
+  if( _pkgconfig_libs )
+    list( REMOVE_DUPLICATES _pkgconfig_libs )
+    string( REPLACE ";" " " _pkgconfig_libs "${_pkgconfig_libs}" )
+
+    set( ${pkgconfig_libs} ${_pkgconfig_libs} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_libs)
+
+##############################################################################
+
+# Transform list of include directories in ${INCLUDE_DIRS}, ignoring any in
+# ${ignore_includes} and ${${PNAME}_INCLUDE_DIRS}, and write pkg-config
+# compatible string to CMake variable ${INCLUDE}
+function( ecbuild_pkgconfig_include INCLUDE INCLUDE_DIRS ignore_includes )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  set( _ignore_includes ${${ignore_includes}} )
+
+  list( APPEND ignore_include_dirs
+    "/usr/include"
+     ${${PNAME}_INCLUDE_DIRS} # These are build-directory includes
+     ${CMAKE_SOURCE_DIR}  # Ignore private includes referencing source tree
+     ${CMAKE_BINARY_DIR}  # Ignore private includes referencing build tree
+     ${_ignore_includes}
+  )
+
+  foreach( _incdir ${${INCLUDE_DIRS}} )
+
+    foreach( _ignore ${ignore_include_dirs} )
+      if( "${_incdir}" MATCHES "${_ignore}" )
+        unset( _incdir )
+        break()
+      endif()
+    endforeach()
+
+    if( _incdir )
+      list( APPEND _include "-I${_incdir}")
+    endif()
+
+  endforeach()
+
+  if( _include )
+    list( REMOVE_DUPLICATES _include)
+    string( REPLACE ";" " " _include "${_include}")
+    set( ${INCLUDE} ${_include} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_include)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_pkgconfig
+# =================
+#
+# Create a pkg-config file for the current project. ::
+#
+#   ecbuild_pkgconfig( [ NAME <name> ]
+#                      [ FILENAME <filename> ]
+#                      [ TEMPLATE <template> ]
+#                      [ URL <url> ]
+#                      [ DESCRIPTION <description> ]
+#                      [ LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ IGNORE_INCLUDE_DIRS <dir1> [ <dir2> ... ] ]
+#                      [ IGNORE_LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ LANGUAGES <language1> [ <language2> ... ] ]
+#                      [ VARIABLES <variable1> [ <variable2> ... ] ]
+#                      [ NO_PRIVATE_INCLUDE_DIRS ] )
+#
+# Options
+# -------
+#
+# NAME : optional, defaults to lower case name of the project
+#   name to be given to the package
+#
+# FILENAME : optional, defaults to ``<NAME>.pc``
+#   file to be generated, including .pc extension
+#
+# TEMPLATE : optional, defaults to ``${ECBUILD_CMAKE_DIR}/pkg-config.pc.in``
+#   template configuration file to use
+#
+#   This is useful to create customised pkg-config files.
+#
+# URL : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_URL``
+#   url of the package
+#
+# DESCRIPTION : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_DESCRIPTION``
+#   description of the package
+#
+# LIBRARIES : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_LIBRARIES``
+#   list of package libraries
+#
+# IGNORE_INCLUDE_DIRS : optional
+#   list of include directories to ignore
+#
+# IGNORE_LIBRARIES : optional
+#   list of libraries to ignore i.e. those are removed from ``LIBRARIES``
+#
+# VARIABLES : optional
+#   list of additional CMake variables to export to the pkg-config file
+#
+# LANGUAGES : optional, defaults to all loaded languages
+#   list of languages to use. Accepted languages: C CXX Fortran
+#
+# NO_PRIVATE_INCLUDE_DIRS
+#   do not add include directories of dependencies to Cflags
+#
+#   This is mainly useful for Fortran only packages, when only modules need
+#   to be added to Cflags.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are used as default values for some of the
+# options listed above, where ``PNAME`` is the project name in upper case:
+#
+# :<PNAME>_LIBRARIES:    list of libraries to export
+# :<PNAME>_DESCRIPTION:  package description
+# :<PNAME>_URL:          package URL
+# :<PNAME>_VERSION:      package version
+# :<PNAME>_GIT_SHA1:     Git revision
+#
+# Usage
+# -----
+#
+# It is good practice to provide a separate pkg-config file for each library a
+# package exports. This can be achieved as follows: ::
+#
+#   foreach( _lib ${${PNAME}_LIBRARIES} )
+#     if( TARGET ${_lib} )
+#       ecbuild_pkgconfig( NAME ${_lib}
+#                          DESCRIPTION "..."
+#                          URL "..."
+#                          LIBRARIES ${_lib} )
+#     endif()
+#   endforeach()
+#
+##############################################################################
+
+function( ecbuild_pkgconfig )
+
+  set( options REQUIRES NO_PRIVATE_INCLUDE_DIRS )
+  set( single_value_args FILENAME NAME TEMPLATE URL DESCRIPTION )
+  set( multi_value_args LIBRARIES IGNORE_INCLUDE_DIRS IGNORE_LIBRARIES VARIABLES LANGUAGES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+  string( TOLOWER ${PROJECT_NAME} LNAME )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  unset( PKGCONFIG_LANGUAGES )
+  if( NOT _PAR_LANGUAGES )
+    if( CMAKE_C_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES C )
+    endif()
+    if( CMAKE_CXX_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES CXX )
+    endif()
+    if( CMAKE_Fortran_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES Fortran )
+    endif()
+  else()
+    foreach( _lang ${_PAR_LANGUAGES} )
+      if( CMAKE_${_lang}_COMPILER_LOADED )
+        list( APPEND PKGCONFIG_LANGUAGES ${_lang} )
+      endif()
+    endforeach()
+  endif()
+
+  foreach( _lang ${PKGCONFIG_LANGUAGES} )
+    set( PKGCONFIG_HAVE_${_lang} 1 )
+  endforeach()
+
+  set( LIBRARIES ${${PNAME}_LIBRARIES} )
+  if( _PAR_LIBRARIES )
+    set( LIBRARIES ${_PAR_LIBRARIES} )
+  endif()
+
+  if( CMAKE_CXX_COMPILER_LOADED )
+   set( _linker_lang CXX )
+  elseif( CMAKE_C_COMPILER_LOADED )
+   set( _linker_lang C )
+  elseif( CMAKE_Fortran_COMPILER_LOADED )
+   set( _linker_lang Fortran )
+  endif()
+
+  set( RPATH_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_${_linker_lang}_FLAG} )
+
+  set( PKGCONFIG_MOD_FLAG ${CMAKE_Fortran_MODPATH_FLAG} )
+
+  if( NOT PKGCONFIG_MOD_FLAG )
+    set( PKGCONFIG_MOD_FLAG "-I" )
+  endif()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS LIBRARIES _PAR_IGNORE_LIBRARIES )
+
+  ecbuild_library_dependencies( _libraries LIBRARIES )
+  foreach( _lib ${LIBRARIES} )
+    list( REMOVE_ITEM _libraries ${_lib} )
+  endforeach()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS_PRIVATE _libraries _PAR_IGNORE_LIBRARIES )
+
+  if( NOT _PAR_NO_PRIVATE_INCLUDE_DIRS )
+    ecbuild_include_dependencies( _include_dirs LIBRARIES )
+    ecbuild_pkgconfig_include( PKGCONFIG_CFLAGS _include_dirs _PAR_IGNORE_INCLUDE_DIRS )
+  endif()
+
+  set( PKGCONFIG_INCLUDE "-I\${includedir}" )
+  if( PKGCONFIG_HAVE_Fortran )
+    set( PKGCONFIG_INCLUDE "${PKGCONFIG_INCLUDE} ${PKGCONFIG_MOD_FLAG}\${fmoddir}" )
+  endif()
+
+  if( NOT _PAR_TEMPLATE )
+    set( _PAR_TEMPLATE "${ECBUILD_MACROS_DIR}/pkg-config.pc.in" )
+  endif()
+
+  set( PKGCONFIG_NAME ${LNAME} )
+  if( _PAR_NAME )
+    set( PKGCONFIG_NAME ${_PAR_NAME} )
+  endif()
+
+  if( NOT _PAR_FILENAME )
+    set( _PAR_FILENAME "${PKGCONFIG_NAME}.pc" )
+  endif()
+
+  set( PKGCONFIG_DESCRIPTION ${${PNAME}_DESCRIPTION} )
+  if( _PAR_DESCRIPTION )
+    set( PKGCONFIG_DESCRIPTION ${_PAR_DESCRIPTION} )
+  endif()
+
+  set( PKGCONFIG_URL ${${PNAME}_URL} )
+  if( _PAR_URL )
+    set( PKGCONFIG_URL ${_PAR_URL} )
+  endif()
+
+  set( PKGCONFIG_VERSION ${${PNAME}_VERSION} )
+  set( PKGCONFIG_GIT_TAG ${${PNAME}_GIT_SHA1} )  # For now set it to a commit id
+
+  if( _PAR_VARIABLES )
+    set( PKGCONFIG_VARIABLES "\n### Features:\n\n")
+    foreach( _var ${_PAR_VARIABLES} )
+      set( PKGCONFIG_VARIABLES "${PKGCONFIG_VARIABLES}${_var}=${${_var}}\n" )
+    endforeach()
+  endif()
+
+  configure_file( ${_PAR_TEMPLATE} "${CMAKE_BINARY_DIR}/${_PAR_FILENAME}" @ONLY )
+  ecbuild_info( "pkg-config file created: ${_PAR_FILENAME}" )
+
+  install( FILES ${CMAKE_BINARY_DIR}/${_PAR_FILENAME}
+           DESTINATION ${INSTALL_LIB_DIR}/pkgconfig/
+           COMPONENT utilities )
+
+endfunction(ecbuild_pkgconfig)
diff --git a/cmake/ecbuild_policies.cmake b/cmake/ecbuild_policies.cmake
new file mode 100644
index 0000000..df2b40f
--- /dev/null
+++ b/cmake/ecbuild_policies.cmake
@@ -0,0 +1,67 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#
+# ecBuild Policies
+# ================
+#
+# NOTE: This file needs to be included with NO_POLICY_SCOPE or it will have no
+#       effect!
+# NOTE: Policies 1 through 17 will be set to NEW by requiring CMake 2.8.4 i.e.
+#       calling cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+#
+##############################################################################
+
+# allow for empty spaces around library names 
+if( POLICY CMP0004 )
+    cmake_policy( SET CMP0004 OLD )
+endif()
+
+# Allow use of the LOCATION target property.
+if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD )
+endif()
+
+# for macosx use @rpath in a target’s install name
+if( POLICY CMP0042 )
+    cmake_policy( SET CMP0042 NEW )
+    set( CMAKE_MACOSX_RPATH ON )
+endif()
+
+# Error on non-existent target in get_target_property
+if( POLICY CMP0045 )
+    cmake_policy( SET CMP0045 NEW )
+endif()
+
+# Error on non-existent dependency in add_dependencies
+if( POLICY CMP0046 )
+    cmake_policy( SET CMP0046 NEW )
+endif()
+
+# Do not manage VERSION variables in project command
+if( POLICY CMP0048 )
+  cmake_policy( SET CMP0048 OLD )
+endif()
+
+# Disallow add_custom_command SOURCE signatures
+if( POLICY CMP0050 )
+    cmake_policy( SET CMP0050 NEW )
+endif()
+
+# Reject source and build dirs in installed INTERFACE_INCLUDE_DIRECTORIES
+if( POLICY CMP0052 )
+    cmake_policy( SET CMP0052 NEW )
+endif()
+
+# inside if() don't dereference variables if they are quoted
+# e.g. "VAR" is not dereferenced
+#      "${VAR}" is dereference only once
+if( POLICY CMP0054 )
+    cmake_policy( SET CMP0054 NEW )
+endif()
diff --git a/cmake/ecbuild_print_summary.cmake b/cmake/ecbuild_print_summary.cmake
new file mode 100644
index 0000000..c863e60
--- /dev/null
+++ b/cmake/ecbuild_print_summary.cmake
@@ -0,0 +1,116 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_print_summary
+# =====================
+#
+# Print a summary of the project, build environment and enabled features. ::
+#
+#   ecbuild_print_summary()
+#
+# If ``project_summary.cmake`` exist in the source root directory, a project
+# summary is printed by including this file.
+#
+# For a top level project, a summary of the build environment and a feature
+# summary are also printed.
+#
+##############################################################################
+
+macro( ecbuild_print_summary )
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Project ${PROJECT_NAME} summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    include( ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+  endif()
+
+  if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    if( NOT ${DEVELOPER_MODE} )
+      ecbuild_info( "Build summary" )
+    else()
+      ecbuild_info( "Build summary -- ( DEVELOPER_MODE )" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    ecbuild_info( "system : [${BUILD_SITE}] [${CMAKE_SYSTEM}] [${EC_OS_NAME}.${EC_OS_BITS}]" )
+    ecbuild_info( "processor        : [${CMAKE_SYSTEM_PROCESSOR}]" )
+    if( EC_BIG_ENDIAN )
+      ecbuild_info( "endiness         : Big Endian -- IEEE [${IEEE_BE}]" )
+    endif()
+    if( EC_LITTLE_ENDIAN )
+      ecbuild_info( "endiness         : Little Endian -- IEEE [${IEEE_LE}]" )
+    endif()
+    ecbuild_info( "build type       : [${CMAKE_BUILD_TYPE}]" )
+    ecbuild_info( "timestamp        : [${EC_BUILD_TIMESTAMP}]" )
+    ecbuild_info( "install prefix   : [${CMAKE_INSTALL_PREFIX}]" )
+    ecbuild_info( "  bin dir        : [${${PNAME}_FULL_INSTALL_BIN_DIR}]" )
+    ecbuild_info( "  lib dir        : [${${PNAME}_FULL_INSTALL_LIB_DIR}]" )
+    ecbuild_info( "  include dir    : [${${PNAME}_FULL_INSTALL_INCLUDE_DIR}]" )
+    ecbuild_info( "  data dir       : [${${PNAME}_FULL_INSTALL_DATA_DIR}]" )
+    ecbuild_info( "  cmake dir      : [${${PNAME}_FULL_INSTALL_CMAKE_DIR}]" )
+    if( EC_LINK_DIR )
+      ecbuild_info( "links prefix     : [${EC_LINK_DIR}]" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    foreach( lang ${langs} )
+      ecbuild_info( "${lang} -- ${CMAKE_${lang}_COMPILER_ID} ${CMAKE_${lang}_COMPILER_VERSION}"  )
+      ecbuild_info( "    compiler   : ${CMAKE_${lang}_COMPILER}" )
+      ecbuild_info( "    flags      : ${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} ${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+      ecbuild_info( "    link flags : ${CMAKE_${lang}_LINK_FLAGS}" )
+    endforeach()
+
+    ecbuild_info( "linker : ${CMAKE_LINKER}")
+    ecbuild_info( "ar     : ${CMAKE_AR}")
+    ecbuild_info( "ranlib : ${CMAKE_RANLIB}")
+    ecbuild_info( "link flags" )
+    ecbuild_info( "    executable [${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    shared lib [${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    static lib [${CMAKE_MODULE_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "install rpath  : ${CMAKE_INSTALL_RPATH}" )
+
+    get_directory_property( defs COMPILE_DEFINITIONS )
+
+    ecbuild_info( "common definitions: ${defs}" )
+
+    ### FEATURE SUMMARY
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Feature summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8.6" )
+      set( __what ALL )
+    else()
+      set( __what ALL INCLUDE_QUIET_PACKAGES )
+    endif()
+
+    # Print feature summary
+    feature_summary( WHAT ${__what} )
+    # Write feature summary to ecbuild.log
+    feature_summary( WHAT ${__what} FILENAME ${ECBUILD_LOG_FILE} APPEND )
+
+    ### WARNINGS
+
+    # issue warnings / errors in case there are unused project files
+    ecbuild_warn_unused_files()
+
+  endif()
+
+endmacro( ecbuild_print_summary )
diff --git a/cmake/ecbuild_project_files.cmake b/cmake/ecbuild_project_files.cmake
new file mode 100644
index 0000000..12c07c2
--- /dev/null
+++ b/cmake/ecbuild_project_files.cmake
@@ -0,0 +1,75 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# resert the variable on each configure
+set( EC_UNUSED_FILES "" CACHE INTERNAL "unused files" )
+
+##############################################################################
+# finds project files and adds them to the passed variable
+
+macro( ecbuild_find_files_recursive aFileList )
+
+list( APPEND ecbuild_project_extensions c cc cpp cxx ) # for the moment skip ( h hh )
+
+foreach( aExt ${ecbuild_project_extensions} )
+  set( globPatterns ${globPatterns} *.${aExt} )
+endforeach()
+
+# This globs for only one pattern at a time
+# Shell extglob patterns are unfortunately not supported.
+file( GLOB_RECURSE ${aFileList} ${globPatterns} )
+
+endmacro()
+
+##############################################################################
+# finds the unused files on all the project
+function( ecbuild_find_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    ecbuild_find_files_recursive( cwdFiles )
+
+    # this list will be kept
+    set( EC_PROJECT_FILES ${EC_PROJECT_FILES} ${cwdFiles} CACHE INTERNAL "" )
+    # this list will be progressevely emptied
+    set( EC_UNUSED_FILES  ${EC_UNUSED_FILES}  ${cwdFiles} CACHE INTERNAL "" )
+  endif()
+
+endfunction()
+
+##############################################################################
+# removed used files from unused list
+macro( ecbuild_declare_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    foreach( _afile ${ARGV} )
+
+      # ecbuild_debug_var( _afile )
+
+      get_property( _src_gen SOURCE ${_afile} PROPERTY GENERATED )
+
+      if( NOT _src_gen )
+
+        get_filename_component( _abspath ${_afile} ABSOLUTE )
+
+        # check for existance of all declared files
+        if( EXISTS ${_abspath} )
+            list( REMOVE_ITEM EC_UNUSED_FILES ${_abspath} )
+        else()
+        ecbuild_critical( "In directory ${CMAKE_CURRENT_SOURCE_DIR} file ${_afile} was declared in CMakeLists.txt but not found" )
+        endif()
+      endif()
+
+    endforeach()
+
+    # rewrite the unused file list in cache
+    set( EC_UNUSED_FILES ${EC_UNUSED_FILES} CACHE INTERNAL "unused files" )
+  endif()
+
+endmacro()
diff --git a/cmake/ecbuild_remove_fortran_flags.cmake b/cmake/ecbuild_remove_fortran_flags.cmake
new file mode 100644
index 0000000..93aab17
--- /dev/null
+++ b/cmake/ecbuild_remove_fortran_flags.cmake
@@ -0,0 +1,64 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_remove_fortran_flags
+# ============================
+#
+# Remove Fortran compiler flags from ``CMAKE_Fortran_FLAGS``. ::
+#
+#   ecbuild_remove_fortran_flags( <flag1> [ <flag2> ... ] [ BUILD <build> ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   remove flags from ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_remove_fortran_flags m_flags )
+
+  set( _flags ${m_flags} )
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( single_value_args BUILD )
+    set( multi_value_args )
+    cmake_parse_arguments( _PAR "" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+    string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+    if( _PAR_BUILD )
+      string( TOUPPER ${_PAR_BUILD} _PAR_BUILD_CAPS )
+    endif()
+
+    if( _PAR_BUILD AND (CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}") )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${_PAR_BUILD} ${CMAKE_Fortran_FLAGS_${_PAR_BUILD}})
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed from build type ${_PAR_BUILD}" )
+      endforeach()
+
+    elseif( NOT _PAR_BUILD )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS} ${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS ${CMAKE_Fortran_FLAGS} )
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed" )
+      endforeach()
+
+    endif()
+
+  endif()
+  unset( _flags )
+
+endmacro()
diff --git a/cmake/ecbuild_requires_macro_version.cmake b/cmake/ecbuild_requires_macro_version.cmake
new file mode 100644
index 0000000..8ff5617
--- /dev/null
+++ b/cmake/ecbuild_requires_macro_version.cmake
@@ -0,0 +1,27 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_requires_macro_version
+# ==============================
+#
+# Check that the ecBuild version satisfied a given minimum version or fail. ::
+#
+#   ecbuild_requires_macro_version( <minimum-version> )
+#
+##############################################################################
+
+macro( ecbuild_requires_macro_version req_vrs )
+
+	if( ECBUILD_MACRO_VERSION VERSION_LESS ${req_vrs} )
+		ecbuild_critical( "${PROJECT_NAME} needs ecbuild macro version >= ${req_vrs}" )
+	endif()
+
+endmacro()
diff --git a/cmake/ecbuild_separate_sources.cmake b/cmake/ecbuild_separate_sources.cmake
new file mode 100644
index 0000000..69b6810
--- /dev/null
+++ b/cmake/ecbuild_separate_sources.cmake
@@ -0,0 +1,103 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_separate_sources
+# ========================
+#
+# Separate a given list of sources according to language. ::
+#
+#   ecbuild_separate_sources( TARGET <name>
+#                             SOURCES <source1> [ <source2> ... ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   base name for the CMake output variables to set
+#
+# SOURCES : required
+#   list of source files to separate
+#
+# Output variables
+# ----------------
+#
+# If any file of the following group of extensions is present in the list of
+# sources, the corresponding CMake variable is set:
+#
+# :<target>_h_srcs:       source files with extension .h, .hxx, .hh, .hpp, .H
+# :<target>_c_srcs:       source files with extension .c
+# :<target>_cxx_srcs:     source files with extension .cc, .cxx, .cpp, .C
+# :<target>_fortran_srcs: source files with extension .f, .F, .for, f77, .f90,
+#                                                     .f95, .F77, .F90, .F95
+# :<target>_cuda_srcs:    source files with extension .cu
+#
+##############################################################################
+
+function( ecbuild_separate_sources )
+
+	set( options )
+	set( single_value_args TARGET  )
+	set( multi_value_args  SOURCES )
+
+	cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+	if(_PAR_UNPARSED_ARGUMENTS)
+	  ecbuild_critical("Unknown keywords given to ecbuild_separate_sources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+	endif()
+
+	if( NOT _PAR_TARGET  )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the TARGET.")
+	endif()
+
+	if( NOT _PAR_SOURCES )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the SOURCES.")
+	endif()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.h$|\\.hxx$|\\.hh$|\\.hpp$|\\.H$)")
+			list( APPEND ${_PAR_TARGET}_h_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.c$)")
+			list( APPEND ${_PAR_TARGET}_c_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.cc$|\\.cxx$|\\.cpp$|\\.C$)")
+			list( APPEND ${_PAR_TARGET}_cxx_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.f$|\\.F$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f03$|\\.f08$|\\.F77$|\\.F90$|\\.F95$|\\.F03$|\\.F08$)")
+			list( APPEND ${_PAR_TARGET}_fortran_srcs ${src} )
+		endif()
+	endforeach()
+
+    foreach( src ${_PAR_SOURCES} )
+        if(${src} MATCHES "(\\.cu$)")
+            list( APPEND ${_PAR_TARGET}_cuda_srcs ${src} )
+        endif()
+    endforeach()
+
+    set_source_files_properties( ${${_PAR_TARGET}_fortran_srcs} PROPERTIES LANGUAGE Fortran )
+
+    set( ${_PAR_TARGET}_h_srcs       "${${_PAR_TARGET}_h_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_c_srcs       "${${_PAR_TARGET}_c_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cxx_srcs     "${${_PAR_TARGET}_cxx_srcs}"     PARENT_SCOPE )
+    set( ${_PAR_TARGET}_fortran_srcs "${${_PAR_TARGET}_fortran_srcs}" PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cuda_srcs    "${${_PAR_TARGET}_cuda_srcs}"    PARENT_SCOPE )
+
+
+endfunction( ecbuild_separate_sources )
diff --git a/cmake/ecbuild_setup_test_framework.cmake b/cmake/ecbuild_setup_test_framework.cmake
new file mode 100644
index 0000000..b4b3ccb
--- /dev/null
+++ b/cmake/ecbuild_setup_test_framework.cmake
@@ -0,0 +1,72 @@
+ecbuild_add_option( FEATURE TESTS
+                    DEFAULT ON
+                    DESCRIPTION "Enable the unit tests" )
+
+if( ENABLE_TESTS AND CMAKE_CXX_COMPILER_LOADED )
+
+  # Try to find compiled boost
+
+  # BOOST_ROOT or BOOSTROOT should take precedence on the search for location
+  if( BOOST_ROOT OR BOOSTROOT OR DEFINED ENV{BOOST_ROOT} OR DEFINED ENV{BOOSTROOT} )
+    set( CMAKE_PREFIX_PATH ${BOOST_ROOT} ${BOOSTROOT} $ENV{BOOST_ROOT} $ENV{BOOSTROOT} ${CMAKE_PREFIX_PATH} )
+  endif()
+
+  set( Boost_USE_MULTITHREADED  ON )
+  #   set( Boost_DEBUG              ON )
+
+  find_package( Boost 1.47.0 COMPONENTS unit_test_framework )
+
+  set( ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/include" )
+
+  if( Boost_FOUND AND Boost_UNIT_TEST_FRAMEWORK_LIBRARY )
+
+    set( HAVE_BOOST_UNIT_TEST 1 )
+    set( BOOST_UNIT_TEST_FRAMEWORK_LINKED 1 )
+
+    ecbuild_info( "Using Boost for unit tests:\n    INC [${Boost_INCLUDE_DIRS}]\n    LIB [${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}]" )
+
+  else()
+
+    ecbuild_info( "Boost unit test framework -- NOT FOUND" )
+
+    set( HAVE_BOOST_UNIT_TEST 0 )
+
+    # set( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY 1 )
+    # comment out this when ecbuild packs boost unit test inside...
+    # list( APPEND ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/contrib/boost-1.55/include" )
+    # set( HAVE_BOOST_UNIT_TEST 1 )
+
+  endif()
+
+endif()
+
+if( ENABLE_TESTS )
+
+  # CTest has built-in support for running with memcheck
+  # (https://cmake.org/cmake/help/latest/manual/ctest.1.html#ctest-memcheck-step)
+  # via `ctest -T memcheck`, however by default memcheck does not exit with a
+  # non-zero error code if any issues are found.
+  #
+  # CTest will run ${MEMORYCHECK_COMMAND} with ${MEMORYCHECK_COMMAND_OPTIONS}.
+  # Suppressions are read from ${MEMORYCHECK_SUPPRESSIONS_FILE} if given.
+
+  find_program( MEMORYCHECK_COMMAND valgrind )
+  ecbuild_debug_var( MEMORYCHECK_COMMAND )
+
+  if( NOT MEMORYCHECK_COMMAND_OPTIONS )
+    set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --error-exitcode=1"
+         CACHE STRING "Options passed to memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_COMMAND_OPTIONS )
+
+  if( NOT MEMORYCHECK_SUPPRESSIONS_FILE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/valgrind_suppress.txt" )
+    set( MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.supp"
+         CACHE FILEPATH "Suppressions file to be used with memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_SUPPRESSIONS_FILE )
+
+else()
+
+  ecbuild_info("Tests have been disabled")
+
+endif()
diff --git a/cmake/ecbuild_source_flags.cmake b/cmake/ecbuild_source_flags.cmake
new file mode 100644
index 0000000..bd63258
--- /dev/null
+++ b/cmake/ecbuild_source_flags.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( __gen_source_flags ${CMAKE_CURRENT_LIST_DIR}/gen_source_flags.py )
+
+# Calls gen_source_flags.py to generate a CMake file with the per
+# source file flags for a given target.
+function( ecbuild_source_flags OUT TARGET DEFAULT_FLAGS SOURCES )
+
+  if( NOT PYTHONINTERP_FOUND OR PYTHON_VERSION VERSION_LESS 2.7 )
+    find_package( PythonInterp 2.7 REQUIRED )
+  endif()
+
+  set( OUTFILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_source_flags.cmake )
+
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    set( __debug "--debug" )
+  endif()
+  execute_process( COMMAND ${PYTHON_EXECUTABLE} ${__gen_source_flags}
+                           ${ECBUILD_SOURCE_FLAGS} ${OUTFILE} "${DEFAULT_FLAGS}"
+                           ${SOURCES} "${__debug}"
+                   RESULT_VARIABLE __res )
+
+  if( __res GREATER 0 )
+    ecbuild_error( "ecbuild_source_flags: failed generating source flags for target ${TARGET} from ${ECBUILD_SOURCE_FLAGS}" )
+  endif()
+  set( ${OUT} ${OUTFILE} PARENT_SCOPE )
+
+endfunction()
diff --git a/cmake/ecbuild_system.cmake b/cmake/ecbuild_system.cmake
new file mode 100644
index 0000000..16c0299
--- /dev/null
+++ b/cmake/ecbuild_system.cmake
@@ -0,0 +1,277 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+########################################################################################################
+# disallow in-source build
+
+if( EXISTS ${CMAKE_SOURCE_DIR}/CMakeCache.txt ) # check for failed attempts to build within the source tree
+    message( FATAL_ERROR "Project ${PROJECT_NAME} contains a CMakeCache.txt inside source tree [${CMAKE_SOURCE_DIR}/CMakeCache.txt].\n Please remove it and
+    make sure that source tree is prestine and clean of unintended files, before retrying." )
+endif()
+
+get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+if(${srcdir} STREQUAL ${bindir})
+    message("######################################################")
+    message("You are attempting to build in your source directory (${srcdir}).")
+    message("You must run cmake from a different build directory.")
+    message("######################################################")
+    message( FATAL_ERROR "${PROJECT_NAME} requires an out of source build.\n Please create a separate build directory and run 'cmake path/to/project [options]' from there.")
+endif()
+
+########################################################################################################
+# ecbuild versioning support
+
+set( ECBUILD_CMAKE_MINIMUM "2.8.10" )
+if( ${CMAKE_VERSION} VERSION_LESS ${ECBUILD_CMAKE_MINIMUM} )
+    message(FATAL_ERROR "${PROJECT_NAME} requires at least CMake ${ECBUILD_CMAKE_MINIMUM} -- you are using ${CMAKE_COMMAND} [${CMAKE_VERSION}]\n Please, get a newer version of CMake @ www.cmake.org" )
+endif()
+
+set( ECBUILD_MACROS_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "where ecbuild system is" )
+
+include( "${ECBUILD_MACROS_DIR}/VERSION.cmake" )
+
+set( ecbuild_VERSION_STR "${ECBUILD_VERSION_STR}" )
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+# set capitalised project name
+
+string( TOUPPER ${PROJECT_NAME} PROJECT_NAME_CAPS )
+string( TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWCASE )
+
+########################################################################################################
+# include our cmake macros, but only do so if this is the top project
+if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    # hostname of where we build
+
+    site_name( BUILD_SITE )
+    mark_as_advanced( BUILD_SITE )
+    mark_as_advanced( BUILD_TESTING )
+
+    set( ECBUILD_PROJECTS  "" CACHE INTERNAL "list of ecbuild (sub)projects that use ecbuild" )
+
+    # Include log macros since these are used right away
+    include( ecbuild_log )
+
+    execute_process( COMMAND env OUTPUT_VARIABLE __env )
+    ecbuild_debug( "---------------------------------------------------------" )
+    ecbuild_debug( "Environment:" )
+    ecbuild_debug( "---------------------------------------------------------\n${__env}" )
+    ecbuild_debug( "---------------------------------------------------------" )
+
+    ecbuild_info( "ecbuild   ${ecbuild_VERSION_STR}\t${ECBUILD_MACROS_DIR}" )
+    ecbuild_info( "cmake     ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}\t${CMAKE_COMMAND}" )
+
+    if( CMAKE_TOOLCHAIN_FILE )
+      ecbuild_info( "toolchain ${CMAKE_TOOLCHAIN_FILE}" )
+    endif()
+
+    if( ECBUILD_CONFIG )
+      ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+    endif()
+
+    if( ECBUILD_CACHE )
+      include( ${ECBUILD_CACHE} )
+      ecbuild_info( "cache     ${ECBUILD_CACHE}" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+    # clear the build dir exported targets file (only on the top project)
+
+    set( TOP_PROJECT_TARGETS_FILE "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-targets.cmake" CACHE INTERNAL "" )
+    file( REMOVE ${TOP_PROJECT_TARGETS_FILE} )
+
+    # add backport support for versions up too 2.8.4
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8" )
+    set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/2.8" ${CMAKE_MODULE_PATH} )
+    endif()
+
+    # add extra macros from external contributions
+    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib" )
+
+    # would bring FindEigen in, so for the moment keep it out
+    # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib/GreatCMakeCookOff" )
+
+    ############################################################################################
+    # define valid build types
+
+    include(ecbuild_define_build_types)
+
+    ############################################################################################
+    # add cmake macros
+
+    include(AddFileDependencies)
+
+    include(CheckTypeSize)
+    include(CheckIncludeFile)
+    include(CheckIncludeFiles)
+
+    include(CheckFunctionExists)
+    include(CheckSymbolExists)
+
+    include(CheckCCompilerFlag)
+    include(CheckCSourceCompiles)
+    include(CheckCSourceRuns)
+
+    include(CMakeParseArguments)
+
+    # include(CMakePrintSystemInformation) # available in cmake 2.8.4
+
+    if( CMAKE_CXX_COMPILER_LOADED )
+        include(CheckIncludeFileCXX)
+        include(CheckCXXCompilerFlag)
+        include(CheckCXXSourceCompiles)
+        include(CheckCXXSourceRuns)
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_LOADED )
+        set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module CACHE PATH "directory for all fortran modules." )
+        include(CheckFortranFunctionExists)
+        if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+            include(FortranCInterface)
+        endif()
+        set( EC_HAVE_FORTRAN 1 )
+    endif()
+
+    include(FeatureSummary) # support features in cmake
+
+    include(TestBigEndian)
+
+    ############################################################################################
+    # backport of cmake > 2.8.4 functions
+
+    if( "${CMAKE_VERSION}" VERSION_LESS "2.8.6" )
+        include( ${CMAKE_CURRENT_LIST_DIR}/2.8/CMakePushCheckState.cmake )
+    else()
+        include(CMakePushCheckState)
+    endif()
+
+    ############################################################################################
+    # add our macros
+
+    include( ecbuild_list_macros )
+    include( ecbuild_list_add_pattern )
+    include( ecbuild_list_exclude_pattern )
+
+    include( ecbuild_check_c_source_return )
+    include( ecbuild_check_cxx_source_return )
+    include( ecbuild_check_cxx11 )
+    include( ecbuild_check_fortran_source_return )
+
+    include( ecbuild_requires_macro_version )
+    include( ecbuild_get_date )
+    include( ecbuild_add_persistent )
+    include( ecbuild_generate_config_headers )
+    include( ecbuild_generate_rpc )
+    include( ecbuild_generate_yy )
+    include( ecbuild_generate_fortran_interfaces )
+    include( ecbuild_echo_targets )
+    include( ecbuild_features )
+    include( ecbuild_add_option )
+    include( ecbuild_add_library )
+    include( ecbuild_add_executable )
+    include( ecbuild_append_to_rpath )
+    include( ecbuild_download_resource )
+    include( ecbuild_get_test_data )
+    include( ecbuild_add_c_flags )
+    include( ecbuild_add_cxx_flags )
+    include( ecbuild_add_cxx11_flags )
+    include( ecbuild_get_cxx11_flags )
+    include( ecbuild_check_fortran )
+    include( ecbuild_add_fortran_flags )
+    include( ecbuild_add_test )
+    include( ecbuild_add_resources )
+    include( ecbuild_get_resources )
+    include( ecbuild_dont_pack )
+    include( ecbuild_project_files )
+    include( ecbuild_declare_project )
+    include( ecbuild_install_project )
+    include( ecbuild_separate_sources )
+    include( ecbuild_find_package )
+    include( ecbuild_use_package )
+    include( ecbuild_list_extra_search_paths )
+    include( ecbuild_add_extra_search_paths )
+    include( ecbuild_print_summary )
+    include( ecbuild_warn_unused_files )
+    include( ecbuild_find_mpi )
+    include( ecbuild_find_omp )
+    include( ecbuild_find_perl )
+    include( ecbuild_find_python )
+    include( ecbuild_find_lexyacc )
+    include( ecbuild_find_fortranlibs )
+    include( ecbuild_git )
+    include( ecbuild_enable_fortran )
+    include( ecbuild_source_flags )
+    include( ecbuild_target_flags )
+    include( ecbuild_bundle )
+    include( ecbuild_pkgconfig )
+    include( ecbuild_cache )
+    include( ecbuild_remove_fortran_flags )
+
+    include( ${CMAKE_CURRENT_LIST_DIR}/contrib/GetGitRevisionDescription.cmake )
+
+    ############################################################################################
+    # kickstart the build system
+
+    if( ECBUILD_CONFIG )
+      include( ${ECBUILD_CONFIG} )
+    endif()
+
+    ecbuild_prepare_cache()
+
+    include( ecbuild_define_options )               # define build options
+    include( ecbuild_compiler_flags )               # compiler flags
+    include( ecbuild_check_compiler )               # check for compiler characteristics
+    include( ecbuild_check_os )                     # check for os characteristics
+    include( ecbuild_check_functions )              # check for available functions
+    include( ecbuild_define_paths )                 # defines installation paths
+    include( ecbuild_define_libs_and_execs_target ) # defines the top level execs and libs
+    include( ecbuild_define_links_target )          # defines the links target
+    include( ecbuild_setup_test_framework )         # setup test framework
+    include( ecbuild_define_uninstall )             # define uninstall target
+
+    ecbuild_flush_cache()
+
+    ############################################################################################
+    # Testing
+
+    include(CTest)                 # add cmake testing support
+    enable_testing()
+
+    # keep this until we modify the meaning to 'check' if installation worked
+    add_custom_target( check COMMAND ${CMAKE_CTEST_COMMAND} )
+
+    ############################################################################################
+    # define the build timestamp, unless the user provided one via EC_BUILD_TIMESTAMP
+
+    if( NOT DEFINED EC_BUILD_TIMESTAMP )
+        ecbuild_get_timestamp( EC_BUILD_TIMESTAMP )
+        set( EC_BUILD_TIMESTAMP  "${EC_BUILD_TIMESTAMP}" CACHE INTERNAL "Build timestamp" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+else()
+
+    # Allow subprojects with different compilation flags. This could be done by defining
+    #     set( ECBUILD_C_FLAGS_DEBUG "-O0" )
+    # or
+    #     set( ECBUILD_CONFIG "<subproject-config>.cmake" )
+    if( ECBUILD_CONFIG )
+        ecbuild_info( "---------------------------------------------------------" )
+        ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+        include( ${ECBUILD_CONFIG} )
+    endif()
+    include( ecbuild_compiler_flags )
+
+endif()
diff --git a/cmake/ecbuild_target_flags.cmake b/cmake/ecbuild_target_flags.cmake
new file mode 100644
index 0000000..b6527d1
--- /dev/null
+++ b/cmake/ecbuild_target_flags.cmake
@@ -0,0 +1,91 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_target_flags
+# ====================
+#
+# Override compiler flags for a given target. ::
+#
+#   ecbuild_target_flags( <target> <c_flags> <cxx_flags> <fortran_flags> )
+#
+# Required arguments:
+#
+# :target:        Target name
+# :c_flags:       Target specific C flags (can be empty)
+# :cxx_flags:     Target specific CXX flags (can be empty)
+# :fortran_flags: Target specific Fortran flags (can be empty)
+#
+# There are 3 cases, only the first applicable case takes effect:
+#
+# 1.  Use custom rules from user specified ``ECBUILD_COMPILE_FLAGS`` file and
+#     append target specific flags.
+#
+# 2.  Use JSON rules from user specified ``ECBUILD_SOURCE_FLAGS`` file and
+#     append target specific flags.
+#
+# 3.  Only the target specific flags are applied to all matching source files.
+#
+##############################################################################
+
+function( ecbuild_target_flags target c_flags cxx_flags fortran_flags )
+
+  get_property( languages GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${languages} )
+
+    string( TOLOWER ${lang} l )
+
+    if( ${target}_${l}_srcs )
+
+      # 1) Override compile flags from user specified CMake file
+      if( ECBUILD_COMPILE_FLAGS )
+
+        # Project specific flags for current language and optionally build type
+        set( pflags "${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+
+        foreach( src ${${target}_${l}_srcs} )
+          get_property( oflags SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS )
+          get_property( oflags_btype SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+          # Override compile flags for source file?
+          if( oflags OR oflags_btype )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${oflags} ${oflags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): overriding flags for ${src} with '${oflags} ${oflags_btype}'" )
+          # Otherwise append source file specific flags to project specific and target specific flags
+          else()
+            get_property( flags SOURCE ${src} PROPERTY COMPILE_FLAGS )
+            get_property( flags_btype SOURCE ${src} PROPERTY COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${pflags} ${${l}_flags} ${flags} ${flags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): setting flags for ${src} to '${pflags} ${${l}_flags} ${flags} ${flags_btype}'" )
+          endif()
+        endforeach()
+
+      # 2) Override compile flags from user specified JSON file
+      elseif( ECBUILD_SOURCE_FLAGS )
+        ecbuild_source_flags( ${target}_${lang}_SOURCE_FLAGS
+                              ${target}_${l}
+                              "${${l}_flags}"
+                              "${${target}_${l}_srcs}" )
+
+        ecbuild_debug("ecbuild_target_flags(${target}): setting source file ${lang} flags from ${${target}_${lang}_SOURCE_FLAGS}")
+        include( ${${target}_${lang}_SOURCE_FLAGS} )
+
+      # 3) Use target specific compile flags
+      elseif( ${l}_flags )
+
+        set_source_files_properties( ${${target}_${l}_srcs} PROPERTIES COMPILE_FLAGS "${${l}_flags}" )
+        ecbuild_debug("ecbuild_target_flags(${target}): setting flags for '${${target}_${l}_srcs}' to '${${l}_flags}'")
+
+      endif()
+    endif()
+
+  endforeach()
+
+endfunction()
diff --git a/cmake/ecbuild_uninstall.cmake.in b/cmake/ecbuild_uninstall.cmake.in
new file mode 100644
index 0000000..33b1949
--- /dev/null
+++ b/cmake/ecbuild_uninstall.cmake.in
@@ -0,0 +1,28 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+
+if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt")
+  file(READ "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt" __files)
+  string(REGEX REPLACE "\n" ";" __files "${__files}")
+  list(APPEND files ${__files})
+endif()
+
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif()
+  else()
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif()
+endforeach(file)
diff --git a/cmake/ecbuild_use_package.cmake b/cmake/ecbuild_use_package.cmake
new file mode 100644
index 0000000..d54e459
--- /dev/null
+++ b/cmake/ecbuild_use_package.cmake
@@ -0,0 +1,341 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_use_package
+# ===================
+#
+# Add a project from a source directory, a subdirectory or search for it. ::
+#
+#   ecbuild_use_package( PROJECT <name>
+#                        [ VERSION <version> [ EXACT ] ]
+#                        [ URL <url> ]
+#                        [ DESCRIPTION <description> ]
+#                        [ TYPE <type> ]
+#                        [ PURPOSE <purpose> ]
+#                        [ FAILURE_MSG <message> ]
+#                        [ REQUIRED ]
+#                        [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   string describing the package (shown in summary and stored in the cache)
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   string describing which functionality this package enables in the project
+#
+# FAILURE_MSG : optional
+#   string to be appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>``
+# is the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :<NAME>_SOURCE:    path to source directory for package
+# :SUBPROJECT_DIRS:  list of additional paths to search for package source
+#
+# See also ``ecbuild_find_package`` for additional CMake variables relevant
+# when search for the package (step 6 below).
+#
+# Usage
+# -----
+#
+# Use another CMake project as a dependency by either building it from source
+# i.e. adding its source directory as a subdirectory or searching for it. This
+# transparently deals with the case where the project has already been included
+# e.g. because multiple projects with shared dependencies are built together.
+#
+# The search proceeds as follows:
+#
+# 1.  If ``SUBPROJECT_DIRS`` is set, each directory in the list is searched
+#     for a subdirectory <name> and ``<NAME>_SOURCE`` is set to the first one
+#     found (if any).
+#
+# 2.  If ``<NAME>_SOURCE`` is set, check if this directory is a CMake project
+#     (contains ``CMakeLists.txt`` and fail if not.
+#
+# 3.  Otherwise, check if the current directory has a ``<name>`` subdirectory.
+#
+# 4.  If the project has not been previously marked as found or added as a
+#     subdirectory and a project source directory has been found in steps 1-3
+#     add this subdirectory.
+#
+# 5.  If the project has been marked as found, check the version.
+#
+# 6.  Otherwise, search for the project using ``ecbuild_find_package``.
+#
+##############################################################################
+
+macro( ecbuild_use_package )
+
+  set( options            REQUIRED QUIET EXACT )
+  set( single_value_args  PROJECT VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_use_package(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_PROJECT  )
+    ecbuild_critical("The call to ecbuild_use_package() doesn't specify the PROJECT.")
+  endif()
+
+  if( _p_EXACT AND NOT _p_VERSION )
+    ecbuild_critical("Call to ecbuild_use_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _p_REQUIRED )
+    set( _p_TYPE REQUIRED )
+  endif()
+
+  # try to find the package as a subproject and build it
+
+  string( TOUPPER ${_p_PROJECT} pkgUPPER )
+
+  # user defined dir with subprojects
+
+  if( NOT DEFINED ${pkgUPPER}_SOURCE AND DEFINED SUBPROJECT_DIRS )
+    ecbuild_warn("ecbuild_use_package(): setting SUBPROJECT_DIRS is deprecated")
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): scanning subproject directories ${SUBPROJECT_DIRS}")
+    foreach( dir ${SUBPROJECT_DIRS} )
+      if( EXISTS ${dir}/${_p_PROJECT} AND EXISTS ${dir}/${_p_PROJECT}/CMakeLists.txt )
+        ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):   setting ${pkgUPPER}_SOURCE to ${dir}/${_p_PROJECT}")
+        set( ${pkgUPPER}_SOURCE "${dir}/${_p_PROJECT}" )
+      endif()
+    endforeach()
+  endif()
+
+  # user defined path to subproject
+
+  if( DEFINED ${pkgUPPER}_SOURCE )
+
+    if( NOT EXISTS ${${pkgUPPER}_SOURCE} OR NOT EXISTS ${${pkgUPPER}_SOURCE}/CMakeLists.txt )
+      ecbuild_critical("User defined source directory '${${pkgUPPER}_SOURCE}' for project '${_p_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    set( ${pkgUPPER}_subproj_dir_ "${${pkgUPPER}_SOURCE}" )
+
+  else() # default is 'dropped in' subdirectory named as project
+
+    if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT} AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}/CMakeLists.txt )
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): found ${_p_PROJECT} in subdirectory ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}")
+      set( ${pkgUPPER}_subproj_dir_ "${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}" )
+    endif()
+
+  endif()
+
+  # check if was already added as subproject ...
+
+  set( _just_added 0 )
+  set( _do_version_check 0 )
+  set( _source_description "" )
+
+  list( FIND ECBUILD_PROJECTS ${_p_PROJECT} _ecbuild_project_${pkgUPPER} )
+
+  if( NOT _ecbuild_project_${pkgUPPER} EQUAL "-1" )
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 1 )
+  else()
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was not previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 0 )
+  endif()
+
+  # solve capitalization issues
+
+  if( ${_p_PROJECT}_FOUND AND NOT ${pkgUPPER}_FOUND )
+    set( ${pkgUPPER}_FOUND 1 )
+  endif()
+  if( ${pkgUPPER}_FOUND AND NOT ${_p_PROJECT}_FOUND )
+    set( ${_p_PROJECT}_FOUND 1 )
+  endif()
+
+  # Case 1) project was NOT previously added as subproject and is NOT already FOUND
+
+  if( NOT ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ )
+
+    # check if SUBPROJDIR is set
+
+    if( DEFINED ${pkgUPPER}_subproj_dir_ )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 1) project was NOT previously added as subproject and is NOT already FOUND")
+
+      # check version is acceptable
+      set( _just_added 1 )
+      set( _do_version_check 1 )
+      set( _source_description "sub-project ${_p_PROJECT} (sources)" )
+
+      # add as a subproject
+
+      set( ${pkgUPPER}_subproj_dir_ ${${pkgUPPER}_subproj_dir_} CACHE PATH "Path to ${_p_PROJECT} source directory" )
+      mark_as_advanced( ${pkgUPPER}_subproj_dir_ )
+
+      set( ECBUILD_PROJECTS ${ECBUILD_PROJECTS} ${_p_PROJECT} CACHE INTERNAL "" )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):    ${_p_PROJECT} found in subdirectory ${${pkgUPPER}_subproj_dir_}")
+      add_subdirectory( ${${pkgUPPER}_subproj_dir_} ${_p_PROJECT} )
+
+      set( ${_p_PROJECT}_BASE_DIR ${CMAKE_BINARY_DIR} )
+
+      set( ${pkgUPPER}_FOUND 1 )
+      set( ${_p_PROJECT}_VERSION ${${pkgUPPER}_VERSION} )
+
+      list( APPEND ${pkgUPPER}_INCLUDE_DIRS ${${pkgUPPER}_TPL_INCLUDE_DIRS} )
+
+    endif()
+
+  endif()
+
+  # Case 2) project was already added as subproject, so is already FOUND -- BUT must check version acceptable
+
+  if( ${pkgUPPER}_previous_subproj_ )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 2) project was already added as subproject, check version is acceptable")
+
+    if( NOT ${pkgUPPER}_FOUND )
+      ecbuild_critical( "${_p_PROJECT} was already included as sub-project but ${pkgUPPER}_FOUND isn't set -- this is likely a BUG in ecbuild" )
+    endif()
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "already existing sub-project ${_p_PROJECT} (sources)" )
+
+  endif()
+
+  # Case 3) project was NOT added as subproject, but is FOUND -- so it was previously found as a binary ( either build or install tree )
+
+  if( ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ AND NOT _just_added )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 3) project was NOT previously added as subproject, but is FOUND")
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "previously found package ${_p_PROJECT} (binaries)" )
+
+  endif()
+
+  # test version for Cases 1,2,3
+
+  # ecbuild_debug_var( _p_PROJECT )
+  # ecbuild_debug_var( _p_VERSION )
+  # ecbuild_debug_var( ${pkgUPPER}_VERSION )
+  # ecbuild_debug_var( ${_p_PROJECT}_VERSION )
+  # ecbuild_debug_var( _just_added )
+  # ecbuild_debug_var( _do_version_check )
+  # ecbuild_debug_var( _source_description )
+  # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+  # ecbuild_debug_var( ${pkgUPPER}_previous_subproj_ )
+
+  if( _p_VERSION AND _do_version_check )
+    if( _p_EXACT )
+      if( NOT ${_p_PROJECT}_VERSION VERSION_EQUAL _p_VERSION )
+        ecbuild_critical( "${PROJECT_NAME} requires (exactly) ${_p_PROJECT} = ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    else()
+      if( _p_VERSION VERSION_LESS ${_p_PROJECT}_VERSION OR _p_VERSION VERSION_EQUAL ${_p_PROJECT}_VERSION )
+        ecbuild_info( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      else()
+        ecbuild_critical( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected only ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    endif()
+  endif()
+
+  # Case 4) is NOT FOUND so far, NOT as sub-project (now or before), and NOT as binary neither
+  #         so try to find precompiled binaries or a build tree
+
+  if( ${pkgUPPER}_FOUND )
+    # Only set package properties if ecbuild_find_package, which itself calls
+    # set_package_properties, is not subsequently called since doing so would
+    # duplicate the purpose
+    set_package_properties( ${_p_PROJECT} PROPERTIES
+                            URL "${_p_URL}"
+                            DESCRIPTION "${_p_DESCRIPTION}"
+                            TYPE "${_p_TYPE}"
+                            PURPOSE "${_p_PURPOSE}" )
+  else()
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 4) project has NOT been added as a subproject and is NOT already FOUND")
+
+    set( _opts )
+    if( _p_VERSION )
+      list( APPEND _opts VERSION ${_p_VERSION} )
+    endif()
+    if( _p_EXACT )
+      list( APPEND _opts EXACT )
+    endif()
+    if( _p_REQUIRED )
+      list( APPEND _opts REQUIRED )
+    endif()
+    if( _p_URL )
+      list( APPEND _opts URL ${_p_URL} )
+    endif()
+    if( _p_DESCRIPTION )
+      list( APPEND _opts DESCRIPTION "${_p_DESCRIPTION}" )
+    endif()
+    if( _p_TYPE )
+      list( APPEND _opts TYPE ${_p_TYPE} )
+    endif()
+    if( _p_PURPOSE )
+      list( APPEND _opts PURPOSE "${_p_PURPOSE}" )
+    endif()
+    if( _p_FAILURE_MSG )
+      ecbuild_debug_var( _p_FAILURE_MSG )
+      list( APPEND _opts FAILURE_MSG "${_p_FAILURE_MSG}" )
+    endif()
+
+    ecbuild_find_package( NAME ${_p_PROJECT} ${_opts} )
+
+    if( ${_p_PROJECT}_FOUND )
+      set( ${pkgUPPER}_FOUND ${${_p_PROJECT}_FOUND} )
+    endif()
+
+  endif()
+
+  if( ${pkgUPPER}_FOUND )
+    list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${_p_PROJECT} )
+    list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+  endif()
+
+  ### for when we change this macro to a function()
+  # set_parent_scope( ${pkgUPPER}_FOUND )
+  # set_parent_scope( ${_p_PROJECT}_FOUND )
+  # set_parent_scope( ${pkgUPPER}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_BINARY_DIR )
+
+endmacro()
diff --git a/cmake/ecbuild_version.h.in b/cmake/ecbuild_version.h.in
new file mode 100644
index 0000000..89b0e33
--- /dev/null
+++ b/cmake/ecbuild_version.h.in
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ecbuild_version_h
+#define ecbuild_version_h
+
+#define ECBUILD_VERSION "@ECBUILD_VERSION@"
+
+#define ECBUILD_MAJOR_VERSION @ECBUILD_MAJOR_VERSION@
+#define ECBUILD_MINOR_VERSION @ECBUILD_MINOR_VERSION@
+#define ECBUILD_PATCH_VERSION @ECBUILD_PATCH_VERSION@
+
+#endif // ecbuild_version_h
diff --git a/cmake/ecbuild_warn_unused_files.cmake b/cmake/ecbuild_warn_unused_files.cmake
new file mode 100644
index 0000000..7d330d7
--- /dev/null
+++ b/cmake/ecbuild_warn_unused_files.cmake
@@ -0,0 +1,77 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_warn_unused_files
+# =========================
+#
+# Print warnings about unused source files in the project. ::
+#
+#   ecbuild_warn_unused_files()
+#
+# If the CMake variable ``CHECK_UNUSED_FILES`` is set, ecBuild will keep track
+# of any source files (.c, .cc, .cpp, .cxx) which are not part of a CMake
+# target. If set, this macro reports unused files if any have been found. This
+# is considered a fatal error unless ``UNUSED_FILES_LEVEL`` is set to a value
+# different from ``ERROR``.
+#
+# .. note ::
+#
+#   Enabling ``CHECK_UNUSED_FILES`` can slow down the CMake configure time
+#   considerably!
+#
+##############################################################################
+
+macro( ecbuild_warn_unused_files )
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME ) # only for top level project
+    
+      # if cache file with unused files exists remove it
+      set( UNUSED_FILE "${CMAKE_BINARY_DIR}/UnusedFiles.txt" )
+      if( EXISTS ${UNUSED_FILE} )
+              file( REMOVE ${UNUSED_FILE} )
+      endif()
+    
+      if( CHECK_UNUSED_FILES ) # to check or not to check...
+    
+          if( NOT DEFINED UNUSED_FILES_LEVEL ) # to err or not...
+              set( UNUSED_FILES_LEVEL "ERROR" )
+          endif()
+    
+          # if unused files where found, put the list on the file
+          if( EC_UNUSED_FILES )
+    
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+            ecbuild_info(" Unused source files found:")
+            foreach( AFILE ${EC_UNUSED_FILES} )
+              ecbuild_info("     ${AFILE}")
+              file( APPEND ${UNUSED_FILE} "${AFILE}\n" )
+            endforeach()
+            ecbuild_info("")
+            ecbuild_info(" List dumped to ${UNUSED_FILE}")
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+    
+            if( UNUSED_FILES_LEVEL STREQUAL "ERROR" )
+              ecbuild_critical( "\n Aborted build system configuration. \n Add unused files to the build system or remove them." )
+            endif()
+    
+          endif()
+    
+      endif()
+    
+    endif()
+
+endmacro( ecbuild_warn_unused_files )
diff --git a/cmake/fcm-make-interfaces.cfg b/cmake/fcm-make-interfaces.cfg
new file mode 100644
index 0000000..a73363a
--- /dev/null
+++ b/cmake/fcm-make-interfaces.cfg
@@ -0,0 +1,31 @@
+# FCM configuration file used to auto-generate interface files
+# for F77 and F90 files.
+# Interface files will have the extention ".intfb.h"
+# Results will be in a directory "interfaces/include" relative to cwd
+
+# Usage: fcm make --config-file=<path -to-this-file> \
+#                 interfaces.ns-incl="<space-sep-list-of-dirs>"
+
+$SRC{?}  = $HERE
+
+step.class[interfaces] = build
+steps  = interfaces
+
+interfaces.target{task}     = ext-iface
+interfaces.target{category} = include
+
+interfaces.source = $SRC
+
+# Exclude all
+interfaces.ns-excl = /
+
+# Include some
+# interfaces.ns-incl = <list of dirs passed at command-line>
+
+# Extention of interface files
+interfaces.prop{file-ext.f90-interface} = .intfb.h
+
+# Do not follow includes
+interfaces.prop{no-dep.f.module} = *
+interfaces.prop{no-dep.include} = *
+
diff --git a/cmake/fortran_features/CheckFortranFeatures.cmake b/cmake/fortran_features/CheckFortranFeatures.cmake
new file mode 100644
index 0000000..992964f
--- /dev/null
+++ b/cmake/fortran_features/CheckFortranFeatures.cmake
@@ -0,0 +1,167 @@
+###############################################################################
+# checks
+set(Fortran_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "fortran file directory")
+
+MACRO(fortran_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/fortran_feature_tests/fortran_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking Fortran support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.F90")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.F90")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.F90")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "Fortran support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(fortran_check_single_feature)
+
+# Find list of all features
+function(fortran_find_all_features outvar)
+  FILE(GLOB ALL_Fortran_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/*.F90")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_Fortran_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_fortran_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/${current_feature}*.F90")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[Fortran] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+  fortran_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_fortran_feature)
+
+function(fortran_feature_check)
+
+  # find all features
+  fortran_find_all_features(ALL_Fortran_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_Fortran_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[Fortran] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_fortran_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_fortran_feature(${current_feature})
+    set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[Fortran] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(fortran_feature_check)
diff --git a/cmake/fortran_features/c_size_t.F90 b/cmake/fortran_features/c_size_t.F90
new file mode 100644
index 0000000..3c47136
--- /dev/null
+++ b/cmake/fortran_features/c_size_t.F90
@@ -0,0 +1,8 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_size_t, c_int, c_long
+
+write(0,*) "c_int    = ",c_int
+write(0,*) "c_long   = ",c_long
+write(0,*) "c_size_t = ",c_size_t
+
+end program
\ No newline at end of file
diff --git a/cmake/fortran_features/c_sizeof.F90 b/cmake/fortran_features/c_sizeof.F90
new file mode 100644
index 0000000..fc1be41
--- /dev/null
+++ b/cmake/fortran_features/c_sizeof.F90
@@ -0,0 +1,3 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_sizeof
+end program
\ No newline at end of file
diff --git a/cmake/fortran_features/derivedtype_interface.F90 b/cmake/fortran_features/derivedtype_interface.F90
new file mode 100644
index 0000000..d59a1c3
--- /dev/null
+++ b/cmake/fortran_features/derivedtype_interface.F90
@@ -0,0 +1,54 @@
+module constructor
+
+implicit none
+
+TYPE :: AnimalType
+  private
+  integer :: m_age
+contains
+  procedure :: age
+  procedure :: speak
+ENDTYPE
+
+! Declare constructor as interface with same name as type
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+contains
+
+function AnimalType__ctor(age) result(self)
+  type(AnimalType) :: self
+  integer :: age
+  write(0,'(A)') "Constructor Animal"
+  self%m_age = age
+end function
+
+function age(self)
+  class(AnimalType), intent(inout) :: self
+  integer :: age
+  age = self%m_age
+end function
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(A)') "Animal::speak not overridden"
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_constructor
+use constructor
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8)
+
+  write(0,'(A,I0)') "age = ",animal%age()
+
+  call animal%speak()
+
+end program
diff --git a/cmake/fortran_features/derivedtype_io.F90 b/cmake/fortran_features/derivedtype_io.F90
new file mode 100644
index 0000000..47a98b0
--- /dev/null
+++ b/cmake/fortran_features/derivedtype_io.F90
@@ -0,0 +1,42 @@
+module write_module
+
+implicit none
+
+TYPE :: AnimalType
+  integer :: m_age
+  integer :: m_paws
+contains
+  procedure :: writetype
+  generic :: write(formatted) => writetype
+ENDTYPE
+
+contains
+
+subroutine writetype(animal, unit, iotype, v_list, iostat, iomsg)
+  ! Argument names here from the std, but you can name them differently.
+  class(AnimalType), intent(in) :: animal ! Object to write.
+  integer, intent(in) :: unit             ! Internal unit to write to.
+  character(*), intent(in) :: iotype      ! LISTDIRECTED or DTxxx
+  integer, intent(in) :: v_list(:)        ! parameters from fmt spec.
+  integer, intent(out) :: iostat          ! non zero on error, etc.
+  character(*), intent(inout) :: iomsg    ! define if iostat non zero.
+
+  write (unit, "(A)", IOSTAT=iostat, IOMSG=iomsg) &
+      "I am a dog"
+end subroutine writetype
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_write
+use write_module
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8,4)
+
+  write(0,'(A,DT)') 'Custom writing: ',animal
+
+end program
diff --git a/cmake/fortran_features/finalization.F90 b/cmake/fortran_features/finalization.F90
new file mode 100644
index 0000000..5bacd5f
--- /dev/null
+++ b/cmake/fortran_features/finalization.F90
@@ -0,0 +1,141 @@
+module final_module
+
+implicit none
+
+integer :: final_counted = 0
+integer :: destroy_counted = 0
+
+TYPE :: AnimalType
+  character(len=20), private :: m_kind = "unidentified"
+  logical :: constructed = .false.
+contains
+  procedure :: speak
+  final :: AnimalType__dtor
+ENDTYPE
+
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+interface assignment(=)
+  module procedure AnimalType__assignment
+end interface
+
+contains
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(2A)') "I am a ",self%m_kind
+end subroutine
+
+subroutine AnimalType__dtor(self)
+  type(AnimalType), intent(inout) :: self
+
+  write(0,'(2A)') "Final animal ",self%m_kind
+  final_counted = final_counted + 1
+
+  ! Destruction guard needed for portability
+  if( self%constructed ) then
+    write(0,'(2A)') "    Destroy animal ",self%m_kind
+    destroy_counted = destroy_counted + 1
+  endif
+end subroutine
+
+function AnimalType__ctor(animaltype_) result(self)
+  type(AnimalType) :: self
+  character(len=*) :: animaltype_
+  self%m_kind = animaltype_
+  write(0,'(3A,I0)') "Constructing animal ",self%m_kind, " -- address = ",loc(self)
+  self%constructed = .true.
+end function
+
+subroutine AnimalType__assignment(animal_out,animal_in)
+  type(AnimalType), intent(out) :: animal_out
+  class(AnimalType), intent(in) :: animal_in
+  write(0,'(3A,I0,A,I0)') '   Copying ',animal_in%m_kind, " -- from address ", loc(animal_in), " to address ", loc(animal_out)
+  animal_out%m_kind = animal_in%m_kind
+  animal_out%constructed = animal_in%constructed
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+subroutine scope_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: cat
+
+  dog = AnimalType("dog")  ! Cray       : final called on temporary AnimalType("dog"); missing final call on dog before assignment
+                           ! Intel      : final called on dog before assignment; and on temporary AnimalType("dog")
+                           ! PGI 14.4   : final NOT called at all, possibly compiler bug
+                           ! GNU 4.9    : final called on dog before assignment; missing call on temporary AnimalType("dog")
+  call dog%speak()
+
+  ! final called on dog when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+subroutine assignment_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: animal
+
+  dog = AnimalType("dog")    ! final called on dog before assignment
+  call dog%speak()
+  write(0,'(A)') "-- animal = dog"
+  animal = dog               ! final called on animal before assignment
+  call animal%speak()
+
+  ! final called on dog when out of scope
+  ! final called on animal when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+program test_final
+use final_module
+implicit none
+  logical :: test_failed = .false.
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin scope_test"
+  call scope_test
+  write(0,'(A)') "<<<<<< end scope_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 1 ) then
+    test_failed = .true.
+    write(0,'(A)') "ASSERTION FAILED: destroy_counted < 1"
+  endif
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin assignment_test"
+  call assignment_test
+  write(0,'(A)') "<<<<<< end assignment_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 2 ) then
+    test_failed = .true.
+    write(0,*) "ASSERTION FAILED: destroy_counted < 2"
+  endif
+  if( test_failed ) STOP 1
+
+end program
diff --git a/cmake/fortran_features/submodules.F90 b/cmake/fortran_features/submodules.F90
new file mode 100644
index 0000000..3a2261f
--- /dev/null
+++ b/cmake/fortran_features/submodules.F90
@@ -0,0 +1,35 @@
+module sb_module
+implicit none
+integer :: a = 1
+
+interface
+  module subroutine sb
+  end subroutine
+end interface
+
+contains
+end module sb_module
+
+! -------------------------------------------------------
+
+submodule (sb_module) sb_submod1
+implicit none
+integer :: b = 2
+
+contains
+
+module subroutine sb()
+  a = b
+end subroutine
+
+end submodule sb_submod1
+
+! -------------------------------------------------------
+
+program test_submodule
+use sb_module
+implicit none
+write(0,*) a
+call sb()
+write(0,*) a
+end program
\ No newline at end of file
diff --git a/cmake/gen_source_flags.py b/cmake/gen_source_flags.py
new file mode 100644
index 0000000..c08ad08
--- /dev/null
+++ b/cmake/gen_source_flags.py
@@ -0,0 +1,84 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+"""
+Generate .cmake file to set source-file specific compiler flags based on
+rules defined in a JSON file.
+"""
+
+from argparse import ArgumentParser
+from fnmatch import fnmatch
+import logging
+from json import JSONDecoder
+from os import path
+
+log = logging.getLogger('gen_source_flags')
+
+
+def match(source, pattern, op, flags, indent=0):
+    if fnmatch(source, pattern):
+
+        suff = '' if op[0] in ('+', '=', '/') else ' (nested pattern)'
+        log.debug('%s-> pattern "%s" matches "%s"%s',
+                  ' ' * (indent + 1), pattern, source, suff)
+
+        if op[0] == "+":
+            flags += [flag for flag in op[1:] if flag not in flags]
+            log.debug('%sappending %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "=":
+            flags = op[1:]
+            log.debug('%ssetting %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "/":
+            flags = [flag for flag in flags if flag not in op[1:]]
+            log.debug('%sremoving %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        else:  # Nested rule
+            log.debug('%sapplying nested rules for "%s" (flags: %s)',
+                      ' ' * (indent + 2), pattern, flags)
+            for nested_pattern, nested_op in op:
+                flags = match(source, nested_pattern, nested_op, flags, indent + 2)
+
+    return flags
+
+
+def generate(rules, out, default_flags, sources, debug=False):
+    logging.basicConfig(level=logging.DEBUG if debug else logging.INFO,
+                        format='-- %(levelname)s - %(name)s: %(message)s')
+
+    with open(path.expanduser(rules)) as f:
+        rules = JSONDecoder(object_pairs_hook=list).decode(f.read())
+
+    with open(path.expanduser(out), 'w') as f:
+        for source in sources:
+            log.debug('%s (default flags: "%s")', source, default_flags)
+            flags = default_flags.split()
+            for pattern, op in rules:
+                flags = match(source, pattern, op, flags)
+
+            if flags:
+                log.debug(' ==> setting flags for %s to %s', source, ' '.join(flags))
+                f.write('set_source_files_properties(%s PROPERTIES COMPILE_FLAGS "%s")\n'
+                        % (source, ' '.join(flags)))
+            else:
+                log.debug(' ==> flags for %s empty', source)
+
+
+def main():
+    """Parse arguments"""
+    parser = ArgumentParser(description=__doc__)
+    parser.add_argument('rules', metavar='RULES.json', help='JSON rules file')
+    parser.add_argument('out', metavar='OUT.cmake', help='CMake script to generate')
+    parser.add_argument('default_flags', help='Default compiler flags to use')
+    parser.add_argument('sources', metavar='file', nargs='+', help='Path to file to apply rules to')
+    parser.add_argument('--debug', '-d', action='store_true', help='Log debug messages')
+    generate(**vars(parser.parse_args()))
+
+if __name__ == '__main__':
+    main()
diff --git a/cmake/include/ecbuild/boost_test_framework.h b/cmake/include/ecbuild/boost_test_framework.h
new file mode 100644
index 0000000..ba48689
--- /dev/null
+++ b/cmake/include/ecbuild/boost_test_framework.h
@@ -0,0 +1,17 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifdef BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#include <boost/test/included/unit_test.hpp>
+#else
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+#endif
+
diff --git a/cmake/md5.in b/cmake/md5.in
new file mode 100644
index 0000000..bf8aeb0
--- /dev/null
+++ b/cmake/md5.in
@@ -0,0 +1 @@
+ at _p_MD5@  @_p_NAME@
diff --git a/cmake/pkg-config.pc.in b/cmake/pkg-config.pc.in
new file mode 100644
index 0000000..e6d903d
--- /dev/null
+++ b/cmake/pkg-config.pc.in
@@ -0,0 +1,35 @@
+# This pkg-config file is generated by ecbuild_pkgconfig()
+# with template ecbuild/cmake/pkg-config.pc.in
+
+git_tag=@PKGCONFIG_GIT_TAG@
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@INSTALL_LIB_DIR@
+includedir=${prefix}/@INSTALL_INCLUDE_DIR@
+bindir=${prefix}/@INSTALL_BIN_DIR@
+fmoddir=${prefix}/@INSTALL_INCLUDE_DIR@
+
+CC=@CMAKE_C_COMPILER@
+CXX=@CMAKE_CXX_COMPILER@
+FC=@CMAKE_Fortran_COMPILER@
+
+rpath=@RPATH_FLAG@${libdir}
+
+libs=-L${libdir} ${rpath} @PKGCONFIG_LIBS@
+
+libs_private=@PKGCONFIG_LIBS_PRIVATE@
+
+cflags=@PKGCONFIG_INCLUDE@ @PKGCONFIG_CFLAGS@
+ at PKGCONFIG_VARIABLES@
+#====================================================================
+Name: @PKGCONFIG_NAME@
+Description: @PKGCONFIG_DESCRIPTION@
+URL: @PKGCONFIG_URL@
+Version: @PKGCONFIG_VERSION@
+Libs: ${libs}
+Libs.private: ${libs_private}
+Requires: @PKGCONFIG_REQUIRES@
+Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@
+Cflags: ${cflags}
+#====================================================================
diff --git a/cmake/project-config-version.cmake.in b/cmake/project-config-version.cmake.in
new file mode 100644
index 0000000..802a2fe
--- /dev/null
+++ b/cmake/project-config-version.cmake.in
@@ -0,0 +1,12 @@
+set(PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# check whether the requested PACKAGE_FIND_VERSION is compatible
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+  set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+  set(PACKAGE_VERSION_COMPATIBLE TRUE)
+  if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+    set(PACKAGE_VERSION_EXACT TRUE)
+  endif()
+endif()
\ No newline at end of file
diff --git a/cmake/project-config.cmake.in b/cmake/project-config.cmake.in
new file mode 100644
index 0000000..0e1e83b
--- /dev/null
+++ b/cmake/project-config.cmake.in
@@ -0,0 +1,97 @@
+# Config file for the @PROJECT_NAME@ package
+# Defines the following variables:
+#
+#  @PNAME at _INCLUDE_DIRS   - include directories
+#  @PNAME at _DEFINITIONS    - preprocessor definitions
+#  @PNAME at _LIBRARIES      - libraries to link against
+#  @PNAME at _FEATURES       - list of enabled features
+#  @PNAME at _VERSION        - version of the package
+#  @PNAME at _GIT_SHA1       - Git revision of the package
+#  @PNAME at _GIT_SHA1_SHORT - short Git revision of the package
+#
+# Also defines @PROJECT_NAME@ third-party library dependencies:
+#  @PNAME at _TPLS             - package names of  third-party library dependencies
+#  @PNAME at _TPL_INCLUDE_DIRS - include directories
+#  @PNAME at _TPL_DEFINITIONS  - preprocessor definitions
+#  @PNAME at _TPL_LIBRARIES    - libraries to link against
+
+### compute paths
+
+get_filename_component(@PNAME at _CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+
+set( @PNAME at _SELF_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@" )
+set( @PNAME at _SELF_DEFINITIONS  "@CONF_DEFINITIONS@" )
+set( @PNAME at _SELF_LIBRARIES    "@CONF_LIBRARIES@" )
+
+set( @PNAME at _TPLS              "@CONF_TPLS@" )
+set( @PNAME at _TPL_INCLUDE_DIRS  "@CONF_TPL_INCLUDE_DIRS@" )
+set( @PNAME at _TPL_DEFINITIONS   "@CONF_TPL_DEFINITIONS@" )
+set( @PNAME at _TPL_LIBRARIES     "@CONF_TPL_LIBRARIES@" )
+
+set( @PNAME at _VERSION           "@PACKAGE_VERSION@" )
+set( @PNAME at _GIT_SHA1          "@PACKAGE_GIT_SHA1@" )
+set( @PNAME at _GIT_SHA1_SHORT    "@PACKAGE_GIT_SHA1_SHORT@" )
+
+### export include paths as absolute paths
+
+set( @PNAME at _INCLUDE_DIRS "" )
+foreach( path ${@PNAME at _SELF_INCLUDE_DIRS} )
+  get_filename_component( abspath ${path} ABSOLUTE )
+  list( APPEND @PNAME at _INCLUDE_DIRS ${abspath} )
+endforeach()
+list( APPEND @PNAME at _INCLUDE_DIRS ${@PNAME at _TPL_INCLUDE_DIRS} )
+
+### export definitions
+
+set( @PNAME at _DEFINITIONS      ${@PNAME at _SELF_DEFINITIONS} ${@PNAME at _TPL_DEFINITIONS} )
+
+### export list of all libraries
+
+set( @PNAME at _LIBRARIES        ${@PNAME at _SELF_LIBRARIES}   ${@PNAME at _TPL_LIBRARIES}   )
+
+### export the features provided by the package
+
+set( @PNAME at _FEATURES    "@CONF_FEATURES@" )
+foreach( _f ${@PNAME at _FEATURES} )
+  set( @PNAME at _HAVE_${_f} 1 )
+endforeach()
+
+# Has this configuration been exported from a build tree?
+set( @PNAME at _IS_BUILD_DIR_EXPORT @_is_build_dir_export@ )
+
+if( EXISTS ${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@ )
+  set( @PNAME at _IMPORT_FILE "${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@" )
+  include( ${@PNAME at _IMPORT_FILE} )
+endif()
+
+# here goes the imports of the TPL's
+
+include( ${CMAKE_CURRENT_LIST_FILE}.tpls OPTIONAL )
+
+# insert definitions for IMPORTED targets
+
+if( NOT @PROJECT_NAME at _BINARY_DIR )
+
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    include( "@TOP_PROJECT_TARGETS_FILE@" OPTIONAL )
+  else()
+    include( "${@PNAME at _CMAKE_DIR}/@PROJECT_NAME at -targets.cmake" OPTIONAL )
+  endif()
+
+endif()
+
+# publish this file as imported
+
+set( @PNAME at _IMPORT_FILE ${CMAKE_CURRENT_LIST_FILE} )
+mark_as_advanced( @PNAME at _IMPORT_FILE )
+
+# set @PROJECT_NAME at _BASE_DIR for final installations or build directories
+
+if( NOT @PROJECT_NAME@ )
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    set( @PROJECT_NAME at _BASE_DIR @CMAKE_BINARY_DIR@ )
+  else()
+    get_filename_component( abspath ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE )
+    set( @PROJECT_NAME at _BASE_DIR ${abspath} )
+  endif()
+endif()
diff --git a/cmake/pymain.c b/cmake/pymain.c
new file mode 100644
index 0000000..823d57d
--- /dev/null
+++ b/cmake/pymain.c
@@ -0,0 +1,5 @@
+#include <Python.h>
+
+int main() {
+  return 0;
+}
diff --git a/cmake/sg.pl b/cmake/sg.pl
new file mode 100755
index 0000000..f8c8e31
--- /dev/null
+++ b/cmake/sg.pl
@@ -0,0 +1,573 @@
+#!/usr/bin/perl
+#!/usr/local/share/perl56
+
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+use strict;
+
+#use Data::Dumper;
+use File::Basename;
+
+#$Data::Dumper::Indent = 1;
+# $ARGV[0] = "test.cc";
+# $ARGV[0] = "/usr/include/g++-3/stl_pair.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_vector.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_list.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_map.h";
+# $ARGV[0] = "x.cc";
+# $ARGV[0] = "/usr/include/g++-3/std/bastring.h";
+
+# script takes 3 parameters:
+# (1) file to process
+my $file = $ARGV[0];
+# (2) [optional] directory to place the generated .b file
+my $base = $ARGV[1];
+# (3) [optional] c++ namespace 
+my $namespace = $ARGV[2];
+
+# no argv[1] passed, take basedir from file
+if( $base eq "" )
+{
+	$base = dirname($file);
+}
+
+# no argv[1] passed, take basedir from file
+if( $namespace eq "" )
+{
+    $namespace = "eclib"
+}
+
+my @c = parser::parse($file);
+#print Dumper(\@c);
+
+
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(STDOUT,">$base/$n.b") || die "$base/$n.b: $!";
+
+	my @init1;
+	push @init1, map { "$_(b)" } $c->super;
+	push @init1, map { "$_(b(\&$_))" } $c->members;
+
+	my $col1;
+	$col1=":\n" if(@init1);
+	my $init1 = join(",\n",map {"\t$_"} @init1);
+
+	my @init2;
+	push @init2, map { "$_(b(\"$n\"))" } $c->super;
+	push @init2, map { "$_(b(\"$n\",\"$_\"))" } $c->members;
+
+	my $col2;
+	$col2=":\n" if(@init2);
+	my $init2 = join(",\n",map {"\t$_"} @init2);
+
+
+	my @s = map { "${_}::describe(s,depth+1)"      } $c->super;
+	my @m = map { "${namespace}::_describe(s,depth+1,\"$_\",$_)" } $c->members;
+	my $d = join(";\n\t","${namespace}::_startClass(s,depth,specName())", at s, at m,"${namespace}::_endClass(s,depth,specName())");
+
+	my @s = map { "${_}::_export(h)"      } $c->super;
+	my @m = map { "${namespace}::_export(h,\"$_\",$_)" } $c->members;
+	my $D = join(";\n\t","${namespace}::_startClass(h,\"$n\")", at s, at m,"${namespace}::_endClass(h,\"$n\")");
+
+	my $spec = "\"$n\"";
+	my @tmpl = $c->template;
+
+	my $spec_type = "const char*";
+
+	if(@tmpl)
+	{
+		$spec_type = "std::string";
+		my $x = join("+ ',' + ",  map { "Traits<$_>::name()"; } @tmpl);
+		$spec = <<"EOS";
+        std::string("$n<\") + $x + ">"
+EOS
+		$spec =~ s/\n/ /g;
+	}
+
+	my $isa = "${namespace}::Isa::add(t,specName());";
+	foreach my $s ( $c->super )
+	{
+		$isa = "${s}::isa(t);$isa";
+	}
+
+	my $schema;
+	@s = map { "${_}::schema(s)"      } $c->super;
+	@m = map { $a=$_->[0]; $b=$_->[1]; "s.member(\"$a\",member_size($n,$a),member_offset($n,$a),\"$b\")" } $c->members_types;
+	$schema = join(";\n\t","s.start(specName(),sizeof($n))", at s, at m,"s.end(specName())");
+
+	print <<"EOF";
+
+${n}(${namespace}::Bless& b)$col1$init1
+{
+}
+
+${n}(${namespace}::Evolve b)$col2$init2
+{
+}
+
+static ${spec_type} specName()      { return ${spec}; }
+static void isa(TypeInfo* t)  { ${isa} }
+static ${namespace}::Isa* isa()             { return ${namespace}::Isa::get(specName());  }
+
+static void schema(${namespace}::Schema& s)
+{
+	$schema;
+}
+
+EOF
+
+if(!$c->has_method("describe"))
+{
+print <<"EOF";
+
+void describe(std::ostream& s,int depth = 0) const {
+	$d;
+}
+
+
+EOF
+}
+
+print <<"EOF";
+
+void _export(${namespace}::Exporter& h) const { 
+	$D;
+}
+
+
+EOF
+
+}
+if(0)
+{
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(OUT,">${n}.b");
+	select OUT;
+	print "static void schema(${namespace}::Schema& s) {\n";
+	foreach my $x ( $c->super )
+	{
+		print "${x}::schema(s);\n";
+		#print "s(\"$x\", 0,sizeof($x));\n";
+	}
+	foreach my $x ( $c->members )
+	{
+		print "s(\"${n}::$x\",offsetof($n,$x),sizeof(&(($n*)0)->$x));\n";
+	}
+	print "}\n";
+}
+}
+package parser;
+use Carp;
+my @TOKENS;
+sub parse {
+	my ($file) = @_;
+	local $/ = undef;
+	open(IN,"<$file") || croak "$file: $!";
+	my $x = <IN>;
+	close(IN);
+	$x =~ s/^#.*$//mg;
+	$x =~ s/\/\/.*$//mg;
+	@TOKENS =
+		grep { length($_);                }
+		map  { /\W/ ? split('',$_) :  $_; }
+		map  { s/\s//g; $_;               }
+		split(/\b/, $x );
+
+	my @c;
+	my $x;
+	while($x = consume_until("(typedef|template|class|struct)"))
+	{
+		if($x eq 'typedef')
+		{
+			consume_until(";");
+			next;
+		}
+
+		if($x eq 'template')
+		{
+			push @c, parse_template();
+		}
+		else
+		{
+			push @c, parse_class();
+		}
+	}
+	return grep { defined $_; } @c;
+}
+
+sub parse_template {
+	my @tmp = template_args();
+	return parse_class(@tmp) if(next_is("(class|struct)"));
+}
+sub template_args {
+	my @tmp;
+	expect_next("<");
+	for(;;)
+	{
+		expect_next("(class|bool|int)");
+		push @tmp, next_ident();
+		if(next_is("="))
+		{
+			my $x = consume_until('(,|\>|\<)');
+			unshift @TOKENS,$x;
+			while($x eq '<')
+			{
+				consume_block('<','>');
+				$x = consume_until('(,|\>|\<)');
+				unshift @TOKENS,$x;
+			}
+		}
+		last unless(next_is(","));
+	}
+	expect_next(">");
+	return @tmp;
+}
+sub parse_class {
+	my (@tmp) = @_;
+	my $self = {};
+	my $name = next_ident();
+	$self->{name}     = $name;
+	$self->{template} = \@tmp if(@tmp);
+	# Foreward declaration
+	return if(next_is(";"));
+	if(next_is(":"))
+	{
+		for(;;)
+		{
+			ignore_while("(public|private|protected|virtual)");
+			push @{$self->{super}}, next_ident();
+			last unless(next_is(","));
+		}
+	}
+	expect_next('{');
+	while(!peek_next('}'))
+	{
+		# print "... : $TOKENS[0], $TOKENS[1], ... \n";
+		if(next_is('\/'))
+		{
+			if(next_is('\*'))
+			{
+				while(!next_is('\/'))
+				{
+					consume_until('\*');
+				}
+				next;
+			}
+			else
+			{
+				unshift @TOKENS, "/";	
+			}
+
+		}
+
+		if(next_is("(public|private|protected)"))
+		{
+			expect_next(":");
+			next;
+		}
+
+		if(next_is("friend"))
+		{
+			my $x = consume_until("(;|{)");
+			if($x eq "{")
+			{
+				unshift @TOKENS, $x;
+				consume_block('{','}');
+			}
+			next;
+		}
+
+		# next_is("explicit");
+		if(next_is("(typedef|using|typename|enum)"))
+		{
+			consume_until(";");
+			next;
+		}
+		if(next_is("(class|struct)"))
+		{
+			push @{$self->{classes}}, parse_class();
+			next;
+		}
+		my %m;
+		while(next_is("template"))
+		{
+			push @{ $m{template} } , template_args();
+		}
+
+		my @x;
+#		push @x,"~" while(next_is('\~'));
+
+		$m{explicit} = 1 if(next_is("explicit"));
+		$m{static}   = 1 if(next_is("static"));
+		$m{virtual}  = 1 if(next_is("virtual"));
+		my $x;
+		while($x = next_is_ident())
+		{
+			# print "--- : $x\n";
+			push @x, $x;
+			push @x,'*' while(next_is('\*'));
+			push @x,'&' while(next_is('\&'));
+			$m{name} = $x;
+			# int a,b,*c; does not work
+			my $s;
+			if($s = next_is('(,|;|=)'))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				if(exists $m{static})
+				{
+					push @{$self->{class_members}}, \%m;
+				}
+				else
+				{
+					push @{$self->{members}}, \%m;
+				}
+				consume_until(";") if($s eq '=');
+				last;
+			}
+			if(peek_next('\('))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				my @args = consume_block('(',')');
+				shift @args;
+				pop @args;
+				my @a;
+				my $n = 0;
+				my @z;
+				foreach my $a ( @args )
+				{
+					if($a eq ',' && $n == 0)
+					{
+						push @a, make_type(@z);
+						@z = ();
+						next;
+					}
+					$n++ if($a eq '<');	
+					$n++ if($a eq '(');	
+					$n-- if($a eq ')');	
+					$n-- if($a eq '>');	
+					push @z,$a;
+				}
+				push @a, make_type(@z) if(@z);
+				$m{const} = 1 if(next_is("const"));
+				$m{args}  = \@a;
+				if(exists $m{static})
+				{
+					push @{$self->{class_methods}}, \%m;
+				}
+				else
+				{
+					push @{$self->{methods}}, \%m;
+				}
+				# print "f: $x\n";
+
+				if(next_is(':'))
+				{
+					# print "{: $TOKENS[0]\n";
+					consume_until('\{');
+					unshift @TOKENS, '{';
+					consume_block('{','}');
+					# print "}: $TOKENS[0]\n";
+				}
+				else
+				{
+					if(peek_next('\{'))
+					{
+						consume_block('{','}');
+					}
+					else
+					{
+						if(next_is("="))
+						{
+							expect_next("0");
+							$m{abstract} = 1;
+						}
+						expect_next(";");
+					}
+				}
+				last;
+			}
+		}
+
+	}
+	expect_next("}");
+	expect_next(";");
+	return bless($self,"class");
+}
+sub consume_until {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		return $x if($x =~ /^$r$/);
+	}
+	return undef;
+}
+sub consume_block {
+	my ($bra,$ket) = @_;
+	my $n = 0;
+	my @x;
+	croak "@TOKENS" unless($bra eq $TOKENS[0]);
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		$n++ if($x eq $bra);
+		$n-- if($x eq $ket);
+		push @x,$x;
+		return @x if($n == 0);
+	}
+}
+sub ignore_while {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		return unless($TOKENS[0] =~ /^$r$/);
+		shift @TOKENS;
+	}
+}
+sub expect_next {
+	my ($r) = @_;
+	my $ident = shift @TOKENS;
+	croak "$ident is not $r" unless($ident =~ /^$r$/);
+	return $ident;
+}
+sub next_ident {
+	my $x = next_is_ident();
+	croak "not an ident " unless($x);
+	return $x;
+}
+sub next_is {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return shift @TOKENS;
+	}
+	return undef;
+}
+sub next_is_ident {
+	my $op = next_is("operator");
+	if($op)
+	{
+		my $x;
+		if($x = next_is("(new|delete)"))
+		{
+			$op .= " $x";
+			if(next_is('\['))
+			{
+				expect_next('\]');
+				$op .= "[]";
+			}
+			return $op;
+		}
+
+		if(next_is('\('))
+		{
+			expect_next('\)');
+			$op .= "()";
+		}
+		my $z;
+		while($z = next_is('[\-+/\*\[\]<>=!]'))
+		{
+			$op .= $z;
+		}
+		return $op;
+	}
+	my $y = next_is('\~');
+	my $x = next_is('\w+');
+	if($x)
+	{
+		$x = "$y$x";
+		if(peek_next("<"))
+		{
+			my @x = consume_block("<",">");
+			$x .= join("", at x);
+		}
+
+		if(next_is(":"))
+		{
+			if(next_is(":"))
+			{
+				my $z = next_is_ident();
+				return "${x}::${z}";
+			}
+			else
+			{
+				unshift @TOKENS, ":";
+			}
+		}
+	}
+	return $x;
+}
+sub peek_next {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return 1;
+	}
+	return 0;
+}
+sub make_type {
+	my (@a) = @_;
+	my $p;
+	my @x;
+	foreach my $a ( @a )
+	{
+		push @x, " " if($p =~ /^(\w+|\&|\*)$/ && $a =~ /^\w+$/);
+		push @x, $a;
+		$p = $a;
+	}
+	my $s = join('', at x);
+	$s =~ s/>>/> >/g;
+	return $s;
+}
+package class;
+sub name {
+	my ($self) = @_;
+	return $self->{name};
+}
+sub super {
+	my ($self) = @_;
+	return $self->{super} ? @{$self->{super}} : ();
+}
+
+sub members {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub methods {
+	my ($self) = @_;
+	my @x = $self->{methods} ? @{$self->{methods}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub has_method {
+	my ($self,$name) = @_;
+	return grep { $_ eq $name } $self->methods;
+}
+
+sub members_types {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { [ $_->{name}, $_->{type} ]  } @x;
+}
+
+sub template {
+	my ($self) = @_;
+	return $self->{template} ? @{$self->{template}} : ();
+}
+1;
+
+
diff --git a/cmake/sha1.in b/cmake/sha1.in
new file mode 100644
index 0000000..8e744a7
--- /dev/null
+++ b/cmake/sha1.in
@@ -0,0 +1 @@
+ at _p_SHA1@  @_p_NAME@
diff --git a/ecbuild/.gitignore b/ecbuild/.gitignore
new file mode 100644
index 0000000..228c608
--- /dev/null
+++ b/ecbuild/.gitignore
@@ -0,0 +1,4 @@
+*sublime-workspace
+CMakeLists.txt.user*
+*.log
+*.swp
diff --git a/ecbuild/AUTHORS b/ecbuild/AUTHORS
new file mode 100644
index 0000000..ac1af3c
--- /dev/null
+++ b/ecbuild/AUTHORS
@@ -0,0 +1,13 @@
+eckit Authors
+=============
+
+eckit is mainly developed at the "European Centre for Medium-Range Weather Forecasts" (ECMWF, http://www.ecmwf.int).
+See attached LICENSE file for copyright.
+
+Developers:
+===========
+
+Tiago Quintino
+Baudouin Raoult
+Willem Deconinck
+Florian Rathgeber
diff --git a/ecbuild/CMakeLists.txt b/ecbuild/CMakeLists.txt
new file mode 100644
index 0000000..e13e070
--- /dev/null
+++ b/ecbuild/CMakeLists.txt
@@ -0,0 +1,47 @@
+############################################################################################
+# cmake options:
+#
+#       -DCMAKE_INSTALL_PREFIX=/path/to/install
+
+cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+
+project( ecbuild C )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} )
+
+include( ecbuild_system )
+
+###############################################################################
+# local project
+
+ecbuild_declare_project()
+
+configure_file( cmake/ecbuild_version.h.in ${CMAKE_BINARY_DIR}/ecbuild_version.h  )
+
+ecbuild_add_option( FEATURE INSTALL
+                    DEFAULT OFF
+                    DESCRIPTION "Wether to install ecbuild files" )
+# contents
+
+ecbuild_add_resources(  TARGET ${PROJECT_NAME}_description_files
+                        SOURCES_PACK
+                            INSTALL
+                            AUTHORS
+                            NOTICE
+                            LICENSE
+                            COPYING
+)
+
+add_subdirectory( bin )
+add_subdirectory( share )
+add_subdirectory( cmake )
+add_subdirectory( doc )
+
+install( DIRECTORY cmake DESTINATION ${INSTALL_DATA_DIR} PATTERN "CMakeLists.txt" EXCLUDE )
+
+############################################################################################
+# finalize
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/COPYING b/ecbuild/COPYING
new file mode 100644
index 0000000..457b4a0
--- /dev/null
+++ b/ecbuild/COPYING
@@ -0,0 +1,201 @@
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2007-2013 European Centre for Medium-Range Weather Forecasts (ECMWF)
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/ecbuild/INSTALL b/ecbuild/INSTALL
new file mode 100644
index 0000000..90214db
--- /dev/null
+++ b/ecbuild/INSTALL
@@ -0,0 +1,19 @@
+============================
+ecbuild - ECMWF build system
+============================
+
+Installation and usage instructions can be found at:
+
+https://software.ecmwf.int/wiki/display/ECbuild/ecBuild
+
+=============
+QUICK INSTALL
+=============
+
+1. Extract the source files
+
+ tar zxvf ecbuild-1.9.0-Source.tar.gz -C /path/to/install
+
+2. Use cmake and point the module path to the installed source.
+
+ cmake -DCMAKE_MODULE_PATH=/path/to/install [...]
diff --git a/ecbuild/LICENSE b/ecbuild/LICENSE
new file mode 100644
index 0000000..475dcc3
--- /dev/null
+++ b/ecbuild/LICENSE
@@ -0,0 +1,190 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   Copyright 1996-2017 ECMWF
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/ecbuild/NOTICE b/ecbuild/NOTICE
new file mode 100644
index 0000000..9d55c17
--- /dev/null
+++ b/ecbuild/NOTICE
@@ -0,0 +1,27 @@
+Notice
+------
+
+1. The files FindLex and FindYacc are based on the FindBison and FindFlex
+   macros from the original CMake code base.
+   As requested, the License text has been preserved. The modifications are
+   governed by the Apache License as described in COPYING.
+
+2. CheckFortranCompilerFlag, CheckFortranSourceCompiles and
+   CMakeCheckCompilerFlagCommonPatterns are from CMake 3.7.0 and distributed
+   under the BSD 3-Clause License.
+
+3. FindEigen3 is based on a macro from Montel Laurent and others and
+   distributed under the BSD 2-Clause License.
+
+4. FindNetCDF4 is based on the macro FindNetCDF from project UCLALES from
+   Max-Planck-Institut für Meteorologie and distributed under the Academic
+   Free License v3.0.
+
+5. FindNumPy is by Continuum Analytics and distributed under the MIT License.
+
+6. GetGitRevisionDescription macros are based on the macro with smae name from
+   Ryan Pavlik <rpavlik at iastate.edu> and  Iowa State University, and
+   distributed under Boost license.
+
+7. CMake modules from GreatCMakeCookOff are by University College London and
+   distributed under the MIT License (see the accompanying LICENSE file).
diff --git a/ecbuild/VERSION.cmake b/ecbuild/VERSION.cmake
new file mode 120000
index 0000000..ef90430
--- /dev/null
+++ b/ecbuild/VERSION.cmake
@@ -0,0 +1 @@
+cmake/VERSION.cmake
\ No newline at end of file
diff --git a/ecbuild/bamboo/flags.cmake b/ecbuild/bamboo/flags.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/ecbuild/bin/CMakeLists.txt b/ecbuild/bin/CMakeLists.txt
new file mode 100644
index 0000000..93cef4e
--- /dev/null
+++ b/ecbuild/bin/CMakeLists.txt
@@ -0,0 +1 @@
+install( PROGRAMS ecbuild DESTINATION ${INSTALL_BIN_DIR} )
diff --git a/ecbuild/bin/ecbuild b/ecbuild/bin/ecbuild
new file mode 100755
index 0000000..22ae01f
--- /dev/null
+++ b/ecbuild/bin/ecbuild
@@ -0,0 +1,446 @@
+#!/bin/bash --noprofile
+
+set -eua
+
+CMAKE_MIN_REQUIRED=2.8.11
+CMAKE_BUILD_VERSION=3.5.2
+
+usage()
+{
+  echo "Usage: ecbuild [--help] [--version]"
+  exit $1
+}
+
+help()
+{
+    cat <<EOF
+USAGE:
+
+  ecbuild [--help] [--version] [--toolchains]
+  ecbuild [option...] [--] [cmake-argument...] <path-to-source>
+  ecbuild [option...] [--] [cmake-argument...] <path-to-existing-build>
+
+DESCRIPTION:
+
+  ecbuild is a build system based on CMake, but providing a lot of macro's
+  to make it easier to work with. Upon execution,
+  the equivalent cmake command is printed.
+
+  ecbuild/cmake must be called from an out-of-source build directory and
+  forbids in-source builds.
+
+SYNOPSIS:
+
+    --help         Display this help
+    --version      Display ecbuild version
+    --toolchains   Display list of pre-installed toolchains (see below)
+
+
+Available values for "option":
+
+    --cmakebin=<path>
+          Set which cmake binary to use. Default is 'cmake'
+
+    --prefix=<prefix>
+          Set the install path to <prefix>.
+          Equivalent to cmake argument "-DCMAKE_INSTALL_PREFIX=<prefix>"
+
+    --build=<build-type>
+          Set the build-type to <build-type>.
+          Equivalent to cmake argument "-DCMAKE_BUILD_TYPE=<build-type>"
+          <build-type> can be any of:
+             - debug : Lowest optimization level, useful for debugging
+             - release : Highest optimization level, for best performance
+             - bit : Highest optimization level while staying bit-reproducible
+             - ...others depending on project
+
+    --log=<log-level>
+          Set the ecbuild log-level
+          Equivalent to "-DECBUILD_LOG_LEVEL=<log-level>"
+          <log-level> can be any of:
+             - DEBUG
+             - INFO
+             - WARN
+             - ERROR
+             - CRITICAL
+             - OFF
+          Every choice outputs also the log-levels listed below itself
+
+    --static
+          Build static libraries.
+          Equivalent to "-DBUILD_SHARED_LIBS=OFF"
+
+    --dynamic, --shared
+          Build dynamic libraries (usually the default).
+          Equivalent to "-DBUILD_SHARED_LIBS=ON"
+
+    --config=<config>
+          Configuration file using CMake syntax that gets included
+          Equivalent to cmake argument "-DECBUILD_CONFIG=<config-file>"
+
+    --toolchain=<toolchain>
+          Use a platform specific toolchain, containing settings such
+          as compilation flags, locations of commonly used dependencies.
+          <toolchain> can be the path to a custom toolchain file, or a
+          pre-installed toolchain provided with ecbuild. For a list of
+          pre-installed toolchains, run "ecbuild --toolchains".
+          Equivalent to cmake argument "-DCMAKE_TOOLCHAIN_FILE=<toolchain-file>"
+
+    --cache=<ecbuild-cache-file>    (advanced)
+          A file called "ecbuild-cache.cmake" is generated during configuration.
+          This file can be moved to a safe location, and specified for future
+          builds to speed up checking of compiler/platform capabilities. Note
+          that this is only accelerating fresh builds, as cmake internally
+          caches also. Therefore this option is *not* recommended.
+
+    --build-cmake[=<prefix>]
+          Automatically download and build CMake version $CMAKE_BUILD_VERSION.
+          Requires an internet connection and may take a while. If no prefix
+          is given, install into $PWD.
+
+    --dryrun
+          Don't actually execute the cmake call, just print what would have
+          been executed.
+
+
+Available values for "cmake-argument":
+
+    Any value that can be usually passed to cmake to (re)configure the build.
+    Typically these values start with "-D".
+        example:  -DENABLE_TESTS=ON  -DENABLE_MPI=OFF  -DECKIT_PATH=...
+
+    They can be explicitly separated from [option...] with a "--", for the case
+    there is a conflicting option with the "cmake" executable, and the latter's
+    option is requested.
+
+------------------------------------------------------------------------
+
+NOTE: When reconfiguring a build, it is only necessary to change the relevant
+options, as everything stays cached. For example:
+  > ecbuild --prefix=PREFIX .
+  > ecbuild -DENABLE_TESTS=ON .
+
+------------------------------------------------------------------------
+
+Compiling:
+
+  To compile the project with <N> threads:
+    > make -j<N>
+
+  To get verbose compilation/linking output:
+    > make VERBOSE=1
+
+Testing:
+
+  To run the project's tests
+    > ctest
+
+  Also check the ctest manual/help for more options on running tests
+
+Installing:
+
+  To install the project in location PREFIX with
+       "--prefix=PREFIX" or
+       "-DCMAKE_INSTALL_PREFIX=PREFIX"
+    > make install
+
+------------------------------------------------------------------------
+ECMWF"
+
+EOF
+    exit $1
+}
+
+INSTALL_DIR="$( cd $( dirname "${BASH_SOURCE[0]}" ) && pwd -P )"
+ECBUILD_MODULE_PATH=""
+# If there is a directory share/ecbuild/cmake relative to the parent directory
+# (as in an install tree), add it to CMAKE_MODULE_PATH
+if [ -d $INSTALL_DIR/../share/ecbuild/cmake ]; then
+  ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../share/ecbuild/cmake" && pwd -P )"
+# If there is a cmake subdirectory relative to the script directory (as in a
+# tarball), add it to CMAKE_MODULE_PATH
+elif [ -d $INSTALL_DIR/../cmake ]; then
+  ECBUILD_MODULE_PATH="$( cd "$INSTALL_DIR/../cmake" && pwd -P )"
+fi
+
+# Fail if we couldn't find ecBuild modules
+if [ ! -f "$ECBUILD_MODULE_PATH/VERSION.cmake" ]; then
+  echo "FATAL: ecBuild modules could not be found in either $INSTALL_DIR/../share/ecbuild/cmake or $INSTALL_DIR/../cmake" >&2
+  exit 1
+fi
+
+ADD_ECBUILD_OPTIONS="-DCMAKE_MODULE_PATH=$ECBUILD_MODULE_PATH"
+
+if [ -d $INSTALL_DIR/../share/ecbuild/toolchains ]; then
+  ECBUILD_TOOLCHAIN_DIR="$( cd "$INSTALL_DIR/../share/ecbuild/toolchains" && pwd -P )"
+elif [ -d $INSTALL_DIR/share/ecbuild/toolchains ]; then
+  ECBUILD_TOOLCHAIN_DIR="$( cd "$INSTALL_DIR/share/ecbuild/toolchains" && pwd -P )"
+fi
+
+version()
+{
+  ecbuild_version=$(cat ${ECBUILD_MODULE_PATH}/VERSION.cmake | grep ECBUILD_VERSION_STR |  perl -p -e 's/.*([\d]\.[\d]\.[\d]).*/\1/' )
+  echo "ecbuild version ${ecbuild_version}"
+  command -v cmake >/dev/null 2>&1 || { exit 0; }
+  cmake --version | head -1
+  exit 0
+}
+
+log()
+{
+  log_level=$(tr "[a-z]" "[A-Z]" <<< "$1")
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_LOG_LEVEL=${log_level}"
+}
+
+toolchains()
+{
+  if [ -d $ECBUILD_TOOLCHAIN_DIR ]; then
+    cd $ECBUILD_TOOLCHAIN_DIR
+    echo "Available toolchains:"
+    ls | while read fname
+    do
+        echo "  - ${fname%%.*}"
+    done
+    exit 0
+  else
+    echo "No toolchains available."
+    exit 1
+  fi
+}
+
+prefix()
+{
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_INSTALL_PREFIX=${1/#\~\//$HOME/}"
+}
+
+config()
+{
+  arg=${1/#\~\//$HOME/}
+  if [ -f $arg ]; then
+    config_file=$arg
+    config_file="$( cd $( dirname "${config_file}" ) && pwd -P )/$( basename ${config_file} )"
+  else
+    echo "Error:"
+    echo "   Config file [$arg] is not found or is not a file."
+    exit 1
+  fi
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CONFIG=${config_file}"
+}
+
+toolchain()
+{
+  arg=${1/#\~\//$HOME/}
+  if [ -f $arg ]; then
+    toolchain_file=$arg
+  else
+    if [ -f $ECBUILD_TOOLCHAIN_DIR/$arg.cmake ]; then
+      toolchain_file=$ECBUILD_TOOLCHAIN_DIR/$arg.cmake
+    fi
+  fi
+  if [ -z ${toolchain_file+x} ]; then
+    echo "Error:"
+    echo "   Toolchain [$arg] is not valid: [$arg.cmake] cannot be"
+    echo "   found in [$ECBUILD_TOOLCHAIN_DIR]"
+    exit 1
+  else
+    ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${toolchain_file}"
+  fi
+}
+
+cache()
+{
+  arg=$1
+  if [ -f $arg ]; then
+    cache_file=$arg
+    cache_file="$( cd $( dirname "${cache_file}" ) && pwd -P )/$( basename ${cache_file} )"
+  else
+    echo "Error:"
+    echo "   Cache file [$arg] is not found or is not a file."
+    exit 1
+  fi
+  ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DECBUILD_CACHE=${cache_file}"
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+while test $# -gt 0; do
+
+    # Split --option=value in $opt="--option" and $val="value"
+
+    opt=""
+    val=""
+
+    case "$1" in
+    --*=*)
+      opt=`echo "$1" | sed 's/=.*//'`
+      val=`echo "$1" | sed 's/--[_a-zA-Z0-9-]*=//'`
+      ;;
+    --*)
+      opt=$1
+      ;;
+    # -D*)
+    #   ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS $1"
+    #   ;;
+    *)
+      break
+      ;;
+    esac
+
+    # echo "debug opt: $opt $val"
+
+    # Parse options
+    case "$opt" in
+      --help)
+        help 0
+  	    ;;
+      --version)
+        version
+        ;;
+      --dryrun)
+        dryrun="yes"
+        ;;
+      --toolchains)
+        toolchains
+        ;;
+      --cmakebin)
+        cmakebin="$val"
+        ;;
+      --prefix)
+        prefix "$val"
+        ;;
+      --build)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DCMAKE_BUILD_TYPE=$val"
+        ;;
+      --log)
+        log $val
+        ;;
+      --static)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=OFF"
+        ;;
+      --dynamic)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON"
+        ;;
+      --shared)
+        ADD_ECBUILD_OPTIONS="$ADD_ECBUILD_OPTIONS -DBUILD_SHARED_LIBS=ON"
+        ;;
+      --toolchain)
+        toolchain $val
+        ;;
+      --config)
+        config $val
+        ;;
+      --cache)
+        cache $val
+        ;;
+      --build-cmake)
+        build_cmake="yes"
+        if [[ -n $val ]]; then
+          cmake_prefix="$val"
+        fi
+        ;;
+      --)
+        shift
+        break
+        ;;
+      *)
+        echo "unknown option: $opt"
+	      usage 1
+        ;;
+    esac
+    shift
+done
+
+# If no arguments remain, set srcARG to "."
+if [ $# -eq 0 ]; then
+  srcARG="."
+fi
+
+if [ -z ${toolchain_file+x} ]; then
+  if [ -z ${ECBUILD_TOOLCHAIN+x} ]; then :
+  else
+    toolchain ${ECBUILD_TOOLCHAIN}
+    echo "ecbuild toolchain set using environment variable ECBUILD_TOOLCHAIN"
+  fi
+fi
+
+src=${srcARG:=""}
+cmake=${cmakebin:=cmake}
+dryrun=${dryrun:=no}
+build_cmake=${build_cmake:=""}
+cmake_prefix=${cmake_prefix:=$PWD}
+cmake_found=""
+cmake_version_sufficient=""
+
+
+# Check that version $1 satisfies $2
+# CMake versions have no more than 4 fields
+# Version sort (sort -V) is not available on all platforms
+version_gte() {
+  [ "$2" = "$(echo -e "$1\n$2" | sort -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g | head -n1)" ]
+}
+
+# Use already built CMake if any
+if [[ -x "${cmake_prefix}/bin/cmake" ]]; then
+  echo "Using already built CMake in ${cmake_prefix}/bin/cmake" >&2
+  cmake="${cmake_prefix}/bin/cmake"
+# Build CMake if requested and no sufficient version found
+elif [[ $build_cmake ]]; then
+  echo "Building CMake version ${CMAKE_BUILD_VERSION} and installing into ${cmake_prefix} ..." >&2
+  tarball=cmake-${CMAKE_BUILD_VERSION}.tar.gz
+  if [[ ! -r $tarball ]]; then
+    url=http://www.cmake.org/files/v${CMAKE_BUILD_VERSION:0:3}/$tarball
+    # -N          Download only if the remote version of the file is newer
+    # --continue  Continue an interrupted download
+    # -T 60       Time out a download attempt after 60 seconds
+    # -t 3        Only make 3 download attempts
+    wget -N --continue -T 60 -t 3 $url || {
+      echo "Failed to download CMake release $CMAKE_BUILD_VERSION." >&2
+      echo "Please download from $url" >&2
+      echo "and place $tarball in $PWD" >&2
+      exit 1
+    }
+  fi
+  tar xzf cmake-${CMAKE_BUILD_VERSION}.tar.gz
+  (
+    mkdir -p build_cmake
+    cd build_cmake
+    ../cmake-${CMAKE_BUILD_VERSION}/bootstrap --prefix="${cmake_prefix}" && make && make install
+  )
+  cmake="${cmake_prefix}/bin/cmake"
+fi
+
+# Check if the cmake version is sufficient
+if $(command -v $cmake >/dev/null 2>&1); then
+  cmake_found="yes"
+  cmake_version=$($cmake --version | head -n1 | awk '{ print $3 }')
+  echo "Found CMake version $cmake_version" >& 2
+  if version_gte $cmake_version $CMAKE_MIN_REQUIRED; then
+    cmake_version_sufficient="yes"
+  fi
+fi
+
+# Fail if we don't have a sufficient CMake
+if [[ ! $cmake_version_sufficient ]]; then
+  if [[ ! $cmake_found ]]; then
+    echo "CMake is required and cannot be found in the PATH." >&2
+  else
+    echo "CMake version $CMAKE_MIN_REQUIRED is required but only $cmake_version was found." >&2
+  fi
+  echo "" >&2
+  echo "  Try 'module load cmake', specify a CMake binary with --cmakebin=/path/to/cmake" >&2
+  echo "  or  let ecbuild download and build CMake with the --build-cmake option." >&2
+  exit 1
+fi
+
+echo ""
+echo "$cmake ${ADD_ECBUILD_OPTIONS} $@ $src"
+echo ""
+
+if [ ${dryrun} == "yes" ]; then
+  echo "[DRYRUN] -- not executing"
+  exit 0
+fi
+
+$cmake ${ADD_ECBUILD_OPTIONS} "$@" $src
diff --git a/ecbuild/cmake/2.8/CMakePushCheckState.cmake b/ecbuild/cmake/2.8/CMakePushCheckState.cmake
new file mode 100644
index 0000000..0a42128
--- /dev/null
+++ b/ecbuild/cmake/2.8/CMakePushCheckState.cmake
@@ -0,0 +1,61 @@
+# This module defines two macros:
+# CMAKE_PUSH_CHECK_STATE()
+# and
+# CMAKE_POP_CHECK_STATE()
+# These two macros can be used to save and restore the state of the variables
+# CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_LIBRARIES
+# and CMAKE_REQUIRED_INCLUDES used by the various Check-files coming with CMake,
+# like e.g. check_function_exists() etc.
+# The variable contents are pushed on a stack, pushing multiple times is supported.
+# This is useful e.g. when executing such tests in a Find-module, where they have to be set,
+# but after the Find-module has been executed they should have the same value
+# as they had before.
+#
+# Usage:
+#   cmake_push_check_state()
+#   set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF)
+#   check_function_exists(...)
+#   cmake_pop_check_state()
+
+#=============================================================================
+# Copyright 2006-2011 Alexander Neundorf, <neundorf at kde.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+
+MACRO(CMAKE_PUSH_CHECK_STATE)
+
+   IF(NOT DEFINED _CMAKE_PUSH_CHECK_STATE_COUNTER)
+      SET(_CMAKE_PUSH_CHECK_STATE_COUNTER 0)
+   ENDIF()
+
+   MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1")
+
+   SET(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}    ${CMAKE_REQUIRED_INCLUDES})
+   SET(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS})
+   SET(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}   ${CMAKE_REQUIRED_LIBRARIES})
+   SET(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}       ${CMAKE_REQUIRED_FLAGS})
+ENDMACRO(CMAKE_PUSH_CHECK_STATE)
+
+MACRO(CMAKE_POP_CHECK_STATE)
+
+# don't pop more than we pushed
+   IF("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0")
+
+      SET(CMAKE_REQUIRED_INCLUDES    ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_LIBRARIES   ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_FLAGS       ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+
+      MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1")
+   ENDIF()
+
+ENDMACRO(CMAKE_POP_CHECK_STATE)
diff --git a/ecbuild/cmake/CMakeLists.txt b/ecbuild/cmake/CMakeLists.txt
new file mode 100644
index 0000000..d42a153
--- /dev/null
+++ b/ecbuild/cmake/CMakeLists.txt
@@ -0,0 +1,5 @@
+file( GLOB_RECURSE ecbuild_support_files  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" )
+
+ecbuild_add_resources(  TARGET ${PROJECT_NAME}_ecbuild_support_files
+						SOURCES_PACK
+							${ecbuild_support_files} )
diff --git a/ecbuild/cmake/FindADSM.cmake b/ecbuild/cmake/FindADSM.cmake
new file mode 100644
index 0000000..daadf0e
--- /dev/null
+++ b/ecbuild/cmake/FindADSM.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ADSM
+# Once done this will define
+#  ADSM_FOUND - System has ADSM
+#  ADSM_INCLUDE_DIRS - The ADSM include directories
+#  ADSM_LIBRARIES - The libraries needed to use ADSM
+
+if( EC_OS_BITS EQUAL 32 )
+	set( ADSM_LIBNAME ApiDS )
+endif()
+if( EC_OS_BITS EQUAL 64 )
+	set( ADSM_LIBNAME ApiTSM64 )
+endif()
+if( NOT DEFINED ADSM_LIBNAME )
+	message( STATUS "MARS only supports ADSM with 32 or 64 bits" )
+endif()
+
+if( DEFINED ADSM_PATH )
+	find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATHS ${ADSM_PATH} ${ADSM_PATH}/include ${ADSM_PATH}/sample NO_DEFAULT_PATH )
+	find_library(ADSM_LIBRARY  ${ADSM_LIBNAME} PATHS ${ADSM_PATH} ${ADSM_PATH}/lib     ${ADSM_PATH}/lib64  NO_DEFAULT_PATH )
+endif()
+
+find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATH_SUFFIXES bin64 )
+find_library( ADSM_LIBRARY ${ADSM_LIBNAME} PATH_SUFFIXES bin64 )
+
+set( ADSM_LIBRARIES    ${ADSM_LIBRARY} )
+set( ADSM_INCLUDE_DIRS ${ADSM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set ADSM_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ADSM  DEFAULT_MSG
+								  ADSM_LIBRARY ADSM_INCLUDE_DIR)
+
+mark_as_advanced(ADSM_INCLUDE_DIR ADSM_LIBRARY )
diff --git a/ecbuild/cmake/FindAEC.cmake b/ecbuild/cmake/FindAEC.cmake
new file mode 100644
index 0000000..0b0f69b
--- /dev/null
+++ b/ecbuild/cmake/FindAEC.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find AEC (Adaptive Entropy Coding library)
+# See https://www.dkrz.de/redmine/projects/aec/wiki
+
+# Once done this will define
+#  AEC_FOUND        - System has AEC
+#  AEC_INCLUDE_DIRS - The AEC include directories
+#  AEC_LIBRARIES    - The libraries needed to use AEC
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  AEC_DIR          - prefix path of the AEC installation
+#  AEC_PATH         - prefix path of the AEC installation
+
+find_path( AEC_INCLUDE_DIR szlib.h
+           PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+           PATH_SUFFIXES include include/aec NO_DEFAULT_PATH )
+find_path( AEC_INCLUDE_DIR szlib.h PATH_SUFFIXES include include/aec )
+
+find_library( AEC_LIBRARY  NAMES aec
+              PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+              PATH_SUFFIXES lib lib64 lib/aec lib64/aec NO_DEFAULT_PATH )
+find_library( AEC_LIBRARY NAMES aec PATH_SUFFIXES lib lib64 lib/aec lib64/aec )
+
+set( AEC_LIBRARIES    ${AEC_LIBRARY} )
+set( AEC_INCLUDE_DIRS ${AEC_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(AEC  DEFAULT_MSG AEC_LIBRARY AEC_INCLUDE_DIR)
+
+mark_as_advanced(AEC_INCLUDE_DIR AEC_LIBRARY )
diff --git a/ecbuild/cmake/FindAIO.cmake b/ecbuild/cmake/FindAIO.cmake
new file mode 100644
index 0000000..5dd9244
--- /dev/null
+++ b/ecbuild/cmake/FindAIO.cmake
@@ -0,0 +1,66 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# OUTPUT:
+# RT_LIB  = the library to link against
+
+if( CMAKE_SYSTEM_NAME MATCHES "Linux" )
+
+	find_package( Realtime )
+
+	if( REALTIME_FOUND ) # check that aio needs realtime
+		set( AIO_LIBRARIES ${RT_LIB} )
+	endif()
+
+endif()
+
+find_path( AIO_INCLUDE_DIRS NAMES aio.h HINTS ENV AIO_PATH ${AIO_PATH} )
+
+mark_as_advanced( AIO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( AIO  DEFAULT_MSG  AIO_INCLUDE_DIRS  )
+
+# checks for AIO64 vs AIO
+if( AIO_FOUND )
+
+	include( CheckCSourceCompiles )
+	include( CMakePushCheckState )
+    
+    cmake_push_check_state()
+    
+		set( CMAKE_REQUIRED_INCLUDES ${AIO_INCLUDE_DIRS} )
+
+		if( AIO_LIBRARIES )
+			set( CMAKE_REQUIRED_LIBRARIES ${AIO_LIBRARIES} )
+		endif()
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb* aiocbp;
+									  int n = aio_write(aiocbp);
+									  n = aio_read(aiocbp);
+									  n = aio_fsync(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB )
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb64* aiocbp;
+									  int n = aio_write64(aiocbp);
+									  n = aio_read64(aiocbp);
+									  n = aio_fsync64(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB64 )
+
+    cmake_pop_check_state()
+
+endif()
diff --git a/ecbuild/cmake/FindArmadillo.cmake b/ecbuild/cmake/FindArmadillo.cmake
new file mode 100644
index 0000000..dfe77a2
--- /dev/null
+++ b/ecbuild/cmake/FindArmadillo.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Armadillo
+# Once done this will define
+#
+#  ARMADILLO_FOUND         - system has Armadillo
+#  ARMADILLO_INCLUDE_DIRS  - the Armadillo include directory
+#  ARMADILLO_LIBRARIES     - the Armadillo library
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  ARMADILLO_PATH          - prefix path of the Armadillo installation
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_path(ARMADILLO_INCLUDE_DIR armadillo
+          PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(ARMADILLO_INCLUDE_DIR  armadillo PATH_SUFFIXES include )
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_library(ARMADILLO_LIBRARY armadillo
+             PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+             PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+find_library( ARMADILLO_LIBRARY  armadillo   PATH_SUFFIXES lib64 lib )
+
+set( ARMADILLO_LIBRARIES    ${ARMADILLO_LIBRARY} )
+set( ARMADILLO_INCLUDE_DIRS ${ARMADILLO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set ARMADILLO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Armadillo  DEFAULT_MSG
+                                  ARMADILLO_LIBRARY ARMADILLO_INCLUDE_DIR)
+
+mark_as_advanced(ARMADILLO_INCLUDE_DIR ARMADILLO_LIBRARY )
diff --git a/ecbuild/cmake/FindCMath.cmake b/ecbuild/cmake/FindCMath.cmake
new file mode 100644
index 0000000..741728a
--- /dev/null
+++ b/ecbuild/cmake/FindCMath.cmake
@@ -0,0 +1,26 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# CMATH_LIBRARIES      = the library to link against (RT etc)
+
+IF(UNIX)
+  if( DEFINED CMATH_PATH )
+    find_library(CMATH_LIBRARIES m PATHS ${CMATH_PATH}/lib NO_DEFAULT_PATH )
+  endif()
+
+  find_library(CMATH_LIBRARIES m )
+
+  include(FindPackageHandleStandardArgs)
+
+  # handle the QUIET and REQUIRED arguments and set CMATH_FOUND to TRUE
+  # if all listed variables are TRUE
+  # Note: capitalisation of the package name must be the same as in the file name
+  find_package_handle_standard_args(CMath DEFAULT_MSG CMATH_LIBRARIES )
+
+ENDIF(UNIX)
diff --git a/ecbuild/cmake/FindCairo.cmake b/ecbuild/cmake/FindCairo.cmake
new file mode 100644
index 0000000..4298a1d
--- /dev/null
+++ b/ecbuild/cmake/FindCairo.cmake
@@ -0,0 +1,59 @@
+# - Try to find the cairo library
+# Once done this will define
+#
+# CAIRO_FOUND - system has cairo
+# CAIRO_INCLUDE_DIRS - the cairo include directory
+# CAIRO_LIBRARIES - Link these to use cairo
+#
+# Define CAIRO_MIN_VERSION for which version desired.
+
+
+if( NOT DEFINED CAIRO_PATH AND NOT "$ENV{CAIRO_PATH}" STREQUAL "" )
+    set( APPEND CAIRO_PATH "$ENV{CAIRO_PATH}" )
+endif()
+
+if( NOT DEFINED CAIRO_PATH )
+
+    include(FindPkgConfig)
+
+    if(Cairo_FIND_REQUIRED)
+        set(_pkgconfig_REQUIRED "REQUIRED")
+    else()
+        set(_pkgconfig_REQUIRED "")
+    endif()
+
+    if(CAIRO_MIN_VERSION)
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION})
+    else()
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKCAIRO_FOUND )
+
+        find_path(CAIRO_INCLUDE_DIR cairo.h HINTS ${PKCAIRO_INCLUDEDIR} ${PKCAIRO_INCLUDE_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+        find_library(CAIRO_LIBRARY  cairo   HINTS ${PKCAIRO_LIBDIR}     ${PKCAIRO_LIBRARY_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+    endif()
+
+else()
+
+    find_path(CAIRO_INCLUDE_DIR cairo.h PATHS ${CAIRO_PATH}/include PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+    find_library(CAIRO_LIBRARY  cairo   PATHS ${CAIRO_PATH}/lib     PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+endif()
+
+find_path(CAIRO_INCLUDE_DIR cairo.h PATH_SUFFIXES cairo )
+find_library( CAIRO_LIBRARY cairo   PATH_SUFFIXES cairo )
+
+set( CAIRO_LIBRARIES    ${CAIRO_LIBRARY} )
+set( CAIRO_INCLUDE_DIRS ${CAIRO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set CAIRO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Cairo  DEFAULT_MSG
+                                  CAIRO_LIBRARY CAIRO_INCLUDE_DIR)
+
+mark_as_advanced( CAIRO_INCLUDE_DIR CAIRO_LIBRARY )
diff --git a/ecbuild/cmake/FindDl.cmake b/ecbuild/cmake/FindDl.cmake
new file mode 100644
index 0000000..31e426f
--- /dev/null
+++ b/ecbuild/cmake/FindDl.cmake
@@ -0,0 +1,23 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# DL_LIBRARIES      = the library to link against (RT etc)
+
+if( DEFINED DL_PATH )
+    find_library(DL_LIBRARIES dl PATHS ${DL_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library(DL_LIBRARIES dl )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set DL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Dl DEFAULT_MSG DL_LIBRARIES )
diff --git a/ecbuild/cmake/FindEMOS.cmake b/ecbuild/cmake/FindEMOS.cmake
new file mode 100644
index 0000000..43f896e
--- /dev/null
+++ b/ecbuild/cmake/FindEMOS.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find EMOS
+# Once done this will define
+#  EMOS_FOUND - System has EMOS
+#  EMOS_INCLUDE_DIRS - The EMOS include directories
+#  EMOS_LIBRARIES - The libraries needed to use EMOS
+
+if( NOT DEFINED EMOS_PATH AND DEFINED $ENV{EMOS_PATH} )
+	set( EMOS_PATH $ENV{EMOS_PATH} )
+endif()
+
+if( DEFINED EMOS_PATH )
+    find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos PATHS ${EMOS_PATH} PATH_SUFFIXES lib lib/emos NO_DEFAULT_PATH)
+endif()
+
+find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos)
+
+ecbuild_find_fortranlibs()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( EMOS  DEFAULT_MSG  EMOS_LIBRARY FORTRANLIBS_FOUND )
+
+mark_as_advanced(EMOS_LIBRARY)
+
+if( EMOS_FOUND )
+    set( EMOS_LIBRARIES  ${EMOS_LIBRARY} ${FORTRAN_LIBRARIES} )
+endif()
diff --git a/ecbuild/cmake/FindFDB.cmake b/ecbuild/cmake/FindFDB.cmake
new file mode 100644
index 0000000..66879c1
--- /dev/null
+++ b/ecbuild/cmake/FindFDB.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find FDB
+# Once done this will define
+#  FDB_FOUND - System has FDB
+#  FDB_INCLUDE_DIRS - The FDB include directories
+#  FDB_LIBRARIES - The libraries needed to use FDB
+
+
+if( NOT FDB_FOUND )
+
+	if( DEFINED FDB_PATH )
+		find_library( FDB_LIBRARY NAMES fdb PATHS ${FDB_PATH} ${FDB_PATH}/lib NO_DEFAULT_PATH)
+	endif()
+	
+	find_library( FDB_LIBRARY NAMES fdb )
+	
+	set( FDB_LIBRARIES  ${FDB_LIBRARY} )
+	
+	include(FindPackageHandleStandardArgs)
+	
+	# handle the QUIETLY and REQUIRED arguments and set FDB_FOUND to TRUE
+	# if all listed variables are TRUE
+	find_package_handle_standard_args(FDB  DEFAULT_MSG
+									  FDB_LIBRARY )
+	
+	mark_as_advanced(FDB_LIBRARY)
+
+endif()
diff --git a/ecbuild/cmake/FindFFTW.cmake b/ecbuild/cmake/FindFFTW.cmake
new file mode 100644
index 0000000..d57c09a
--- /dev/null
+++ b/ecbuild/cmake/FindFFTW.cmake
@@ -0,0 +1,211 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# FindFFTW
+# ========
+#
+# Find the FFTW library. ::
+#
+#   find_package(FFTW [REQUIRED] [QUIET]
+#                [COMPONENTS [single] [double] [long_double] [quad]])
+#
+# By default, search for the double precision library ``fftw3``
+#
+# Components
+# ----------
+#
+# If a different version or multiple versions of the library are required,
+# these need to be specified as ``COMPONENTS``. Note that double must be given
+# explicitly if any ``COMPONENTS`` are specified.
+#
+# The libraries corresponding to each of the ``COMPONENTS`` are:
+#
+# :single:      ``fftw3f``
+# :double:      ``fftw3``
+# :long_double: ``fftw3l``
+# :quad:        ``fftw3q``
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set on completion:
+#
+# :FFTW_FOUND:      true if FFTW is found on the system
+# :FFTW_LIBRARIES:  full paths to requested FFTW libraries
+# :FFTW_INCLUDES:   FFTW include directory
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are checked by the function:
+#
+# :FFTW_USE_STATIC_LIBS:  if true, only static libraries are found
+# :FFTW_ROOT:             if set, this path is exclusively searched
+# :FFTW_DIR:              equivalent to FFTW_ROOT
+# :FFTW_PATH:             equivalent to FFTW_ROOT
+# :FFTW_LIBRARY:          FFTW library to use
+# :FFTW_INCLUDE_DIR:      FFTW include directory
+#
+##############################################################################
+
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_ROOT} )
+  set( FFTW_ROOT ${FFTW_ROOT} )
+endif()
+if( NOT FFTW_ROOT AND $FFTW_DIR )
+  set( FFTW_ROOT ${FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_DIR} )
+  set( FFTW_ROOT $ENV{FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTWDIR )
+  set( FFTW_ROOT ${FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTWDIR} )
+  set( FFTW_ROOT $ENV{FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTW_PATH )
+  set( FFTW_ROOT ${FFTW_PATH} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_PATH})
+  set( FFTW_ROOT $ENV{FFTW_PATH} )
+endif()
+
+if( FFTW_ROOT ) # On cc[a|b|t] FFTW_DIR is set to the lib directory :(
+  get_filename_component(_dirname ${FFTW_ROOT} NAME)
+  if( _dirname MATCHES "lib" )
+    set( FFTW_ROOT "${FFTW_ROOT}/.." )
+  endif()
+endif()
+
+if( NOT FFTW_ROOT )
+  # Check if we can use PkgConfig
+  find_package(PkgConfig)
+
+  #Determine from PKG
+  if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT )
+    pkg_check_modules( PKG_FFTW QUIET "fftw3" )
+  endif()
+endif()
+
+#Check whether to search static or dynamic libs
+set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} )
+
+if( ${FFTW_USE_STATIC_LIBS} )
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} )
+else()
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} )
+endif()
+
+if( FFTW_FIND_COMPONENTS )
+  ecbuild_debug( "FindFFTW: looking for components: ${FFTW_FIND_COMPONENTS}" )
+  foreach( _component ${FFTW_FIND_COMPONENTS} )
+    if( _component MATCHES "single" )
+      ecbuild_debug( "FindFFTW: looking for single precision (fftw3f)" )
+      set( _require_sp TRUE )
+    elseif( _component MATCHES "double" )
+      ecbuild_debug( "FindFFTW: looking for double precision (fftw3)" )
+      set( _require_dp TRUE )
+    elseif( _component MATCHES "long_double" )
+      ecbuild_debug( "FindFFTW: looking for long double precision (fftw3l)" )
+      set( _require_lp TRUE )
+    elseif( _component MATCHES "quad" )
+      ecbuild_debug( "FindFFTW: looking for quad precision (fftw3q)" )
+      set( _require_qp TRUE )
+    else()
+    endif()
+  endforeach()
+else()
+  ecbuild_debug( "FindFFTW: no components specified, looking for double precision (fftw3)" )
+  set( _require_dp TRUE )
+endif()
+
+if( FFTW_ROOT )
+  set( _default_paths NO_DEFAULT_PATH )
+  set( _lib_paths ${FFTW_ROOT} )
+  set( _include_paths ${FFTW_ROOT} )
+else()
+  set( _lib_paths ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} )
+  set( _include_paths ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} )
+endif()
+
+#find libs
+
+if( _require_dp )
+  find_library(
+    FFTW_LIB
+    NAMES "fftw3"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTW_LIB )
+    ecbuild_warn("FindFFTW: double precision required, but fftw3 was not found")
+  endif()
+endif()
+
+if( _require_sp )
+  find_library(
+    FFTWF_LIB
+    NAMES "fftw3f"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWF_LIB )
+    ecbuild_warn("FindFFTW: single precision required, but fftw3f was not found")
+  endif()
+endif()
+
+if( _require_lp )
+  find_library(
+    FFTWL_LIB
+    NAMES "fftw3l"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWL_LIB )
+    ecbuild_warn("FindFFTW: long double precision required, but fftw3l was not found")
+  endif()
+endif()
+
+if( _require_qp )
+  find_library(
+    FFTWQ_LIB
+    NAMES "fftw3q"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWQ_LIB )
+    ecbuild_warn("FindFFTW: quad precision required, but fftw3q was not found")
+  endif()
+endif()
+
+#find includes
+
+find_path(
+  FFTW_INCLUDES
+  NAMES "fftw3.h"
+  PATHS ${_include_paths}
+  PATH_SUFFIXES "include"
+  ${_default_paths}
+)
+
+set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB} ${FFTWL_LIB} ${FFTWQ_LIB})
+
+set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} )
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(FFTW DEFAULT_MSG
+                                  FFTW_INCLUDES FFTW_LIBRARIES)
+
+mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB)
diff --git a/ecbuild/cmake/FindGeoTIFF.cmake b/ecbuild/cmake/FindGeoTIFF.cmake
new file mode 100644
index 0000000..7226f61
--- /dev/null
+++ b/ecbuild/cmake/FindGeoTIFF.cmake
@@ -0,0 +1,46 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the GeoTIFF includes and library
+# This module defines
+#
+#  GEOTIFF_FOUND         - System has GeoTIFF
+#  GEOTIFF_INCLUDE_DIRS  - the GeoTIFF include directories
+#  GEOTIFF_LIBRARIES     - the libraries needed to use GeoTIFF
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  GEOTIFF_DIR   - root folder of the GeoTIFF installation
+#  GEOTIFF_PATH  - root folder of the GeoTIFF installation
+
+find_path( GEOTIFF_INCLUDE_DIR geotiff.h
+           PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                 ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+           PATH_SUFFIXES include include/libgeotiff
+           NO_DEFAULT_PATH )
+find_path( GEOTIFF_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES include include/libgeotiff )
+
+find_library( GEOTIFF_LIBRARY NAMES geotiff
+              PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                    ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+find_library( GEOTIFF_LIBRARY NAMES geotiff )
+
+set( GEOTIFF_LIBRARIES    ${GEOTIFF_LIBRARY} )
+set( GEOTIFF_INCLUDE_DIRS ${GEOTIFF_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GEOTIFF_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( GeoTIFF DEFAULT_MSG
+                                   GEOTIFF_LIBRARY GEOTIFF_INCLUDE_DIR )
+
+mark_as_advanced( GEOTIFF_INCLUDE_DIR GEOTIFF_LIBRARY )
diff --git a/ecbuild/cmake/FindHPSS.cmake b/ecbuild/cmake/FindHPSS.cmake
new file mode 100644
index 0000000..1cc829e
--- /dev/null
+++ b/ecbuild/cmake/FindHPSS.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find HPSS
+# Once done this will define
+#  HPSS_FOUND - System has HPSS
+#  HPSS_INCLUDE_DIRS - The HPSS include directories
+#  HPSS_LIBRARIES - The libraries needed to use HPSS
+#  HPSS_DEFINITIONS - Compiler switches required for using HPSS
+
+if( DEFINED HPSS_PATH )
+	find_path(HPSS_INCLUDE_DIR hpss_api.h PATHS ${HPSS_PATH}/include PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+	find_library(HPSS_LIBRARY  hpss       PATHS ${HPSS_PATH}/lib     PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+endif()
+
+find_path(HPSS_INCLUDE_DIR hpss_api.h PATH_SUFFIXES hpss )
+find_library( HPSS_LIBRARY hpss       PATH_SUFFIXES hpss )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set HPSS_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(HPSS  DEFAULT_MSG
+                                  HPSS_LIBRARY HPSS_INCLUDE_DIR)
+
+mark_as_advanced(HPSS_INCLUDE_DIR HPSS_LIBRARY )
+
+if( HPSS_FOUND )
+    set( HPSS_LIBRARIES    ${HPSS_LIBRARY} )
+    set( HPSS_INCLUDE_DIRS ${HPSS_INCLUDE_DIR} )
+else()
+    set( HPSS_LIBRARIES    "" )
+    set( HPSS_INCLUDE_DIRS "" )
+endif()
diff --git a/ecbuild/cmake/FindLEX.cmake b/ecbuild/cmake/FindLEX.cmake
new file mode 100644
index 0000000..b6ab838
--- /dev/null
+++ b/ecbuild/cmake/FindLEX.cmake
@@ -0,0 +1,129 @@
+# - Find lex executable and provides a macro to generate custom build rules
+#
+# The module defines the following variables:
+#  LEX_FOUND - true is lex executable is found
+#  LEX_EXECUTABLE - the path to the lex executable
+#  LEX_LIBRARIES - The lex libraries
+#  LEX_INCLUDE_DIRS - The path to the lex headers
+#
+#
+# If lex is found on the system, the module provides the macro:
+#  LEX_TARGET(Name LexInput LexOutput [COMPILE_FLAGS <string>])
+# which creates a custom command  to generate the <LexOutput> file from
+# the <LexInput> file.  If  COMPILE_FLAGS option is specified, the next
+# parameter is added to the lex  command line. Name is an alias used to
+# get  details of  this custom  command.  Indeed the  macro defines  the
+# following variables:
+#  LEX_${Name}_DEFINED - true is the macro ran successfully
+#  LEX_${Name}_OUTPUTS - the source file generated by the custom rule, an
+#  alias for LexOutput
+#  LEX_${Name}_INPUT - the lex source file, an alias for ${LexInput}
+#
+# Lex scanners oftenly use tokens  defined by Yacc: the code generated
+# by Lex  depends of the header  generated by Yacc.   This module also
+# defines a macro:
+#  ADD_LEX_YACC_DEPENDENCY(LexTarget YaccTarget)
+# which  adds the  required dependency  between a  scanner and  a parser
+# where  <LexTarget>  and <YaccTarget>  are  the  first parameters  of
+# respectively LEX_TARGET and YACC_TARGET macros.
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(LEX_EXECUTABLE lex DOC "path to the lex executable")
+MARK_AS_ADVANCED(LEX_EXECUTABLE)
+
+FIND_LIBRARY(FL_LIBRARY NAMES fl
+  DOC "Path to the fl library")
+
+FIND_PATH(LEX_INCLUDE_DIR LexLexer.h
+  DOC "Path to the lex headers")
+
+MARK_AS_ADVANCED(FL_LIBRARY LEX_INCLUDE_DIR)
+
+SET(LEX_INCLUDE_DIRS ${LEX_INCLUDE_DIR})
+SET(LEX_LIBRARIES ${FL_LIBRARY})
+
+IF(LEX_EXECUTABLE)
+
+  #============================================================
+  # LEX_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(LEX_TARGET Name Input Output)
+    SET(LEX_TARGET_usage "LEX_TARGET(<Name> <Input> <Output> [COMPILE_FLAGS <string>]")
+    IF(${ARGC} GREATER 3)
+      IF(${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          SET(LEX_EXECUTABLE_opts  "${ARGV4}")
+          SEPARATE_ARGUMENTS(LEX_EXECUTABLE_opts)
+        ELSE()
+          MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+        ENDIF()
+      ELSE()
+        MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+      ENDIF()
+    ENDIF()
+
+    message( STATUS "${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}" )
+
+    ADD_CUSTOM_COMMAND(OUTPUT ${Output}
+      COMMAND ${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}
+      DEPENDS ${Input}
+      COMMENT "[LEX][${Name}] Building scanner with lex ${LEX_VERSION}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+    SET(LEX_${Name}_DEFINED TRUE)
+    SET(LEX_${Name}_OUTPUTS ${Output})
+    SET(LEX_${Name}_INPUT ${Input})
+    SET(LEX_${Name}_COMPILE_FLAGS ${LEX_EXECUTABLE_opts})
+  ENDMACRO(LEX_TARGET)
+  #============================================================
+
+
+  #============================================================
+  # ADD_LEX_YACC_DEPENDENCY (public macro)
+  #============================================================
+  #
+  MACRO(ADD_LEX_YACC_DEPENDENCY LexTarget YaccTarget)
+
+    IF(NOT LEX_${LexTarget}_OUTPUTS)
+      MESSAGE(SEND_ERROR "Lex target `${LexTarget}' does not exists.")
+    ENDIF()
+
+    IF(NOT YACC_${YaccTarget}_OUTPUT_HEADER)
+      MESSAGE(SEND_ERROR "Yacc target `${YaccTarget}' does not exists.")
+    ENDIF()
+
+    SET_SOURCE_FILES_PROPERTIES(${LEX_${LexTarget}_OUTPUTS}
+      PROPERTIES OBJECT_DEPENDS ${YACC_${YaccTarget}_OUTPUT_HEADER})
+  ENDMACRO(ADD_LEX_YACC_DEPENDENCY)
+  #============================================================
+
+ENDIF(LEX_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LEX REQUIRED_VARS LEX_EXECUTABLE)
+
+# FindLEX.cmake ends here
diff --git a/ecbuild/cmake/FindLibGFortran.cmake b/ecbuild/cmake/FindLibGFortran.cmake
new file mode 100644
index 0000000..7f9cc64
--- /dev/null
+++ b/ecbuild/cmake/FindLibGFortran.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# gfortran libs
+
+set( __libgfortran_names gfortran libgfortran.so.1 libgfortran.so.3 )
+
+# use gfortran to find the library
+
+find_program( GFORTRAN_EXECUTABLE gfortran )
+
+if( GFORTRAN_EXECUTABLE )
+
+	execute_process(COMMAND ${GFORTRAN_EXECUTABLE} "-print-search-dirs"
+		RESULT_VARIABLE _GFORTRAN_SEARCH_SUCCESS
+		OUTPUT_VARIABLE _GFORTRAN_VALUES_OUTPUT
+		ERROR_VARIABLE _GFORTRAN_ERROR_VALUE
+		OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+#	ecbuild_debug_var(_GFORTRAN_SEARCH_SUCCESS)
+#	ecbuild_debug_var(_GFORTRAN_VALUES_OUTPUT)
+#	ecbuild_debug_var(_GFORTRAN_ERROR_VALUE)
+
+	if(_GFORTRAN_SEARCH_SUCCESS MATCHES 0)
+		string(REGEX REPLACE ".*libraries: =(.*)" "\\1" _result  ${_GFORTRAN_VALUES_OUTPUT})
+		string(REGEX REPLACE ":" ";" _gfortran_hints ${_result} )
+	endif()
+
+	ecbuild_debug_var( _gfortran_hints )
+
+endif()
+
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${LIBGFORTRAN_PATH} ENV LIBGFORTRAN_PATH PATHS PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH )
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${_gfortran_hints} PATHS PATH_SUFFIXES lib64 lib )
+
+mark_as_advanced( GFORTRAN_LIB )
+
+if( GFORTRAN_LIB )
+	set( GFORTRAN_LIBRARIES ${GFORTRAN_LIB} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBGFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibGFortran DEFAULT_MSG GFORTRAN_LIBRARIES  )
diff --git a/ecbuild/cmake/FindLibIFort.cmake b/ecbuild/cmake/FindLibIFort.cmake
new file mode 100644
index 0000000..4c3e299
--- /dev/null
+++ b/ecbuild/cmake/FindLibIFort.cmake
@@ -0,0 +1,50 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# date:   July 2015
+# author: Florian Rathgeber
+
+###############################################################################
+
+# - Try to find Intel Fortran (ifort) runtime libraries libifcore and libifport
+# Once done this will define
+#
+#  LIBIFORT_FOUND   - system has Intel Fortran (ifort) runtime libraries
+#  IFORT_LIBRARIES  - the Intel Fortran (ifort) runtime libraries
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  INTEL_PATH       - prefix path of the Intel installation
+#
+# Otherwise the libraries are assumed to be on LIBRARY_PATH or LD_LIBRARY_PATH
+
+# FIXME: might need to add further libraries in future, see
+# http://nf.nci.org.au/facilities/software/Compilers/Intel8/doc/f_ug1/files_32.htm
+
+# Search with priority for INTEL_PATH if given as CMake or env var
+find_library( IFORT_LIB_CORE ifcore PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+
+# Otherwise, search LIBRARY_PATH and LD_LIBRARY_PATH
+find_library( IFORT_LIB_CORE ifcore PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+
+mark_as_advanced( IFORT_LIB_CORE IFORT_LIB_PORT )
+
+if( IFORT_LIB_CORE AND IFORT_LIB_PORT )
+  set( IFORT_LIBRARIES ${IFORT_LIB_CORE} ${IFORT_LIB_PORT} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBIFORT_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibIFort DEFAULT_MSG IFORT_LIBRARIES )
diff --git a/ecbuild/cmake/FindLustreAPI.cmake b/ecbuild/cmake/FindLustreAPI.cmake
new file mode 100644
index 0000000..6db0ba2
--- /dev/null
+++ b/ecbuild/cmake/FindLustreAPI.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find lib Lustre API
+
+# usually installed on Cray systems under /opt/cray/lustre-cray_ari_s/default / create_test.c -L
+# .../include/lustre/lustreapi.h
+# .../lib64/liblustreapi.so
+
+# Once done this will define
+#  LUSTREAPI_FOUND        - System has LustreAPI
+#  LUSTREAPI_INCLUDE_DIRS - The LustreAPI include directories
+#  LUSTREAPI_LIBRARIES    - The libraries needed to use LustreAPI
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  LUSTREAPI_DIR          - prefix path of the LustreAPI installation
+#  LUSTREAPI_PATH         - prefix path of the LustreAPI installation
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h
+           PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+           PATH_SUFFIXES include NO_DEFAULT_PATH )
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h PATH_SUFFIXES include )
+
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi
+              PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+              PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi PATH_SUFFIXES lib lib64 )
+
+set( LUSTREAPI_LIBRARIES    ${LUSTREAPI_LIBRARY} )
+set( LUSTREAPI_INCLUDE_DIRS ${LUSTREAPI_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(LUSTREAPI  DEFAULT_MSG LUSTREAPI_LIBRARY LUSTREAPI_INCLUDE_DIR)
+
+mark_as_advanced(LUSTREAPI_INCLUDE_DIR LUSTREAPI_LIBRARY )
diff --git a/ecbuild/cmake/FindMKL.cmake b/ecbuild/cmake/FindMKL.cmake
new file mode 100644
index 0000000..59eaa60
--- /dev/null
+++ b/ecbuild/cmake/FindMKL.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find MKL
+# Once done this will define
+#
+#  MKL_FOUND         - system has Intel MKL
+#  MKL_INCLUDE_DIRS  - the MKL include directories
+#  MKL_LIBRARIES     - link these to use MKL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  MKLROOT           - root directory of the MKL installation
+#  MKL_PATH          - root directory of the MKL installation
+#  MKL_ROOT          - root directory of the MKL installation
+
+option( MKL_PARALLEL "if mkl shoudl be parallel" OFF )
+
+if( MKL_PARALLEL )
+
+  set( __mkl_lib_par  MKL_LIB_INTEL_THREAD )
+  set( __mkl_lib_name mkl_intel_thread )
+
+  find_package(Threads)
+
+else()
+
+  set( __mkl_lib_par MKL_LIB_SEQUENTIAL )
+  set( __mkl_lib_name mkl_sequential )
+
+endif()
+
+# Search with priority for MKLROOT, MKL_PATH and MKL_ROOT if set in CMake or env
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATHS ${MKLROOT} ${MKL_PATH} ${MKL_ROOT} ENV MKLROOT ENV MKL_PATH ENV MKL_ROOT
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATH_SUFFIXES include)
+
+if( MKL_INCLUDE_DIR ) # use include dir to find libs
+
+  set( MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} )
+
+  if( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" )
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/intel64 ABSOLUTE )
+    set( __libsfx _lp64 )
+  else()
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/ia32 ABSOLUTE )
+    set( __libsfx "" )
+  endif()
+
+  find_library( MKL_LIB_INTEL         NAMES mkl_intel${__libsfx} PATHS ${MKL_LIB_PATH} )
+  find_library( ${__mkl_lib_par}      NAMES ${__mkl_lib_name} PATHS ${MKL_LIB_PATH} )
+  find_library( MKL_LIB_CORE          NAMES mkl_core PATHS ${MKL_LIB_PATH} )
+
+  if( MKL_PARALLEL )
+    find_library( MKL_LIB_IOMP5  NAMES iomp5 PATHS ${MKL_LIB_PATH} )
+  endif()
+
+  if( MKL_LIB_INTEL AND ${__mkl_lib_par} AND MKL_LIB_CORE )
+    set( MKL_LIBRARIES ${MKL_LIB_INTEL} ${${__mkl_lib_par}} ${MKL_LIB_CORE} ${MKL_LIB_IOMP5} ${CMAKE_THREAD_LIBS_INIT} )
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( MKL DEFAULT_MSG
+                                   MKL_LIBRARIES MKL_INCLUDE_DIRS )
+
+mark_as_advanced( MKL_INCLUDE_DIR MKL_LIB_LAPACK MKL_LIB_INTEL MKL_LIB_SEQUENTIAL MKL_LIB_CORE )
diff --git a/ecbuild/cmake/FindNAG.cmake b/ecbuild/cmake/FindNAG.cmake
new file mode 100644
index 0000000..883a375
--- /dev/null
+++ b/ecbuild/cmake/FindNAG.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the NAG includes and library
+# This module defines
+#
+#  NAG_FOUND         - System has NAG
+#  NAG_INCLUDE_DIRS  - the NAG include directories
+#  NAG_LIBRARIES     - the libraries needed to use NAG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  NAG_DIR   - root folder of the NAG installation
+#  NAG_PATH  - root folder of the NAG installation
+
+find_path( NAG_INCLUDE_DIR nag_library.mod
+           PATHS ${NAG_PATH} ENV NAG_PATH
+                 ${NAG_DIR}  ENV NAG_DIR
+           PATH_SUFFIXES include
+           NO_DEFAULT_PATH )
+
+find_library( NAG_LIBRARY NAMES nag nag_nag
+              PATHS ${NAG_PATH} ENV NAG_PATH
+                    ${NAG_DIR}  ENV NAG_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+
+set( NAG_LIBRARIES    ${NAG_LIBRARY} )
+set( NAG_INCLUDE_DIRS ${NAG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set NAG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( NAG DEFAULT_MSG
+                                   NAG_LIBRARY NAG_INCLUDE_DIR )
+
+mark_as_advanced( NAG_INCLUDE_DIR NAG_LIBRARY )
diff --git a/ecbuild/cmake/FindNDBM.cmake b/ecbuild/cmake/FindNDBM.cmake
new file mode 100644
index 0000000..8cd350e
--- /dev/null
+++ b/ecbuild/cmake/FindNDBM.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find NetCDF
+# Once done this will define
+#  NDBM_FOUND - System has NetCDF
+#  NDBM_INCLUDE_DIRS - The NetCDF include directories
+#  NDBM_LIBRARIES - The libraries needed to use NetCDF
+#  NDBM_DEFINITIONS - Compiler switches required for using NetCDF
+
+if( DEFINED NDBM_PATH )
+	find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATHS ${NDBM_PATH} ${NDBM_PATH}/include PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+	find_library(NDBM_LIBRARY  NAMES ndbm dbm PATHS ${NDBM_PATH} ${NDBM_PATH}/lib     PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+endif()
+
+find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATH_SUFFIXES ndbm )
+find_library( NDBM_LIBRARY NAMES ndbm dbm PATH_SUFFIXES ndbm )
+
+set( NDBM_LIBRARIES    ${NDBM_LIBRARY} )
+set( NDBM_INCLUDE_DIRS ${NDBM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(NDBM  DEFAULT_MSG
+								  NDBM_LIBRARY NDBM_INCLUDE_DIR)
+
+mark_as_advanced(NDBM_INCLUDE_DIR NDBM_LIBRARY )
diff --git a/ecbuild/cmake/FindNetCDF.cmake b/ecbuild/cmake/FindNetCDF.cmake
new file mode 100644
index 0000000..69b88bd
--- /dev/null
+++ b/ecbuild/cmake/FindNetCDF.cmake
@@ -0,0 +1,164 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF3 or NetCDF4 -- default is 4
+#
+# find_package( NetCDF <version> COMPONENTS C CXX Fortran )
+#
+# Input:
+#  * NETCDF_PATH    - user defined path where to search for the library first
+#  * NETCDF_DIR     - user defined path where to search for the library first
+#  * NETCDF_ROOT    - user defined path where to search for the library first
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_DEFINITIONS
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+# default is netcdf4
+if( NetCDF_FIND_VERSION STREQUAL "3" )
+  set( PREFER_NETCDF3 1 )
+endif()
+
+if( NOT PREFER_NETCDF3 )
+  set( PREFER_NETCDF4 1 )
+else()
+  set( PREFER_NETCDF4 0 )
+endif()
+mark_as_advanced( PREFER_NETCDF4 PREFER_NETCDF3 )
+
+set( NETCDF_FIND_REQUIRED   ${NetCDF_FIND_REQUIRED} )
+set( NETCDF_FIND_QUIETLY    ${NetCDF_FIND_QUIETLY} )
+set( NETCDF_FIND_COMPONENTS ${NetCDF_FIND_COMPONENTS} )
+
+list( APPEND NETCDF_FIND_COMPONENTS C )
+
+if( NETCDF_CXX )
+  ecbuild_debug( "FindNetCDF: also looking for C++ libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS CXX )
+endif()
+
+if( NETCDF_Fortran OR NETCDF_FORTRAN OR NETCDF_F90 )
+  ecbuild_debug( "FindNetCDF: also looking for Fortran libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "FORTRAN" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS F90 )
+endif()
+
+list (FIND NETCDF_FIND_COMPONENTS "F90" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "Fortran" _index)
+if(${_index} GREATER -1)
+  list( REMOVE_ITEM NETCDF_FIND_COMPONENTS Fortran )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_FIND_COMPONENTS )
+ecbuild_debug( "FindNetCDF: looking for components ${NETCDF_FIND_COMPONENTS}" )
+
+### NetCDF4
+
+if( PREFER_NETCDF4 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF4" )
+
+  ## hdf5
+
+  # Note: Only the HDF5 C-library is required for NetCDF
+  #       ( even for Fortan and CXX bindings)
+  find_package( HDF5 COMPONENTS C QUIET )
+
+  ## netcdf4
+
+  # CONFIGURE the NETCDF_FIND_COMPONENTS variable
+
+  # Find NetCDF4
+
+  # message( "NETCDF CMAKE_PREFIX_PATH = [${CMAKE_PREFIX_PATH}]")
+  # ecbuild_debug_var( NETCDF_ROOT )
+  # ecbuild_debug_var( NETCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NETCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NETCDF_FIND_REQUIRED )
+  find_package( NetCDF4 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+  # ecbuild_debug_var( NETCDF4_FOUND )
+  # ecbuild_debug_var( NETCDF_FOUND )
+  # ecbuild_debug_var( NETCDF_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+
+  list( APPEND NETCDF_Fortran_LIBRARIES ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_F90_LIBRARIES} )
+  if( NETCDF_Fortran_LIBRARIES )
+    list( REMOVE_DUPLICATES NETCDF_Fortran_LIBRARIES )
+  endif()
+
+  # ecbuild_debug_var( NETCDF_Fortran_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_C_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_CXX_LIBRARIES )
+
+
+  set_package_properties( NetCDF4 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF4 file format" )
+
+  if( NETCDF_FOUND AND HDF5_FOUND )
+    # list( APPEND NETCDF_DEFINITIONS  ${HDF5_DEFINITIONS} )
+    list( APPEND NETCDF_LIBRARIES    ${HDF5_HL_LIBRARIES} ${HDF5_LIBRARIES}  )
+    list( APPEND NETCDF_INCLUDE_DIRS ${HDF5_INCLUDE_DIRS} )
+  endif()
+
+  #ecbuild_debug_var( NETCDF_FOUND )
+  #ecbuild_debug_var( NETCDF_LIBRARIES )
+  #ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_FOUND )
+  #ecbuild_debug_var( HDF5_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_HL_LIBRARIES )
+  #ecbuild_debug_var( HDF5_LIBRARIES )
+
+endif()
+
+### NetCDF3
+
+if( PREFER_NETCDF3 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF3" )
+
+  # ecbuild_debug_var( NetCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NetCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NetCDF_FIND_REQUIRED )
+
+  list(FIND NetCDF_FIND_COMPONENTS "CXX" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_CXX 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "Fortran" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "FORTRAN" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "F90" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  #message( "NETCDF CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}" )
+
+  find_package( NetCDF3 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+
+  set_package_properties( NetCDF3 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF3 file format" )
+
+endif()
diff --git a/ecbuild/cmake/FindNetCDF3.cmake b/ecbuild/cmake/FindNetCDF3.cmake
new file mode 100644
index 0000000..1783a72
--- /dev/null
+++ b/ecbuild/cmake/FindNetCDF3.cmake
@@ -0,0 +1,113 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF
+#
+# Input:
+#  * NETCDF_PATH     - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_DIR      - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_CXX      - search also for netcdf_c++ wrapper library
+#  * NETCDF_Fortran  - search also for netcdff wrapper library
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+### TODO: generalize this into a macro for all ecbuild
+
+if( DEFINED NETCDF_PATH )
+    list( APPEND _netcdf_incs ${NETCDF_PATH} ${NETCDF_PATH}/include )
+    list( APPEND _netcdf_libs ${NETCDF_PATH} ${NETCDF_PATH}/lib )
+endif()
+	
+if( DEFINED NETCDF_DIR )
+    list( APPEND _netcdf_incs ${NETCDF_DIR} ${NETCDF_DIR}/include )
+    list( APPEND _netcdf_libs ${NETCDF_DIR} ${NETCDF_DIR}/lib )
+endif()
+
+# Honour environment variables NETCDF_DIR, NETCDF_PATH
+list( APPEND _netcdf_incs ENV NETCDF_DIR ENV NETCDF_PATH )
+list( APPEND _netcdf_libs ENV NETCDF_DIR ENV NETCDF_PATH )
+
+###
+
+set( _inc_sfx netcdf include )
+set( _lib_sfx netcdf lib64 lib )
+
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH )
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  NO_DEFAULT_PATH )
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  )
+
+set( NETCDF_LIBRARIES    ${NETCDF_LIBRARY} )
+set( NETCDF_INCLUDE_DIRS ${NETCDF_INCLUDE_DIR} )
+
+mark_as_advanced(NETCDF_INCLUDE_DIR NETCDF_LIBRARY )
+
+list( APPEND NETCDF_REQUIRED_VARS NETCDF_LIBRARY NETCDF_INCLUDE_DIR )
+
+if( NETCDF_CXX )
+
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_cxx netcdf_c++ netcdf_c++ netcdf_c++4 )
+
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_CXX_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_CXX_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+    mark_as_advanced(NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+endif()
+
+if( NETCDF_Fortran )
+
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_fortran netcdff )
+
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_Fortran_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_Fortran_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+    mark_as_advanced(NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+if( NETCDF_FIND_QUIETLY )
+  set( NETCDF3_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+endif()
+if( NETCDF_FIND_REQUIRED )
+  set( NETCDF3_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+endif()
+
+# Handle the QUIET and REQUIRED arguments and set NETCDF3_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF3  DEFAULT_MSG ${NETCDF_REQUIRED_VARS} )
+
+set( NETCDF_FOUND ${NETCDF3_FOUND} )
+
diff --git a/ecbuild/cmake/FindODB.cmake b/ecbuild/cmake/FindODB.cmake
new file mode 100644
index 0000000..7c35d6e
--- /dev/null
+++ b/ecbuild/cmake/FindODB.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB
+# Once done this will define
+#  ODB_FOUND - System has ODB
+#  ODB_INCLUDE_DIRS - The ODB include directories
+#  ODB_LIBRARIES - The libraries needed to use ODB
+
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/include/odbdump.h
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/module/odb.mod
+
+# -lodb -lodbec -lifsaux -lmpi_serial -lodbdummy
+
+find_package( Dl ) # find the dynamic linker
+
+list( APPEND _odb_search_libs odb odbec ifsaux mpi_serial odbdummy  )
+
+if( DEFINED ODB_PATH )
+    find_path(ODB_INCLUDE_DIR odbdump.h PATHS  ${ODB_ROOT} ${ODB_ROOT}/include ${ODB_PATH} ${ODB_PATH}/include PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    find_path(ODB_MODULE_DIR odb.mod PATHS ${ODB_ROOT} ${ODB_ROOT}/module ${ODB_PATH} ${ODB_PATH}/module PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    foreach( _lib ${_odb_search_libs} )
+      find_library(ODB_LIBRARY_${_lib}  ${_lib} PATHS ${ODB_ROOT} ${ODB_ROOT}/lib ${ODB_PATH} ${ODB_PATH}/lib     PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    endforeach()
+endif()
+
+find_path(ODB_INCLUDE_DIR odbdump.h PATH_SUFFIXES odb )
+find_path(ODB_MODULE_DIR odb.mod PATH_SUFFIXES odb )
+foreach( _lib ${_odb_search_libs} )
+  find_library( ODB_LIBRARY_${_lib} ${_lib} PATH_SUFFIXES odb )
+endforeach()
+
+foreach( _lib ${_odb_search_libs} )
+  list( APPEND ODB_LIB_LIST   ODB_LIBRARY_${_lib} )
+  list( APPEND ODB_LIBRARIES  ${ODB_LIBRARY_${_lib}} )
+  mark_as_advanced(${ODB_LIBRARY_${_lib}})
+endforeach()
+
+set( ODB_INCLUDE_DIRS ${ODB_INCLUDE_DIR} ${ODB_MODULE_DIR})
+mark_as_advanced(ODB_INCLUDE_DIR )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ODB  DEFAULT_MSG
+                                  ODB_INCLUDE_DIR ${ODB_LIB_LIST} )
+
diff --git a/ecbuild/cmake/FindOpenCL.cmake b/ecbuild/cmake/FindOpenCL.cmake
new file mode 100644
index 0000000..510a3a9
--- /dev/null
+++ b/ecbuild/cmake/FindOpenCL.cmake
@@ -0,0 +1,70 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find OpenCL
+# Once done this will define
+#
+#  OPENCL_FOUND           - system has OpenCL
+#  OPENCL_INCLUDE_DIRS    - the OpenCL include directory
+#  OPENCL_LIBRARIES       - link these to use OpenCL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENCL_ROOT            - root folder of the OpenCL installation
+#  CUDA_TOOLKIT_ROOT_DIR  - root folder of the CUDA installation (ships OpenCL)
+#  CUDA_ROOT              - root folder of the CUDA installation (ships OpenCL)
+
+if(UNIX)
+
+  if(APPLE)
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATH_SUFFIXES lib )
+
+  else()
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+                 PATH_SUFFIXES lib64 lib )
+
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set OPENCL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( OpenCL DEFAULT_MSG
+                                   OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS )
+
+mark_as_advanced( OPENCL_INCLUDE_DIRS OPENCL_LIBRARIES )
diff --git a/ecbuild/cmake/FindOpenJPEG.cmake b/ecbuild/cmake/FindOpenJPEG.cmake
new file mode 100644
index 0000000..66d976e
--- /dev/null
+++ b/ecbuild/cmake/FindOpenJPEG.cmake
@@ -0,0 +1,54 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find the OpenJPEG includes and library (version 1.5.x or 2.1.x)
+# This module defines
+#
+#  OPENJPEG_FOUND         - System has OpenJPEG
+#  OPENJPEG_INCLUDE_DIRS  - the OpenJPEG include directories
+#  OPENJPEG_LIBRARIES     - the libraries needed to use OpenJPEG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENJPEG_DIR   - root folder of the OpenJPEG installation
+#  OPENJPEG_PATH  - root folder of the OpenJPEG installation
+
+# Note: OpenJPEG has a version-specific subdirectory in the include
+# e.g. include/openjpeg-2.0 or include/openjpeg-2.1.
+# Only version 1.5.x and 2.1.x are supported.
+# The library name is different for 1.x (libopenjpeg) and 2.x (libopenjp2).
+
+set( _suff include include/openjpeg include/openjpeg-1.5 include/openjpeg-2.1 )
+find_path( OPENJPEG_INCLUDE_DIR openjpeg.h
+           PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                 ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+           PATH_SUFFIXES ${_suff}
+           NO_DEFAULT_PATH )
+find_path( OPENJPEG_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES ${_suff} )
+unset( _suff )
+
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                    ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+              PATH_SUFFIXES lib lib/openjpeg
+              NO_DEFAULT_PATH )
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATH_SUFFIXES lib lib/openjpeg )
+
+set( OPENJPEG_LIBRARIES    ${OPENJPEG_LIBRARY} )
+set( OPENJPEG_INCLUDE_DIRS ${OPENJPEG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set OPENJPEG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(OpenJPEG  DEFAULT_MSG
+                                  OPENJPEG_LIBRARY OPENJPEG_INCLUDE_DIR)
+
+mark_as_advanced( OPENJPEG_INCLUDE_DIR OPENJPEG_LIBRARY )
diff --git a/ecbuild/cmake/FindPGIFortran.cmake b/ecbuild/cmake/FindPGIFortran.cmake
new file mode 100644
index 0000000..5d33239
--- /dev/null
+++ b/ecbuild/cmake/FindPGIFortran.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# FORTRAN support
+
+# set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl  pgftnrtl nspgc pgc rt pgsse1 pgsse2 ) # init
+# set( PGIFORTRAN_SEARCH_LIBS pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf pgc pgf90 rt pgsse1 pgsse2 )             # mars client linux.2
+# set( PGIFORTRAN_SEARCH_LIBS pgftnrtl nspgc pgc rt pgsse1 pgsse2 )                                                    # mars client linux.3
+
+if( NOT DEFINED PGIFORTRAN_SEARCH_LIBS )
+	set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf nspgc pgc pgf90 pgf902 pghpf_rpm1 pghpf2 pgsse1 pgsse2 ) # better ?                                                    #
+endif()
+
+set( pgi_fortran_all_libs_found 1 )
+
+foreach( pglib ${PGIFORTRAN_SEARCH_LIBS} )
+
+	find_library( ${pglib}_lib  ${pglib} PATHS ${PGI_PATH} PATH_SUFFIXES lib libso NO_DEFAULT_PATH )
+
+	find_library( ${pglib}_lib  ${pglib} HINTS /usr/local/apps/pgi/pgi-10.8/linux86-64/10.8 PATH PATH_SUFFIXES lib libso )
+
+    if( ${pglib}_lib )
+        list( APPEND PGIFORTRAN_LIBRARIES ${${pglib}_lib} )
+#	else()
+#		set( pgi_fortran_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set PGIFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( PGIFortran DEFAULT_MSG pgi_fortran_all_libs_found PGIFORTRAN_LIBRARIES  )
+
+if( PGIFORTRAN_FOUND )
+	find_package( Realtime )
+endif()
+
+if( REALTIME_FOUND )
+	set( PGIFORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} ${RT_LIB} )
+endif()
diff --git a/ecbuild/cmake/FindPango.cmake b/ecbuild/cmake/FindPango.cmake
new file mode 100644
index 0000000..f135864
--- /dev/null
+++ b/ecbuild/cmake/FindPango.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Pango
+
+# Output:
+#   PANGO_FOUND
+#   PANGO_LIBRARIES
+#   PANGO_INCLUDE_DIRS
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGO QUIET pango)
+
+ecbuild_debug_var( PC_LIBPANGO_FOUND )
+ecbuild_debug_var( PC_LIBPANGO_VERSION )
+ecbuild_debug_var( PC_LIBPANGO_LIBRARIES )
+ecbuild_debug_var( PC_LIBPANGO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set PANGO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( Pango DEFAULT_MSG PC_LIBPANGO_LIBRARIES PC_LIBPANGO_INCLUDE_DIRS )
+
+set( PANGO_VERSION ${PC_LIBPANGO_VERSION} )
+set( PANGO_LIBRARIES ${PC_LIBPANGO_LIBRARIES} )
+set( PANGO_INCLUDE_DIRS ${PC_LIBPANGO_INCLUDE_DIRS} )
diff --git a/ecbuild/cmake/FindPangoCairo.cmake b/ecbuild/cmake/FindPangoCairo.cmake
new file mode 100644
index 0000000..cb70737
--- /dev/null
+++ b/ecbuild/cmake/FindPangoCairo.cmake
@@ -0,0 +1,96 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find PangoCairo
+
+# Output:
+#   PANGOCAIRO_FOUND
+#   PANGOCAIRO_LIBRARIES
+#   PANGOCAIRO_INCLUDE_DIRS
+
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGOCAIRO QUIET pangocairo)
+
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_FOUND )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_VERSION )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LIBRARIES )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS_OTHER )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+
+if(PC_LIBPANGOCAIRO_FOUND)
+
+    include(FindPackageHandleStandardArgs)
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo DEFAULT_MSG PC_LIBPANGOCAIRO_LIBRARIES PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+    set( PANGOCAIRO_VERSION ${PC_LIBPANGOCAIRO_VERSION} )
+    set( PANGOCAIRO_LIBRARIES "${PC_LIBPANGOCAIRO_LDFLAGS} ${PC_LIBPANGOCAIRO_LDFLAGS_OTHER}" )
+    set( PANGOCAIRO_INCLUDE_DIRS ${PC_LIBPANGOCAIRO_INCLUDE_DIRS} )
+
+else()
+
+    # this is to get magics compiling on mac with macbrew
+
+    include(FindPackageHandleStandardArgs)
+
+    set(PANGO_VERSION 1.0)
+    set(GLIB_VERSION 2.0)
+
+    find_path( _PANGOCAIRO_INCLUDE_DIRS
+        NAMES pango/pangocairo.h
+        HINTS /usr/local/include PATH_SUFFIXES pango-${PANGO_VERSION})
+
+    find_path( _CAIRO_INCLUDE_DIRS
+        NAMES cairo.h
+        HINTS /usr/local/include PATH_SUFFIXES cairo)
+
+    find_path( _GLIB_INCLUDE_DIRS_1
+        NAMES glib.h
+        HINTS /usr/local/include PATH_SUFFIXES glib-${GLIB_VERSION})
+
+    find_path( _GLIB_INCLUDE_DIRS_2
+        NAMES glibconfig.h
+        HINTS /usr/local/lib/glib-${GLIB_VERSION} PATH_SUFFIXES include)
+
+
+    find_package(X11)
+
+    set(PANGOCAIRO_INCLUDE_DIRS
+        ${_PANGOCAIRO_INCLUDE_DIRS}
+        ${_CAIRO_INCLUDE_DIRS}
+        ${_GLIB_INCLUDE_DIRS_1}
+        ${_GLIB_INCLUDE_DIRS_2}
+	${X11_INCLUDE_DIR}
+    )
+
+    find_library( _PANGOCAIRO_LIBRARIES NAMES pangocairo pangocairo-${PANGO_VERSION})
+    find_library( _PANGO_LIBRARIES NAMES pango pango-${PANGO_VERSION})
+    find_library( _CAIRO_LIBRARIES NAMES cairo)
+    find_library( _GLIB_LIBRARIES NAMES glib-${GLIB_VERSION})
+
+    set(PANGOCAIRO_LIBRARIES
+        ${_PANGOCAIRO_LIBRARIES}
+        ${_PANGO_LIBRARIES}
+        ${_CAIRO_LIBRARIES}
+        ${_GLIB_LIBRARIES}
+	${X11_LIBRARIES}
+    )
+
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo  DEFAULT_MSG
+                                       PANGOCAIRO_LIBRARIES
+                                       PANGOCAIRO_INCLUDE_DIRS  )
+
+endif()
+
diff --git a/ecbuild/cmake/FindProj4.cmake b/ecbuild/cmake/FindProj4.cmake
new file mode 100644
index 0000000..a3560b2
--- /dev/null
+++ b/ecbuild/cmake/FindProj4.cmake
@@ -0,0 +1,69 @@
+# - Try to find the proj4 library
+# Once done this will define
+#
+# PROJ4_FOUND - system has proj4
+# PROJ4_INCLUDE_DIRS - the proj4 include directory
+# PROJ4_LIBRARIES - Link these to use proj4
+#
+# Define PROJ4_MIN_VERSION for which version desired.
+
+if( NOT PROJ4_PATH )
+    if ( NOT "$ENV{PROJ4_PATH}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_PATH}" )
+    elseif ( NOT "$ENV{PROJ4_DIR}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_DIR}" )
+    endif()
+endif()
+
+if( NOT PROJ4_PATH )
+
+    include(FindPkgConfig)
+
+#    if(Proj4_FIND_REQUIRED)
+#        set(_pkgconfig_REQUIRED "REQUIRED")
+#    else()
+#        set(_pkgconfig_REQUIRED "")
+#    endif()
+
+    if(PROJ4_MIN_VERSION)
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4>=${PROJ4_MIN_VERSION})
+    else()
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKPROJ4_FOUND )
+
+        find_path(PROJ4_INCLUDE_DIR proj_api.h HINTS ${PKPROJ4_INCLUDEDIR} ${PKPROJ4_INCLUDE_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+        find_library(PROJ4_LIBRARY  proj       HINTS ${PKPROJ4_LIBDIR}     ${PKPROJ4_LIBRARY_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+    endif()
+
+#    ecbuild_debug_var( PKG_CONFIG_FOUND )
+#    ecbuild_debug_var( PKPROJ4_FOUND )
+#    ecbuild_debug_var( PROJ4_MIN_VERSION )
+
+endif()
+
+if( PROJ4_PATH )
+
+    find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS ${PROJ4_PATH} ${PROJ4_PATH}/include PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+    find_library(PROJ4_LIBRARY  NAMES proj       PATHS ${PROJ4_PATH} ${PROJ4_PATH}/lib     PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+endif()
+
+find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS PATH_SUFFIXES proj4 )
+find_library( PROJ4_LIBRARY NAMES proj       PATHS PATH_SUFFIXES proj4 )
+
+
+# ecbuild_debug_var( PROJ4_INCLUDE_DIR )
+# ecbuild_debug_var( PROJ4_LIBRARY )
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Proj4  DEFAULT_MSG
+                                  PROJ4_LIBRARY PROJ4_INCLUDE_DIR)
+
+set( PROJ4_LIBRARIES    ${PROJ4_LIBRARY} )
+set( PROJ4_INCLUDE_DIRS ${PROJ4_INCLUDE_DIR} )
+
+mark_as_advanced( PROJ4_INCLUDE_DIR PROJ4_LIBRARY )
diff --git a/ecbuild/cmake/FindREADLINE.cmake b/ecbuild/cmake/FindREADLINE.cmake
new file mode 100644
index 0000000..a146c62
--- /dev/null
+++ b/ecbuild/cmake/FindREADLINE.cmake
@@ -0,0 +1,87 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find READLINE
+# Once done this will define
+#  READLINE_FOUND - System has READLINE
+#  READLINE_INCLUDE_DIRS - The READLINE include directories
+#  READLINE_LIBRARIES - The libraries needed to use READLINE
+#  READLINE_DEFINITIONS - Compiler switches required for using READLINE
+
+if( DEFINED READLINE_PATH )
+    find_path(READLINE_INCLUDE_DIR readline/readline.h PATHS ${READLINE_PATH}/include NO_DEFAULT_PATH)
+    find_library(READLINE_LIBRARY  readline            PATHS ${READLINE_PATH}/lib     PATH_SUFFIXES readline NO_DEFAULT_PATH)
+endif()
+
+find_path(READLINE_INCLUDE_DIR readline/readline.h )
+find_library( READLINE_LIBRARY readline            PATH_SUFFIXES readline )
+
+# check what version we got
+cmake_push_check_state()
+
+  set( CMAKE_REQUIRED_LIBRARIES ${READLINE_LIBRARY} )
+  set( CMAKE_REQUIRED_INCLUDES  ${READLINE_INCLUDE_DIR} )
+
+  # sometimes the link might fail missing -ltermcap or -l(n)curses
+  # if we searched before for Curses, then lets try to use it
+  if(CURSES_FOUND)
+      list( APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARIES} )
+      list( APPEND CMAKE_REQUIRED_INCLUDES  ${CURSES_INCLUDE_DIR} )
+  endif()
+
+  ecbuild_check_cxx_source_return(
+     "#include <stdio.h>
+      #include <readline/readline.h>
+      #include <iostream>
+      int main() {
+          std::cout << rl_library_version << std::flush;
+          return 0;
+     }"
+     VAR readline_version
+     OUTPUT __readline_version_out )
+
+cmake_pop_check_state()
+
+# ecbuild_debug_var( readline_version )
+# ecbuild_debug_var( __readline_version_out )
+
+set( __readline_fail 0 )
+if( __readline_version_out )
+
+    if( "${__readline_version_out}" MATCHES "^EditLine" )
+      message( STATUS "Found EditLine instead of Readline at '${READLINE_INCLUDE_DIR}'" )
+      if( READLINE_WRAPPER_OK )
+        set( READLINE_WRAPPER      "EditLine" )
+        set( __readline_fail 0 )
+      else()
+        message( STATUS "Readline wrapper not accepted -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+        set( __readline_fail 1 )
+      endif()
+    endif()
+
+else()
+    message( STATUS "Readline test run failed -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+    set( __readline_fail 1 )
+endif()
+
+if( __readline_fail )
+    set( READLINE_LIBRARY      READLINE_LIBRARY-NOTFOUND )
+    set( READLINE_INCLUDE_DIR  READLINE_INCLUDE_DIR-NOTFOUND )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(READLINE  DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR)
+
+if( READLINE_FOUND )
+    set( READLINE_VERSION      ${__readline_version_out} )
+    set( READLINE_LIBRARIES    ${READLINE_LIBRARY} )
+    set( READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR} )
+endif()
+
+mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY )
diff --git a/ecbuild/cmake/FindRPCGEN.cmake b/ecbuild/cmake/FindRPCGEN.cmake
new file mode 100644
index 0000000..42ed90c
--- /dev/null
+++ b/ecbuild/cmake/FindRPCGEN.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RPCGEN_FOUND           = prcgen was found
+# RPCGEN_EXECUTABLE      = the executable rpcgen
+
+if( DEFINED RPCGEN_PATH )
+    find_program( RPCGEN_EXECUTABLE NAMES rpcgen PATHS ${RPCGEN_PATH} PATH_SUFFIXES bin NO_DEFAULT_PATH )
+endif()
+find_program( RPCGEN_EXECUTABLE NAMES rpcgen )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( RPCGEN  DEFAULT_MSG RPCGEN_EXECUTABLE )
diff --git a/ecbuild/cmake/FindRealtime.cmake b/ecbuild/cmake/FindRealtime.cmake
new file mode 100644
index 0000000..78fa4c5
--- /dev/null
+++ b/ecbuild/cmake/FindRealtime.cmake
@@ -0,0 +1,24 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RT_LIB      = the library to link against
+
+if( DEFINED REALTIME_PATH )
+    find_library(RT_LIB rt PATHS ${REALTIME_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library( RT_LIB rt )
+
+mark_as_advanced( RT_LIB )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set REALTIME_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Realtime  DEFAULT_MSG RT_LIB )
diff --git a/ecbuild/cmake/FindSZip.cmake b/ecbuild/cmake/FindSZip.cmake
new file mode 100644
index 0000000..f005546
--- /dev/null
+++ b/ecbuild/cmake/FindSZip.cmake
@@ -0,0 +1,30 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find SZip
+# Once done this will define
+#  SZIP_FOUND - System has SZip
+#  SZIP_INCLUDE_DIRS - The SZip include directories
+#  SZIP_LIBRARIES - The libraries needed to use SZip
+
+if( DEFINED SZIP_PATH )
+    find_path( SZIP_INCLUDE_DIR szlib.h       PATHS ${SZIP_PATH}/include PATH_SUFFIXES szip NO_DEFAULT_PATH )
+    find_library( SZIP_LIBRARY  NAMES szip sz PATHS ${SZIP_PATH}/lib     PATH_SUFFIXES szip NO_DEFAULT_PATH )
+endif()
+
+find_path( SZIP_INCLUDE_DIR szlib.h PATH_SUFFIXES szip )
+find_library( SZIP_LIBRARY NAMES szip sz  PATH_SUFFIXES szip )
+
+set( SZIP_LIBRARIES    ${SZIP_LIBRARY} )
+set( SZIP_INCLUDE_DIRS ${SZIP_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SZip  DEFAULT_MSG SZIP_LIBRARY SZIP_INCLUDE_DIR)
+
+mark_as_advanced(SZIP_INCLUDE_DIR SZIP_LIBRARY )
diff --git a/ecbuild/cmake/FindTrilinos.cmake b/ecbuild/cmake/FindTrilinos.cmake
new file mode 100644
index 0000000..b1584f8
--- /dev/null
+++ b/ecbuild/cmake/FindTrilinos.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find the Trilinos library
+#
+# Needs environmental variables
+#   TRILINOS_PATH
+# Sets
+#   TRILINOS_INCLUDE_DIRS
+#   TRILINOS_LIBRARIES
+#   TRILINOS_FOUND
+
+# Try to find Trilinos using Trilinos recommendations
+
+if( DEFINED $ENV{TRILINOS_PATH} )
+    find_package(Trilinos PATHS $ENV{TRILINOS_PATH}/lib/cmake/Trilinos $ENV{TRILINOS_PATH}/include )
+endif()
+
+if( TRILINOS_PATH )
+    find_package(Trilinos PATHS ${TRILINOS_PATH}/lib/cmake/Trilinos ${TRILINOS_PATH}/include )
+endif()
+
+if( Trilinos_FOUND )
+
+        set( TRILINOS_INCLUDE_DIRS "" )
+
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_INCLUDE_DIRS} )
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_TPL_INCLUDE_DIRS} )
+
+        foreach( test_lib ${Trilinos_LIBRARIES} )
+          if(NOT ${test_lib} STREQUAL "pytrilinos")
+            find_library( ${test_lib}_lib ${test_lib} PATHS  ${Trilinos_LIBRARY_DIRS}  NO_DEFAULT_PATH)
+            find_library( ${test_lib}_lib ${test_lib})
+            mark_as_advanced( ${test_lib}_lib )
+            list( APPEND TRILINOS_LIBRARIES ${${test_lib}_lib} )
+          endif()
+        endforeach()
+
+        list( APPEND TRILINOS_LIBRARIES ${Trilinos_TPL_LIBRARIES} )
+
+    set( TRILINOS_FOUND TRUE )
+	set( TRILINOS_VERSION ${Trilinos_VERSION} )
+
+endif()
diff --git a/ecbuild/cmake/FindViennaCL.cmake b/ecbuild/cmake/FindViennaCL.cmake
new file mode 100644
index 0000000..8f442c9
--- /dev/null
+++ b/ecbuild/cmake/FindViennaCL.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find ViennaCL
+# Once done this will define
+#
+#  VIENNACL_FOUND         - system has ViennaCL
+#  VIENNACL_INCLUDE_DIRS  - the ViennaCL include directories
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  VIENNACL_PATH          - prefix path of the ViennaCL installation
+#
+# ViennaCL is header only, so there are no libraries to be found
+
+# Search with priority for VIENNACL_PATH if given as CMake or env var
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATHS ${VIENNACL_PATH} ENV VIENNACL_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATH_SUFFIXES include )
+
+set( VIENNACL_INCLUDE_DIRS ${VIENNACL_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set VIENNACL_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(ViennaCL  DEFAULT_MSG
+                                  VIENNACL_INCLUDE_DIR)
+
+mark_as_advanced(VIENNACL_INCLUDE_DIRS)
diff --git a/ecbuild/cmake/FindXLFortranLibs.cmake b/ecbuild/cmake/FindXLFortranLibs.cmake
new file mode 100644
index 0000000..80f2923
--- /dev/null
+++ b/ecbuild/cmake/FindXLFortranLibs.cmake
@@ -0,0 +1,51 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+
+list( APPEND xl_libs xlf90 xlopt xlf xlsmp )
+
+set( xlf_all_libs_found 1 )
+
+foreach( lib ${xl_libs} )
+
+	find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+	find_library( ${lib}_lib  ${lib} )
+
+	if( ${lib}_lib )
+        list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+	else()
+		set( xlf_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIET and REQUIRED arguments and set XLFORTRANLIBS_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( XLFortranLibs DEFAULT_MSG xlf_all_libs_found XLFORTRAN_LIBRARIES  )
+
+# HACK for support libraries
+
+if(  LIBXLFORTRAN_FOUND )
+	list( APPEND xl_extra_libs pthreads m essl )
+	foreach( lib ${xl_extra_libs} )
+
+		find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+		find_library( ${lib}_lib  ${lib} )
+
+		if( ${lib}_lib )
+			list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+		endif()
+
+	endforeach()
+endif()
+
diff --git a/ecbuild/cmake/FindYACC.cmake b/ecbuild/cmake/FindYACC.cmake
new file mode 100644
index 0000000..3eb3b5c
--- /dev/null
+++ b/ecbuild/cmake/FindYACC.cmake
@@ -0,0 +1,157 @@
+# - Find yacc executable and provides macros to generate custom build rules
+# The module defines the following variables:
+#
+#  YACC_EXECUTABLE - path to the yacc program
+#  YACC_FOUND - true if the program was found
+#
+# The minimum required version of yacc can be specified using the
+# standard CMake syntax, e.g. find_package(YACC 2.1.3)
+#
+# If yacc is found, the module defines the macros:
+#  YACC_TARGET(<Name> <YaccInput> <CodeOutput> [VERBOSE <file>]
+#              [COMPILE_FLAGS <string>])
+# which will create  a custom rule to generate  a parser. <YaccInput> is
+# the path to  a yacc file. <CodeOutput> is the name  of the source file
+# generated by yacc.  A header file is also  be generated, and contains
+# the  token  list.  If  COMPILE_FLAGS  option is  specified,  the  next
+# parameter is  added in the yacc  command line.  if  VERBOSE option is
+# specified, <file> is created  and contains verbose descriptions of the
+# grammar and parser. The macro defines a set of variables:
+#  YACC_${Name}_DEFINED - true is the macro ran successfully
+#  YACC_${Name}_INPUT - The input source file, an alias for <YaccInput>
+#  YACC_${Name}_OUTPUT_SOURCE - The source file generated by yacc
+#  YACC_${Name}_OUTPUT_HEADER - The header file generated by yacc
+#  YACC_${Name}_OUTPUTS - The sources files generated by yacc
+#  YACC_${Name}_COMPILE_FLAGS - Options used in the yacc command line
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(YACC_EXECUTABLE yacc DOC "path to the yacc/yacc executable")
+MARK_AS_ADVANCED(YACC_EXECUTABLE)
+
+IF(YACC_EXECUTABLE)
+  # the yacc commands should be executed with the C locale, otherwise
+  # the message (which are parsed) may be translated
+  SET(_Yacc_SAVED_LC_ALL "$ENV{LC_ALL}")
+  SET(ENV{LC_ALL} C)
+
+  SET(ENV{LC_ALL} ${_Yacc_SAVED_LC_ALL})
+
+  # internal macro
+  MACRO(YACC_TARGET_option_verbose Name YaccOutput filename)
+    LIST(APPEND YACC_TARGET_cmdopt "--verbose")
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_path "${YaccOutput}" PATH)
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_name "${YaccOutput}" NAME_WE)
+    ADD_CUSTOM_COMMAND(OUTPUT ${filename}
+      COMMAND ${CMAKE_COMMAND}
+      ARGS -E copy
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      "${filename}"
+      DEPENDS
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      COMMENT "[YACC][${Name}] Copying yacc verbose table to ${filename}"
+      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+    SET(YACC_${Name}_VERBOSE_FILE ${filename})
+    LIST(APPEND YACC_TARGET_extraoutputs
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output")
+  ENDMACRO(YACC_TARGET_option_verbose)
+
+  # internal macro
+  MACRO(YACC_TARGET_option_extraopts Options)
+    SET(YACC_TARGET_extraopts "${Options}")
+    SEPARATE_ARGUMENTS(YACC_TARGET_extraopts)
+    LIST(APPEND YACC_TARGET_cmdopt ${YACC_TARGET_extraopts})
+  ENDMACRO(YACC_TARGET_option_extraopts)
+
+  #============================================================
+  # YACC_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(YACC_TARGET Name YaccInput YaccOutput)
+    SET(YACC_TARGET_output_header "")
+    SET(YACC_TARGET_cmdopt "")
+    SET(YACC_TARGET_outputs "${YaccOutput}")
+    IF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+      MESSAGE(SEND_ERROR "Usage")
+    ELSE()
+      # Parsing parameters
+      IF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV4}")
+        ENDIF()
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV4}")
+        ENDIF()
+      ENDIF()
+
+      IF(${ARGC} EQUAL 7)
+        IF("${ARGV5}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV6}")
+        ENDIF()
+
+        IF("${ARGV5}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV6}")
+        ENDIF()
+      ENDIF()
+
+      # Header's name generated by yacc (see option -d)
+      LIST(APPEND YACC_TARGET_cmdopt "-d")
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${ARGV2}")
+      STRING(REPLACE "c" "h" _fileext ${_fileext})
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}"
+          YACC_${Name}_OUTPUT_HEADER "${ARGV2}")
+      LIST(APPEND YACC_TARGET_outputs "${YACC_${Name}_OUTPUT_HEADER}")
+
+#       message ( STATUS "${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}" )
+
+      ADD_CUSTOM_COMMAND(OUTPUT ${YACC_TARGET_outputs} ${YACC_TARGET_extraoutputs}
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${ARGV1} ${CMAKE_CURRENT_BINARY_DIR}
+        COMMAND ${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}
+        DEPENDS ${ARGV1}
+        COMMENT "[YACC][${Name}] Building parser with yacc"
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+      # define target variables
+      SET(YACC_${Name}_DEFINED TRUE)
+      SET(YACC_${Name}_INPUT ${ARGV1})
+      SET(YACC_${Name}_OUTPUTS ${YACC_TARGET_outputs})
+      SET(YACC_${Name}_COMPILE_FLAGS ${YACC_TARGET_cmdopt})
+      SET(YACC_${Name}_OUTPUT_SOURCE "${YaccOutput}")
+
+    ENDIF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+  ENDMACRO(YACC_TARGET)
+  #
+  #============================================================
+
+ENDIF(YACC_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(YACC REQUIRED_VARS  YACC_EXECUTABLE )
+
+# FindYACC.cmake ends here
diff --git a/ecbuild/cmake/Findgrib_api.cmake b/ecbuild/cmake/Findgrib_api.cmake
new file mode 100644
index 0000000..c6f6421
--- /dev/null
+++ b/ecbuild/cmake/Findgrib_api.cmake
@@ -0,0 +1,133 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find GRIB_API
+# Once done this will define
+#  GRIB_API_FOUND - System has GRIB_API
+#  GRIB_API_INCLUDE_DIRS - The GRIB_API include directories
+#  GRIB_API_LIBRARIES - The libraries needed to use GRIB_API
+#  GRIB_API_DEFINITIONS - Compiler switches required for using GRIB_API
+
+option( NO_GRIB_API_BINARIES "skip trying to find grib_api installed binaries" OFF )
+option( GRIB_API_PNG "use png with grib_api" ON )
+option( GRIB_API_JPG "use jpg with grib_api" ON )
+
+if( NOT grib_api_FOUND AND NOT NO_GRIB_API_BINARIES )
+
+    if( GRIB_API_JPG ) # jpeg support
+
+        find_package( JPEG     QUIET ) # grib_api might be a static .a library in which
+
+        if( NOT "$ENV{JASPER_PATH}" STREQUAL "" )
+            list( APPEND CMAKE_PREFIX_PATH "$ENV{JASPER_PATH}" )
+        endif()
+        find_package( Jasper   QUIET ) # case we don't know if which jpeg library was used
+
+        find_package( OpenJPEG QUIET ) # so we try to find all jpeg libs and link to them
+
+        if(JPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JPEG_LIBRARIES} )
+        endif()
+        if(JASPER_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JASPER_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JASPER_LIBRARIES} )
+        endif()
+        if(OPENJPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${OPENJPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${OPENJPEG_LIBRARIES} )
+        endif()
+
+    endif()
+
+    if( GRIB_API_PNG ) # png support
+
+        find_package(PNG)
+
+        if( DEFINED PNG_PNG_INCLUDE_DIR AND NOT DEFINED PNG_INCLUDE_DIRS )
+          set( PNG_INCLUDE_DIRS ${PNG_PNG_INCLUDE_DIR}  CACHE INTERNAL "PNG include dirs" )
+        endif()
+        if( DEFINED PNG_LIBRARY AND NOT DEFINED PNG_LIBRARIES )
+          set( PNG_LIBRARIES ${PNG_LIBRARY} CACHE INTERNAL "PNG libraries" )
+        endif()
+
+        if(PNG_FOUND)
+            list( APPEND _grib_api_png_defs ${PNG_DEFINITIONS} )
+            list( APPEND _grib_api_png_incs ${PNG_INCLUDE_DIRS} )
+            list( APPEND _grib_api_png_libs ${PNG_LIBRARIES} )
+        endif()
+
+    endif()
+
+	# The grib_api on macos that comes with 'port' is linked against ghostscript
+	if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+		find_library(GS_LIBRARIES NAMES gs)
+		if( GS_LIBRARIES )
+			list( APPEND GRIB_API_LIBRARIES ${GS_LIBRARIES} )
+		endif()
+	endif()
+
+    # find external grib_api
+
+    if( NOT DEFINED GRIB_API_PATH AND NOT "$ENV{GRIB_API_PATH}" STREQUAL "" )
+        list( APPEND GRIB_API_PATH "$ENV{GRIB_API_PATH}" )
+    endif()
+
+    if( DEFINED GRIB_API_PATH )
+        find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/include PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIBRARY  NAMES grib_api   PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F90  NAMES grib_api_f90 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F77  NAMES grib_api_f77 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_program(GRIB_API_INFO     NAMES grib_info  PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/bin     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+    endif()
+
+    find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIBRARY NAMES grib_api   PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F90 NAMES grib_api_f90 PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F77 NAMES grib_api_f77 PATHS PATH_SUFFIXES grib_api )
+    find_program(GRIB_API_INFO     NAMES grib_info  PATHS PATH_SUFFIXES grib_api )
+
+    list( APPEND GRIB_API_LIBRARIES    ${GRIB_API_LIBRARY} ${GRIB_API_LIB_F90} ${GRIB_API_LIB_F77} )
+    set( GRIB_API_INCLUDE_DIRS ${GRIB_API_INCLUDE_DIR} )
+
+    if( GRIB_API_INFO )
+
+        execute_process( COMMAND ${GRIB_API_INFO} -v  OUTPUT_VARIABLE _grib_info_out ERROR_VARIABLE _grib_info_err OUTPUT_STRIP_TRAILING_WHITESPACE )
+
+        # ecbuild_debug_var( _grib_info_out )
+
+        string( REPLACE "." " " _version_list ${_grib_info_out} ) # dots to spaces
+        separate_arguments( _version_list )
+
+        list( GET _version_list 0 GRIB_API_MAJOR_VERSION )
+        list( GET _version_list 1 GRIB_API_MINOR_VERSION )
+        list( GET _version_list 2 GRIB_API_PATCH_VERSION )
+
+        set( GRIB_API_VERSION     "${GRIB_API_MAJOR_VERSION}.${GRIB_API_MINOR_VERSION}.${GRIB_API_PATCH_VERSION}" )
+        set( GRIB_API_VERSION_STR "${_grib_info_out}" )
+
+        set( grib_api_VERSION     "${GRIB_API_VERSION}" )
+        set( grib_api_VERSION_STR "${GRIB_API_VERSION_STR}" )
+
+    endif()
+
+    include(FindPackageHandleStandardArgs)
+
+    # handle the QUIETLY and REQUIRED arguments and set GRIB_API_FOUND to TRUE
+    find_package_handle_standard_args( grib_api DEFAULT_MSG
+                                       GRIB_API_LIBRARY GRIB_API_INCLUDE_DIR GRIB_API_INFO )
+
+    mark_as_advanced( GRIB_API_INCLUDE_DIR GRIB_API_LIBRARY GRIB_API_INFO )
+
+    list( APPEND GRIB_API_DEFINITIONS  ${_grib_api_jpg_defs} ${_grib_api_png_defs} )
+    list( APPEND GRIB_API_INCLUDE_DIRS ${_grib_api_jpg_incs} ${_grib_api_png_incs} )
+	list( APPEND GRIB_API_LIBRARIES    ${_grib_api_jpg_libs} ${_grib_api_png_libs} )
+
+    set( grib_api_FOUND ${GRIB_API_FOUND} )
+
+endif()
diff --git a/ecbuild/cmake/Findodb_api.cmake b/ecbuild/cmake/Findodb_api.cmake
new file mode 100644
index 0000000..00f4152
--- /dev/null
+++ b/ecbuild/cmake/Findodb_api.cmake
@@ -0,0 +1,97 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT odb_api_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED ODB_API_PATH AND NOT "$ENV{ODB_API_PATH}" STREQUAL "" )
+        list( APPEND ODB_API_PATH "$ENV{ODB_API_PATH}" )
+    endif()
+
+    if( DEFINED ODB_API_PATH )
+
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS ${ODB_API_PATH} ${ODB_API_PATH}/include
+                   PATH_SUFFIXES odb_api
+                   NO_DEFAULT_PATH )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+    endif()
+    
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS
+                   PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+
+    # get the version
+
+    if( ODB_API_INCLUDE_DIR )
+
+        set(_odb_api_VERSION_REGEX "([0-9]+)")
+
+        foreach( v MAJOR MINOR PATCH )
+
+          file(STRINGS "${ODB_API_INCLUDE_DIR}/odb_api_config.h" _odb_api_${v}_VERSION_CONTENTS REGEX "#define ODB_API_${v}_VERSION ")
+
+          if( "${_odb_api_${v}_VERSION_CONTENTS}" MATCHES ".*#define ODB_API_${v}_VERSION ${_odb_api_VERSION_REGEX}.*")
+
+              set( ODB_API_${v}_VERSION "${CMAKE_MATCH_1}" )
+
+          endif()
+
+        endforeach()
+
+    endif()
+
+    set( ODB_API_VERSION     "${ODB_API_MAJOR_VERSION}.${ODB_API_MINOR_VERSION}.${ODB_API_PATCH_VERSION}" )
+    set( ODB_API_VERSION_STR "${_odb_info_out}" )
+
+    set( odb_api_VERSION     "${ODB_API_VERSION}" )
+    set( odb_api_VERSION_STR "${ODB_API_VERSION_STR}" )
+
+    # handle the QUIETLY and REQUIRED arguments and set ODB_API_FOUND to TRUE
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( odb_api DEFAULT_MSG
+                                       ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY ODB_API_INCLUDE_DIR )
+    
+    set( ODB_API_LIBRARIES    ${ODB_API_LIBRARY} ${ODB_API_ECLIB_LIBRARY} )
+    set( ODB_API_INCLUDE_DIRS ${ODB_API_INCLUDE_DIR} )
+
+    mark_as_advanced( ODB_API_INCLUDE_DIR ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY )
+    
+    set( odb_api_FOUND ${ODB_API_FOUND} )
+
+endif()
diff --git a/ecbuild/cmake/Findspot.cmake b/ecbuild/cmake/Findspot.cmake
new file mode 100644
index 0000000..d932bfe
--- /dev/null
+++ b/ecbuild/cmake/Findspot.cmake
@@ -0,0 +1,65 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT spot_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED SPOT_PATH AND NOT "$ENV{SPOT_PATH}" STREQUAL "" )
+        list( APPEND SPOT_PATH "$ENV{SPOT_PATH}" )
+    endif()
+
+    if( DEFINED SPOT_PATH )
+
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS ${SPOT_PATH} ${SPOT_PATH}/include
+                   NO_DEFAULT_PATH )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS ${SPOT_PATH} ${SPOT_PATH}/lib
+                      NO_DEFAULT_PATH )
+
+    endif()
+    
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS
+                   PATH_SUFFIXES spot )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES spot )
+
+
+    # get the version
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( spot DEFAULT_MSG
+                                       SPOT_LIBRARY SPOT_INCLUDE_DIR )
+    
+    set( SPOT_LIBRARIES    ${SPOT_LIBRARY} )
+    set( SPOT_INCLUDE_DIRS ${SPOT_INCLUDE_DIR} )
+
+    mark_as_advanced( SPOT_INCLUDE_DIR SPOT_LIBRARY )
+    
+    set( spot_FOUND ${SPOT_FOUND} )
+
+endif()
diff --git a/ecbuild/cmake/VERSION.cmake b/ecbuild/cmake/VERSION.cmake
new file mode 100644
index 0000000..a95e4a9
--- /dev/null
+++ b/ecbuild/cmake/VERSION.cmake
@@ -0,0 +1,7 @@
+set( ECBUILD_MAJOR_VERSION "2" )
+set( ECBUILD_MINOR_VERSION "7" )
+set( ECBUILD_PATCH_VERSION "0" )
+
+set( ECBUILD_VERSION_STR  "2.7.0" )
+
+set( ECBUILD_MACRO_VERSION "${ECBUILD_VERSION_STR}" )
diff --git a/ecbuild/cmake/compiler_flags/Clang_C.cmake b/ecbuild/cmake/compiler_flags/Clang_C.cmake
new file mode 100644
index 0000000..51cf72b
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Clang_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Clang_CXX.cmake b/ecbuild/cmake/compiler_flags/Clang_CXX.cmake
new file mode 100644
index 0000000..7f6524e
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Clang_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Cray_C.cmake b/ecbuild/cmake/compiler_flags/Cray_C.cmake
new file mode 100644
index 0000000..f9b6e4b
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Cray_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C flags"  FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C flags"               FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug Cflags"                     FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Cray_CXX.cmake b/ecbuild/cmake/compiler_flags/Cray_CXX.cmake
new file mode 100644
index 0000000..fdc4749
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Cray_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C++ flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C++ flags" FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C++ flags"              FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C++ flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug CXX flags"                   FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake b/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake
new file mode 100644
index 0000000..8575bba
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake
@@ -0,0 +1,15 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# -emf activates .mods and uses lower case
+# -rmoid produces a listing file
+set( CMAKE_Fortran_FLAGS_RELEASE        "-emf -rmoid -O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"                    CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-emf -rmoid -O2 -hfp1 -Gfast -DNDEBUG"                                 CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-emf -rmoid -O2 -hfp1 -G2"                                             CACHE STRING "Production Fortran flags"              FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-emf -rmoid -O2 -hfp1 -G2 -hflex_mp=conservative -hadd_paren -DNDEBUG" CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-emf -rmoid -O0 -G0"                                                   CACHE STRING "Debug Fortran flags"                   FORCE )
diff --git a/ecbuild/cmake/compiler_flags/GNU_C.cmake b/ecbuild/cmake/compiler_flags/GNU_C.cmake
new file mode 100644
index 0000000..288fc70
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/GNU_C.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/ecbuild/cmake/compiler_flags/GNU_CXX.cmake b/ecbuild/cmake/compiler_flags/GNU_CXX.cmake
new file mode 100644
index 0000000..1a01e2d
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/GNU_CXX.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake b/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake
new file mode 100644
index 0000000..cd4dbfa
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake
@@ -0,0 +1,23 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -funroll-all-loops -finline-functions"                                CACHE STRING "Fortran compiler flags for Release builds"          FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG -fno-range-check -fconvert=big-endian" CACHE STRING "Fortran compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -fcheck=bounds -fbacktrace -finit-real=snan"                       CACHE STRING "Fortran compiler flags for Debug builds"            FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for Production builds."      FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for RelWithDebInfo builds."  FORCE )
+
+set( Fortran_FLAG_STACK_ARRAYS "-fstack-arrays" )
+
+####################################################################
+
+# Meaning of flags
+# ----------------
+# -fstack-arrays     : Allocate automatic arrays on the stack (needs large stacksize!!!)
+# -funroll-all-loops : Unroll all loops
+# -fcheck=bounds     : Bounds checking
diff --git a/ecbuild/cmake/compiler_flags/Intel_C.cmake b/ecbuild/cmake/compiler_flags/Intel_C.cmake
new file mode 100644
index 0000000..5736004
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Intel_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C compiler flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C compiler flags"  FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C compiler flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C compiler flags"                    FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C compiler flags"               FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Intel_CXX.cmake b/ecbuild/cmake/compiler_flags/Intel_CXX.cmake
new file mode 100644
index 0000000..8e466e5
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Intel_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C++ compiler flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C++ compiler flags" FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C++ compiler flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C++ compiler flags"                   FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C++ compiler flags"              FORCE )
diff --git a/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake b/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake
new file mode 100644
index 0000000..d6e9719
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( Fortran_AUTOMATIC_ARRAYS_LIMIT 32768 )  # (32 kb)
+math( EXPR Fortran_AUTOMATIC_ARRAYS_LIMIT_KB "${Fortran_AUTOMATIC_ARRAYS_LIMIT}/1024" )
+
+set( Fortran_FLAG_STACK_ARRAYS     "-no-heap-arrays" )
+set( Fortran_FLAG_AUTOMATIC_ARRAYS "-heap-arrays ${Fortran_AUTOMATIC_ARRAYS_LIMIT_KB}" )
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-O2 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+# -check all implies -check bounds
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -traceback ${Fortran_FLAG_AUTOMATIC_ARRAYS} -check all" CACHE STRING "Debug Fortran flags"                   FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O3 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Production Fortran compiler flags"     FORCE )
diff --git a/ecbuild/cmake/compiler_flags/PGI_C.cmake b/ecbuild/cmake/compiler_flags/PGI_C.cmake
new file mode 100644
index 0000000..1cc6485
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/PGI_C.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C compiler flags" FORCE )
+
+set( CMAKE_C_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/cmake/compiler_flags/PGI_CXX.cmake b/ecbuild/cmake/compiler_flags/PGI_CXX.cmake
new file mode 100644
index 0000000..6d31cf4
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/PGI_CXX.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C++ compiler flags" FORCE )
+
+set( CMAKE_CXX_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake b/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake
new file mode 100644
index 0000000..86c2a35
--- /dev/null
+++ b/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE "-fast -O3" CACHE STRING "Release Fortran compiler flags" FORCE )
+
+set( CMAKE_Fortran_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake b/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
new file mode 100644
index 0000000..1b5178d
--- /dev/null
+++ b/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
@@ -0,0 +1,33 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# Do NOT include this module directly into any of your code. It is meant as
+# a library for Check*CompilerFlag.cmake modules. It's content may change in
+# any way between releases.
+
+macro (CHECK_COMPILER_FLAG_COMMON_PATTERNS _VAR)
+   set(${_VAR}
+     FAIL_REGEX "[Uu]nrecogni[sz]ed .*option"               # GNU, NAG
+     FAIL_REGEX "unknown .*option"                          # Clang
+     FAIL_REGEX "optimization flag .* not supported"        # Clang
+     FAIL_REGEX "unknown argument ignored"                  # Clang (cl)
+     FAIL_REGEX "ignoring unknown option"                   # MSVC, Intel
+     FAIL_REGEX "warning D9002"                             # MSVC, any lang
+     FAIL_REGEX "option.*not supported"                     # Intel
+     FAIL_REGEX "invalid argument .*option"                 # Intel
+     FAIL_REGEX "ignoring option .*argument required"       # Intel
+     FAIL_REGEX "ignoring option .*argument is of wrong type" # Intel
+     FAIL_REGEX "[Uu]nknown option"                         # HP
+     FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+     FAIL_REGEX "command option .* is not recognized"       # XL
+     FAIL_REGEX "command option .* contains an incorrect subargument" # XL
+     FAIL_REGEX "not supported in this configuration. ignored"       # AIX
+     FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+     FAIL_REGEX "[Uu]nknown switch"                         # PGI
+     FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+     FAIL_REGEX "Incorrect command line option:"            # Borland
+     FAIL_REGEX "Warning: illegal option"                   # SunStudio 12
+     FAIL_REGEX "[Ww]arning: Invalid suboption"             # Fujitsu
+   )
+endmacro ()
diff --git a/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake b/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake
new file mode 100644
index 0000000..8519fcc
--- /dev/null
+++ b/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake
@@ -0,0 +1,53 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranCompilerFlag
+# ------------------------
+#
+# Check whether the Fortran compiler supports a given flag.
+#
+# CHECK_Fortran_COMPILER_FLAG(<flag> <var>)
+#
+# ::
+#
+#   <flag> - the compiler flag
+#   <var>  - variable to store the result
+#            Will be created as an internal cache variable.
+#
+# This internally calls the check_fortran_source_compiles macro and
+# sets CMAKE_REQUIRED_DEFINITIONS to <flag>.  See help for
+# CheckFortranSourceCompiles for a listing of variables that can
+# otherwise modify the build.  The result only tells that the compiler
+# does not give an error message when it encounters the flag.  If the
+# flag has any effect or even a specific one is beyond the scope of
+# this module.
+
+include(CheckFortranSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+macro (CHECK_Fortran_COMPILER_FLAG _FLAG _RESULT)
+  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+
+  # Normalize locale during test compilation.
+  set(_CheckFortranCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(_CheckFortranCompilerFlag_SAVED_${v} "$ENV{${v}}")
+    set(ENV{${v}} C)
+  endforeach()
+  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+  CHECK_Fortran_SOURCE_COMPILES("       program test\n       stop\n       end program" ${_RESULT}
+    # Some compilers do not fail with a bad flag
+    FAIL_REGEX "command line option .* is valid for .* but not for Fortran" # GNU
+    ${_CheckFortranCompilerFlag_COMMON_PATTERNS}
+    )
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(ENV{${v}} ${_CheckFortranCompilerFlag_SAVED_${v}})
+    unset(_CheckFortranCompilerFlag_SAVED_${v})
+  endforeach()
+  unset(_CheckFortranCompilerFlag_LOCALE_VARS)
+  unset(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+
+  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+endmacro ()
diff --git a/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake b/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake
new file mode 100644
index 0000000..c42254c
--- /dev/null
+++ b/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake
@@ -0,0 +1,106 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranSourceCompiles
+# --------------------------
+#
+# Check if given Fortran source compiles and links into an executable::
+#
+#   CHECK_Fortran_SOURCE_COMPILES(<code> <var> [FAIL_REGEX <fail-regex>]
+#                                 [SRC_EXT <ext>])
+#
+# The arguments are:
+#
+# ``<code>``
+#   Source code to try to compile.  It must define a PROGRAM entry point.
+# ``<var>``
+#   Variable to store whether the source code compiled.
+#   Will be created as an internal cache variable.
+# ``FAIL_REGEX <fail-regex>``
+#   Fail if test output matches this regex.
+# ``SRC_EXT <ext>``
+#   Use source extension ``.<ext>`` instead of the default ``.F``.
+#
+# The following variables may be set before calling this macro to modify
+# the way the check is run::
+#
+#   CMAKE_REQUIRED_FLAGS = string of compile command line flags
+#   CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+#   CMAKE_REQUIRED_INCLUDES = list of include directories
+#   CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+#   CMAKE_REQUIRED_QUIET = execute quietly without messages
+
+macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(_FAIL_REGEX)
+    set(_SRC_EXT)
+    set(_key)
+    foreach(arg ${ARGN})
+      if("${arg}" MATCHES "^(FAIL_REGEX|SRC_EXT)$")
+        set(_key "${arg}")
+      elseif(_key)
+        list(APPEND _${_key} "${arg}")
+      else()
+        message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
+      endif()
+    endforeach()
+    if(NOT _SRC_EXT)
+      set(_SRC_EXT F)
+    endif()
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_compile(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+
+    foreach(_regex ${_FAIL_REGEX})
+      if("${OUTPUT}" MATCHES "${_regex}")
+        set(${VAR} 0)
+      endif()
+    endforeach()
+
+    if(${VAR})
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Fortran SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Fortran SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/ecbuild/cmake/contrib/FindEigen3.cmake b/ecbuild/cmake/contrib/FindEigen3.cmake
new file mode 100644
index 0000000..9315294
--- /dev/null
+++ b/ecbuild/cmake/contrib/FindEigen3.cmake
@@ -0,0 +1,97 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  else()
+	set( EIGEN3_VERSION ${EIGEN3_VERSION} CACHE INTERNAL "Eigen3 version" )
+  endif()
+
+endmacro(_eigen3_check_version)
+
+if(EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else(EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      ${EIGEN3_PATH}/include
+      ${EIGEN3_DIR}/include
+      ${EIGEN3_ROOT}/include
+      ${EIGEN_PATH}/include
+      ${EIGEN_DIR}/include
+      ${EIGEN_ROOT}/include
+      ENV EIGEN3_PATH
+      ENV EIGEN3_DIR
+      ENV EIGEN3_ROOT
+      ENV EIGEN_PATH
+      ENV EIGEN_DIR
+      ENV EIGEN_ROOT
+      PATH_SUFFIXES eigen3 eigen include/eigen3 include/eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+set( EIGEN3_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR} )
diff --git a/ecbuild/cmake/contrib/FindNetCDF4.cmake b/ecbuild/cmake/contrib/FindNetCDF4.cmake
new file mode 100644
index 0000000..70e54af
--- /dev/null
+++ b/ecbuild/cmake/contrib/FindNetCDF4.cmake
@@ -0,0 +1,337 @@
+# Project uclales
+# http://gitorious.org/uclales
+# License: Academic Free License v3.0
+#
+# - Find NETCDF, a library for reading and writing self describing array data.
+#
+# This module invokes the NETCDF wrapper compiler that should be installed
+# alongside NETCDF.  Depending upon the NETCDF Configuration, the wrapper compiler
+# is called either h5cc or h5pcc.  If this succeeds, the module will then call
+# the compiler with the -show argument to see what flags are used when compiling
+# an NETCDF client application.
+#
+# The module will optionally accept the COMPONENTS argument.  If no COMPONENTS
+# are specified, then the find module will default to finding only the NETCDF C
+# library.  If one or more COMPONENTS are specified, the module will attempt to
+# find the language bindings for the specified components.  Currently, the only
+# valid components are C, CXX, FORTRAN and F90.
+#
+# On UNIX systems, this module will read the variable NETCDF_USE_STATIC_LIBRARIES
+# to determine whether or not to prefer a static link to a dynamic link for NETCDF
+# and all of it's dependencies.  To use this feature, make sure that the
+# NETCDF_USE_STATIC_LIBRARIES variable is set before the call to find_package.
+#
+# To provide the module with a hint about where to find your NETCDF installation,
+# set the CMake or environment variable NETCDF_ROOT, NETCDF_DIR, NETCDF_PATH or
+# NETCDF4_DIR. The Find module will then look in this path when searching for
+# NETCDF executables, paths, and libraries.
+#
+# In addition to finding the includes and libraries required to compile an NETCDF
+# client application, this module also makes an effort to find tools that come
+# with the NETCDF distribution that may be useful for regression testing.
+#
+# This module will define the following variables:
+#  NETCDF_INCLUDE_DIRS - Location of the NETCDF includes
+#  NETCDF_INCLUDE_DIR - Location of the NETCDF includes (deprecated)
+#  NETCDF_DEFINITIONS - Required compiler definitions for NETCDF
+#  NETCDF_C_LIBRARIES - Required libraries for the NETCDF C bindings.
+#  NETCDF_CXX_LIBRARIES - Required libraries for the NETCDF C++ bindings
+#  NETCDF_FORTRAN_LIBRARIES - Required libraries for the NETCDF FORTRAN bindings
+#  NETCDF_F90_LIBRARIES - Required libraries for the NETCDF FORTRAN 90 bindings
+#  NETCDF_LIBRARIES - Required libraries for all requested bindings
+#  NETCDF_FOUND - true if NETCDF was found on the system
+#  NETCDF_LIBRARY_DIRS - the full set of library directories
+#  NETCDF_IS_PARALLEL - Whether or not NETCDF was found with parallel IO support
+#  NETCDF_CONFIG_EXECUTABLE - the path to the NC-CONFIG tool
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+# This module is maintained by Thijs Heus <thijs.heus at zmaw.de>.
+
+include(SelectLibraryConfigurations)
+include(FindPackageHandleStandardArgs)
+
+# List of the valid NETCDF components
+set( NETCDF_VALID_COMPONENTS
+    FORTRAN
+    F90
+    CXX
+    C
+)
+
+# Invoke the NETCDF wrapper compiler.  The compiler return value is stored to the
+# return_value argument, the text output is stored to the output variable.
+macro( _NETCDF_CONFIG flag output return_value )
+    if( NETCDF_CONFIG_EXECUTABLE )
+        exec_program( ${NETCDF_CONFIG_EXECUTABLE}
+            ARGS ${flag}
+            OUTPUT_VARIABLE ${output}
+            RETURN_VALUE ${return_value}
+        )
+        if( ${${return_value}} EQUAL 0 )
+            # do nothing
+        else()
+            message( STATUS
+              "Unable to determine ${flag} from NC-CONFIG." )
+        endif()
+    endif()
+endmacro()
+#
+# try to find the NETCDF wrapper compilers
+find_program( NETCDF_CONFIG_EXECUTABLE
+    NAMES nc-config
+    HINTS ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+          ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+    PATH_SUFFIXES bin Bin
+    DOC "NETCDF CONFIG PROGRAM.  Used only to detect NETCDF compile flags." )
+mark_as_advanced( NETCDF_CONFIG_EXECUTABLE )
+ecbuild_debug("FindNetCDF4: nc-config executable = ${NETCDF_CONFIG_EXECUTABLE}")
+
+set(output "no")
+_NETCDF_CONFIG (--has-hdf5 output return)
+set(HAS_HDF5 FALSE)
+
+if(${output} STREQUAL yes)
+  set(HAS_HDF5 TRUE)
+  set(HDF5_FIND_QUIETLY ${NETCDF_FIND_QUIETLY})
+  set(HDF5_FIND_REQUIRED ${NETCDF_FIND_REQUIRED})
+  find_package(HDF5)
+#        list( APPEND NETCDF_LIBRARIES_DEBUG
+#            ${HDF5_LIBRARIES_DEBUG} )
+#        list( APPEND NETCDF_LIBRARIES_RELEASE
+#            ${HDF5_LIBRARIES_RELEASE} )
+  set (NETCDF_IS_PARALLEL ${HDF5_IS_PARALLEL})
+endif()
+_NETCDF_CONFIG (--has-pnetcdf output return)
+if(${output} STREQUAL yes)
+  set (NETCDF_IS_PARALLEL TRUE)
+else()
+#   set(NETCDF_IS_PARALLEL FALSE)
+endif()
+set( NETCDF_IS_PARALLEL TRUE CACHE BOOL
+    "NETCDF library compiled with parallel IO support" )
+
+
+if( NETCDF_INCLUDE_DIRS AND NETCDF_LIBRARIES )
+    # Do nothing: we already have NETCDF_INCLUDE_PATH and NETCDF_LIBRARIES in the
+    # cache, it would be a shame to override them
+else()
+    if( NOT NETCDF_FIND_COMPONENTS )
+        set( NETCDF_LANGUAGE_BINDINGS "C" )
+    else()
+        # add the extra specified components, ensuring that they are valid.
+        foreach( component ${NETCDF_FIND_COMPONENTS} )
+            list( FIND NETCDF_VALID_COMPONENTS ${component} component_location )
+            if( ${component_location} EQUAL -1 )
+                message( FATAL_ERROR
+                    "\"${component}\" is not a valid NETCDF component." )
+            else()
+                list( APPEND NETCDF_LANGUAGE_BINDINGS ${component} )
+            endif()
+        endforeach()
+    endif()
+
+    # seed the initial lists of libraries to find with items we know we need
+    set( NETCDF_C_INCLUDE_NAMES netcdf.h )
+    set( NETCDF_CXX_INCLUDE_NAMES netcdfcpp.h ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_FORTRAN_INCLUDE_NAMES ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_F90_INCLUDE_NAMES netcdf.mod typesizes.mod ${NETCDF_C_INCLUDE_NAMES} )
+
+    set( NETCDF_C_LIBRARY_NAMES netcdf)
+    set( NETCDF_CXX_LIBRARY_NAMES netcdf_c++ netcdf_c++4 ${NETCDF_C_LIBRARY_NAMES} )
+    set( NETCDF_FORTRAN_LIBRARY_NAMES netcdff ${NETCDF_C_LIBRARY_NAMES})
+    set( NETCDF_F90_LIBRARY_NAMES ${NETCDF_FORTRAN_LIBRARY_NAMES} )
+
+    set( NETCDF_REQUIRED netcdf.h netcdfcpp.h netcdf.mod typesizes.mod netcdf netcdff netcdf_c++ netcdf_c++4)
+
+    foreach( LANGUAGE ${NETCDF_LANGUAGE_BINDINGS} )
+        ecbuild_debug("FindNetCDF4: looking for ${LANGUAGE} language bindings")
+
+        set( NETCDF_${LANGUAGE}_FOUND 1 ) # disable this in following if necessary
+
+        # find the NETCDF includes
+        foreach( INC ${NETCDF_${LANGUAGE}_INCLUDE_NAMES} )
+          #ecbuild_debug( "FindNetCDF4: looking for include file ${INC}")
+
+          find_path( NETCDF_${INC}_INCLUDE_DIR ${INC}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+          )
+          if( NOT NETCDF_${INC}_INCLUDE_DIR )
+            #ecbuild_debug( "FindNetCDF4: ${INC} not found" )
+            GET_FILENAME_COMPONENT( _basename ${INC} NAME_WE )
+            GET_FILENAME_COMPONENT( _ext ${INC} EXT )
+            string( TOUPPER ${_basename} _BASENAME )
+            set( INC_MOD "${_BASENAME}${_ext}")
+            #ecbuild_debug( "FindNetCDF4:     try ${INC_MOD}" )
+            find_path( NETCDF_${INC}_INCLUDE_DIR ${INC_MOD}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+            )
+          endif()
+
+          mark_as_advanced( NETCDF_${INC}_INCLUDE_DIR )
+          #ecbuild_debug_var( NETCDF_${INC}_INCLUDE_DIR)
+          if (NETCDF_${INC}_INCLUDE_DIR)
+            list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_${INC}_INCLUDE_DIR} )
+          else()
+            list( FIND NETCDF_REQUIRED ${INC} location )
+            if( ${location} EQUAL -1 )
+              else()
+              if(NETCDF_FIND_REQUIRED)
+                ecbuild_error( "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              endif()
+              set( NETCDF_${LANGUAGE}_FOUND 0 )
+            else()
+            endif()
+          endif()
+        endforeach()
+        # find the NETCDF libraries
+        foreach( LIB ${NETCDF_${LANGUAGE}_LIBRARY_NAMES} )
+            if( UNIX AND NETCDF_USE_STATIC_LIBRARIES )
+                # According to bug 1643 on the CMake bug tracker, this is the
+                # preferred method for searching for a static library.
+                # See http://www.cmake.org/Bug/view.php?id=1643.  We search
+                # first for the full static library name, but fall back to a
+                # generic search on the name if the static search fails.
+                set( THIS_LIBRARY_SEARCH_DEBUG lib${LIB}d.a ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} )
+            else()
+                set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} )
+            endif()
+            find_library( NETCDF_${LIB}_LIBRARY_DEBUG
+                NAMES ${THIS_LIBRARY_SEARCH_DEBUG}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib)
+            find_library( NETCDF_${LIB}_LIBRARY_RELEASE
+                NAMES ${THIS_LIBRARY_SEARCH_RELEASE}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib )
+            select_library_configurations( NETCDF_${LIB} )
+            # even though we adjusted the individual library names in
+            # select_library_configurations, we still need to distinguish
+            # between debug and release variants because NETCDF_LIBRARIES will
+            # need to specify different lists for debug and optimized builds.
+            # We can't just use the NETCDF_${LIB}_LIBRARY variable (which was set
+            # up by the selection macro above) because it may specify debug and
+            # optimized variants for a particular library, but a list of
+            # libraries is allowed to specify debug and optimized only once.
+          if (NETCDF_${LIB}_LIBRARY_RELEASE)
+            list( APPEND NETCDF_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_DEBUG)
+            list( APPEND NETCDF_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_RELEASE OR NETCDF_${LIB}_LIBRARY_DEBUG )
+          else()
+            list( FIND NETCDF_REQUIRED ${LIB} location )
+            if( ${location} EQUAL -1 )
+            else()
+              if(NETCDF_FIND_REQUIRED)
+                message( SEND_ERROR "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              else()
+                set( NETCDF_${LANGUAGE}_FOUND 0 )
+              endif()
+           endif()
+          endif()
+        endforeach()
+        list( APPEND NETCDF_LIBRARY_DIRS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS} )
+
+        # Append the libraries for this language binding to the list of all
+        # required libraries.
+
+        if( NETCDF_${LANGUAGE}_FOUND )
+            ecbuild_debug( "FindNetCDF4: ${LANGUAGE} language bindings found" )
+            if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    debug ${NETCDF_${LANGUAGE}_LIBRARIES_DEBUG}
+                    optimized ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            else()
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            endif()
+        endif()
+        # ecbuild_debug_var( NETCDF_${LANGUAGE}_LIBRARIES )
+        list( APPEND NETCDF_FOUND_REQUIRED_VARS NETCDF_${LANGUAGE}_FOUND )
+    endforeach()
+
+    # We may have picked up some duplicates in various lists during the above
+    # process for the language bindings (both the C and C++ bindings depend on
+    # libz for example).  Remove the duplicates.
+    if( NETCDF_INCLUDE_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+    endif()
+    if( NETCDF_LIBRARIES_DEBUG )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_DEBUG )
+    endif()
+    if( NETCDF_LIBRARIES_RELEASE )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_RELEASE )
+    endif()
+    if( NETCDF_LIBRARY_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARY_DIRS )
+    endif()
+
+    # Construct the complete list of NETCDF libraries with debug and optimized
+    # variants when the generator supports them.
+    if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+        set( NETCDF_LIBRARIES
+            debug ${NETCDF_LIBRARIES_DEBUG}
+            optimized ${NETCDF_LIBRARIES_RELEASE} )
+    else()
+        set( NETCDF_LIBRARIES ${NETCDF_LIBRARIES_RELEASE} )
+    endif()
+endif()
+
+set( NETCDF4_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+set( NETCDF4_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+# handle the QUIET and REQUIRED arguments and set NETCDF4_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF4 DEFAULT_MSG
+    ${NETCDF_FOUND_REQUIRED_VARS}
+    NETCDF_LIBRARIES
+    NETCDF_INCLUDE_DIRS
+)
+
+mark_as_advanced(
+    NETCDF_INCLUDE_DIRS
+    NETCDF_LIBRARIES
+    NETCDF_LIBRARY_DIRS
+)
+
+set( NETCDF_FOUND ${NETCDF4_FOUND} )
+
+# For backwards compatibility we set NETCDF_INCLUDE_DIR to the value of
+# NETCDF_INCLUDE_DIRS
+set( NETCDF_INCLUDE_DIR "${NETCDF_INCLUDE_DIRS}" )
+
diff --git a/ecbuild/cmake/contrib/FindNumPy.cmake b/ecbuild/cmake/contrib/FindNumPy.cmake
new file mode 100644
index 0000000..ba02cec
--- /dev/null
+++ b/ecbuild/cmake/contrib/FindNumPy.cmake
@@ -0,0 +1,101 @@
+# - Find the NumPy libraries
+# This module finds if NumPy is installed, and sets the following variables
+# indicating where it is.
+#
+# TODO: Update to provide the libraries and paths for linking npymath lib.
+#
+#  NUMPY_FOUND               - was NumPy found
+#  NUMPY_VERSION             - the version of NumPy found as a string
+#  NUMPY_VERSION_MAJOR       - the major version number of NumPy
+#  NUMPY_VERSION_MINOR       - the minor version number of NumPy
+#  NUMPY_VERSION_PATCH       - the patch version number of NumPy
+#  NUMPY_VERSION_DECIMAL     - e.g. version 1.6.1 is 10601
+#  NUMPY_INCLUDE_DIRS        - path to the NumPy include files
+
+#============================================================================
+# Copyright 2012 Continuum Analytics, Inc.
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+#============================================================================
+
+# Finding NumPy involves calling the Python interpreter
+if(NumPy_FIND_REQUIRED)
+    find_package(PythonInterp REQUIRED)
+else()
+    find_package(PythonInterp)
+endif()
+
+if(NOT PYTHONINTERP_FOUND)
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+    "import numpy as n; print(n.__version__); print(n.get_include());"
+    RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS
+    OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT
+    ERROR_VARIABLE _NUMPY_ERROR_VALUE
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0)
+    if(NumPy_FIND_REQUIRED)
+        message(FATAL_ERROR
+            "NumPy import failure:\n${_NUMPY_ERROR_VALUE}")
+    endif()
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+# Convert the process output into a list
+string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT})
+string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES})
+# Just in case there is unexpected output from the Python command.
+list(GET _NUMPY_VALUES -2 NUMPY_VERSION)
+list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS)
+
+string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}")
+if("${_VER_CHECK}" STREQUAL "")
+    # The output from Python was unexpected. Raise an error always
+    # here, because we found NumPy, but it appears to be corrupted somehow.
+    message(FATAL_ERROR
+        "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n")
+    return()
+endif()
+
+# Make sure all directory separators are '/'
+string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS})
+
+# Get the major and minor version numbers
+string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION})
+list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR)
+list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR)
+list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH)
+string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH})
+math(EXPR NUMPY_VERSION_DECIMAL
+    "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}")
+
+find_package_message(NUMPY
+    "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}"
+    "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}")
+
+set(NUMPY_FOUND TRUE)
\ No newline at end of file
diff --git a/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake b/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake
new file mode 100644
index 0000000..ea1da07
--- /dev/null
+++ b/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake
@@ -0,0 +1,123 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+#  get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+#  git_describe(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+#  git_get_exact_tag(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+    return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+    set(GIT_PARENT_DIR "${PROJECT_SOURCE_DIR}")
+    set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    while(NOT EXISTS "${GIT_DIR}")  # .git dir not found, search parent directories
+        set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+        get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+        if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+            # We have reached the root directory, we are not in git
+            set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            return()
+        endif()
+        set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    endwhile()
+    set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+    if(NOT EXISTS "${GIT_DATA}")
+        file(MAKE_DIRECTORY "${GIT_DATA}")
+    endif()
+
+    if(NOT EXISTS "${GIT_DIR}/HEAD")
+        return()
+    endif()
+    set(HEAD_FILE "${GIT_DATA}/HEAD")
+    configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+    configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+        "${GIT_DATA}/grabRef.cmake"
+        @ONLY)
+    include("${GIT_DATA}/grabRef.cmake")
+
+    set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+    set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+    if(NOT GIT_FOUND)
+        find_package(Git QUIET)
+    endif()
+    get_git_head_revision(refspec hash)
+    if(NOT GIT_FOUND)
+        set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+    if(NOT hash)
+        set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+
+    # TODO sanitize
+    #if((${ARGN}" MATCHES "&&") OR
+    #   (ARGN MATCHES "||") OR
+    #   (ARGN MATCHES "\\;"))
+    #   message("Please report the following error to the project!")
+    #   message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+    #endif()
+
+    #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+    execute_process(COMMAND
+        "${GIT_EXECUTABLE}"
+        describe
+        ${hash}
+        ${ARGN}
+        WORKING_DIRECTORY
+        "${CMAKE_SOURCE_DIR}"
+        RESULT_VARIABLE
+        res
+        OUTPUT_VARIABLE
+        out
+        ERROR_QUIET
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if(NOT res EQUAL 0)
+        set(out "${out}-${res}-NOTFOUND")
+    endif()
+
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+    git_describe(out --exact-match ${ARGN})
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in b/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in
new file mode 100644
index 0000000..9fd3e64
--- /dev/null
+++ b/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,42 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+    # named branch
+    string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+    if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+        configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+    elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
+        configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+        set(HEAD_HASH "${HEAD_REF}")
+    endif()
+else()
+    # detached HEAD
+    configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+  if(EXISTS "@GIT_DATA@/head-ref")
+    file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+    string(STRIP "${HEAD_HASH}" HEAD_HASH)
+  else()
+    set(HEAD_HASH "unknown")
+  endif()
+endif()
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes b/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes
new file mode 100644
index 0000000..0b6acf1
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc  diff=astextplain
+*.DOC  diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF  diff=astextplain
+*.rtf  diff=astextplain
+*.RTF  diff=astextplain
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore b/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore
new file mode 100644
index 0000000..8a94597
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore
@@ -0,0 +1,6 @@
+build/
+.*.swp
+.*.swo
+*.pyc
+wiki
+*.*~
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
new file mode 100644
index 0000000..ac7f456
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
@@ -0,0 +1,59 @@
+include(CheckCXXCompilerFlag)
+
+#�On older cmake versions + newer compilers, 
+#�the given version of CheckCXXCompilerFlags does not quite work.
+if(CMAKE_VERSION VERSION_LESS 2.8.9)
+  macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+     set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+     set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+     CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+       # Some compilers do not fail with a bad flag
+       FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+       FAIL_REGEX "unrecognized .*option"                     # GNU
+       FAIL_REGEX "unknown .*option"                          # Clang
+       FAIL_REGEX "ignoring unknown option"                   # MSVC
+       FAIL_REGEX "warning D9002"                             # MSVC, any lang
+       FAIL_REGEX "option.*not supported"                     # Intel
+       FAIL_REGEX "invalid argument .*option"                 # Intel
+       FAIL_REGEX "ignoring option .*argument required"       # Intel
+       FAIL_REGEX "[Uu]nknown option"                         # HP
+       FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+       FAIL_REGEX "command option .* is not recognized"       # XL
+       FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+       FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+       FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+       )
+     set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+  endmacro ()
+endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+if(MINGW) 
+  check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+  check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+endif(MINGW)
+if(has_std_gnupp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
+elseif(has_std_gnupp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
+elseif(has_std_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+elseif(has_std_cpp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+elseif(has_hstd_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -hstd=c++11")
+endif(has_std_gnupp11)
+
+if(MSVC) 
+  set(MSWINDOBE TRUE)
+  add_definitions(/EHsc)
+  # Wd4251 stops MSCrapWare from issuing meaningless warnings. Seems Microsoft engineers don't grok
+  # dynamic libraries yet. Or templates. Or both acting alone or together. In any case, issuing
+  # warning sure is easier on them than fixing  their OS.
+  # Unfortunately, it does disable warnings that may be of interest. Possibly. 
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_VARIADIC_MAX=10 /wd4251")
+endif(MSVC)
+
+set(PROJECT_USES_CPP11 True CACHE INTERNAL "Uses c++11.")
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
new file mode 100644
index 0000000..7db9357
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
@@ -0,0 +1,72 @@
+# CMake arguments for gtest.
+set(GTEST_CMAKE_ARGS 
+      -DBUILD_SHARED_LIBS=OFF
+      -Dgtest_force_shared_crt=ON
+      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+      -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
+      -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+      -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+      -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+      -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+      -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+      -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL})
+if(MINGW)
+  list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON)
+else(MINGW)
+  find_package(Threads)
+endif(MINGW)
+
+
+# Add gtest
+if(NOT EXTERNAL_ROOT)
+  set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+endif(NOT EXTERNAL_ROOT)
+include(ExternalProject)
+ExternalProject_Add(
+    googletest
+    PREFIX ${EXTERNAL_ROOT}
+    SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
+    TIMEOUT 10
+    # Force separate output paths for debug and release builds to allow easy
+    # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
+    CMAKE_ARGS ${GTEST_CMAKE_ARGS}
+    # Disable install step
+    INSTALL_COMMAND ""
+    # Wrap download, configure and build steps in a script to log output
+    LOG_DOWNLOAD ON
+    LOG_CONFIGURE ON
+    LOG_BUILD ON)
+
+if(PROJECT_USES_CPP11)
+  add_definitions(-DGTEST_LANG_CXX11)
+endif(PROJECT_USES_CPP11)
+
+macro(add_gtest name source)
+
+  ExternalProject_Get_Property(googletest source_dir)
+  include_directories(${source_dir}/include)
+  # Better, but only works on CMake 2.8.6?
+  # get_target_property(THISTEST_INCLUDE test_${name} INCLUDE_DIRECTORIES)
+  # set_target_properties(test_${name} PROPERTIES INCLUDE_DIRECTORIES
+  #                       "${source_dir}/include;${THISTEST_INCLUDE}") 
+
+  add_executable(test_${name} ${source})
+  ExternalProject_Get_Property(googletest binary_dir)
+  if(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/${CMAKE_CFG_INTDIR}/gtest.lib)
+  else(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/libgtest.a)
+  endif(MSVC)
+  if(CMAKE_THREAD_LIBS_INIT)
+    target_link_libraries(test_${name} ${CMAKE_THREAD_LIBS_INIT})
+  endif(CMAKE_THREAD_LIBS_INIT)
+
+  add_dependencies(test_${name} googletest)
+  if(NOT "${ARGN}" STREQUAL "")
+    target_link_libraries(test_${name} ${ARGN})
+  endif(NOT "${ARGN}" STREQUAL "")
+
+  add_test(cxx_${name} ${EXECUTABLE_OUTPUT_PATH}/test_${name}
+              --gtest_output=xml:${CMAKE_BINARY_DIR}/test-results/test_${name}.xml)
+  set_tests_properties(cxx_${name} PROPERTIES LABELS "gtest")
+endmacro()
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
new file mode 100644
index 0000000..593b62f
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
@@ -0,0 +1,198 @@
+# Checks for C++11 features
+#
+# USAGE: There are two functions
+#
+# cxx11_find_all_features(OUTPUT_VARIABLE)
+# This function returns a variable with all possible features.
+#
+# cxx11_feature_check([feature feature] [REQUIRED [feature feature]])
+# If no arguments are provided, then checks all available features
+# Features appeacing before REQUIRED are optional.
+# If arguments are provided and those features are available, sets
+# the variable HAS_CXX11_FEATURENAME, where FEATURENAME is the input in capital letters.
+# Fails if required feature are not available
+#
+# For possible features, please print out the result from the first function.
+#
+# Original script by Rolf Eike Beer
+# Modifications by Andreas Weis
+# Further Modifications by RSDT at UCL
+# Adapted to ecBuild by Florian Rathgeber <florian.rathgeber at ecmwf.int>
+
+set(CPP11_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp11 CACHE INTERNAL "c++11 file directory")
+
+MACRO(cxx11_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/cxx11_feature_tests/cxx11_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking C++11 support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.cpp")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(cxx11_check_single_feature)
+
+# Find list of all features
+function(cxx11_find_all_features outvar)
+  FILE(GLOB ALL_CPP11_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/*.cpp")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_CPP11_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_cxx11_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/${current_feature}*.cpp")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[c++11] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+  cxx11_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_cxx11_feature)
+
+function(cxx11_feature_check)
+
+  # find all features
+  cxx11_find_all_features(ALL_CPP11_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_CPP11_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[c++11] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # MinGW has not implemented std::random_device fully yet. Unfortunately, this can only be detected
+  # by running a program which tries to call std::random_device. However that generates an error that
+  # is *not* caught by CMake's try_run.
+  if(MSYS)
+    list(REMOVE_ITEM OPTIONALS "random_device")
+    list(FIND REQUIRED "random_device" feature_was_found)
+    if(NOT feature_was_found EQUAL "-1")
+      ecbuild_critical("[c++1] MSYS does not implement Random devices fully.\n"
+                       "       It cannot be required on this system.")
+    endif()
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_cxx11_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_cxx11_feature(${current_feature})
+    set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[c++11] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(cxx11_feature_check)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
new file mode 100644
index 0000000..bc74e08
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
@@ -0,0 +1,53 @@
+# Tries and find which isnan to use
+# defines ISNAN_VARIATION, which should be included in a configuration files somewher as
+#   @ISNAN_VARIATION@
+if(ISNAN_VARIATION)
+  return()
+endif(ISNAN_VARIATION)
+
+include(CheckCXXSourceCompiles)
+CHECK_CXX_SOURCE_COMPILES(
+  "#include <cmath>\nint main() { bool a = std::isnan(0e0); return 0; }\n" 
+  CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = isnan(0e0); return 0; }\n" 
+    CXX_HAS_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS___ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "# include <float.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS_FLOAT_H_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+  message(FATAL_ERROR "[isnan] could not find standard function on this OS.")
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+
+if(CXX_HAS_STD_ISNAN)
+  set(ISNAN_HEADERS "#include <cmath>")
+  set(ISNAN_VARIATION "std::isnan")
+elseif(CXX_HAS_ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "::isnan")
+elseif(CXX_HAS___ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "__isnan")
+elseif(CXX_HAS_FLOAT_H_ISNAN)
+  set(ISNAN_HEADERS "#include <float.h>")
+  set(ISNAN_VARIATION "_isnan")
+else()
+  message(FATAL_ERROR "AM HERE")
+endif()
+if(ISNAN_VARIATION)
+  set(ISNAN_VARIATION ${ISNAN_VARIATION} CACHE INTERNAL "Definition for isnan\n")
+  set(ISNAN_HEADERS ${ISNAN_HEADERS} CACHE INTERNAL "Headers containing isnan definition\n")
+endif(ISNAN_VARIATION)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
new file mode 100644
index 0000000..646d5dd
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
@@ -0,0 +1,131 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+# Modified by RSDT at UCL 
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  endif(NOT EIGEN3_VERSION_OK)
+endmacro(_eigen3_check_version)
+
+if(NOT EIGEN3_INCLUDE_DIR)
+  if(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+    set(EIGEN3_INCLUDE_DIR $ENV{EIGEN3_INCLUDE_DIR})
+  endif(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+endif(NOT EIGEN3_INCLUDE_DIR)
+if (EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else (EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      $ENV{HOME}/usr/include
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      /usr/include
+      /usr/local/include
+      ${EXTERNAL_ROOT}/include
+      PATH_SUFFIXES eigen3 eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+if(NOT EIGEN3_FOUND)
+  if(CMAKE_VERSION VERSION_LESS 2.8.10)
+    # Doesn't have Hg download prior to 2.8.10
+    message(FATAL_ERROR "Please install eigen.")
+  else(CMAKE_VERSION VERSION_LESS 2.8.10)
+    if(NOT EXTERNAL_ROOT)
+      set(EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/external)
+    endif(NOT EXTERNAL_ROOT)
+    find_package(Hg)
+    if(HG_FOUND)
+    
+      message(STATUS "Eigen3 not found. Will attempt to download it.")
+      include(ExternalProject)
+      ExternalProject_Add(
+          eigen
+          PREFIX ${EXTERNAL_ROOT}
+          HG_REPOSITORY https://bitbucket.org/eigen/eigen/
+          HG_TAG 3.2.0
+          TIMEOUT 10
+          CMAKE_ARGS 
+            -DCMAKE_INSTALL_PREFIX=${EXTERNAL_ROOT}
+            -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+            -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+            -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+            -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+            -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+            -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL}
+          # Wrap download, configure and build steps in a script to log output
+          LOG_DOWNLOAD ON
+          LOG_CONFIGURE ON
+          LOG_BUILD ON)
+      set(EIGEN3_INCLUDE_DIR ${EXTERNAL_ROOT}/include/eigen3)
+  
+    else(HG_FOUND)
+   
+      message(FATAL_ERROR "Hg not found, and eigen not found.\nNeed one or the other.")
+   
+    endif(HG_FOUND)
+  endif(CMAKE_VERSION VERSION_LESS 2.8.10)
+endif(NOT EIGEN3_FOUND)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE b/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE
new file mode 100644
index 0000000..8dbb759
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 University College London
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md b/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md
new file mode 100644
index 0000000..a0a6947
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md
@@ -0,0 +1,176 @@
+The Great CMake CookOff
+=======================
+
+
+This is a repository of usefull and less than usefull cmake recipes.  It is distributed under the
+[MIT License](http://opensource.org/licenses/MIT)
+
+
+Adding [Eigen](http://eigen.tuxfamily.org/) to a project
+========================================================
+
+Looks for the Eigen installed on system. If not found, then uses external project to download it.
+Usage is as follows:
+
+```cmake
+# Tell cmake to look into GreatCMakeCookOff for recipes
+set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/GreatCMakeCookOff) 
+
+# Optionally, tell cmake where to download eigen, if needed.
+# Defaults to value below.
+set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+
+# Now look for cmake.
+find_package(Eigen)
+```
+
+**NOTE:** After building the first time, run cmake again. It will find the eigen it downloaded
+previously, and it will stop checking for updates. 
+
+Adding [GTest](https://code.google.com/p/googletest/) to a project
+==================================================================
+
+For googly reasons, whether valid or 404, GTest prefers to be compiled for each an every project. 
+This script does two things:
+
+- it adds GTest as an external project
+- it provides a function to add gtests to ctest
+
+This implies that GTest is downloaded the first time that make runs. Furthermore, it will be
+checked each and every time that makes runs. So, make now requires a working internet connection.
+Unlike Eigen above, there is currently no option avoid checking for updates.
+
+The CMakeLists.txt file could look like this:
+
+```cmake
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  find_package(GTest)
+  enable_testing()
+endif(tests)
+```
+
+And adding a test comes down to
+
+```cmake
+if(tests)
+
+  add_gtest(testme testme.cc mylib)
+
+endif(tests)
+```
+
+- first argument: name of the test
+- second argument: list of source files
+- other arguments: additional libraries to add during linking
+
+The test do expect an explicit main function. See the test generated in ``tests/addgtest.cmake``.
+
+**NOTE:** When using c++11, it is recommended to first include the c++11 flag script
+``AddCPP11Flags.cmake`` (see below) so that the gtest can be compiled with ``GTEST_LANG_CXX11``. 
+
+C++11
+=====
+
+Checking for specific features
+------------------------------
+
+Look for some c++11 features. Uses a script modified from [here](http://pageant.ghulbus.eu/?p=664).
+Usage is given below.
+
+```cmake
+# First need to enable c++
+enable_language(CXX)
+
+# The following will print out all available features.
+cxx11_find_all_features(ALL_FEATURES)
+message(STATUS "[c++11] features we can check for: ${ALL_FEATURES}")
+
+# The following checks for all features
+cxx11_feature_check()
+
+# An internal value is set if a particular feature exists.
+if(HAS_CXX11_AUTO)
+  message(STATUS "[c++11] has auto.")
+endif()
+if(HAS_CXX11_LAMBDA)
+  message(STATUS "[c++11] has lambda.")
+endif()
+```
+
+Alternatively, only a subset of features can be checked for, and some can be required:
+```cmake
+cxx11_feature_check(auto lambda REQUIRED long_long share_ptr variadic_templates)
+```
+The previous statement will fail if ``long long``, ``std::shared_ptr<...>``, and variadic templates
+are not available. It will also check for the availability of ``auto`` and ``lambda``, but without
+failing.
+
+Figuring out flags for some compilers
+-------------------------------------
+
+The script checks the existence of a few flags to enable c++11 features on different compilers.
+The output is somewhat verbose, but it seems to do the job for gcc, darwin-gcc, and microsoft visual
+studio. In addition, the intel compilers have to be told to use an external c++11 standard library.
+This script cannot figure where this library would be (hint: g++ provides it), so that is left up to
+the user. The script can be activated with a one liner.
+
+```cmake
+include("path/to/cookoff/AddCPP11Flags.cmake")
+```
+
+**NOTE:** On windows + visual studio, disables warnings 4251 and ups fake variadic templates to 10.
+
+
+Figure out ``isnan``
+====================
+
+Each and every vendor provides a different ``isnan``. There is a script to help define a portable
+c++ macro. It is meant to be used within a configuration file as follows:
+
+```cmake
+include("path/to/cookoff/CheckIsNaN.cmake")
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+configure_file(/path/to/config.h.in /path/to/config.h)
+```
+
+Two cmake variables are defined:
+
+- ISNAN_HEADERS will the header(s) relevant to the local ``isnan`` definition
+- ISNAN_VARIATIOPN is the fully qualified name to the local ``isnan`` definition
+
+They can be used as follows in a the configuration file ``config.h.in``:
+
+``cpp
+ at ISNAN_HEADERS@
+
+#define not_a_number(X) @ISNAN_VARIATION@
+``
+
+One should then use the macro ``not_a_number`` in-place of any ``isnan`` flavour.
+
+In c++11, it is also possible to define a function that takes only arithmetic type, thus obviating
+the need for a macro:
+
+```cpp
+ at ISNAN_HEADERS@
+#include <type_traits>
+
+template<class T>
+  typename std::enable_if<std::is_arithmetic<T>::value, bool> :: type 
+    not_a_number(T const &_in) { return @ISNAN_VARIATION@(_in); }
+```
+
+Testing CMake scripts
+=====================
+
+The file ``TestCMake.cmake`` contains a function to test cmake scripts. It converts an input cmake
+file into a project which is then configured, built, and run using ``ctest``. Unless an optional
+"SOURCE" is provided as argument, the test program is an empty ``main`` function returning 0. If the
+keyword is provided, then a ``main.cc`` or ``main.c`` file should provided the cmake script.
+
+For examples, look at the tests in this package.
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
new file mode 100644
index 0000000..4fc877a
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
@@ -0,0 +1,58 @@
+function(_args_have_option outvar optionname arglist)
+  list(FIND ${arglist} "${optionname}" HASOPTION)
+  if(HASOPTION EQUAL -1)
+    set(${outvar} False PARENT_SCOPE)
+  else()
+    set(${outvar} True PARENT_SCOPE)
+    list(REMOVE_ITEM ${arglist} ${optionname})
+    set(${arglist} ${${arglist}} PARENT_SCOPE)
+  endif()
+endfunction(_args_have_option)
+
+function(cmake_test testname)
+
+  # Parse further arguments
+  # Let caller create a source file
+  set(ALL_OPTIONS ${ARGN})
+  _args_have_option(SOURCE "SOURCE" ALL_OPTIONS)
+  # Let caller create executable to run
+  # Should be used with test-command
+  _args_have_option(NOEXEC "NOEXEC" ALL_OPTIONS)
+
+  # set source and build dir.
+  set(FAKE_PROJECT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${testname})
+  set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/fake_project_builds/${testname})
+
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${testname}.cmake 
+                 ${FAKE_PROJECT_DIR}/CMakeData.cmake @ONLY)
+  message(STATUS "[${testname}] project in ${FAKE_PROJECT_DIR}")
+
+  if(NOT SOURCE)
+    file(WRITE ${FAKE_PROJECT_DIR}/main.c "int main() { return 0; }" )
+  endif(NOT SOURCE)
+  file(WRITE ${FAKE_PROJECT_DIR}/CMakeLists.txt
+       "cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)\n"
+       "project(allfeatures)\n"
+       "include(\"${FAKE_PROJECT_DIR}/CMakeData.cmake\")\n"
+       "enable_language(C)\n"
+       "if(NOT ${NOEXEC})\n"
+       "  file(GLOB ALLFILES \${PROJECT_SOURCE_DIR}/*.c \${PROJECT_SOURCE_DIR}/*.cc)\n"
+       "  add_executable(${testname} \${ALLFILES})\n"
+       "endif(NOT ${NOEXEC})\n")
+  
+  
+  if(EXISTS ${BUILD_DIR})
+    file(REMOVE_RECURSE ${BUILD_DIR})
+  endif(EXISTS ${BUILD_DIR})
+  
+  file(MAKE_DIRECTORY ${BUILD_DIR})
+  
+  add_test(cmake_test_${testname}
+             ${CMAKE_CTEST_COMMAND} --build-and-test ${FAKE_PROJECT_DIR} ${BUILD_DIR}
+                                    --build-generator ${CMAKE_GENERATOR}
+                                    --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+                                    --build-project ${testname}
+                                    --build-options -Dcookoff_path=${CMAKE_SOURCE_DIR}/..
+                                    ${ALL_OPTIONS})
+
+endfunction(cmake_test)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
new file mode 100644
index 0000000..d961df8
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
@@ -0,0 +1,8 @@
+#include <cstring>
+
+int main()
+{
+	if (!__func__) { return 1; }
+	if(std::strlen(__func__) <= 0) { return 1; }
+	return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
new file mode 100644
index 0000000..948648e
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
@@ -0,0 +1,12 @@
+
+int main()
+{
+	auto i = 5;
+	auto f = 3.14159f;
+	auto d = 3.14159;
+	bool ret = (
+		(sizeof(f) < sizeof(d)) &&
+		(sizeof(i) == sizeof(int))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
new file mode 100644
index 0000000..870ba92
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
@@ -0,0 +1,12 @@
+#include <iterator>
+#include <vector>
+#include <exception>
+
+int main() {
+  std::vector<int> vector(2, 1);
+  auto i_first = std::begin(vector);
+  auto i_end = std::end(vector);
+  if(i_first + 2 != i_end) return 1;
+  if(*i_first != 1) return 2;
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
new file mode 100644
index 0000000..ed62451
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
@@ -0,0 +1,19 @@
+constexpr int square(int x)
+{
+	return x*x;
+}
+
+constexpr int the_answer()
+{
+	return 42;
+}
+
+int main()
+{
+	int test_arr[square(3)];
+	bool ret = (
+		(square(the_answer()) == 1764) &&
+		(sizeof(test_arr)/sizeof(test_arr[0]) == 9)
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
new file mode 100644
index 0000000..4934d83
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
@@ -0,0 +1,32 @@
+#include <cmath>
+#include <assert.h>
+
+class A {
+  public:
+    int a;
+    double b;
+
+    A() : A(0, 0) {}
+    explicit A(int _a) : A(_a, 0) {}
+    explicit A(double _b) : A(0, _b) {}
+    A(int _a, double _b) : a(_a), b(_b) {}
+};
+
+int main() {
+  A instance(1, 1.5e0);
+  assert(instance.a == 1);
+  assert(std::abs(instance.b - 1.5) < 1e-8);
+
+  A instance1(1);
+  assert(instance1.a == 1);
+  assert(std::abs(instance1.b) < 1e-8);
+
+  A instance2(1.5);
+  assert(instance2.a == 0);
+  assert(std::abs(instance2.b - 1.5) < 1e-8);
+
+  A instance3;
+  assert(instance3.a == 0);
+  assert(std::abs(instance3.b) < 1e-8);
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
new file mode 100644
index 0000000..be2878f
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
@@ -0,0 +1,10 @@
+#include <cstdint>
+int main()
+{
+	bool test = 
+		(sizeof(int8_t) == 1) &&
+		(sizeof(int16_t) == 2) &&
+		(sizeof(int32_t) == 4) &&
+		(sizeof(int64_t) == 8);
+	return test ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
new file mode 100644
index 0000000..843f83a
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
@@ -0,0 +1,11 @@
+
+bool check_size(int i)
+{
+	return sizeof(int) == sizeof(decltype(i));
+}
+
+int main()
+{
+	bool ret = check_size(42);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
new file mode 100644
index 0000000..565da27
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
@@ -0,0 +1,10 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
new file mode 100644
index 0000000..a71d565
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
@@ -0,0 +1,11 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  A second(first);
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
new file mode 100644
index 0000000..4eed94b
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
@@ -0,0 +1,8 @@
+#include <type_traits>
+
+template<int N>
+  typename std::enable_if<N==2, int>::type testme() { return 0; }
+
+int main() {
+  return testme<2>();
+};
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
new file mode 100644
index 0000000..37c97f9
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
@@ -0,0 +1,13 @@
+class A {
+  public:
+   A(int a = 5) : a_(a) {};
+   explicit operator bool() const { return a_ == 2; }
+  protected:
+   int a_;
+};
+
+int main () {
+  A a(6);
+  A const b(2);
+  return (static_cast<bool>(a) == false and static_cast<bool>(b) == true) ? 0: 1;  
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
new file mode 100644
index 0000000..462575f
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
@@ -0,0 +1,7 @@
+#include <vector>
+
+int main() {
+  std::vector<double> a{0, 1, 2, 3};
+  std::vector< std::vector<double> > b{ {5, 6, 7}, {8, 9, 10, 11} };
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
new file mode 100644
index 0000000..4c33ed5
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int ret = 0;
+	return ([&ret]() -> int { return ret; })();
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
new file mode 100644
index 0000000..8f005ee
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return sizeof(long double) > sizeof(double) ? 0: 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
new file mode 100644
index 0000000..0911127
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
@@ -0,0 +1,7 @@
+int main(void)
+{
+	long long l;
+	unsigned long long ul;
+
+	return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
new file mode 100644
index 0000000..0ea69d4
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
@@ -0,0 +1,9 @@
+class A {
+    public:
+    A() noexcept {};
+};
+int main()
+{
+   A a = A();
+   return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
new file mode 100644
index 0000000..c78fac4
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int* test = nullptr;
+	return test ? 1 : 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
new file mode 100644
index 0000000..7ab77a2
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int i = nullptr;
+	return 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
new file mode 100644
index 0000000..d8d4e0d
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    virtual void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
new file mode 100644
index 0000000..19f43c9
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
new file mode 100644
index 0000000..a777421
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
@@ -0,0 +1,13 @@
+#include <random>
+#include <sstream>
+
+int main()
+{
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<> dis(1, 6);
+    std::ostringstream sstr;
+    for(int n=0; n<10; ++n) sstr << dis(gen) << ' ';
+    return 0;
+}
+
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
new file mode 100644
index 0000000..75fb555
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
@@ -0,0 +1,15 @@
+int foo(int& lvalue)
+{
+	return 123;
+}
+
+int foo(int&& rvalue)
+{
+	return 321;
+}
+
+int main()
+{
+	int i = 42;
+	return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
new file mode 100644
index 0000000..4a4ff82
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::shared_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
new file mode 100644
index 0000000..a55fc09
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
@@ -0,0 +1,14 @@
+struct foo {
+	char bar;
+	int baz;
+};
+
+int main(void)
+{
+	bool ret = (
+		(sizeof(foo::bar) == 1) &&
+		(sizeof(foo::baz) >= sizeof(foo::bar)) &&
+		(sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
new file mode 100644
index 0000000..c3d74ca
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(0 < 1, "your ordering of integers is screwed");
+	return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
new file mode 100644
index 0000000..4cb1183
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(1 < 0, "this should fail");
+	return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
new file mode 100644
index 0000000..52881a9
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
@@ -0,0 +1,8 @@
+template<class A, class B> struct AClass {
+  typedef A t_first;
+  typedef B t_second;
+};
+
+template<class B> using Specialized = AClass<int, B>;
+
+int main() { return 0; }
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
new file mode 100644
index 0000000..8cf8319
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_trivially_move_constructible<int>::value;
+  bool const b = std::is_trivially_move_assignable<int>::value;
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
new file mode 100644
index 0000000..4344f76
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_move_constructible<int>::value;
+  bool const b = std::is_move_assignable<int>::value;
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
new file mode 100644
index 0000000..159591d
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::unique_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
new file mode 100644
index 0000000..4518e88
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
@@ -0,0 +1,23 @@
+int Accumulate()
+{
+	return 0;
+}
+
+template<typename T, typename... Ts>
+int Accumulate(T v, Ts... vs)
+{
+	return v + Accumulate(vs...);
+}
+
+template<int... Is>
+int CountElements()
+{
+	return sizeof...(Is);
+}
+
+int main()
+{
+	int acc = Accumulate(1, 2, 3, 4, -5);
+	int count = CountElements<1,2,3,4,5>();
+	return ((acc == 5) && (count == 5)) ? 0 : 1;
+}
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
new file mode 100644
index 0000000..f2eb4d0
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)
+project(COOKOFF_TEST)
+
+enable_testing()
+
+include(${PROJECT_SOURCE_DIR}/../TestCMake.cmake)
+
+cmake_test(checkisnan SOURCE)
+cmake_test(checkcpp11flags)
+cmake_test(addgtest NOEXEC SOURCE --test-command ${CMAKE_MAKE_PROGRAM} test)
+
+add_subdirectory(cpp11)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
new file mode 100644
index 0000000..b1139d9
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
@@ -0,0 +1,20 @@
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  include(${cookoff_path}/AddGTest.cmake)
+  enable_testing()
+endif(tests)
+
+file(WRITE ${CMAKE_SOURCE_DIR}/mytest.cc 
+     "#include <gtest/gtest.h>\n\n"
+     "class TestMe : public ::testing::Test {};\n\n"
+     "TEST_F(TestMe, TestThis) {\n"
+     "  EXPECT_TRUE(true); \n"
+     "}\n\n"
+     "int main(int argc, char **argv) {\n"
+     "  ::testing::InitGoogleTest(&argc, argv);\n"
+     "  return RUN_ALL_TESTS();\n"
+     "}\n")
+
+
+add_gtest(mytest mytest.cc)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
new file mode 100644
index 0000000..1f7e8a4
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
@@ -0,0 +1 @@
+include(${cookoff_path}/AddCPP11Flags.cmake)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
new file mode 100644
index 0000000..e9cbdaa
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
@@ -0,0 +1,19 @@
+include(${cookoff_path}/CheckIsNaN.cmake)
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+enable_language(CXX)
+file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in
+     "\@ISNAN_HEADERS\@\n"
+     "#include <limits>\n"
+     "#include <exception>\n"
+     "#define not_a_number(X) \@ISNAN_VARIATION\@(X)\n"
+     "int main() {\n"
+     "  if(not_a_number(0.5e0)) throw std::exception(); \n"
+     "  if(not_a_number(2l)) throw std::exception(); \n"
+     "  if(not not_a_number(std::numeric_limits<double>::quiet_NaN())) throw std::exception(); \n"
+     "  return 0;\n"
+     "}\n")
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
new file mode 100644
index 0000000..45d20a5
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_test(allfeatures)
+cmake_test(parse_input_features)
+cmake_test(check_features)
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
new file mode 100644
index 0000000..f4dec87
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
@@ -0,0 +1,7 @@
+enable_language(CXX)
+include(${cookoff_path}/CheckCXX11Features.cmake)
+cxx11_find_all_features(ALL_CPP11_FEATURES)
+LIST(LENGTH ALL_CPP11_FEATURES LIST_LENGTH)
+if(${LIST_LENGTH} EQUAL 0)
+  message(FATAL_ERROR "No c++11 features found")
+endif()
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
new file mode 100644
index 0000000..1ea2fdb
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
@@ -0,0 +1,9 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function to test.
+cxx11_feature_check("auto")
+cxx11_feature_check()
diff --git a/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
new file mode 100644
index 0000000..ca88de0
--- /dev/null
+++ b/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
@@ -0,0 +1,68 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function with fake input
+set(ALL_FEATURES "first;second;third")
+
+# No input OPTIONALS == ALL_FEATURES
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "")
+if(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+  message(FATAL_ERROR "OPTIONALS results should contain everything.") 
+endif(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif(NOT "${REQUIRED}" STREQUAL "")
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif(NOT "${ERRORS}" STREQUAL "")
+
+# Single input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "second")
+if(NOT "${OPTIONALS}" STREQUAL "second")
+  message(FATAL_ERROR "OPTIONALS results should second.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# Single error input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "none")
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
+
+# Single valid input with REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS REQUIRED first)
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# one of each
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS second third REQUIRED first none)
+if(NOT "${OPTIONALS}" STREQUAL "second;third")
+  message(FATAL_ERROR "OPTIONALS results should be second;optional.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
diff --git a/ecbuild/cmake/ecbuild_add_c_flags.cmake b/ecbuild/cmake/ecbuild_add_c_flags.cmake
new file mode 100644
index 0000000..138e4d8
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_c_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_c_flags
+# ===================
+#
+# Add C compiler flags to CMAKE_C_FLAGS only if supported by the compiler. ::
+#
+#   ecbuild_add_c_flags( <flag1> [ <flag2> ... ]
+#                        [ BUILD <build> ]
+#                        [ NAME <name> ]
+#                        [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_C_FLAGS_<build>`` instead of ``CMAKE_C_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_c_flags m_c_flags )
+
+  set( _flags ${m_c_flags} )
+
+  if( _flags AND CMAKE_C_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+      if( NOT DEFINED N_CFLAG )
+        set( N_CFLAG 0 )
+      endif()
+
+      math( EXPR N_CFLAG '${N_CFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_c_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_c_compiler_flag( ${_flags} C_FLAG_TEST_${N_CFLAG} )
+          set( _flag_ok ${C_FLAG_TEST_${N_CFLAG}} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_C_FLAGS_${_PAR_BUILD} "${CMAKE_C_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised C flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised C flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_c_flags m_c_flags )
+  ecbuild_deprecate( " cmake_add_c_flags is deprecated, use ecbuild_add_c_flags instead." )
+  ecbuild_add_c_flags( ${m_c_flags} )
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake b/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake
new file mode 100644
index 0000000..6c5c861
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx11_flags
+# =======================
+#
+# Add cxx11 flags to CXX compilation flags. ::
+#
+#   ecbuild_add_cxx11_flags()
+#
+# This macro uses macros from http://github.com/UCL/GreatCMakeCookOff.
+#
+##############################################################################
+
+macro( ecbuild_add_cxx11_flags )
+
+	# if( CMAKE_COMPILER_IS_GNUCXX )
+	# 	if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7 )
+	# 		ecbuild_add_cxx_flags("-std=c++0x")
+	# 	else()
+	# 		ecbuild_add_cxx_flags("-std=c++11")
+	# 	endif()
+	# endif()
+
+	include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake )
+
+endmacro( ecbuild_add_cxx11_flags )
diff --git a/ecbuild/cmake/ecbuild_add_cxx_flags.cmake b/ecbuild/cmake/ecbuild_add_cxx_flags.cmake
new file mode 100644
index 0000000..699c50a
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_cxx_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx_flags
+# =====================
+#
+# Add C++ compiler flags to CMAKE_CXX_FLAGS only if supported by compiler. ::
+#
+#   ecbuild_add_cxx_flags( <flag1> [ <flag2> ... ]
+#                          [ BUILD <build> ]
+#                          [ NAME <name> ]
+#                          [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_CXX_FLAGS_<build>`` instead of ``CMAKE_CXX_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_cxx_flags m_cxx_flags )
+
+  set( _flags ${m_cxx_flags} )
+  if( _flags AND CMAKE_CXX_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+
+      if( NOT DEFINED N_CXXFLAG )
+        set( N_CXXFLAG 0 )
+      endif()
+
+      math( EXPR N_CXXFLAG '${N_CXXFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_cxx_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_cxx_compiler_flag( ${_flags} CXX_FLAG_TEST_${N_CXXFLAG} )
+          set( _flag_ok CXX_FLAG_TEST_${N_CXXFLAG} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_CXX_FLAGS_${_PAR_BUILD} "${CMAKE_CXX_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised CXX flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised CXX flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_cxx_flags m_cxx_flags )
+  ecbuild_deprecate( " cmake_add_cxx_flags is deprecated, use ecbuild_add_cxx_flags instead." )
+  ecbuild_add_cxx_flags( ${m_cxx_flags} )
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_add_executable.cmake b/ecbuild/cmake/ecbuild_add_executable.cmake
new file mode 100644
index 0000000..f6cb128
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_executable.cmake
@@ -0,0 +1,346 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_executable
+# ======================
+#
+# Add an executable with a given list of source files. ::
+#
+#   ecbuild_add_executable( TARGET <name>
+#                           SOURCES <source1> [<source2> ...]
+#                           [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                           [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                           [ OBJECTS <obj1> [<obj2> ...] ]
+#                           [ TEMPLATES <template1> [<template2> ...] ]
+#                           [ LIBS <library1> [<library2> ...] ]
+#                           [ INCLUDES <path1> [<path2> ...] ]
+#                           [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                           [ PERSISTENT <file1> [<file2> ...] ]
+#                           [ GENERATED <file1> [<file2> ...] ]
+#                           [ DEPENDS <target1> [<target2> ...] ]
+#                           [ CONDITION <condition> ]
+#                           [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                           [ NOINSTALL ]
+#                           [ VERSION <version> | AUTO_VERSION ]
+#                           [ CFLAGS <flag1> [<flag2> ...] ]
+#                           [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                           [ FFLAGS <flag1> [<flag2> ...] ]
+#                           [ LINKER_LANGUAGE <lang> ]
+#                           [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the executable
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   version to use as executable version
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   automatically version the executable with the package version
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_executable )
+
+  set( options NOINSTALL AUTO_VERSION )
+  set( single_value_args TARGET COMPONENT LINKER_LANGUAGE VERSION OUTPUT_NAME )
+  set( multi_value_args SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                        TEMPLATES LIBS INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                        CFLAGS CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_executable() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_executable() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+        ecbuild_error("ecbuild_add_executable(${_PAR_TARGET}): CUDA source files detected"
+                      "but CUDA was not found.")
+      endif()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): CUDA sources detected."
+                    "Building executable with ecbuild_add_executable() rather than intrinsic"
+                    "add_executable().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+    else()
+      cuda_add_executable( ${_PAR_TARGET} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list(REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION )
+        ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      endif()
+    endif()
+
+    # installation
+
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): installing to ${INSTALL_BIN_DIR}")
+
+      # add installation paths and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+               EXPORT  ${PROJECT_NAME}-targets
+               RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+               LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+               ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #        COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous command)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    else()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): not installing")
+      # NOINSTALL targets are always built the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_exe( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_EXES ${${PROJECT_NAME}_ALL_EXES} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endmacro( ecbuild_add_executable  )
diff --git a/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake b/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake
new file mode 100644
index 0000000..690cde6
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+#
+# macro for adding search paths to CMAKE_PREFIX_PATH
+# for example the ECMWF /usr/local/apps paths
+#
+# usage: ecbuild_add_extra_search_paths( netcdf4 )
+
+function( ecbuild_add_extra_search_paths pkg )
+
+  ecbuild_deprecate( " ecbuild_add_extra_search_paths modifies CMAKE_PREFIX_PATH,"
+                     " which can affect future package discovery if not undone by the caller."
+                     " The current CMAKE_PREFIX_PATH is being backed up as _CMAKE_PREFIX_PATH"
+                     " so it can later be restored." )
+
+  # Back up current CMAKE_PREFIX_PATH so the caller can reset it
+  set( _CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+
+  string( TOUPPER ${pkg} _PKG )
+
+  ecbuild_list_extra_search_paths( ${pkg} CMAKE_PREFIX_PATH )
+
+  set( CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+  # ecbuild_debug_var( CMAKE_PREFIX_PATH )
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_add_fortran_flags.cmake b/ecbuild/cmake/ecbuild_add_fortran_flags.cmake
new file mode 100644
index 0000000..2ac89e8
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_fortran_flags.cmake
@@ -0,0 +1,109 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_fortran_flags
+# =========================
+#
+# Add Fortran compiler flags to CMAKE_Fortran_FLAGS only if supported by the
+# compiler. ::
+#
+#   ecbuild_add_fortran_flags( <flag1> [ <flag2> ... ]
+#                              [ BUILD <build> ]
+#                              [ NAME <name> ]
+#                              [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_add_fortran_flags m_fortran_flags )
+
+  set( _flags ${m_fortran_flags} )
+
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+
+    if( _try_add_flag )
+      if( NOT DEFINED N_FortranFLAG )
+        set( N_FortranFLAG 0 )
+      endif()
+
+      math( EXPR N_FortranFLAG '${N_FortranFLAG}+1' )
+
+      if( ECBUILD_TRUST_FLAGS )
+        set( _flag_ok 1 )
+      # Due to a bug in CMake < 3.0, check_fortran_compiler_flag ALWAYS fails with ifort
+      # see https://cmake.org/Bug/view.php?id=14507
+      elseif( CMAKE_MAJOR_VERSION LESS 3 AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+        set( _flag_ok 1 )
+        ecbuild_warn( "Not testing Fortran flags due to a bug in CMake < 3.0 with ifort" )
+      else()
+        if( DEFINED _PAR_NAME )
+          check_fortran_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_fortran_compiler_flag( ${_flags} Fortran_FLAG_TEST_${N_FortranFLAG} )
+          set( _flag_ok ${Fortran_FLAG_TEST_${N_FortranFLAG}} )
+        endif()
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_Fortran_FLAGS_${_PAR_BUILD} "${CMAKE_Fortran_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised Fortran flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised Fortran flag [${_flags}]" )
+      endif()
+    endif()
+
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_fortran_flags m_fortran_flags )
+  ecbuild_deprecate( " cmake_add_fortran_flags is deprecated, use ecbuild_add_fortran_flags instead." )
+  ecbuild_add_fortran_flags( ${m_fortran_flags} )
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_add_library.cmake b/ecbuild/cmake/ecbuild_add_library.cmake
new file mode 100644
index 0000000..f9f567f
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_library.cmake
@@ -0,0 +1,578 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_library
+# ===================
+#
+# Add a library with a given list of source files. ::
+#
+#   ecbuild_add_library( TARGET <name>
+#                        SOURCES <source1> [<source2> ...]
+#                        [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                        [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                        [ TYPE SHARED|STATIC|MODULE|OBJECT ]
+#                        [ OBJECTS <obj1> [<obj2> ...] ]
+#                        [ TEMPLATES <template1> [<template2> ...] ]
+#                        [ LIBS <library1> [<library2> ...] ]
+#                        [ INCLUDES <path1> [<path2> ...] ]
+#                        [ PRIVATE_INCLUDES <path1> [<path2> ...] ]
+#                        [ PUBLIC_INCLUDES <path1> [<path2> ...] ]
+#                        [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                        [ PERSISTENT <file1> [<file2> ...] ]
+#                        [ GENERATED <file1> [<file2> ...] ]
+#                        [ DEPENDS <target1> [<target2> ...] ]
+#                        [ CONDITION <condition> ]
+#                        [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                        [ NOINSTALL ]
+#                        [ HEADER_DESTINATION <path> ]
+#                        [ INSTALL_HEADERS LISTED|ALL ]
+#                        [ INSTALL_HEADERS_LIST <header1> [<header2> ...] ]
+#                        [ INSTALL_HEADERS_REGEX <pattern> ]
+#                        [ VERSION <version> | AUTO_VERSION ]
+#                        [ SOVERSION <soversion> | AUTO_SOVERSION ]
+#                        [ CFLAGS <flag1> [<flag2> ...] ]
+#                        [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                        [ FFLAGS <flag1> [<flag2> ...] ]
+#                        [ LINKER_LANGUAGE <lang> ]
+#                        [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# TYPE : optional
+#   library type, one of:
+#
+#   :SHARED: libraries are linked dynamically and loaded at runtime
+#   :STATIC: archives of object files for use when linking other targets.
+#   :MODULE: plugins that are not linked into other targets but may be loaded
+#            dynamically at runtime using dlopen-like functionality
+#   :OBJECT: files are just compiled into objects
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : (DEPRECATED) optional
+#   list of paths to add to include directories, behaves as PUBLIC_INCLUDES if CMake >= 2.8.11
+#   and reverts to include_directories() for CMake < 2.8.11
+#
+# PUBLIC_INCLUDES : optional
+#   list of paths to add to include directories which will be publicly exported to other projects
+#
+# PRIVATE_INCLUDES : optional
+#   list of paths to add to include directories which won't be exported to other projects,
+#   equivalent to using a include_directories() before calling this macro
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the library
+#
+# HEADER_DESTINATION
+#   directory to install headers (if not specified, INSTALL_INCLUDE_DIR is used)
+#
+# INSTALL_HEADERS : optional
+#   specify which header files to install:
+#
+#   :LISTED: install header files listed as SOURCES
+#   :ALL:    install all header files ending in .h, .hh, .hpp, .H
+#
+# INSTALL_HEADERS_LIST : optional
+#   list of extra headers to install
+#
+# INSTALL_HEADERS_REGEX : optional
+#   regular expression to match extra headers to install
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   build version of the library
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   use MAJOR.MINOR package version as build version of the library
+#
+# SOVERSION : optional, AUTO_SOVERSION or LIBS_SOVERSION is used if not specified
+#   ABI version of the library
+#
+# AUTO_SOVERSION : optional, ignored if SOVERSION is specified
+#   use MAJOR package version as ABI version of the library
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+function( ecbuild_add_library_impl )
+
+  set( options NOINSTALL AUTO_VERSION AUTO_SOVERSION )
+  set( single_value_args TARGET TYPE COMPONENT INSTALL_HEADERS
+                         INSTALL_HEADERS_REGEX LINKER_LANGUAGE
+                         HEADER_DESTINATION VERSION SOVERSION OUTPUT_NAME )
+  set( multi_value_args  SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                         TEMPLATES LIBS INCLUDES PRIVATE_INCLUDES
+                         PUBLIC_INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                         INSTALL_HEADERS_LIST CFLAGS CXXFLAGS FFLAGS GENERATED
+                         CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_library(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_library() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # defines the type of library
+    if( DEFINED _PAR_TYPE )
+      # checks that is either SHARED or STATIC or MODULE
+      if( NOT _PAR_TYPE MATCHES "STATIC" AND
+          NOT _PAR_TYPE MATCHES "SHARED" AND
+          NOT _PAR_TYPE MATCHES "OBJECT" AND
+          NOT _PAR_TYPE MATCHES "MODULE" )
+        ecbuild_critical( "library type must be one of [ STATIC | SHARED | MODULE | OBJECT ]" )
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): library type is ${_PAR_TYPE}")
+    endif()
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CUDA was not found.")
+      endif()
+      if( _PAR_TYPE MATCHES "OBJECT" )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CMake OBJECT libraries with CUDA are not supported.")
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): CUDA sources detected."
+                    "Building library with cuda_add_library() rather than intrinsic"
+                    "add_library().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    else()
+      cuda_add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list( REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" OR ECBUILD_USE_INCLUDE_DIRECTORIES )
+            include_directories( ${path} )
+          else()
+            target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+          endif()
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add private include dirs if defined
+    if( DEFINED _PAR_PRIVATE_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PRIVATE_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PRIVATE_INCLUDES )
+      foreach( path ${_PAR_PRIVATE_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add public include dirs if defined
+    if( DEFINED _PAR_PUBLIC_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PUBLIC_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PUBLIC_INCLUDES )
+      foreach( path ${_PAR_PUBLIC_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # FIX: Cray compiler PIC option is not detected by CMake
+
+    get_property( _target_pic TARGET ${_PAR_TARGET} PROPERTY POSITION_INDEPENDENT_CODE )
+    if( _target_pic )
+      if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CFLAGS "-fPIC -h PIC ${_PAR_CFLAGS}" )
+      endif()
+      if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CXXFLAGS "-fPIC -h PIC ${_PAR_CXXFLAGS}" )
+      endif()
+      if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_FFLAGS "-fPIC -h PIC ${_PAR_FFLAGS}" )
+      endif()
+    endif()
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION OR LIBS_VERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      elseif( DEFINED LIBS_VERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${LIBS_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${LIBS_VERSION}" )
+      endif()
+    endif()
+
+    # define SOVERSION if requested
+    if( DEFINED _PAR_SOVERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${_PAR_SOVERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${_PAR_SOVERSION}" )
+    else()
+      if( _PAR_AUTO_SOVERSION OR LIBS_SOVERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${${PNAME}_MAJOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${${PNAME}_MAJOR_VERSION}" )
+      elseif( DEFINED LIBS_SOVERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${LIBS_SOVERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${LIBS_SOVERSION}" )
+      endif()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    if( DEFINED _PAR_GENERATED )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+      set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # installation (except for OBJECT libraries)
+
+    if( NOT _PAR_NOINSTALL AND NOT _PAR_TYPE MATCHES "OBJECT" )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): installing to ${INSTALL_LIB_DIR}")
+
+      # and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+        EXPORT  ${PROJECT_NAME}-targets
+        RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+        LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+        ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #              COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # install headers
+      if( _PAR_HEADER_DESTINATION )
+        set( _h_destination "${_PAR_HEADER_DESTINATION}" )
+      else()
+        set( _h_destination "${INSTALL_INCLUDE_DIR}" )
+      endif()
+
+      if( _PAR_INSTALL_HEADERS )
+        if( _PAR_INSTALL_HEADERS MATCHES "LISTED" )
+          foreach( file ${${_PAR_TARGET}_h_srcs} )
+            get_filename_component( _file_dir ${file} PATH )
+            install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+          endforeach()
+          if( DEFINED _PAR_TEMPLATES )
+            foreach( file ${_PAR_TEMPLATES} )
+              get_filename_component( _file_dir ${file} PATH )
+              install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+          if( DEFINED _PAR_PERSISTENT )
+            foreach( file ${_PAR_PERSISTENT} )
+              get_filename_component( _file_dir ${file} PATH )
+              get_filename_component( _file_we  ${file} NAME_WE )
+              set( pfile "${CMAKE_CURRENT_BINARY_DIR}/${_file_dir}/${_file_we}.b" )
+              install( FILES ${pfile} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+        endif()
+        if( _PAR_INSTALL_HEADERS MATCHES "ALL" ) # "(\\.h|\\.b|\\.hxx|\\.hh|\\.hpp|\\.H)" ????
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.h" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hh" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hpp" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.H" )
+        endif()
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_LIST )
+        install( FILES ${_PAR_INSTALL_HEADERS_LIST} DESTINATION ${_h_destination} )
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_REGEX )
+        install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "${_PAR_INSTALL_HEADERS_REGEX}")
+      endif()
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous 2 commands)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    if( NOT _PAR_TYPE MATCHES "OBJECT" )
+      add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+    endif()
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_lib( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_LIBS ${${PROJECT_NAME}_ALL_LIBS} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endfunction( ecbuild_add_library_impl  )
+
+##############################################################################
+# auxiliary macro for adding a library
+##############################################################################
+
+macro( ecbuild_add_library )
+
+  set( options  )
+  set( single_value_args TARGET TYPE )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _p_TYPE ) # don't do anything if TYPE was specified
+
+    if( _p_TYPE MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} OUTPUT_NAME ${_p_TARGET} DEPENDS ${_p_TARGET} )
+
+    else()
+
+      ecbuild_add_library_impl( ${ARGV} )
+
+    endif()
+
+  else()
+
+    if( NOT DEFINED _p_TARGET )
+      ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+    else()
+
+      if( BUILD_SHARED_LIBS MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} DEPENDS ${_p_TARGET} )
+
+        # If the library is built conditionally the target might not exist
+        if ( TARGET ${_p_TARGET}-static )
+          set_target_properties( ${_p_TARGET}-static PROPERTIES OUTPUT_NAME ${_p_TARGET} )
+        endif()
+
+      else()
+
+        ecbuild_add_library_impl( ${ARGV} )
+
+      endif()
+
+    endif()
+
+  endif()
+
+endmacro( ecbuild_add_library )
diff --git a/ecbuild/cmake/ecbuild_add_option.cmake b/ecbuild/cmake/ecbuild_add_option.cmake
new file mode 100644
index 0000000..6d5363f
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_option.cmake
@@ -0,0 +1,348 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_option
+# ==================
+#
+# Add a CMake configuration option, which may depend on a list of packages. ::
+#
+#   ecbuild_add_option( FEATURE <name>
+#                       [ DEFAULT ON|OFF ]
+#                       [ DESCRIPTION <description> ]
+#                       [ REQUIRED_PACKAGES <package1> [<package2> ...] ]
+#                       [ CONDITION <condition> ]
+#                       [ ADVANCED ] [ NO_TPL ] )
+#
+# Options
+# -------
+#
+# FEATURE : required
+#   name of the feature / option
+#
+# DEFAULT : optional, defaults to ON
+#   if set to ON, the feature is enabled even if not explicitly requested
+#
+# DESCRIPTION : optional
+#   string describing the feature (shown in summary and stored in the cache)
+#
+# REQUIRED_PACKAGES : optional
+#   list of packages required to be found for this feature to be enabled
+#
+#   The package specification can have one of two forms. Either ::
+#
+#     "<package> [ <version> ... ]"
+#
+#   to search for a given package using the CMake ``find_package`` mechanism.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``find_package`` are supported.
+#
+#   The other specification must start with ``PROJECT`` like this ::
+#
+#     "PROJECT <name> [ VERSION <version> ... ]"
+#
+#   and is used to search for an ecBuild project via ``ecbuild_use_package``.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``ecbuild_use_package`` are supported.
+#
+#   .. note::
+#
+#     Arguments inside the package string that require quoting need to use the
+#     `bracket argument syntax`_ introduced in CMake 3.0 since
+#     regular quotes even when escaped are swallowed by the CMake parser.
+#
+#     Alternatively, the name of a CMake variable containing the string can be
+#     passed, which will be expanded by ``ecbuild_find_package``: ::
+#
+#       set( ECCODES_FAIL_MSG
+#            "grib_api can be used instead (select with -DENABLE_ECCODES=OFF)" )
+#       ecbuild_add_option( FEATURE ECCODES
+#                           DESCRIPTION "Use eccodes instead of grib_api"
+#                           REQUIRED_PACKAGES "PROJECT eccodes REQUIRED FAILURE_MSG ECCODES_FAIL_MSG"
+#                           DEFAULT ON )
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this option to be
+#   enabled (must be valid in a CMake ``if`` statement)
+#
+# ADVANCED : optional
+#   mark the feature as advanced
+#
+# NO_TPL : optional
+#   do not add any ``REQUIRED_PACKAGES`` to the list of third party libraries
+#
+# Usage
+# -----
+#
+# Features with ``DEFAULT OFF`` need to be explcitly enabled by the user with
+# ``-DENABLE_<FEATURE>=ON``. If a feature is enabled, all ``REQUIRED_PACKAGES``
+# are found and ``CONDITION`` is met, ecBuild sets the variable
+# ``HAVE_<FEATURE>`` to ``ON``. This is the variable to use to check for the
+# availability of the feature.
+#
+# If a feature is explicitly enabled but the required packages are not found,
+# configuration fails. This only applies when configuring from *clean cache*.
+# With an already populated cache, use ``-DENABLE_<FEATURE>=REQUIRE`` to make
+# the feature a required feature (this cannot be done via the CMake GUI).
+#
+# .. _bracket argument syntax: https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument
+#
+##############################################################################
+
+macro( ecbuild_add_option )
+
+  set( options ADVANCED NO_TPL )
+  set( single_value_args FEATURE DEFAULT DESCRIPTION TYPE PURPOSE )
+  set( multi_value_args  REQUIRED_PACKAGES CONDITION )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( _p_UNPARSED_ARGUMENTS )
+    ecbuild_critical("Unknown keywords given to ecbuild_add_option(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # check FEATURE parameter
+
+  if( NOT _p_FEATURE  )
+    ecbuild_critical("The call to ecbuild_add_option() doesn't specify the FEATURE.")
+  endif()
+
+  # check DEFAULT parameter
+
+  if( NOT DEFINED _p_DEFAULT )
+    set( _p_DEFAULT ON )
+  else()
+    if( NOT _p_DEFAULT MATCHES "[Oo][Nn]" AND NOT _p_DEFAULT MATCHES "[Oo][Ff][Ff]" )
+      ecbuild_critical("In macro ecbuild_add_option(), DEFAULT is either ON or OFF: \"${_p_DEFAULT}\"")
+    endif()
+  endif()
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defaults to ${_p_DEFAULT}")
+
+  if( _p_PURPOSE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument PURPOSE is ignored and will be removed in a future release." )
+  endif()
+  if( _p_TYPE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument TYPE is ignored and will be removed in a future release." )
+  endif()
+
+  # check CONDITION parameter
+  if( DEFINED _p_CONDITION )
+    set(_feature_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_p_FEATURE}_condition.cmake")
+    file( WRITE  ${_feature_condition_file} "  if( ")
+    foreach( term ${_p_CONDITION} )
+      file( APPEND ${_feature_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_feature_condition_file} " )\n    set(_${_p_FEATURE}_condition TRUE)\n  else()\n    set(_${_p_FEATURE}_condition FALSE)\n  endif()\n")
+    include( ${_feature_condition_file} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): checking condition ${_p_CONDITION} -> ${_${_p_FEATURE}_condition}")
+  else()
+    set( _${_p_FEATURE}_condition TRUE )
+  endif()
+
+  # Check if user explicitly enabled/disabled the feature in cache
+  get_property( _in_cache CACHE ENABLE_${_p_FEATURE} PROPERTY VALUE SET )
+
+  # A feature set to REQUIRE is always treated as explicitly enabled
+  if( ENABLE_${_p_FEATURE} MATCHES "REQUIRE" )
+    set( ENABLE_${_p_FEATURE} ON CACHE BOOL "" FORCE )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} was required")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" FORCE )
+  elseif( NOT ENABLE_${_p_FEATURE} STREQUAL "" AND _in_cache )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}} was found in cache")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" )
+  else()
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} not found in cache")
+    set( ${_p_FEATURE}_user_provided_input 0 CACHE BOOL "" )
+  endif()
+
+  mark_as_advanced( ${_p_FEATURE}_user_provided_input )
+
+
+  # define the option -- for cmake GUI
+
+  option( ENABLE_${_p_FEATURE} "${_p_DESCRIPTION}" ${_p_DEFAULT} )
+  get_property( _feature_desc GLOBAL PROPERTY _CMAKE_${_p_FEATURE}_DESCRIPTION )
+  if( _feature_desc )
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${_feature_desc}, ${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  else()
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  endif()
+
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defining option ENABLE_${_p_FEATURE} '${_p_DESCRIPTION}' ${_p_DEFAULT}")
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}}")
+
+  if( ENABLE_${_p_FEATURE} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature enabled")
+
+    set( HAVE_${_p_FEATURE} 1 )
+
+    if( _${_p_FEATURE}_condition )
+
+      ### search for dependent packages
+
+      set( _failed_to_find_packages )  # clear variable
+      foreach( pkg ${_p_REQUIRED_PACKAGES} )
+        ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for dependent package ${pkg}")
+
+        string(REPLACE " " ";" pkglist ${pkg}) # string to list
+
+        list( GET pkglist 0 pkgname )
+
+        if( pkgname STREQUAL "PROJECT" )  # if 1st entry is PROJECT, then we are looking for a ecbuild project
+          set( pkgproject 1 )
+          list( GET pkglist 1 pkgname )
+          # Use feature description as package description if there is none
+          list( FIND pkglist DESCRIPTION __description )
+          if( __description LESS 0 )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): no description for ${pkgname}, using feature description '${_p_DESCRIPTION}'")
+            list( APPEND pkglist DESCRIPTION "${_p_DESCRIPTION}" )
+          endif()
+        else()                            # else 1st entry is package name
+          set( pkgproject 0 )
+        endif()
+
+        # ecbuild_debug_var( pkg )
+        # ecbuild_debug_var( pkglist )
+        # ecbuild_debug_var( pkgname )
+
+        string( TOUPPER ${pkgname} pkgUPPER )
+        string( TOLOWER ${pkgname} pkgLOWER )
+
+        set( __help_msg "Provide ${pkgname} location with -D${pkgUPPER}_PATH=/..." )
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+
+          ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ${pkgname} has already been found")
+          set( ${pkgname}_already_found 1 )
+
+        else()
+
+          if( pkgproject )
+
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for ecbuild project ${pkgname} - ecbuild_use_package( ${pkglist} )")
+            ecbuild_use_package( ${pkglist} )
+
+          else()
+
+            if( pkgname STREQUAL "LAPACK" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for LAPACK - ecbuild_find_package( NAME ${pkglist} )")
+              ecbuild_find_package( NAME ${pkglist} )
+              if( HAVE_LAPACK AND TARGET lapack )
+                ecbuild_debug( "LAPACK found as CMake target lapack" )
+                set( LAPACK_LIBRARIES lapack )
+              endif()
+            elseif( pkgname STREQUAL "MPI" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "MPI" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for MPI - ecbuild_find_mpi( ${_find_args} )")
+              ecbuild_find_mpi( ${_find_args} )
+            elseif( pkgname STREQUAL "OMP" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "OMP" )
+              if( NOT ENABLE_${_p_FEATURE} )
+                list( APPEND _find_args STUBS )
+              endif()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for OpenMP - ecbuild_find_omp( ${_find_args} )")
+              ecbuild_find_omp( ${_find_args} )
+            elseif( pkgname STREQUAL "Python" OR pkgname STREQUAL "PYTHON" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for Python - ecbuild_find_python( ${_find_args} )")
+              ecbuild_find_python( ${_find_args} )
+              set( __help_msg "Specify the location of the Python interpreter with -DPYTHON_EXECUTABLE=/..." )
+            elseif( pkgname STREQUAL "LEXYACC" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for lex-yacc - ecbuild_find_lexyacc( ${_find_args} )")
+              ecbuild_find_lexyacc( ${_find_args} )
+            else()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for package ${pkgname} - find_package( ${pkglist} )")
+              find_package( ${pkglist} )
+            endif()
+
+          endif()
+
+        endif()
+
+        # ecbuild_debug_var( ${pkgname}_FOUND  )
+        # ecbuild_debug_var( ${pkgLOWER}_FOUND )
+        # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+
+        # we have feature if all required packages were FOUND
+
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+          ecbuild_info( "Found package ${pkgname} required for feature ${_p_FEATURE}" )
+
+          # append to list of third-party libraries (to be forward to other packages )
+          # unless the NO_TPL option was given
+          if( NOT _p_NO_TPL )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): appending ${pkgname} to ${PROJECT_NAME_CAPS}_TPLS")
+            list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${pkgname} )
+            list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+          endif()
+
+        else()
+          ecbuild_info( "Could NOT find package ${pkgname} required for feature ${_p_FEATURE} -- ${__help_msg}" )
+          set( HAVE_${_p_FEATURE} 0 )
+          list( APPEND _failed_to_find_packages ${pkgname} )
+        endif()
+
+      endforeach()
+    else( _${_p_FEATURE}_condition )
+      set( HAVE_${_p_FEATURE} 0 )
+    endif( _${_p_FEATURE}_condition )
+
+    # FINAL CHECK
+
+    if( HAVE_${_p_FEATURE} )
+
+      ecbuild_enable_feature( ${_p_FEATURE} )
+
+      ecbuild_info( "Feature ${_p_FEATURE} enabled" )
+
+    else() # if user provided input and we cannot satisfy FAIL otherwise WARN
+
+      ecbuild_disable_feature( ${_p_FEATURE} )
+
+      if( ${_p_FEATURE}_user_provided_input )
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+      else()
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+        set( ENABLE_${_p_FEATURE} OFF )
+        ecbuild_disable_feature( ${_p_FEATURE} )
+      endif()
+
+    endif()
+
+  else()
+
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature disabled")
+    set( HAVE_${_p_FEATURE} 0 )
+    ecbuild_disable_feature( ${_p_FEATURE} )
+
+  endif()
+
+
+  if( ${_p_ADVANCED} )
+    mark_as_advanced( ENABLE_${_p_FEATURE} )
+  endif()
+
+  set( ${PROJECT_NAME_CAPS}_HAVE_${_p_FEATURE} ${HAVE_${_p_FEATURE}} )
+
+endmacro( ecbuild_add_option  )
diff --git a/ecbuild/cmake/ecbuild_add_persistent.cmake b/ecbuild/cmake/ecbuild_add_persistent.cmake
new file mode 100644
index 0000000..e5a875b
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_persistent.cmake
@@ -0,0 +1,85 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_persistent
+# ======================
+#
+# Add persistent layer object classes. ::
+#
+#   ecbuild_add_persistent( SRC_LIST <variable>
+#                           FILES <file1> [<file2> ...] ]
+#                           [ NAMESPACE <namespace> ] )
+#
+# Options
+# -------
+#
+# SRC_LIST : required
+#   CMake variable to append the generated persistent layer objects to
+#
+# FILES : required
+#   list of base names of files to build persistent class information for
+#
+#   The source file is expected to have a .h extension, the generated file
+#   gets a .b extension.
+#
+# NAMESPACE : optional
+#   C++ namespace to place the persistent class information in
+#
+##############################################################################
+
+# define the script to build the persistent class information
+set( sg_perl "${CMAKE_CURRENT_LIST_DIR}/sg.pl" CACHE INTERNAL "perl script to generate persistent objects" )
+
+macro( ecbuild_add_persistent )
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args SRC_LIST NAMESPACE )
+  set( multi_value_args  FILES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_persistent(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SRC_LIST  )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the SRC_LIST.")
+  endif()
+
+  if( NOT _PAR_FILES )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the FILES.")
+  endif()
+
+  foreach( file ${_PAR_FILES} )
+
+    get_filename_component( _file_dir    ${file} PATH )
+    get_filename_component( _file_we     ${file} NAME_WE )
+
+    set( file ${_file_we} )
+    if( _file_dir )
+      file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} )
+      set( file ${_file_dir}/${_file_we} )
+    endif()
+
+    # ecbuild_debug_var(file)
+
+    add_custom_command( OUTPUT  ${file}.b
+                        COMMAND ${PERL_EXECUTABLE} ${sg_perl} ${CMAKE_CURRENT_SOURCE_DIR}/${file}.h
+                                ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} ${_PAR_NAMESPACE}
+                        DEPENDS ${sg_perl} ${file}.h )
+    set_source_files_properties( ${file}.h PROPERTIES OBJECT_DEPENDS "${file}.b" )
+    list( APPEND ${_PAR_SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/${file}.b )
+
+  endforeach()
+
+endmacro( ecbuild_add_persistent  )
diff --git a/ecbuild/cmake/ecbuild_add_resources.cmake b/ecbuild/cmake/ecbuild_add_resources.cmake
new file mode 100644
index 0000000..2dfaa5e
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_resources.cmake
@@ -0,0 +1,151 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_resources
+# =====================
+#
+# Add resources as project files but optionally exclude them from packaging. ::
+#
+#   ecbuild_add_resources( TARGET <name>
+#                          [ SOURCES <source1> [<source2> ...] ]
+#                          [ SOURCES_PACK <source1> [<source2> ...] ]
+#                          [ SOURCES_DONT_PACK <source1> [<source2> ...] ]
+#                          [ PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK_DIRS <directory1> [<directory2> ...] ]
+#                          [ DONT_PACK_REGEX <regex1> [<regex2> ...] ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name (target will only be created if there are any sources)
+#
+# SOURCES : optional, alias for SOURCES_PACK
+#   list of source files included when packaging
+#
+# SOURCES_PACK : optional, alias for SOURCES
+#   list of source files included when packaging
+#
+# SOURCES_DONT_PACK : optional
+#   list of source files excluded when packaging
+#
+# PACK : optional, priority over DONT_PACK, DONT_PACK_DIRS, DONT_PACK_REGEX
+#   list of files to include when packaging
+#
+# DONT_PACK : optional
+#   list of files to exclude when packaging
+#
+# DONT_PACK_DIRS : optional
+#   list of directories to exclude when packaging
+#
+# DONT_PACK_REGEX : optional
+#   list of regular expressions to match files and directories to exclude when
+#   packaging
+#
+##############################################################################
+
+macro( ecbuild_add_resources )
+
+    set( options )
+    set( single_value_args TARGET )
+    set( multi_value_args  SOURCES SOURCES_PACK SOURCES_DONT_PACK PACK DONT_PACK DONT_PACK_DIRS DONT_PACK_REGEX )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_add_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_TARGET  )
+      ecbuild_critical("The call to ecbuild_add_resources() doesn't specify the TARGET.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_DONT_PACK_REGEX )
+        foreach( exp ${_PAR_DONT_PACK_REGEX} )
+            file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${exp} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+        endforeach()
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DONT_PACK_DIRS )
+        foreach( dir ${_PAR_DONT_PACK_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_DONT_PACK} )
+    endif()
+
+    # now lets remove files that we want to pack from the list
+    # note that these have priority over the files not to pack
+    # so we can GLOB_RECURSE * -> DONT_PACK and then select only the ones we pack
+
+    # files to pack but are not project files
+    if( DEFINED _PAR_PACK )
+        foreach( file ${_PAR_PACK} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # define as project files, but dont pack them
+    if( DEFINED _PAR_SOURCES_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_SOURCES_DONT_PACK} )
+		foreach( sfile ${_PAR_SOURCES_DONT_PACK} )
+			set( _full_sfile "${CMAKE_CURRENT_SOURCE_DIR}/${sfile}" )
+	        if( EXISTS ${_full_sfile} )
+				list( APPEND ${_PAR_TARGET}_files ${_full_sfile} )
+			endif()
+		endforeach()
+    endif()
+
+    # define as project files and pack them
+    # SOURCES_PACK is alias to SOURCES
+    if( DEFINED _PAR_SOURCES_PACK )
+        list( APPEND _PAR_SOURCES ${_PAR_SOURCES_PACK} )
+    endif()
+    if( DEFINED _PAR_SOURCES )
+        list( APPEND ${_PAR_TARGET}_files ${_PAR_SOURCES} )
+        foreach( file ${_PAR_SOURCES} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # there are project files, so lets create the target
+    if( DEFINED ${_PAR_TARGET}_files )
+        add_custom_target( ${_PAR_TARGET} SOURCES ${${_PAR_TARGET}_files} )
+    endif()
+
+    # remove CMakeLists.txt
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        if( ${file} MATCHES "CMakeLists.txt" )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endif()
+    endforeach()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro( ecbuild_add_resources  )
diff --git a/ecbuild/cmake/ecbuild_add_test.cmake b/ecbuild/cmake/ecbuild_add_test.cmake
new file mode 100644
index 0000000..da0bd22
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_add_test.cmake
@@ -0,0 +1,506 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_test
+# ================
+#
+# Add a test as a script or an executable with a given list of source files. ::
+#
+#   ecbuild_add_test( [ TARGET <name> ]
+#                     [ SOURCES <source1> [<source2> ...] ]
+#                     [ OBJECTS <obj1> [<obj2> ...] ]
+#                     [ COMMAND <executable> ]
+#                     [ TYPE EXE|SCRIPT|PYTHON ]
+#                     [ LABELS <label1> [<label2> ...] ]
+#                     [ ARGS <argument1> [<argument2> ...] ]
+#                     [ RESOURCES <file1> [<file2> ...] ]
+#                     [ TEST_DATA <file1> [<file2> ...] ]
+#                     [ BOOST ]
+#                     [ MPI <number-of-mpi-tasks> ]
+#                     [ OMP <number-of-threads-per-mpi-task> ]
+#                     [ ENABLED ON|OFF ]
+#                     [ LIBS <library1> [<library2> ...] ]
+#                     [ INCLUDES <path1> [<path2> ...] ]
+#                     [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                     [ PERSISTENT <file1> [<file2> ...] ]
+#                     [ GENERATED <file1> [<file2> ...] ]
+#                     [ DEPENDS <target1> [<target2> ...] ]
+#                     [ TEST_DEPENDS <target1> [<target2> ...] ]
+#                     [ CONDITION <condition> ]
+#                     [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                     [ ENVIRONMENT <variable1> [<variable2> ...] ]
+#                     [ WORKING_DIRECTORY <path> ]
+#                     [ CFLAGS <flag1> [<flag2> ...] ]
+#                     [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                     [ FFLAGS <flag1> [<flag2> ...] ]
+#                     [ LINKER_LANGUAGE <lang> ] )
+#
+# Options
+# -------
+#
+# TARGET : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   target name to be built
+#
+# SOURCES : required if TARGET is provided
+#   list of source files to be compiled
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# COMMAND : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   command or script to execute (no executable is built)
+#
+# TYPE : optional
+#   test type, one of:
+#
+#   :EXE:    run built executable, default if TARGET is provided
+#   :SCRIPT: run command or script, default if COMMAND is provided
+#   :PYTHON: run a Python script (requires the Python interpreter to be found)
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   The project name in lower case is always added as a label. Additional
+#   labels are assigned depending on the type of test:
+#
+#   :executable: for type ``EXE``
+#   :script:     for type ``SCRIPT``
+#   :python:     for type ``PYTHON``
+#   :boost:      uses Boost unit test
+#   :mpi:        if ``MPI`` is set
+#   :openmp:     if ``OMP`` is set
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# ARGS : optional
+#   list of arguments to pass to TARGET or COMMAND when running the test
+#
+# RESOURCES : optional
+#   list of files to copy from the test source directory to the test directory
+#
+# TEST_DATA : optional
+#   list of test data files to download
+#
+# BOOST : optional
+#   use the Boost Unit Test Framework
+#
+# MPI : optional
+#   Run with MPI using the given number of MPI tasks.
+#
+#   If greater than 1, and ``MPIEXEC`` is not available, the test is disabled.
+#
+# OMP : optional
+#   number of OpenMP threads per MPI task to use.
+#
+#   If set, the environment variable OMP_NUM_THREADS will set.
+#   Also, in case of launchers like aprun, the OMP_NUMTHREADS_FLAG will be used.
+#
+# ENABLED : optional
+#   if set to OFF, the test is built but not enabled as a test case
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# TEST_DEPENDS : optional
+#   list of tests to be run before this one
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# ENVIRONMENT : optional
+#   list of environment variables to set in the test environment
+#
+# WORKING_DIRECTORY : optional
+#   directory to switch to before running the test
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_test )
+
+  set( options           BOOST )
+  set( single_value_args TARGET ENABLED COMMAND TYPE LINKER_LANGUAGE MPI OMP WORKING_DIRECTORY )
+  set( multi_value_args  SOURCES OBJECTS LIBS INCLUDES TEST_DEPENDS DEPENDS LABELS ARGS
+                         PERSISTENT DEFINITIONS RESOURCES TEST_DATA CFLAGS
+                         CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES ENVIRONMENT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_test(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  set( _TEST_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+
+  # Undocumented flag for disabling all MPI tests for test environment without suitable MPI(EXEC)
+  if( _PAR_MPI AND ECBUILD_DISABLE_MPI_TESTS )
+    ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ECBUILD_DISABLE_MPI_TESTS set - disabling test")
+    set( _PAR_ENABLED 0 )
+  elseif( _PAR_MPI )
+    # Check for MPIEXEC if it not set
+    find_program( MPIEXEC NAMES mpiexec mpirun lamexec srun
+                  DOC "Executable for running MPI programs." )
+    if( MPIEXEC )
+      set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC")
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): Running using ${MPIEXEC} on ${_PAR_MPI} MPI rank(s)")
+      set( _PAR_LABELS mpi ${_PAR_LABELS} )
+    elseif( _PAR_MPI GREATER 1 )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${_PAR_MPI} MPI ranks requested but MPIEXEC not available - disabling test")
+      set( _PAR_ENABLED 0 )
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): 1 MPI rank requested but MPIEXEC not available - running sequentially")
+      set( _PAR_MPI 0 )
+    endif()
+  endif()
+
+  # Check for OMP
+  if( DEFINED _PAR_OMP )
+    set( _PAR_LABELS openmp ${_PAR_LABELS} )
+  else()
+    set( _PAR_OMP 1 )
+  endif()
+  list( APPEND _PAR_ENVIRONMENT "OMP_NUM_THREADS=${_PAR_OMP}" )
+
+
+  # default is enabled
+  if( NOT DEFINED _PAR_ENABLED )
+    set( _PAR_ENABLED 1 )
+  endif()
+
+
+  ### check test type
+
+  # command implies script
+  if( DEFINED _PAR_COMMAND )
+    set( _PAR_TYPE "SCRIPT" )
+    set( _PAR_LABELS script ${_PAR_LABELS} )
+  endif()
+
+  # default of TYPE
+  if( NOT _PAR_TYPE AND DEFINED _PAR_TARGET )
+    set( _PAR_TYPE "EXE" )
+    set( _PAR_LABELS executable ${_PAR_LABELS} )
+    if( NOT _PAR_SOURCES )
+      ecbuild_critical("The call to ecbuild_add_test() defines a TARGET without SOURCES.")
+    endif()
+  endif()
+
+  if( _PAR_TYPE MATCHES "PYTHON" )
+    if( PYTHONINTERP_FOUND )
+      set( _PAR_COMMAND ${PYTHON_EXECUTABLE} )
+      set( _PAR_LABELS python ${_PAR_LABELS} )
+    else()
+      ecbuild_warn( "Requested a python test but python interpreter not found - disabling test\nPYTHON_EXECUTABLE: [${PYTHON_EXECUTABLE}]" )
+      set( _PAR_ENABLED 0 )
+    endif()
+  endif()
+
+  ### further checks
+
+  if( _PAR_ENABLED AND NOT _PAR_TARGET AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a TARGET nor a COMMAND.")
+  endif()
+
+  if( _PAR_ENABLED AND NOT _PAR_COMMAND AND NOT _PAR_SOURCES )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a COMMAND nor SOURCES, so no test can be defined or built.")
+  endif()
+
+  if( _PAR_TYPE MATCHES "SCRIPT" AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines a 'script' but doesn't specify the COMMAND.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${_TEST_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  # boost unit test linking to unit_test lib ?
+
+  if( _PAR_BOOST AND ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    if( HAVE_BOOST_UNIT_TEST )
+      set( _PAR_LABELS boost ${_PAR_LABELS} )
+      if( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} )
+        include_directories( ${Boost_INCLUDE_DIRS}  ) # temporary until we ship Boost Unit Test with ecBuild
+      else()
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} ${Boost_INCLUDE_DIRS} )
+      endif()
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): boost unit test framework not available - not building test")
+      set( _${_PAR_TARGET}_condition FALSE )
+    endif()
+
+  endif()
+
+  ### enable the tests
+
+  if( ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    # add resources
+
+    if( DEFINED _PAR_RESOURCES )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): copying resources ${_PAR_RESOURCES}")
+      foreach( rfile ${_PAR_RESOURCES} )
+        execute_process( COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${rfile} ${_TEST_DIR} )
+      endforeach()
+    endif()
+
+    # build executable
+
+    if( DEFINED _PAR_SOURCES )
+
+      # add include dirs if defined
+      if( DEFINED _PAR_INCLUDES )
+        list(REMOVE_DUPLICATES _PAR_INCLUDES )
+        foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+          if( path )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add ${path} to include_directories")
+            include_directories( ${path} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+          endif()
+        endforeach()
+      endif()
+
+      # add persistent layer files
+      if( DEFINED _PAR_PERSISTENT )
+        if( DEFINED PERSISTENT_NAMESPACE )
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+        else()
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+        endif()
+      endif()
+
+      # insert already compiled objects (from OBJECT libraries)
+      unset( _all_objects )
+      foreach( _obj ${_PAR_OBJECTS} )
+        list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+      endforeach()
+
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+
+      # add extra dependencies
+      if( DEFINED _PAR_DEPENDS)
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+        add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+      endif()
+
+      # add the link libraries
+      if( DEFINED _PAR_LIBS )
+        list(REMOVE_DUPLICATES _PAR_LIBS )
+        list(REMOVE_ITEM _PAR_LIBS debug)
+        list(REMOVE_ITEM _PAR_LIBS optimized)
+        foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+          if( lib )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): linking with ${lib}")
+            target_link_libraries( ${_PAR_TARGET} ${lib} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${lib} not found - not linking")
+          endif()
+        endforeach()
+      endif()
+
+      # add test libraries
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_LINKED AND HAVE_BOOST_UNIT_TEST )
+        target_link_libraries( ${_PAR_TARGET} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_TEST_EXEC_MONITOR_LIBRARY} )
+      endif()
+
+      # filter sources
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+
+      # Override compilation flags on a per source file basis
+      ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+      if( DEFINED _PAR_GENERATED )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+        set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+      endif()
+
+      # modify definitions to compilation ( -D... )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+
+      if( DEFINED _PAR_DEFINITIONS )
+        list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      endif()
+
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        list( APPEND _target_defs BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+      endif()
+
+      if( _target_defs )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using definitions ${_target_defs}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+      endif()
+
+      # set build location to local build dir
+      # not the project base as defined for libs and execs
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${_TEST_DIR} )
+
+      # whatever project settings are, we always build tests with the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+
+      # set linker language
+      if( DEFINED _PAR_LINKER_LANGUAGE )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      endif()
+
+      # make sure target is removed before - some problems with AIX
+      get_target_property(EXE_FILENAME ${_PAR_TARGET} OUTPUT_NAME)
+      add_custom_command( TARGET ${_PAR_TARGET}
+                          PRE_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E remove ${EXE_FILENAME} )
+
+    endif() # _PAR_SOURCES
+
+    if( DEFINED _PAR_COMMAND AND NOT _PAR_TARGET ) # in the absence of target, we use the command as a name
+      set( _PAR_TARGET ${_PAR_COMMAND} )
+    endif()
+
+    # scripts dont have actual build targets
+    # we build a phony target to trigger the dependencies
+    if( DEFINED _PAR_COMMAND AND DEFINED _PAR_DEPENDS )
+
+      add_custom_target( ${_PAR_TARGET}.x ALL COMMAND ${CMAKE_COMMAND} -E touch ${_PAR_TARGET}.x )
+
+      add_dependencies( ${_PAR_TARGET}.x ${_PAR_DEPENDS} )
+
+    endif()
+
+
+    # define the arguments
+    set( TEST_ARGS "" )
+    # Boost Unit Test >= 1.60 requires arguments to be passed to the application to be separated by --
+    if( DEFINED _PAR_ARGS AND _PAR_BOOST )
+      list( APPEND TEST_ARGS "--" ${_PAR_ARGS} )
+    elseif( DEFINED _PAR_ARGS )
+      list( APPEND TEST_ARGS ${_PAR_ARGS} )
+    endif()
+
+    # Wrap with MPIEXEC
+    if( _PAR_MPI )
+
+      set( MPIEXEC_TASKS ${MPIEXEC_NUMPROC_FLAG} ${_PAR_MPI} )
+      if( DEFINED MPIEXEC_NUMTHREAD_FLAG )
+        set( MPIEXEC_THREADS ${MPIEXEC_NUMTHREAD_FLAG} ${_PAR_OMP} )
+      endif()
+
+      set( _LAUNCH ${MPIEXEC} ${MPIEXEC_TASKS} ${MPIEXEC_THREADS} )
+
+      if( DEFINED _PAR_COMMAND )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_PAR_COMMAND}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_PAR_COMMAND} )
+      else()
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET} )
+      endif()
+    endif()
+
+    ### define the test
+
+    if( _PAR_ENABLED ) # we can disable and still build it but not run it with 'make tests'
+
+      if( DEFINED _PAR_COMMAND )
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_COMMAND} ${TEST_ARGS} ${_working_dir} ) # run a command as test
+      else()
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_TARGET}  ${TEST_ARGS} ${_working_dir} ) # run the test that was generated
+      endif()
+
+      # Set custom properties
+      if( ${_PAR_PROPERTIES} )
+        set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+      endif()
+
+      # get test data
+
+      if( _PAR_TEST_DATA )
+
+        ecbuild_get_test_multidata( TARGET ${_PAR_TARGET}_data NAMES ${_PAR_TEST_DATA} )
+
+        list( APPEND _PAR_TEST_DEPENDS ${_PAR_TARGET}_data )
+
+      endif()
+
+      # Add lower case project name to custom test labels
+      set( _PAR_LABELS ${PROJECT_NAME_LOWCASE} ${_PAR_LABELS} )
+      list( REMOVE_DUPLICATES _PAR_LABELS )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): assign labels ${_PAR_LABELS}")
+      set_property( TEST ${_PAR_TARGET} APPEND PROPERTY LABELS "${_PAR_LABELS}" )
+
+      if( DEFINED _PAR_ENVIRONMENT )
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY ENVIRONMENT "${_PAR_ENVIRONMENT}" )
+      endif()
+
+      if( DEFINED _PAR_WORKING_DIRECTORY )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set working directory to ${_PAR_WORKING_DIRECTORY}")
+        set_tests_properties( ${_PAR_TARGET} PROPERTIES WORKING_DIRECTORY "${_PAR_WORKING_DIRECTORY}")
+      endif()
+
+      if( DEFINED _PAR_TEST_DEPENDS )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set test dependencies to ${_PAR_TEST_DEPENDS}")
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY DEPENDS "${_PAR_TEST_DEPENDS}" )
+      endif()
+
+    endif()
+
+    # add to the overall list of tests
+    list( APPEND ECBUILD_ALL_TESTS ${_PAR_TARGET} )
+    list( REMOVE_DUPLICATES ECBUILD_ALL_TESTS )
+    set( ECBUILD_ALL_TESTS ${ECBUILD_ALL_TESTS} CACHE INTERNAL "" )
+
+  endif() # _condition
+
+  # finally mark project files
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+
+endmacro( ecbuild_add_test )
diff --git a/ecbuild/cmake/ecbuild_append_to_rpath.cmake b/ecbuild/cmake/ecbuild_append_to_rpath.cmake
new file mode 100644
index 0000000..38ecbb2
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_append_to_rpath.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_append_to_rpath
+# =======================
+#
+# Append paths to the rpath. ::
+#
+#   ecbuild_append_to_rpath( RPATH_DIRS )
+#
+# ``RPATH_DIRS`` is a list of directories to append to ``CMAKE_INSTALL_RPATH``.
+#
+# * If a directory is absolute, simply append it.
+# * If a directory is relative, build a platform-dependent relative path
+#   (using ``@loader_path`` on Mac OSX, ``$ORIGIN`` on Linux and Solaris)
+#   or fall back to making it absolute by prepending the install prefix.
+#
+##############################################################################
+
+function( _path_append var path )
+	if( "${${var}}" STREQUAL "" )
+		set( ${var} "${path}" PARENT_SCOPE )
+	else()
+		list( FIND ${var} ${path} _found )
+		if( _found EQUAL "-1" )
+			set( ${var} "${${var}}:${path}" PARENT_SCOPE )
+		endif()
+	endif()
+endfunction()
+
+macro( ecbuild_append_to_rpath RPATH_DIRS )
+   
+   if( NOT ${ARGC} EQUAL 1 )
+     ecbuild_error( "ecbuild_append_to_rpath takes 1 argument")
+   endif()
+
+   foreach( RPATH_DIR ${RPATH_DIRS} )
+     
+		if( NOT ${RPATH_DIR} STREQUAL "" )
+
+			file( TO_CMAKE_PATH ${RPATH_DIR} RPATH_DIR ) # sanitize the path
+
+			if( IS_ABSOLUTE ${RPATH_DIR} )
+
+				_path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+
+			else()
+
+				set( _done 0 )
+
+				if( EC_OS_NAME STREQUAL "macosx" )
+
+					if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS 3.0) # cmake < 3.0
+						set( CMAKE_INSTALL_NAME_DIR "@loader_path/${RPATH_DIR}" )
+					endif()
+					_path_append( CMAKE_INSTALL_RPATH "@loader_path/${RPATH_DIR}" )
+
+					set( _done 1 )
+
+				endif()
+
+                if( EC_OS_NAME STREQUAL "freebsd" )
+                    _path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+                if( EC_OS_NAME STREQUAL "linux" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+				if( EC_OS_NAME STREQUAL "solaris" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+                if( EC_OS_NAME STREQUAL "aix" ) # always relative to exectuable path
+                    _path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+				# fallback
+
+				if( NOT _done )
+					_path_append( CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${RPATH_DIR}" )
+				endif()
+
+			endif()
+
+     endif()
+
+   endforeach()
+
+endmacro( ecbuild_append_to_rpath )
diff --git a/ecbuild/cmake/ecbuild_bundle.cmake b/ecbuild/cmake/ecbuild_bundle.cmake
new file mode 100644
index 0000000..9778e27
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_bundle.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+include(CMakeParseArguments)
+
+include(ecbuild_git)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_initialize
+# =========================
+#
+# Initialise the ecBuild environment for a bundle. *Must* be called *before*
+# any call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_initialize()
+#
+##############################################################################
+
+macro( ecbuild_bundle_initialize )
+
+  include( local-config.cmake OPTIONAL )
+
+  # ecmwf_stash( PROJECT ecbuild DIR ${PROJECT_SOURCE_DIR}/ecbuild STASH "ecsdk/ecbuild" BRANCH develop )
+
+  # set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/ecbuild/cmake;${CMAKE_MODULE_PATH}" )
+
+  include( ecbuild_system )
+
+  ecbuild_requires_macro_version( 1.6 )
+
+  ecbuild_declare_project()
+
+  file( GLOB local_config_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *local-config.cmake )
+
+  ecbuild_add_resources( TARGET ecbuild_bundle_dont_pack DONT_PACK "${local_config_files}" )
+
+  if( EXISTS "${PROJECT_SOURCE_DIR}/README.md" )
+    add_custom_target( ${PROJECT_NAME}_readme SOURCES "${PROJECT_SOURCE_DIR}/README.md" )
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle
+# ==============
+#
+# Declare a subproject to be built as part of this bundle. ::
+#
+#   ecbuild_bundle( PROJECT <name>
+#                   STASH <repository> | GIT <giturl> | SOURCE <path>
+#                   [ BRANCH <gitbranch> | TAG <gittag> ]
+#                   [ UPDATE | NOREMOTE ] )
+#                   [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# STASH : cannot be combined with GIT or SOURCE
+#   Stash repository in the form <project>/<repository>
+#
+# GIT : cannot be combined with STASH or SOURCE
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# SOURCE : cannot be combined with STASH or GIT
+#   Path to an existing local repository, which will be symlinked
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+# Usage
+# -----
+#
+# A bundle is used to build a number of projects together. Each subproject
+# needs to be declared with a call to ecbuild_bundle, where the order of
+# projects is important and needs to respect dependencies: if project B
+# depends on project A, A should be listed before B in the bundle.
+#
+# The first time a bundle is built, the sources of all subprojects are cloned
+# into directories named according to project in the *source* tree of the
+# bundle (which means these directories should be added to ``.gitignore``).
+# If the ``SOURCE`` option is used it must point to an existing local
+# repository on disk and no new repository is cloned. Be aware that using the
+# ``BRANCH`` or ``TAG`` option leads to the corresponding version being checked
+# out in that repository!
+#
+# Subprojects are configured and built in order. Due to being added as a
+# subproject, the usual project discovery mechanism (i.e. locating and
+# importing a ``<project>-config.cmake`` file) is not used. Also there are no
+# ``<project>-config.cmake`` files being generated for individual subprojects.
+# However there *are* package-config files being generated for each library.
+#
+# To switch off a subproject when building a bundle, set the CMake variable
+# ``BUNDLE_SKIP_<PNAME>`` where ``PNAME`` is the capitalised project name.
+#
+##############################################################################
+
+macro( ecbuild_bundle )
+
+  set( options )
+  set( single_value_args PROJECT STASH GIT SOURCE )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  string(TOUPPER "${_PAR_PROJECT}" PNAME)
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( BUNDLE_SKIP_${PNAME} )
+    ecbuild_info( "Skipping bundle project ${_PAR_PROJECT}" )
+  else()
+    ecbuild_info( "Adding bundle project ${_PAR_PROJECT}" )
+
+    if( _PAR_STASH )
+      ecmwf_stash( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} STASH ${_PAR_STASH} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_GIT )
+      ecbuild_git( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} URL ${_PAR_GIT} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_SOURCE )
+      if( DEFINED ${PNAME}_SOURCE )
+        ecbuild_critical( "ecbuild_bundle called with SOURCE for project ${_PAR_PROJECT} but ${PNAME}_SOURCE is defined" )
+      endif()
+      execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${_PAR_SOURCE} ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} )
+    endif()
+
+    if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT} OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}/CMakeLists.txt )
+      ecbuild_critical("Source directory '${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}' for subproject '${_PAR_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    # Do not descend into ecbuild if included in a bundle (ECBUILD-333)
+    if( NOT _PAR_PROJECT STREQUAL "ecbuild" )
+      ecbuild_use_package( PROJECT ${_PAR_PROJECT} )
+    endif()
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_finalize
+# =======================
+#
+# Finalise the ecBuild environment for a bundle. *Must* be called *after* the
+# last call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_finalize()
+#
+# Options
+# -------
+#
+# See documentation for ecbuild_install_project() since all arguments are
+# forwarded to an internal call to that macro.
+#
+# If no arguments are passed, then the default installation NAME is set to
+# the default project name ${CMAKE_PROJECT_NAME}
+#
+##############################################################################
+
+macro( ecbuild_bundle_finalize )
+
+  add_custom_target( update DEPENDS ${git_update_targets} )
+
+  ecbuild_info("---------------------------------------------------------")
+  ecbuild_info("Bundle ${CMAKE_PROJECT_NAME}")
+  ecbuild_info("---------------------------------------------------------")
+
+  if("${ARGV1}")
+      ecbuild_install_project( ${ARGV} )
+  else()
+      ecbuild_install_project( NAME ${CMAKE_PROJECT_NAME} )
+  endif()
+
+  ecbuild_print_summary()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_cache.cmake b/ecbuild/cmake/ecbuild_cache.cmake
new file mode 100644
index 0000000..d45b31a
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_cache.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecBuild Cache
+# =============
+#
+# During initialisation, ecBuild introspects the compiler and operating system
+# and performs a number of checks. The result of these is written to a
+# dedicated ``ecbuild-cache.cmake`` file in the build tree. This cache may be
+# used to speed up subsequent *clean* builds i.e. those where no CMakeCache.txt
+# exists yet.
+#
+# To use the ecBuild cache, configure with ``-DECBUILD_CACHE=<cache-file>``,
+# where ``<cache-file>`` is the path to an existing ``ecbuild-cache.cmake``.
+#
+# .. note ::
+#
+#   The ecBuild cache is specific to compiler *and* operating system. Do *not*
+#   attempt to use a cache file created on a different machine or with a
+#   different compiler!
+#
+##############################################################################
+
+# Prepare the cache and clobber any existing ecbuild-cache.cmake
+macro( ecbuild_prepare_cache )
+    include( CheckSymbolExists )
+    include( CheckIncludeFiles )
+    include( CheckCSourceCompiles )
+    include( CheckCXXSourceCompiles )
+    include( CheckTypeSize )
+    set( ecbuild_cache_file ${CMAKE_BINARY_DIR}/ecbuild-cache.cmake )
+    file(WRITE ${ecbuild_cache_file} "# ecbuild cache file\n\n")    
+endmacro()
+
+# Buffer the CMake variable var to be written to the ecBuild cache
+function( ecbuild_cache_var var )
+  if( NOT ${var} )
+    set( ${var} 0 )
+  endif()
+  set( ECBUILD_CACHE_BUFFER "${ECBUILD_CACHE_BUFFER}set( ${var} ${${var}} )\n" CACHE INTERNAL "Cache buffer" )
+endfunction()
+
+# Call check_symbol_exists only if the output is not defined yet
+function( ecbuild_cache_check_symbol_exists symbol includes output )
+  if( NOT DEFINED ${output} )
+    check_symbol_exists( ${symbol} ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_include_files only if the output is not defined yet
+function( ecbuild_cache_check_include_files includes output )
+  if( NOT DEFINED ${output} )
+    check_include_files( ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_c_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_c_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_c_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_cxx_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_cxx_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_cxx_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_type_size only if the output is not defined yet
+function( ecbuild_cache_check_type_size type output )
+  if( NOT DEFINED ${output} )
+    check_type_size( "${type}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Flush the ecBuild cache to disk and reset the buffer
+function( ecbuild_flush_cache )
+  file( APPEND ${ecbuild_cache_file} "${ECBUILD_CACHE_BUFFER}" )
+  set( ECBUILD_CACHE_BUFFER "" CACHE INTERNAL "Cache buffer" )
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_check_c_source_return.cmake b/ecbuild/cmake/ecbuild_check_c_source_return.cmake
new file mode 100644
index 0000000..38d89f7
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_c_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_c_source_return
+# =============================
+#
+# Compile and run a given C source code and return its output. ::
+#
+#   ecbuild_check_c_source_return( <source>
+#                                  VAR <name>
+#                                  OUTPUT <name>
+#                                  [ INCLUDES <path1> [ <path2> ... ] ]
+#                                  [ LIBS <library1> [ <library2> ... ] ]
+#                                  [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .c file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_c_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_c_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_c_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+    
+        # write the source file
+    
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_check_compiler.cmake b/ecbuild/cmake/ecbuild_check_compiler.cmake
new file mode 100644
index 0000000..197f0b5
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_compiler.cmake
@@ -0,0 +1,156 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###################################################################################################
+# enable C to use in system introspection
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+endif()
+
+############################################################################################
+# try to get compiler version if cmake did not
+
+if( NOT CMAKE_C_COMPILER_VERSION )
+
+    set( EC_COMPILER_VERSION "?.?" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Intel" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -dumpversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE "([0-9])\\.([0-9])(\\.([0-9]))?" "\\1.\\2"  EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "Clang" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} --version
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*clang version ([0-9])\\.([0-9])(\\.([0-9]))?.*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "SunPro" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -V
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -qversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*V([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+
+    endif()
+
+    if( NOT EC_COMPILER_VERSION STREQUAL "?.?" )
+        set(CMAKE_C_COMPILER_VERSION "${EC_COMPILER_VERSION}" )
+    endif()
+
+endif()
+
+############################################################################################
+# c compiler tests
+
+if( CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+	ecbuild_cache_check_c_source_compiles(
+		  " typedef int foo_t;
+			static inline foo_t static_foo(){return 0;}
+			foo_t foo(){return 0;}
+			int main(int argc, char *argv[]){return 0;}
+		  " EC_HAVE_C_INLINE )
+
+endif()
+
+############################################################################################
+# c++ compiler tests
+
+if( CMAKE_CXX_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+    # check for __FUNCTION__
+    ecbuild_cache_check_cxx_source_compiles( "#include <iostream>\nint main(int argc, char* argv[]) { std::cout << __FUNCTION__ << std::endl; }"
+      EC_HAVE_FUNCTION_DEF )
+
+    # check for c++ abi, usually present in GNU compilers
+    ecbuild_cache_check_cxx_source_compiles( "#include <cxxabi.h>\n int main() { char * type; int status; char * r = abi::__cxa_demangle(type, 0, 0, &status); }"
+    EC_HAVE_CXXABI_H )
+
+    # check for bool
+    ecbuild_cache_check_cxx_source_compiles( "int main() { bool aflag = true; }"
+	  EC_HAVE_CXX_BOOL )
+
+    # check for sstream
+    ecbuild_cache_check_cxx_source_compiles( "#include <sstream>\nint main() { std::stringstream s; }"
+	  EC_HAVE_CXX_SSTREAM )
+
+endif()
+
+############################################################################################
+# enable warnings
+
+if( CMAKE_COMPILER_IS_GNUCC )
+
+    ecbuild_add_c_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_c_flags("-Wall")
+        # ecbuild_add_c_flags("-pedantic")
+        # ecbuild_add_c_flags("-Wextra")
+    endif()
+
+endif()
+
+if( CMAKE_COMPILER_IS_GNUCXX )
+
+   ecbuild_add_cxx_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_cxx_flags("-Wall")
+        #    ecbuild_add_cxx_flags("-Wextra")
+    endif()
+
+endif()
+
+if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+  ecbuild_add_fortran_flags("-warn all")
+endif()
+
+############################################################################################
+# compiler dependent fixes
+
+# For Cray compilers add "-Wl,-Bdynamic" at very end of linker commands, in order to produce dynamic executables by default
+
+if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+  set(CMAKE_Fortran_LINK_EXECUTABLE "<CMAKE_Fortran_COMPILER> <CMAKE_Fortran_LINK_FLAGS> <LINK_FLAGS> <FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+############################################################################################
+# Fortran compiler specific flags
+# if( NOT HAVE_SINGLE_PRECISION )
+#  if(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
+#      ecbuild_add_fortran_flags("-r8")
+#  elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+#      # NOTE that if we add -fdefault-real-8 then we NEED -fdefault-double-8 to avoid quadmath
+#      ecbuild_add_fortran_flags("-fdefault-real-8 -fdefault-double-8")
+#  endif()
+# endif()
diff --git a/ecbuild/cmake/ecbuild_check_cxx11.cmake b/ecbuild/cmake/ecbuild_check_cxx11.cmake
new file mode 100644
index 0000000..d2f9629
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_cxx11.cmake
@@ -0,0 +1,138 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx11
+# ===================
+#
+# Check for C++11 features. ::
+#
+#   ecbuild_check_cxx11( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                        [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                        [ PRINT ] )
+#
+# This function uses macros from http://github.com/UCL/GreatCMakeCookOff
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_cxx11 )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_cxx11(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake )
+
+  cxx11_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  # Save CXX flags
+  set( CXX_FLAGS_SNASHOT ${CMAKE_CXX_FLAGS} )
+
+  # Add C++11 flags
+  include( ${ECBUILD_MACROS_DIR}/ecbuild_get_cxx11_flags.cmake )
+  ecbuild_get_cxx11_flags( CXX11_FLAGS )
+  ecbuild_debug( "ecbuild_check_cxx11: detected C++11 flag as ${CXX11_FLAGS}" )
+  set( CMAKE_CXX_FLAGS "${CXX11_FLAGS} ${CMAKE_CXX_FLAGS}" )
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    cxx11_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      cxx11_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      cxx11_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  # Restore CXX flags
+  set( CMAKE_CXX_FLAGS ${CXX_FLAGS_SNAPSHOT} )
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( CXX11_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( CXX11_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${CXX11_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_CXX11_${FEAT} )
+       list( APPEND CXX11_SUPPORTED_FEATURES ${f} )
+    else()
+       list( APPEND CXX11_NOT_SUPPORTED_FEATURES ${f} )
+    endif()
+  endforeach()
+
+  if( CXX11_CHECKED_FEATURES )
+    list( SORT CXX11_CHECKED_FEATURES )
+  endif()
+  if( CXX11_SUPPORTED_FEATURES )
+    list( SORT CXX11_SUPPORTED_FEATURES )
+  endif()
+  if( CXX11_NOT_SUPPORTED_FEATURES )
+    list( SORT CXX11_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( CXX11_CHECKED_FEATURES       ${CXX11_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( CXX11_SUPPORTED_FEATURES     ${CXX11_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( CXX11_NOT_SUPPORTED_FEATURES ${CXX11_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( CXX11_CHECKED_FEATURES )
+      join( CXX11_CHECKED_FEATURES " " CXX11_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked C++11 features: ${CXX11_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no C++11 features" )
+    endif()
+    if( CXX11_SUPPORTED_FEATURES )
+      join( CXX11_SUPPORTED_FEATURES " " CXX11_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found C++11 features: ${CXX11_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no C++11 features" )
+    endif()
+    if( CXX11_NOT_SUPPORTED_FEATURES )
+      join( CXX11_NOT_SUPPORTED_FEATURES " " CXX11_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found C++11 features: ${CXX11_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked C++11 features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_cxx11 )
diff --git a/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake b/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake
new file mode 100644
index 0000000..7270540
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx_source_return
+# ===============================
+#
+# Compile and run a given C++ code and return its output. ::
+#
+#   ecbuild_check_cxx_source_return( <source>
+#                                    VAR <name>
+#                                    OUTPUT <name>
+#                                    [ INCLUDES <path1> [ <path2> ... ] ]
+#                                    [ LIBS <library1> [ <library2> ... ] ]
+#                                    [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .cxx file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_cxx_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_cxx_source_return(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _p_VAR OR NOT _p_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_cxx_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+    set( _msg "Testing ${_p_VAR}:" )
+
+    if( NOT DEFINED ${_p_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_p_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES)
+        if(CMAKE_REQUIRED_LIBRARIES)
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _p_LIBS )
+            list( APPEND __add_libs ${_p_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES)
+        if(CMAKE_REQUIRED_INCLUDES)
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _p_INCLUDES )
+            list( APPEND __add_incs ${_p_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx" "${SOURCE}\n" )
+
+        ecbuild_debug( "${_msg}" )
+        try_run( ${_p_VAR}_EXITCODE ${_p_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+
+        # ecbuild_debug_var( ${_p_VAR}_COMPILED )
+        # ecbuild_debug_var( ${_p_VAR}_EXITCODE )
+
+        # if it did not compile make the return value fail code of 1
+
+        if( NOT ${_p_VAR}_COMPILED )
+          ecbuild_debug( "${_msg} failed to compile" )
+        endif()
+
+        if( "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN" )
+          ecbuild_debug( "${_msg} failed to run" )
+        endif()
+
+        # if the return value was 0 then it worked
+        if( ${_p_VAR}_COMPILED AND "${${_p_VAR}_EXITCODE}" EQUAL 0 )
+
+          ecbuild_debug("${_msg} Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_p_VAR}     1              CACHE INTERNAL "Test ${_p_VAR}")
+          set( ${_p_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_p_VAR} output")
+
+        else()
+
+          if(CMAKE_CROSSCOMPILING AND "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_p_VAR} "${${_p_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_p_VAR} "" CACHE INTERNAL "Test ${_p_VAR}")
+            set(${_p_OUTPUT} "" CACHE INTERNAL "Test ${_p_VAR} output")
+          endif()
+
+          ecbuild_debug("Test ${_p_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+
+    endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_check_fortran.cmake b/ecbuild/cmake/ecbuild_check_fortran.cmake
new file mode 100644
index 0000000..d7a63cf
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_fortran.cmake
@@ -0,0 +1,126 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran
+# =====================
+#
+# Check for Fortran features. ::
+#
+#   ecbuild_check_fortran( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                          [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                          [ PRINT ] )
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for, fails if not detected
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_fortran )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/fortran_features/CheckFortranFeatures.cmake )
+
+  fortran_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    fortran_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      fortran_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      fortran_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( Fortran_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( Fortran_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${Fortran_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_Fortran_${FEAT} )
+       list( APPEND Fortran_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 1 PARENT_SCOPE )
+    else()
+       list( APPEND Fortran_NOT_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 0 PARENT_SCOPE )
+    endif()
+  endforeach()
+
+  if( Fortran_CHECKED_FEATURES )
+    list( SORT Fortran_CHECKED_FEATURES )
+  endif()
+  if( Fortran_SUPPORTED_FEATURES )
+    list( SORT Fortran_SUPPORTED_FEATURES )
+  endif()
+  if( Fortran_NOT_SUPPORTED_FEATURES )
+    list( SORT Fortran_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( Fortran_CHECKED_FEATURES       ${Fortran_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( Fortran_SUPPORTED_FEATURES     ${Fortran_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( Fortran_NOT_SUPPORTED_FEATURES ${Fortran_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( Fortran_CHECKED_FEATURES )
+      join( Fortran_CHECKED_FEATURES " " Fortran_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked Fortran features: ${Fortran_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no Fortran features" )
+    endif()
+    if( Fortran_SUPPORTED_FEATURES )
+      join( Fortran_SUPPORTED_FEATURES " " Fortran_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found Fortran features: ${Fortran_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no Fortran features" )
+    endif()
+    if( Fortran_NOT_SUPPORTED_FEATURES )
+      join( Fortran_NOT_SUPPORTED_FEATURES " " Fortran_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found Fortran features: ${Fortran_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked Fortran features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_fortran )
diff --git a/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake b/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake
new file mode 100644
index 0000000..92168b9
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran_source_return
+# ===================================
+#
+# Compile and run a given Fortran code and return its output. ::
+#
+#   ecbuild_check_fortran_source_return( <source>
+#                                        VAR <name>
+#                                        OUTPUT <name>
+#                                        [ INCLUDES <path1> [ <path2> ... ] ]
+#                                        [ LIBS <library1> [ <library2> ... ] ]
+#                                        [ DEFINITIONS <def1> [ <def2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .f file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_fortran_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_fortran_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_fortran_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_check_functions.cmake b/ecbuild/cmake/ecbuild_check_functions.cmake
new file mode 100644
index 0000000..59fbd3a
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_functions.cmake
@@ -0,0 +1,186 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# os capability checks
+
+if( ENABLE_OS_FUNCTIONS_TEST )
+
+    ### symbol checks ##################
+
+    ecbuild_cache_check_symbol_exists( fseek        "stdio.h"                         EC_HAVE_FSEEK  )
+    ecbuild_cache_check_symbol_exists( fseeko       "stdio.h"                         EC_HAVE_FSEEKO )
+    ecbuild_cache_check_symbol_exists( ftello       "stdio.h"                         EC_HAVE_FTELLO )
+    ecbuild_cache_check_symbol_exists( lseek        "sys/types.h;unistd.h"            EC_HAVE_LSEEK  )
+    ecbuild_cache_check_symbol_exists( ftruncate    "sys/types.h;unistd.h"            EC_HAVE_FTRUNCATE  )
+    ecbuild_cache_check_symbol_exists( open         "sys/types.h;sys/stat.h;fcntl.h"  EC_HAVE_OPEN   )
+    ecbuild_cache_check_symbol_exists( fopen        "stdio.h"                         EC_HAVE_FOPEN  )
+    ecbuild_cache_check_symbol_exists( fmemopen     "stdio.h"                         EC_HAVE_FMEMOPEN )
+    ecbuild_cache_check_symbol_exists( funopen      "stdio.h"                         EC_HAVE_FUNOPEN )
+    ecbuild_cache_check_symbol_exists( flock        "sys/file.h"                      EC_HAVE_FLOCK  )
+    ecbuild_cache_check_symbol_exists( mmap         "sys/mman.h"                      EC_HAVE_MMAP   )
+
+    ecbuild_cache_check_symbol_exists( posix_memalign "stdlib.h"                      EC_HAVE_POSIX_MEMALIGN )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK      "fcntl.h"                         EC_HAVE_F_GETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLK      "fcntl.h"                         EC_HAVE_F_SETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW     "fcntl.h"                         EC_HAVE_F_SETLKW  )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK64     "fcntl.h"                        EC_HAVE_F_GETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLK64     "fcntl.h"                        EC_HAVE_F_SETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW64    "fcntl.h"                        EC_HAVE_F_SETLKW64  )
+
+    ecbuild_cache_check_symbol_exists( MAP_ANONYMOUS "sys/mman.h"                     EC_HAVE_MAP_ANONYMOUS )
+    ecbuild_cache_check_symbol_exists( MAP_ANON      "sys/mman.h"                     EC_HAVE_MAP_ANON )
+
+    ### include files checks ##################
+
+    ecbuild_cache_check_include_files( assert.h       EC_HAVE_ASSERT_H      )
+    ecbuild_cache_check_include_files( stdlib.h       EC_HAVE_STDLIB_H      )
+    ecbuild_cache_check_include_files( unistd.h       EC_HAVE_UNISTD_H      )
+    ecbuild_cache_check_include_files( string.h       EC_HAVE_STRING_H      )
+    ecbuild_cache_check_include_files( strings.h      EC_HAVE_STRINGS_H     )
+    ecbuild_cache_check_include_files( sys/stat.h     EC_HAVE_SYS_STAT_H    )
+    ecbuild_cache_check_include_files( sys/time.h     EC_HAVE_SYS_TIME_H    )
+    ecbuild_cache_check_include_files( sys/types.h    EC_HAVE_SYS_TYPES_H   )
+    ecbuild_cache_check_include_files( malloc.h       EC_HAVE_MALLOC_H      )
+    ecbuild_cache_check_include_files( sys/malloc.h   EC_HAVE_SYS_MALLOC_H  )
+
+    ecbuild_cache_check_include_files( sys/param.h    EC_HAVE_SYS_PARAM_H   )
+    ecbuild_cache_check_include_files( sys/mount.h    EC_HAVE_SYS_MOUNT_H   )
+    ecbuild_cache_check_include_files( sys/vfs.h      EC_HAVE_SYS_VFS_H     )
+
+    ### capability checks ##################
+
+    # test off_t
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\nint main(){ off_t l=0; return 0;}\n" EC_HAVE_OFFT )
+    # test off64_t
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){ off64_t l=0; return 0;}\n" EC_HAVE_OFF64T  )
+    # test struct stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; return 0; }"   EC_HAVE_STRUCT_STAT )
+    # test struct stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; return 0; }" EC_HAVE_STRUCT_STAT64 )
+    # test stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s;	stat(\"\",&s); return 0; }"    EC_HAVE_STAT )
+    # test stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; stat64(\"\",&s); return 0; }"  EC_HAVE_STAT64 )
+    # test fstat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; fstat(1,&s); return 0; }" EC_HAVE_FSTAT )
+    # test fstat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; fstat64(1,&s); return 0; }" EC_HAVE_FSTAT64 )
+    # test fseeko64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l=0;fseeko64(file,l,SEEK_CUR);return 0;}\n" EC_HAVE_FSEEKO64 )
+
+    # test for ftello64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l = ftello64(file);return 0;}\n"  EC_HAVE_FTELLO64 )
+
+    # test for lseek64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/types.h>\n#include <unistd.h>\nint main(){off64_t h = lseek64(0,0,SEEK_SET);return 0;}\n"  EC_HAVE_LSEEK64 )
+    # test for open64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){int fd = open64(\"name\",O_RDWR|O_CREAT,0777);return 0;}\n" EC_HAVE_OPEN64 )
+    # test for fopen64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\nint main(){FILE* file = fopen64(\"name\",\"w\");return 0;}\n"  EC_HAVE_FOPEN64 )
+    # test for ftruncate64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <unistd.h>\n#include <sys/types.h>\nint main(){ftruncate64(0,(off64_t)0);return 0;}\n" EC_HAVE_FTRUNCATE64 )
+    # test for flock64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){struct flock64 l;return 0;}\n" EC_HAVE_FLOCK64 )
+    # test for mmap64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/mman.h>\nint main(){void* addr = mmap64(0,10,PROT_READ|PROT_WRITE,MAP_PRIVATE,10,0); return 0;}\n" EC_HAVE_MMAP64 )
+    # test for struct statvfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/statvfs.h>\nint main(){ struct statvfs v; }" EC_HAVE_STRUCT_STATVFS )
+    # test for struct statvfs64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/statvfs.h>\nint main(){ struct statvfs64 v; }" EC_HAVE_STRUCT_STATVFS64 )
+
+    # test for fopencookie
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <stdio.h>\nint main(){ void* cookie; const char* mode; cookie_io_functions_t iof; FILE* fopencookie(void *cookie, const char *mode, cookie_io_functions_t iof); }" EC_HAVE_FOPENCOOKIE )
+
+    # test for fsync
+    ecbuild_cache_check_symbol_exists(fsync "unistd.h" EC_HAVE_FSYNC)
+    # test for fdatasync
+    ecbuild_cache_check_symbol_exists(fdatasync "unistd.h" EC_HAVE_FDATASYNC)
+    # test for dirfd
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <dirent.h>\nint main(){ DIR *dirp; int i = dirfd(dirp); }\n" EC_HAVE_DIRFD )
+    # test for sys/proc.h
+    ecbuild_cache_check_c_source_compiles( "#include <sys/proc.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROC )
+    # test for procfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/procfs.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROCFS )
+    # test for backtrace
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <execinfo.h>\n int main(){ void ** buffer; int i = backtrace(buffer, 256); }\n" EC_HAVE_EXECINFO_BACKTRACE )
+
+    #### reentrant funtions support  #############
+
+    # test for gmtime_r
+    ecbuild_cache_check_c_source_compiles( "#include <time.h>\nint main(){ time_t now; time(&now); struct tm t; gmtime_r(&now,&t); }\n" EC_HAVE_GMTIME_R )
+    # test for getpwuid_r
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <sys/types.h>\n#include <pwd.h>\nint main(){ char buf[4096]; struct passwd pwbuf; struct passwd *pwbufp = 0; getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pwbufp); }\n" EC_HAVE_GETPWUID_R )
+    # test for getpwnam_r
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <pwd.h>\nint main(){ struct passwd p; char line[1024]; int n = getpwnam_r(\"user\",&p,line,sizeof(line),0); }\n" EC_HAVE_GETPWNAM_R )
+    # test for readdir_r
+    ecbuild_cache_check_c_source_compiles( "#include <dirent.h>\nint main(){ DIR *dirp; struct dirent *entry; struct dirent **result; int i = readdir_r(dirp, entry, result); }\n" EC_HAVE_READDIR_R )
+    # test for gethostbyname_r
+    ecbuild_cache_check_c_source_compiles( "#include <netdb.h>\nint main(){ const char *name; struct hostent *ret; char *buf; struct hostent **result; size_t buflen; int *h_errnop; int i = gethostbyname_r(name,ret,buf,buflen,result,h_errnop); }\n" EC_HAVE_GETHOSTBYNAME_R )
+
+    #### special compiler __atributes__  #############
+
+    # test for __attribute__ ((__constructor__)) -- usually present in GCC, Clang, Intel on Linux, Solaris, MacOSX; not present in AIX XLC
+    ecbuild_cache_check_c_source_compiles( "#include <stdio.h>\nstatic int argc_;static char** argv_;static char** envp_;\nint main(){printf(\"%d\", argc_);}\n__attribute__ ((__constructor__)) static void before_main(int argc, char* argv[], char* envp[]){argc_ = argc;argv_ = argv;envp_ = envp;}\n" EC_HAVE_ATTRIBUTE_CONSTRUCTOR )
+
+    if( NOT DEFINED EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+        ecbuild_check_c_source_return("
+            #include <stdio.h>
+            #include <string.h>
+            int main(){return 0;}
+            __attribute__ ((__constructor__))
+            static void before_main(int argc, char* argv[], char* envp[])
+            {
+                printf(\"%d:%d\",argc, strstr(argv[0],\"cmTryCompileExec\")?1:0);
+            }"
+            VAR    EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+            OUTPUT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT )
+
+        if( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV AND NOT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT STREQUAL "1:1" )
+          set(EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV 0 CACHE INTERNAL "ATTRIBUTE_CONSTRUCTOR doesnt init argv correctly")
+        endif()
+    endif()
+    ecbuild_cache_var( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+
+
+    #### check for some Linux stuff #############
+
+    if( NOT DEFINED EC_HAVE_PROCFS )
+        ecbuild_check_c_source_return("
+            #include <sys/types.h>
+            #include <dirent.h>
+            int main()
+            {
+                DIR* d = opendir(\"/proc\");
+                if(d)
+                    return 0;
+                else
+                    return -1;
+            }"
+            VAR    EC_HAVE_PROCFS
+            OUTPUT EC_HAVE_PROCFS_OUTPUT )
+    endif()
+    ecbuild_cache_var( EC_HAVE_PROCFS )
+
+#    ecbuild_debug_var(EC_HAVE_PROCFS)
+#    ecbuild_debug_var(EC_HAVE_PROCFS_OUTPUT)
+
+    #### check support for DL library #############
+
+    ecbuild_cache_check_include_files( dlfcn.h  EC_HAVE_DLFCN_H )
+
+    cmake_push_check_state(RESET)
+    set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS} )
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <dlfcn.h>\nint main(){ void* addr; Dl_info info; dladdr(addr, &info); }\n" EC_HAVE_DLADDR )
+    cmake_pop_check_state()
+
+endif()
+
+
diff --git a/ecbuild/cmake/ecbuild_check_os.cmake b/ecbuild/cmake/ecbuild_check_os.cmake
new file mode 100644
index 0000000..d2af403
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_check_os.cmake
@@ -0,0 +1,536 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# check size of pointer
+
+# Re-check size of void pointer since for some compiler combinations this is not properly set
+ecbuild_cache_check_type_size( "void*" CMAKE_SIZEOF_VOID_P  )
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+
+endif()
+
+math( EXPR EC_OS_BITS "${CMAKE_SIZEOF_VOID_P} * 8" )
+
+# we only support 32 and 64 bit operating systems
+if( NOT EC_OS_BITS EQUAL "32" AND NOT EC_OS_BITS EQUAL "64" )
+  ecbuild_critical( "operating system ${CMAKE_SYSTEM} ${EC_OS_BITS} bits -- ecbuild only supports 32 or 64 bit OS's" )
+endif()
+
+############################################################################################
+# For 64 bit architectures enable PIC (position-independent code)
+
+# Allow overriding the position independent code setting (ECBUILD-220)
+if( DEFINED ECBUILD_POSITION_INDEPENDENT_CODE )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ${ECBUILD_POSITION_INDEPENDENT_CODE} )
+elseif( ${EC_OS_BITS} EQUAL 64 )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ON )
+endif()
+
+
+############################################################################################
+# check architecture
+
+if( ENABLE_OS_TYPES_TEST )
+
+  set( EC_SIZEOF_PTR ${CMAKE_SIZEOF_VOID_P} )
+  ecbuild_cache_var( EC_SIZEOF_PTR )
+  ecbuild_cache_check_type_size( char           EC_SIZEOF_CHAR        )
+  ecbuild_cache_check_type_size( short          EC_SIZEOF_SHORT       )
+  ecbuild_cache_check_type_size( int            EC_SIZEOF_INT         )
+  ecbuild_cache_check_type_size( long           EC_SIZEOF_LONG        )
+  ecbuild_cache_check_type_size( "long long"    EC_SIZEOF_LONG_LONG   )
+  ecbuild_cache_check_type_size( float          EC_SIZEOF_FLOAT       )
+  ecbuild_cache_check_type_size( double         EC_SIZEOF_DOUBLE      )
+  ecbuild_cache_check_type_size( "long double"  EC_SIZEOF_LONG_DOUBLE )
+  ecbuild_cache_check_type_size( size_t         EC_SIZEOF_SIZE_T      )
+  ecbuild_cache_check_type_size( ssize_t        EC_SIZEOF_SSIZE_T     )
+  ecbuild_cache_check_type_size( off_t          EC_SIZEOF_OFF_T       )
+
+  ecbuild_debug( "sizeof void*       [${EC_SIZEOF_PTR}]" )
+  ecbuild_debug( "sizeof char        [${EC_SIZEOF_CHAR}]" )
+  ecbuild_debug( "sizeof short       [${EC_SIZEOF_SHORT}]" )
+  ecbuild_debug( "sizeof int         [${EC_SIZEOF_INT}]" )
+  ecbuild_debug( "sizeof long        [${EC_SIZEOF_LONG}]" )
+  ecbuild_debug( "sizeof long long   [${EC_SIZEOF_LONG_LONG}]" )
+  ecbuild_debug( "sizeof float       [${EC_SIZEOF_FLOAT}]" )
+  ecbuild_debug( "sizeof double      [${EC_SIZEOF_DOUBLE}]" )
+  ecbuild_debug( "sizeof long double [${EC_SIZEOF_LONG_DOUBLE}]" )
+  ecbuild_debug( "sizeof size_t      [${EC_SIZEOF_SIZE_T}]" )
+  ecbuild_debug( "sizeof ssize_t     [${EC_SIZEOF_SSIZE_T}]" )
+  ecbuild_debug( "sizeof off_t       [${EC_SIZEOF_OFF_T}]" )
+
+endif()
+
+############################################################################################
+# check for large file support
+
+# ensure we use 64bit access to files even on 32bit os -- aka Large File Support
+# by making off_t 64bit and stat behave as stat64
+
+if( ENABLE_LARGE_FILE_SUPPORT )
+
+  ecbuild_cache_check_type_size( off_t EC_SIZEOF_OFF_T )
+
+  if( EC_SIZEOF_OFF_T LESS "8" )
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+      add_definitions( -D_FILE_OFFSET_BITS=64 )
+    endif()
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+      add_definitions( -D_LARGE_FILES=64 )
+    endif()
+
+    get_directory_property( __compile_defs COMPILE_DEFINITIONS )
+
+    if( __compile_defs )
+      foreach( def ${__compile_defs} )
+        list( APPEND CMAKE_REQUIRED_DEFINITIONS -D${def} )
+      endforeach()
+    endif()
+
+  endif()
+
+endif()
+
+############################################################################################
+# check endiness
+
+if( ENABLE_OS_ENDINESS_TEST )
+
+  if( NOT DEFINED EC_BIG_ENDIAN AND NOT DEFINED EC_LITTLE_ENDIAN )
+
+    test_big_endian( _BIG_ENDIAN )
+
+    if( _BIG_ENDIAN )
+        set( EC_BIG_ENDIAN    1 )
+        set( EC_LITTLE_ENDIAN 0 )
+    else()
+        set( EC_BIG_ENDIAN    0 )
+        set( EC_LITTLE_ENDIAN 1 )
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( EC_BIG_ENDIAN )
+  ecbuild_cache_var( EC_LITTLE_ENDIAN )
+
+  if( NOT DEFINED IEEE_BE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0x30,0x61,0xDE,0x80,0x93,0x67,0xCC,0xD9,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x05,0x83,0x48,0x22,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_BE )
+
+    if( "${IEEE_BE}" STREQUAL "" )
+      set( IEEE_BE 0 CACHE INTERNAL "Test IEEE_BE")
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( IEEE_BE )
+
+  if( EC_BIG_ENDIAN AND NOT IEEE_BE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Big-Endian but compiled code runs differently -- to ignore this pass -DIEEE_BE=0 to CMake/ecBuild")
+  endif()
+
+  if( NOT DEFINED IEEE_LE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0xD9,0xCC,0x67,0x93,0x80,0xDE,0x61,0x30,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x22,0x48,0x83,0x05,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_LE )
+
+    if( "${IEEE_LE}" STREQUAL "" )
+      set( IEEE_LE 0 CACHE INTERNAL "Test IEEE_LE")
+    endif()
+  endif()
+
+  ecbuild_cache_var( IEEE_LE )
+
+  if( EC_LITTLE_ENDIAN AND NOT IEEE_LE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Little-Endian but compiled code runs differently -- to ignore this pass -DIEEE_LE=0 to CMake/ecBuild")
+  endif()
+
+endif()
+
+############################################################################################
+# enable profiling via gprof
+
+if( ENABLE_PROFILING )
+  ecbuild_deprecate( "ENABLE_PROFILING is deprecated and ignored, use ENABLE_GPROF instead" )
+endif()
+
+if( ENABLE_GPROF )
+
+  # User defined profiling flag takes precedence
+  if( ECBUILD_GPROF_FLAG )
+
+    ecbuild_debug( "Enabling profiling with user defined flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p  Generate extra code to write profile information suitable for the analysis program
+  #     prof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # -pg Generate extra code to write profile information suitable for the analysis program
+  #     gprof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # --coverage
+  #      This option is used to compile and link code instrumented for coverage analysis.  The
+  #      option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov
+  #      (when linking).  See the documentation for those options for more details.
+  #
+  #      *   Compile the source files with -fprofile-arcs plus optimization and code generation
+  #          options.  For test coverage analysis, use the additional -ftest-coverage option.
+  #          You do not need to profile every source file in a program.
+  #
+  #      *   Link your object files with -lgcov or -fprofile-arcs (the latter implies the
+  #          former).
+  #
+  #      *   Run the program on a representative workload to generate the arc profile
+  #          information.  This may be repeated any number of times.  You can run concurrent
+  #          instances of your program, and provided that the file system supports locking, the
+  #          data files will be correctly updated.  Also "fork" calls are detected and correctly
+  #          handled (double counting will not happen).
+  #
+  #      *   For profile-directed optimizations, compile the source files again with the same
+  #          optimization and code generation options plus -fbranch-probabilities.
+  #
+  #      *   For test coverage analysis, use gcov to produce human readable information from the
+  #          .gcno and .gcda files.  Refer to the gcov documentation for further information.
+  #
+  #      With -fprofile-arcs, for each function of your program GCC creates a program flow
+  #      graph, then finds a spanning tree for the graph.  Only arcs that are not on the
+  #      spanning tree have to be instrumented: the compiler adds code to count the number of
+  #      times that these arcs are executed.  When an arc is the only exit or only entrance to a
+  #      block, the instrumentation code can be added to the block; otherwise, a new basic block
+  #      must be created to hold the instrumentation code.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+
+    set( ECBUILD_GPROF_FLAG "-pg --coverage" )
+    ecbuild_debug( "Enabling profiling with GNU flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p
+  #
+  #        Compiles and links for function profiling
+  #               with gprof(1).
+  #
+  #        Architecture  Restrictions:  Not  available  on  Intel(R)   64   architecture
+  #        targeting the
+  #               Intel(R)  Xeon  Phi(TM) coprocessor x100 product family (formerly code
+  #               name  Knights  Corner),  on  IA-32  architecture  targeting   Intel(R)
+  #               Graphics Technology, or on Intel(R) 64 architecture targeting Intel(R)
+  #               Graphics Technology
+  #
+  #        Arguments:
+  #
+  #        None
+  #
+  #        Default:
+  #
+  #        OFF               Files are compiled and linked without profiling.
+  #
+  #        Description:
+  #
+  #        This option compiles and links for function profiling with gprof(1).
+  #
+  #        When you specify this option, inlining is disabled. However, you can override
+  #        this  by  specifying  pragma forceinline, declspec forceinline (Windows* OS),
+  #        attribute always_inline (Linux* OS and OS X*), or a compiler option  such  as
+  #        [Q]inline-forceinline.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "Intel" )
+
+    set( ECBUILD_GPROF_FLAG "-p" )
+    ecbuild_debug( "Enabling profiling with Intel flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -Mprof[=option[,option,...]]
+  #        Set performance profiling options.  Use of these options will cause the resulting
+  #        executable to create a performance profile that can be viewed and analyzed with the
+  #        PGPROF performance profiler.  In the descriptions below, PGI-style profiling implies
+  #        compiler-generated source instrumentation.  MPICH-style profiling implies the use of
+  #        instrumented wrappers for MPI library routines.  The -Mprof options are:
+  #
+  #        ccff
+  #
+  #        dwarf     Generate limited DWARF symbol information sufficient for most performance
+  #                  profilers.
+  #
+  #        func      Perform PGI-style function level profiling.
+  #
+  #        hwcts     Generate a profile using event-based sampling of hardware counters via the
+  #                  PAPI interface (linux86-64 only, PAPI must be installed).
+  #
+  #        lines     Perform PGI-style line level profiling.
+  #
+  #        hpmpi     (PGI CDK only) Perform MPICH-style profiling for the HP Implies
+  #                  -Mmpi=hpmpi.
+  #
+  #        mpich1    (PGI CDK only) Perform MPICH-style profiling for MPICH-1.  Implies
+  #                  -Mmpi=mpich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mpich2    (PGI CDK only) Perform MPICH-style profiling for MPICH-2.  Implies
+  #                  -Mmpi=mpich2.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mvapich1  (PGI CDK only) Perform MPICH-style profiling for MVAPICH.  Implies
+  #                  -Mmpi=mvapich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag
+  #                  is no longer fully supported.
+  #
+  #        time      Generate a profile using time-based instruction-level statistical
+  #                  sampling. This is equivalent to -pg, except that the profile is saved in a
+  #                  file named pgprof.out instead of gmon.out.
+  #
+  #        On Linux systems that have OProfile installed, PGPROF supports collection of
+  #        performance data without recompilation. Use of -Mprof=dwarf is useful for this mode
+  #        of profiling.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "PGI" )
+
+    set( ECBUILD_GPROF_FLAG "-Mprof=dwarf,time" )
+    ecbuild_debug( "Enabling profiling with PGI flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # There is no equivalent to -pg for clang:
+  # http://lists.llvm.org/pipermail/cfe-dev/2010-September/011255.html
+  else()
+
+    ecbuild_warn( "Profiling enabled but ecbuild doesn't know how to enable for this particular compiler ${CMAKE_C_COMPILER_ID}")
+
+  endif()
+
+  set( CMAKE_EXE_LINKER_FLAGS    "${CMAKE_EXE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+
+  set( _trust_flags ${ECBUILD_TRUST_FLAGS} )
+  set( ECBUILD_TRUST_FLAGS ON )
+  ecbuild_add_c_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_cxx_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_fortran_flags( "${ECBUILD_GPROF_FLAG}" )
+  set( ECBUILD_TRUST_FLAGS ${_trust_flags} )
+  unset( _trust_flags )
+
+endif()
+
+############################################################################################
+# check operating system
+
+set( EC_OS_NAME "UNKNOWN" )
+
+### Unix's -- Proper operating systems
+
+if( UNIX )
+
+  ### APPLE ###
+
+  if( APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+    set( EC_OS_NAME "macosx" )
+  endif()
+
+  ### Linux ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" )
+
+    set( EC_OS_NAME "linux" )
+
+    # The following option allows enabling the new dtags linker option
+    # (when set to OFF). ONLY SET TO OFF IF YOU KNOW WHAT YOU ARE DOING AND
+    # NEVER WHEN BUILDING PRODUCTION SOFTWARE. YOU HAVE BEEN WARNED!
+    option( ECBUILD_DISABLE_NEW_DTAGS "Set the linker flag --disable-new-dtags" ON )
+    mark_as_advanced( ECBUILD_DISABLE_NEW_DTAGS )
+
+    if( ECBUILD_DISABLE_NEW_DTAGS )
+      # recent linkers default to --enable-new-dtags
+      # which then adds both RPATH and RUNPATH to executables
+      # thus invalidating RPATH setting, and making LD_LIBRARY_PATH take precedence
+      # to be sure, use tool 'readelf -a <exe> | grep PATH' to see what paths are built-in
+      # see:
+      #  * http://blog.qt.digia.com/blog/2011/10/28/rpath-and-runpath
+      #  * http://www.cmake.org/Wiki/CMake_RPATH_handling
+      #  * man ld
+      #  * http://blog.tremily.us/posts/rpath
+      #  * http://fwarmerdam.blogspot.co.uk/2010/12/rpath-runpath-and-ldlibrarypath.html
+      set(CMAKE_EXE_LINKER_FLAGS     "${CMAKE_EXE_LINKER_FLAGS}    -Wl,--disable-new-dtags")
+      set(CMAKE_SHARED_LINKER_FLAGS  "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-new-dtags")
+      set(CMAKE_MODULE_LINKER_FLAGS  "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--disable-new-dtags")
+    endif()
+
+  endif()
+
+  ### FreeBSD ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
+    set( EC_OS_NAME "freebsd" )
+  endif()
+
+  ### Solaris ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" )
+    set( EC_OS_NAME "solaris" )
+  endif()
+
+  ### AIX ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+
+    set( EC_OS_NAME "aix" )
+
+    set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -bbigtoc" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+      set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker" )
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCC )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_c_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCXX )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_cxx_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_c_flags("-qpic=large")
+#            ecbuild_add_c_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_c_flags("-qstrict")
+          ecbuild_add_c_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_c_flags("-qfullpath")
+          ecbuild_add_c_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_CXX_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_cxx_flags("-qpic=large")
+      ecbuild_add_cxx_flags("-bmaxdata:0x40000000")
+      ecbuild_add_cxx_flags("-qrtti")
+      ecbuild_add_cxx_flags("-qfuncsect")
+
+#           ecbuild_add_cxx_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_cxx_flags("-qstrict")
+          ecbuild_add_cxx_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_cxx_flags("-qfullpath")
+          ecbuild_add_cxx_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_fortran_flags("-qxflag=dealloc_cfptr")
+      ecbuild_add_fortran_flags("-qextname")
+      ecbuild_add_fortran_flags("-qdpc=e")
+      ecbuild_add_fortran_flags("-bmaxdata:0x40000000")
+      ecbuild_add_fortran_flags("-bloadmap:loadmap -bmap:loadmap")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_fortran_flags("-q32")
+      endif()
+    endif()
+
+  endif()
+
+endif()
+
+### Cygwin
+
+if( ${CMAKE_SYSTEM_NAME} MATCHES "CYGWIN" )
+
+  set( EC_OS_NAME "cygwin" )
+  ecbuild_warn( "Building on Cygwin should work but is untested" )
+
+endif()
+
+### final warning / error
+
+if( ${EC_OS_NAME} MATCHES "UNKNOWN" )
+
+  if( DISABLE_OS_CHECK )
+    ecbuild_warn( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                  " -- DISABLE_OS_CHECK is ON so proceeding at your own risk ..." )
+  else()
+    ecbuild_critical( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                      " -- refusing to continue. Disable this check with -DDISABLE_OS_CHECK=ON" )
+  endif()
+
+endif()
diff --git a/ecbuild/cmake/ecbuild_compiler_flags.cmake b/ecbuild/cmake/ecbuild_compiler_flags.cmake
new file mode 100644
index 0000000..139038b
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_compiler_flags.cmake
@@ -0,0 +1,217 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_compiler_flags
+# ======================
+#
+# Set compiler specific default compilation flags for a given language. ::
+#
+#   ecbuild_compiler_flags( <lang> )
+#
+# The procedure is as follows:
+#
+# 1.  ecBuild does **not** set ``CMAKE_<lang>_FLAGS`` i.e. the user can set
+#     these via ``-D`` or the CMake cache and these will be the "base" flags.
+#
+# 2.  ecBuild **overwrites** ``CMAKE_<lang>_FLAGS_<btype>`` in the CMake cache
+#     for all build types with compiler specific defaults for the currently
+#     loaded compiler i.e. any value set by the user via ``-D`` or the CMake
+#     cache **has no effect**.
+#
+# 3.  Any value the user provides via ``ECBUILD_<lang>_FLAGS`` or
+#     ``ECBUILD_<lang>_FLAGS_<btype>`` **overrides** the corresponding
+#     ``CMAKE_<lang>_FLAGS`` or ``CMAKE_<lang>_FLAGS_<btype>`` **without being
+#     written to the CMake cache**.
+#
+##############################################################################
+
+macro( ecbuild_compiler_flags _lang )
+
+  # Set compiler and language specific default flags - OVERWRITES variables in CMake cache
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): try include ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake ")
+    include( ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake OPTIONAL )
+  endif()
+
+  # Apply user or toolchain specified compilation flag overrides (NOT written to cache)
+
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    if( DEFINED ECBUILD_${_lang}_FLAGS_${_btype} )
+      ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS_${_btype} with ${ECBUILD_${_lang}_FLAGS_${_btype}}")
+      set( CMAKE_${_lang}_FLAGS_${_btype} ${ECBUILD_${_lang}_FLAGS_${_btype}} )
+    endif()
+    mark_as_advanced( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+  if( DEFINED ECBUILD_${_lang}_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS with ${ECBUILD_${_lang}_FLAGS}")
+    set( CMAKE_${_lang}_FLAGS "${ECBUILD_${_lang}_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_FLAGS )
+
+  if( DEFINED ECBUILD_${_lang}_LINK_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_LINK_FLAGS with ${ECBUILD_${_lang}_LINK_FLAGS}")
+    set( CMAKE_${_lang}_LINK_FLAGS "${ECBUILD_${_lang}_LINK_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_LINK_FLAGS )
+
+  ecbuild_debug_var( CMAKE_${_lang}_FLAGS )
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    ecbuild_debug_var( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# Using custom compilation flags
+# ==============================
+#
+# If compilation flags need to be controlled on a per source file basis,
+# ecBuild supports defining custom rules in a CMake or JSON file.
+#
+# When using this approach, *default compilation flags are NOT loaded*!
+#
+# Overriding compilation flags on a per source file basis using CMake rules
+# -------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_COMPILE_FLAGS`` to the *full path* of a CMake file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_COMPILE_FLAGS``
+# takes precendence and ``ECBUILD_COMPILE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# Flags can be overridden in 3 different ways:
+#
+# 1.  By defining project specific flags for a language and (optionally)
+#     build type e.g. ::
+#
+#       set(<PNAME>_Fortran_FLAGS "...") # common flags for all build types
+#       set(<PNAME>_Fortran_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 2.  By defining source file specific flags which are *combined* with the
+#     project and target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES COMPILE_FLAGS "..."  # common flags for all build types
+#                    COMPILE_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 3.  By defining source file specific flags which *override* the project and
+#     target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES OVERRIDE_COMPILE_FLAGS "..."
+#                    OVERRIDE_COMPILE_FLAGS_DEBUG "...")
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+# Overriding compilation flags on a per source file basis using JSON rules
+# ------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_SOURCE_FLAGS`` to the *full path* of a JSON file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_SOURCE_FLAGS``
+# takes precendence and ``ECBUILD_SOURCE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# The JSON file lists shell glob patterns and the rule to apply to each source
+# file matching the pattern, defined as an array ``[op, flag1, ...]``
+# containing an operator followed by one or more flags. Valid operators are:
+#
+# :+: Add the flags to the default compilation flags for matching files
+# :=: Set the flags for matching files, disregarding default compilation flags
+# :/: Remove the flags from the default compilation flags for matching files
+#
+# Rules can be nested to e.g. only apply to a subdirectory by setting the rule
+# to a dictionary, which will only apply to source files matching its pattern.
+#
+# An example JSON file demonstrating different rule types is given below: ::
+#
+#   {
+#     "*"       : [ "+", "-g0" ],
+#     "*.cxx"   : [ "+", "-cxx11" ],
+#     "*.f90"   : [ "+", "-pipe" ],
+#     "foo.c"   : [ "+", "-O0" ],
+#     "foo.cc"  : [ "+", "-O2", "-pipe" ],
+#     "bar/*": {
+#       "*.f90" : [ "=", "-O1" ]
+#     },
+#     "baz/*": {
+#       "*.f90" : [ "/", "-pipe" ],
+#       "*.f90" : [ "/", "-O2" ],
+#       "*.f90" : [ "+", "-O3" ]
+#     }
+#   }
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+##############################################################################
+
+# Custom (project specific) compilation flags enabled?
+foreach( _flags COMPILE SOURCE )
+  if( ${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS )
+    if ( ECBUILD_${_flags}_FLAGS )
+      ecbuild_debug( "Override ECBUILD_${_flags}_FLAGS (${ECBUILD_${_flags}_FLAGS}) with ${PROJECT_NAME} specific flags (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    else()
+      ecbuild_debug( "Use ${PROJECT_NAME} specific ECBUILD_${_flags}_FLAGS (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    endif()
+    set( ECBUILD_${_flags}_FLAGS ${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS} )
+  endif()
+  # Ensure ECBUILD_${_flags}_FLAGS is a valid file path
+  if( DEFINED ECBUILD_${_flags}_FLAGS AND NOT EXISTS ${ECBUILD_${_flags}_FLAGS} )
+    ecbuild_warn( "ECBUILD_${_flags}_FLAGS points to non-existent file ${ECBUILD_${_flags}_FLAGS} and will be ignored" )
+    unset( ECBUILD_${_flags}_FLAGS )
+    unset( ECBUILD_${_flags}_FLAGS CACHE )
+  endif()
+endforeach()
+if( ECBUILD_COMPILE_FLAGS )
+  include( "${ECBUILD_COMPILE_FLAGS}" )
+endif()
+
+foreach( _lang C CXX Fortran )
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+
+    # Clear default compilation flags potentially inherited from parent scope
+    # when using custom compilation flags
+    if( ECBUILD_SOURCE_FLAGS OR ECBUILD_COMPILE_FLAGS )
+      set(CMAKE_${_lang}_FLAGS "")
+      foreach(_btype ALL RELEASE RELWITHDEBINFO PRODUCTION BIT DEBUG)
+        set(CMAKE_${_lang}_FLAGS_${_btype} "")
+      endforeach()
+    # Load default compilation flags only if custom compilation flags not enabled
+    else()
+      ecbuild_compiler_flags( ${_lang} )
+    endif()
+
+  endif()
+endforeach()
+
+# Apply user or toolchain specified linker flag overrides per object type (NOT written to cache)
+foreach( _obj EXE SHARED MODULE )
+  if( ECBUILD_${_obj}_LINKER_FLAGS )
+    set( CMAKE_${_obj}_LINKER_FLAGS ${ECBUILD_${_obj}_LINKER_FLAGS} )
+  endif()
+endforeach()
+
+foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+
+  foreach( _obj EXE SHARED MODULE )
+    if( ECBUILD_${_obj}_LINKER_FLAGS_${_btype} )
+      set( CMAKE_${_obj}_LINKER_FLAGS_${_btype} ${ECBUILD_${_obj}_LINKER_FLAGS_${_btype}} )
+    endif()
+  endforeach()
+
+endforeach()
diff --git a/ecbuild/cmake/ecbuild_config.h.in b/ecbuild/cmake/ecbuild_config.h.in
new file mode 100644
index 0000000..a7d7d75
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_config.h.in
@@ -0,0 +1,201 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef @PROJECT_NAME at _ecbuild_config_h
+#define @PROJECT_NAME at _ecbuild_config_h
+
+/* ecbuild info */
+
+#ifndef ECBUILD_VERSION_STR
+#define ECBUILD_VERSION_STR "@ECBUILD_VERSION_STR@"
+#endif
+#ifndef ECBUILD_MACROS_DIR
+#define ECBUILD_MACROS_DIR  "@ECBUILD_MACROS_DIR@"
+#endif
+
+/* cpu arch info */
+
+#cmakedefine EC_BIG_ENDIAN       @EC_BIG_ENDIAN@
+#cmakedefine EC_LITTLE_ENDIAN    @EC_LITTLE_ENDIAN@
+
+/* compiler support */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+/* os capability checks */
+
+/* --- symbols --- */
+
+#cmakedefine EC_HAVE_FSEEK
+#cmakedefine EC_HAVE_FSEEKO
+#cmakedefine EC_HAVE_FTELLO
+#cmakedefine EC_HAVE_LSEEK
+#cmakedefine EC_HAVE_FTRUNCATE
+#cmakedefine EC_HAVE_OPEN
+#cmakedefine EC_HAVE_FOPEN
+#cmakedefine EC_HAVE_FMEMOPEN
+#cmakedefine EC_HAVE_FUNOPEN
+#cmakedefine EC_HAVE_FLOCK
+#cmakedefine EC_HAVE_MMAP
+#cmakedefine EC_HAVE_FOPENCOOKIE
+
+#cmakedefine EC_HAVE_POSIX_MEMALIGN
+
+#cmakedefine EC_HAVE_F_GETLK
+#cmakedefine EC_HAVE_F_SETLKW
+#cmakedefine EC_HAVE_F_SETLK
+
+#cmakedefine EC_HAVE_F_GETLK64
+#cmakedefine EC_HAVE_F_SETLKW64
+#cmakedefine EC_HAVE_F_SETLK64
+
+#cmakedefine EC_HAVE_MAP_ANONYMOUS
+#cmakedefine EC_HAVE_MAP_ANON
+
+/* --- include files --- */
+
+#cmakedefine EC_HAVE_ASSERT_H
+#cmakedefine EC_HAVE_STDLIB_H
+#cmakedefine EC_HAVE_UNISTD_H
+#cmakedefine EC_HAVE_STRING_H
+#cmakedefine EC_HAVE_STRINGS_H
+#cmakedefine EC_HAVE_SYS_STAT_H
+#cmakedefine EC_HAVE_SYS_TIME_H
+#cmakedefine EC_HAVE_SYS_TYPES_H
+
+#cmakedefine EC_HAVE_MALLOC_H
+#cmakedefine EC_HAVE_SYS_MALLOC_H
+
+#cmakedefine EC_HAVE_SYS_PARAM_H
+#cmakedefine EC_HAVE_SYS_MOUNT_H
+#cmakedefine EC_HAVE_SYS_VFS_H
+
+/* --- capabilities --- */
+
+#cmakedefine EC_HAVE_OFFT
+#cmakedefine EC_HAVE_OFF64T
+
+#cmakedefine EC_HAVE_STRUCT_STAT
+#cmakedefine EC_HAVE_STRUCT_STAT64
+#cmakedefine EC_HAVE_STAT
+#cmakedefine EC_HAVE_STAT64
+#cmakedefine EC_HAVE_FSTAT
+#cmakedefine EC_HAVE_FSTAT64
+
+#cmakedefine EC_HAVE_FSEEKO64
+#cmakedefine EC_HAVE_FTELLO64
+#cmakedefine EC_HAVE_LSEEK64
+#cmakedefine EC_HAVE_OPEN64
+#cmakedefine EC_HAVE_FOPEN64
+#cmakedefine EC_HAVE_FTRUNCATE64
+#cmakedefine EC_HAVE_FLOCK64
+#cmakedefine EC_HAVE_MMAP64
+
+#cmakedefine EC_HAVE_STRUCT_STATVFS
+#cmakedefine EC_HAVE_STRUCT_STATVFS64
+#cmakedefine EC_HAVE_STATVFS
+#cmakedefine EC_HAVE_STATVFS64
+
+#cmakedefine EC_HAVE_FSYNC
+#cmakedefine EC_HAVE_FDATASYNC
+#cmakedefine EC_HAVE_DIRFD
+#cmakedefine EC_HAVE_SYSPROC
+#cmakedefine EC_HAVE_SYSPROCFS
+
+#cmakedefine EC_HAVE_EXECINFO_BACKTRACE
+
+/* --- asynchronous IO support --- */
+
+#cmakedefine EC_HAVE_AIOCB
+#cmakedefine EC_HAVE_AIO64CB
+
+/* --- reentrant funtions support --- */
+
+#cmakedefine EC_HAVE_GMTIME_R
+#cmakedefine EC_HAVE_GETPWUID_R
+#cmakedefine EC_HAVE_GETPWNAM_R
+#cmakedefine EC_HAVE_READDIR_R
+#cmakedefine EC_HAVE_GETHOSTBYNAME_R
+
+/* --- compiler __attribute__ support --- */
+
+#cmakedefine EC_HAVE_ATTRIBUTE_CONSTRUCTOR
+#cmakedefine EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+#cmakedefine EC_HAVE_PROCFS
+
+
+/* --- dl library support --- */
+
+#cmakedefine EC_HAVE_DLFCN_H
+#cmakedefine EC_HAVE_DLADDR
+
+/* --- c compiler support --- */
+
+#cmakedefine EC_HAVE_C_INLINE
+
+/* --- c++ compiler support --- */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+#cmakedefine EC_HAVE_CXXABI_H
+#cmakedefine EC_HAVE_CXX_BOOL
+
+#cmakedefine EC_HAVE_CXX_SSTREAM
+
+/* config info */
+
+#define @PNAME at _OS_NAME          "@CMAKE_SYSTEM@"
+#define @PNAME at _OS_BITS          @EC_OS_BITS@
+#define @PNAME at _OS_BITS_STR      "@EC_OS_BITS@"
+#define @PNAME at _OS_STR           "@EC_OS_NAME at .@EC_OS_BITS@"
+#define @PNAME at _OS_VERSION       "@CMAKE_SYSTEM_VERSION@"
+#define @PNAME at _SYS_PROCESSOR    "@CMAKE_SYSTEM_PROCESSOR@"
+
+#define @PNAME at _BUILD_TIMESTAMP  "@EC_BUILD_TIMESTAMP@"
+#define @PNAME at _BUILD_TYPE       "@CMAKE_BUILD_TYPE@"
+
+#define @PNAME at _C_COMPILER_ID      "@CMAKE_C_COMPILER_ID@"
+#define @PNAME at _C_COMPILER_VERSION "@CMAKE_C_COMPILER_VERSION@"
+
+#define @PNAME at _CXX_COMPILER_ID      "@CMAKE_CXX_COMPILER_ID@"
+#define @PNAME at _CXX_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@"
+
+#define @PNAME at _C_COMPILER       "@CMAKE_C_COMPILER@"
+#define @PNAME at _C_FLAGS          "@EC_C_FLAGS@"
+
+#define @PNAME at _CXX_COMPILER     "@CMAKE_CXX_COMPILER@"
+#define @PNAME at _CXX_FLAGS        "@EC_CXX_FLAGS@"
+
+/* Needed for finding per package config files */
+
+#define @PNAME at _INSTALL_DIR       "@CMAKE_INSTALL_PREFIX@"
+#define @PNAME at _INSTALL_BIN_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_BIN_DIR@"
+#define @PNAME at _INSTALL_LIB_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB_DIR@"
+#define @PNAME at _INSTALL_DATA_DIR  "@CMAKE_INSTALL_PREFIX@/@INSTALL_DATA_DIR@"
+
+#define @PNAME at _DEVELOPER_SRC_DIR "@CMAKE_SOURCE_DIR@"
+#define @PNAME at _DEVELOPER_BIN_DIR "@CMAKE_BINARY_DIR@"
+
+#cmakedefine EC_HAVE_FORTRAN
+
+#ifdef EC_HAVE_FORTRAN
+
+#define @PNAME at _Fortran_COMPILER_ID      "@CMAKE_Fortran_COMPILER_ID@"
+#define @PNAME at _Fortran_COMPILER_VERSION "@CMAKE_Fortran_COMPILER_VERSION@"
+
+#define @PNAME at _Fortran_COMPILER "@CMAKE_Fortran_COMPILER@"
+#define @PNAME at _Fortran_FLAGS    "@EC_Fortran_FLAGS@"
+
+#endif
+
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_LINKED
+
+#endif /* @PROJECT_NAME at _ecbuild_config_h */
diff --git a/ecbuild/cmake/ecbuild_declare_project.cmake b/ecbuild/cmake/ecbuild_declare_project.cmake
new file mode 100644
index 0000000..b3f29e7
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_declare_project.cmake
@@ -0,0 +1,198 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_declare_project
+# =======================
+#
+# Initialise an ecBuild project. A CMake project must have previously been
+# declared with ``project( <name> ... )``. ::
+#
+#   ecbuild_declare_project()
+#
+# Sets the following CMake variables
+# (where ``PNAME`` is the capitalised project name):
+#
+# :<PNAME>_GIT_SHA1:       Git revision (if project is a Git repo)
+# :<PNAME>_GIT_SHA1_SHORT: short Git revision (if project is a Git repo)
+# :<PNAME>_VERSION:        version in format ``MAJOR.MINOR.PATCH``
+# :<PNAME>_VERSION_STR:    version as given in ``VERSION.cmake`` or 0.0.0
+# :<PNAME>_MAJOR_VERSION:  major version number
+# :<PNAME>_MINOR_VERSION:  minor version number
+# :<PNAME>_PATCH_VERSION:  patch version number
+# :INSTALL_BIN_DIR:        relative install directory for executables
+# :INSTALL_LIB_DIR:        relative install directory for libraries
+# :INSTALL_INCLUDE_DIR:    relative install directory for include files
+# :INSTALL_DATA_DIR:       relative install directory for data
+# :INSTALL_CMAKE_DIR:      relative install directory for CMake files
+#
+# Customising install locations
+# -----------------------------
+#
+# The relative installation directories of components can be customised by
+# setting the following CMake variables on the command line or in cache:
+#
+# :INSTALL_BIN_DIR:        directory for installing executables
+#                          (default: ``bin``)
+# :INSTALL_LIB_DIR:        directory for installing libraries
+#                          (default: ``lib``)
+# :INSTALL_INCLUDE_DIR:    directory for installing include files
+#                          (default: ``include``)
+# :INSTALL_DATA_DIR:       directory for installing data
+#                          (default: ``share/<project_name>``)
+# :INSTALL_CMAKE_DIR:      directory for installing CMake files
+#                          (default: ``share/<project_name>/cmake``)
+#
+# Using *relative* paths is recommended, which are interpreted relative to the
+# ``CMAKE_INSTALL_PREFIX``. Using absolute paths makes the build
+# non-relocatable and may break the generation of relocatable binary packages.
+#
+##############################################################################
+
+macro( ecbuild_declare_project )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  # reset the lists of targets (executables, libs, tests & resources)
+
+  set( ${PROJECT_NAME}_ALL_EXES "" CACHE INTERNAL "" )
+  set( ${PROJECT_NAME}_ALL_LIBS "" CACHE INTERNAL "" )
+
+  # if git project get its HEAD SHA1
+  # leave it here so we may use ${PNAME}_GIT_SHA1 on the version file
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/.git )
+    get_git_head_revision( GIT_REFSPEC ${PNAME}_GIT_SHA1 )
+    if( ${PNAME}_GIT_SHA1 )
+      string( SUBSTRING "${${PNAME}_GIT_SHA1}" 0 7 ${PNAME}_GIT_SHA1_SHORT )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1 )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1_SHORT )
+    else()
+      ecbuild_debug( "Could not get git-sha1 for project ${PNAME}")
+    endif()
+  endif()
+
+  # read and parse project version file
+  if( EXISTS ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+    include( ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+  else()
+    set( ${PROJECT_NAME}_VERSION_STR "0.0.0" )
+  endif()
+
+  string( REPLACE "." " " _version_list ${${PROJECT_NAME}_VERSION_STR} ) # dots to spaces
+
+  separate_arguments( _version_list )
+
+  list( GET _version_list 0 ${PNAME}_MAJOR_VERSION )
+  list( GET _version_list 1 ${PNAME}_MINOR_VERSION )
+  list( GET _version_list 2 ${PNAME}_PATCH_VERSION )
+
+  # cleanup patch version of any extra qualifiers ( -dev -rc1 ... )
+
+  string( REGEX REPLACE "^([0-9]+).*" "\\1" ${PNAME}_PATCH_VERSION "${${PNAME}_PATCH_VERSION}" )
+
+  set( ${PNAME}_VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}.${${PNAME}_PATCH_VERSION}"
+       CACHE INTERNAL "package ${PNAME} version" )
+
+  set( ${PNAME}_VERSION_STR "${${PROJECT_NAME}_VERSION_STR}"
+       CACHE INTERNAL "package ${PNAME} version string" ) # ignore caps
+
+  #    ecbuild_debug_var( ${PNAME}_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_VERSION_STR )
+  #    ecbuild_debug_var( ${PNAME}_MAJOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_MINOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_PATCH_VERSION )
+
+  # install dirs for this project
+
+  # Use defaults unless values are already present in cache
+  if( NOT INSTALL_BIN_DIR )
+    set( INSTALL_BIN_DIR bin )
+  endif()
+  if( NOT INSTALL_LIB_DIR )
+    set( INSTALL_LIB_DIR lib )
+  endif()
+  if( NOT INSTALL_INCLUDE_DIR )
+    set( INSTALL_INCLUDE_DIR include )
+  endif()
+  # INSTALL_DATA_DIR is package specific and needs to be reset for subpackages
+  # in a bundle. Users *cannot* override this directory (ECBUILD-315)
+  set( INSTALL_DATA_DIR share/${PROJECT_NAME} )
+  # share/${PROJECT_NAME}/cmake is a convention - it makes no sense to override it
+  set( INSTALL_CMAKE_DIR share/${PROJECT_NAME}/cmake )
+
+  mark_as_advanced( INSTALL_BIN_DIR )
+  mark_as_advanced( INSTALL_LIB_DIR )
+  mark_as_advanced( INSTALL_INCLUDE_DIR )
+  mark_as_advanced( INSTALL_DATA_DIR )
+  mark_as_advanced( INSTALL_CMAKE_DIR )
+
+  # warnings for non-relocatable projects
+
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+    if( IS_ABSOLUTE ${INSTALL_${p}_DIR} )
+      ecbuild_warn( "Defining INSTALL_${p}_DIR as absolute path '${INSTALL_${p}_DIR}' makes this build non-relocatable, possibly breaking the installation of RPMS and DEB packages" )
+    endif()
+  endforeach()
+
+  # make relative paths absolute ( needed later on ) and cache them ...
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+
+    set( var INSTALL_${p}_DIR )
+
+    if( NOT IS_ABSOLUTE "${${var}}" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${CMAKE_INSTALL_PREFIX}/${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    else()
+      ecbuild_warn( "Setting an absolute path for ${VAR} in project ${PNAME}, breakes generation of relocatable binary packages (rpm,deb,...)" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    endif()
+
+    #        ecbuild_debug_var( ${PNAME}_FULL_INSTALL_${p}_DIR )
+
+  endforeach()
+
+  # correctly set CMAKE_INSTALL_RPATH
+
+  if( ENABLE_RPATHS )
+
+    if( ENABLE_RELATIVE_RPATHS )
+
+      file( RELATIVE_PATH relative_rpath ${${PNAME}_FULL_INSTALL_BIN_DIR} ${${PNAME}_FULL_INSTALL_LIB_DIR} )
+      # ecbuild_debug_var( relative_rpath )
+
+      ecbuild_append_to_rpath( ${relative_rpath} )
+
+    else() # make rpaths absolute
+
+      if( IS_ABSOLUTE ${INSTALL_LIB_DIR} )
+        ecbuild_append_to_rpath( "${INSTALL_LIB_DIR}" )
+      else()
+        ecbuild_append_to_rpath( "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" )
+      endif()
+
+    endif()
+
+  endif()
+
+  # ecbuild_debug_var( CMAKE_INSTALL_RPATH )
+
+  # print project header
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( ${PNAME}_GIT_SHA1_SHORT )
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR}) [${${PNAME}_GIT_SHA1_SHORT}]" )
+  else()
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR})" )
+  endif()
+
+endmacro( ecbuild_declare_project )
diff --git a/ecbuild/cmake/ecbuild_define_build_types.cmake b/ecbuild/cmake/ecbuild_define_build_types.cmake
new file mode 100644
index 0000000..7cfc591
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_build_types.cmake
@@ -0,0 +1,59 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# define default build type
+
+set( _BUILD_TYPE_MSG "Build type options are: [ None | Debug | Bit | Production | Release | RelWithDebInfo ]" )
+
+if( NOT ECBUILD_DEFAULT_BUILD_TYPE )
+	set( ECBUILD_DEFAULT_BUILD_TYPE "RelWithDebInfo" )
+endif()
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE ${ECBUILD_DEFAULT_BUILD_TYPE} CACHE STRING  ${_BUILD_TYPE_MSG}  FORCE )
+endif()
+
+# capitalize the build type for easy use with conditionals
+string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+# correct capitatlization of the build type
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "NONE" )
+  set(CMAKE_BUILD_TYPE None CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "DEBUG" )
+  set(CMAKE_BUILD_TYPE Debug CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "BIT" )
+  set(CMAKE_BUILD_TYPE Bit CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "PRODUCTION" )
+  set(CMAKE_BUILD_TYPE Production CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELEASE" )
+  set(CMAKE_BUILD_TYPE Release CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELWITHDEBINFO" )
+  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+# fail if build type is not one of the defined ones
+if( NOT CMAKE_BUILD_TYPE MATCHES "None"  AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Debug" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Bit" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Production" AND
+    NOT CMAKE_BUILD_TYPE MATCHES "Release"  AND
+    NOT CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" )
+    ecbuild_critical( "CMAKE_BUILD_TYPE is not recognized. ${_BUILD_TYPE_MSG}" )
+endif()
diff --git a/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake b/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake
new file mode 100644
index 0000000..799eb7d
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake
@@ -0,0 +1,29 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+############################################################################################
+# define libs and execs targets
+
+macro( ecbuild_define_libs_and_execs_targets )
+
+  add_custom_target( libs )
+
+  if( EC_ALL_LIBS )
+    add_dependencies( libs ${EC_ALL_LIBS} )
+  endif()
+
+  add_custom_target( execs )
+
+  if( EC_ALL_EXECS )
+    add_dependencies( execs ${EC_ALL_EXES} )
+  endif()
+
+endmacro(ecbuild_define_libs_and_execs_targets)
diff --git a/ecbuild/cmake/ecbuild_define_links_target.cmake b/ecbuild/cmake/ecbuild_define_links_target.cmake
new file mode 100644
index 0000000..745288c
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_links_target.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# macro for adding a link to library on a development system
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+macro( ecbuild_link_exe TARGET  FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/bin
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/bin/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/bin/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/bin/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_EXES ${EC_ALL_EXES} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_exe  )
+
+###############################################################################
+# macro for adding a link to library on a development system
+
+macro( ecbuild_link_lib  TARGET FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/lib
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/lib/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/lib/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/lib/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_LIBS ${EC_ALL_LIBS} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_lib  )
+
+############################################################################################
+# define make links target
+
+macro( ecbuild_define_links_target )
+
+  if( DEFINED EC_LINK_DIR )
+
+    foreach( lib ${EC_ALL_LIBS} )
+      list( APPEND ec_link_libs ${lib}_link )
+    endforeach()
+    foreach( exe ${EC_ALL_EXES} )
+      list( APPEND ec_link_exes ${exe}_link )
+    endforeach()
+
+    add_custom_target( links DEPENDS ${ec_link_libs} ${ec_link_exes} )
+
+     #   ecbuild_debug_var( EC_ALL_EXES )
+     #   ecbuild_debug_var( ec_link_exes )
+
+     #  ecbuild_debug_var( EC_ALL_LIBS )
+     #   ecbuild_debug_var( ec_link_libs )
+
+  endif()
+
+endmacro(ecbuild_define_links_target)
diff --git a/ecbuild/cmake/ecbuild_define_options.cmake b/ecbuild/cmake/ecbuild_define_options.cmake
new file mode 100644
index 0000000..fc6376e
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_options.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# general options
+
+option( BUILD_SHARED_LIBS       "build shared libraries when possible"            ON  )
+
+option( ENABLE_RPATHS           "when installing insert RPATHS into binaries"     ON  )
+option( ENABLE_RELATIVE_RPATHS  "try to use relative RPATHS, including build dir" ON  )
+option( ENABLE_WARNINGS         "enable compiler warnings"                        OFF )
+
+option( ENABLE_LARGE_FILE_SUPPORT "build with large file support"   ON  )
+
+option( ENABLE_PROFILING        "build with profiling support" OFF )
+
+mark_as_advanced( ENABLE_LARGE_FILE_SUPPORT )
+
+option( ENABLE_OS_TESTS          "Run all OS tests" ON )
+
+mark_as_advanced( ENABLE_OS_TESTS )
+
+option( ENABLE_FORTRAN_C_INTERFACE "Enable Fortran/C Interface" OFF )
+mark_as_advanced( ENABLE_FORTRAN_C_INTERFACE )
+
+option( DEVELOPER_MODE           "activates developer mode"               OFF )
+option( CHECK_UNUSED_FILES       "check for unused project files (slow)"  OFF )
+
+mark_as_advanced( DEVELOPER_MODE  )
+mark_as_advanced( CHECK_UNUSED_FILES  )
+
+include( CMakeDependentOption ) # make options depend on one another
+
+cmake_dependent_option( ENABLE_OS_TYPES_TEST     "Run sizeof tests on C types" ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_ENDINESS_TEST  "Run OS endiness tests"       ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_FUNCTIONS_TEST "Run OS functions tests"      ON "ENABLE_OS_TESTS" OFF)
+
+mark_as_advanced( ENABLE_OS_TYPES_TEST ENABLE_OS_ENDINESS_TEST ENABLE_OS_FUNCTIONS_TEST  )
+
+option( ECBUILD_USE_INCLUDE_DIRECTORIES "Forces to use global include_directories() instead of target specific. Adverse effect on PkgConfig generation." OFF )
+
+mark_as_advanced( ECBUILD_USE_INCLUDE_DIRECTORIES )
+
+set( CMAKE_NO_SYSTEM_FROM_IMPORTED ON )
+
+# hide some CMake options from CMake UI
+
+mark_as_advanced( CMAKE_OSX_ARCHITECTURES CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OSX_SYSROOT )
\ No newline at end of file
diff --git a/ecbuild/cmake/ecbuild_define_paths.cmake b/ecbuild/cmake/ecbuild_define_paths.cmake
new file mode 100644
index 0000000..16d7b94
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_paths.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# define project paths
+
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+#######################################################################################################
+
+# setup library building rpaths (both in build dir and then when installed)
+
+# add the automatic parts to RPATH which point to dirs outside build tree
+set( CMAKE_INSTALL_RPATH_USE_LINK_PATH   TRUE  )
+
+# use RPATHs for the build tree
+set( CMAKE_SKIP_BUILD_RPATH              FALSE  )
+
+# If INSTALL_LIB_DIR is set to anything other than lib, the relative install
+# RPATH is wrong in the build tree
+if( ENABLE_RELATIVE_RPATHS )
+  ecbuild_debug( "Relative RPATHS are enabled" )
+  if( INSTALL_LIB_DIR STREQUAL "lib" OR (NOT INSTALL_LIB_DIR) )
+    # when building, use the install RPATH immediately (we don't want to relink)
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      TRUE  )
+    ecbuild_debug( "Building with install RPATH" )
+  else()
+    # when building, don't use the install RPATH yet, but later on when installing
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      FALSE  )
+    ecbuild_debug( "Not building with install RPATH, need to relink when installing" )
+  endif()
+endif()
+
+# Always include srcdir and builddir in include path
+# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+# in about every subdir
+
+set( CMAKE_INCLUDE_CURRENT_DIR OFF )
+
+# put the include dirs which are in the source or build tree
+# before all other include dirs, so the headers in the sources
+# are prefered over the already installed ones (since cmake 2.4.1)
+
+set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
diff --git a/ecbuild/cmake/ecbuild_define_uninstall.cmake b/ecbuild/cmake/ecbuild_define_uninstall.cmake
new file mode 100644
index 0000000..cc6efa9
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_define_uninstall.cmake
@@ -0,0 +1,7 @@
+### adds uninstall target ###############
+
+configure_file(
+  "${CMAKE_CURRENT_LIST_DIR}/ecbuild_uninstall.cmake.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake" IMMEDIATE @ONLY)
+
+add_custom_target( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake")
diff --git a/ecbuild/cmake/ecbuild_dont_pack.cmake b/ecbuild/cmake/ecbuild_dont_pack.cmake
new file mode 100644
index 0000000..9f9f4a4
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_dont_pack.cmake
@@ -0,0 +1,82 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_dont_pack
+# =================
+#
+# Specify files and directories to exclude from packaging. ::
+#
+#   ecbuild_dont_pack( [ FILES <file1> [ <file2> ... ] ]
+#                      [ DIRS <dir1> [ <dir2> ... ] ]
+#                      [ REGEX <regex> ] )
+#
+# Options
+# -------
+#
+# FILES : optional, one of FILES, DIRS, REGEX required
+#   list of files to exclude from packaging
+#
+# DIRS : optional, one of FILES, DIRS, REGEX required
+#   list of directories to exclude from packaging
+#
+# REGEX : optional, one of FILES, DIRS, REGEX required
+#   regular expression to match files / directories to exclude from packaging
+#
+##############################################################################
+
+macro( ecbuild_dont_pack )
+
+    set( options )
+    set( single_value_args REGEX )
+    set( multi_value_args  FILES DIRS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_dont_pack(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT DEFINED _PAR_REGEX AND NOT  DEFINED _PAR_FILES AND NOT  DEFINED _PAR_DIRS )
+      ecbuild_critical("Call to ecbuild_dont_pack does not speficify any list to avoid packing.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_REGEX )
+        file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_PAR_REGEX} )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DIRS )
+        foreach( dir ${_PAR_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_FILES )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_FILES} )
+    endif()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_download_resource.cmake b/ecbuild/cmake/ecbuild_download_resource.cmake
new file mode 100644
index 0000000..e1e8eff
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_download_resource.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_download_resource
+# =========================
+#
+# Download a file from a given URL and save to FILE at configure time. ::
+#
+#   ecbuild_download_resource( FILE URL )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+##############################################################################
+
+function( ecbuild_download_resource _p_OUT _p_URL )
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  if( NOT EXISTS ${_p_OUT} )
+
+    find_program( CURL_PROGRAM curl )
+    mark_as_advanced(CURL_PROGRAM)
+    if( CURL_PROGRAM )
+
+      execute_process( COMMAND ${CURL_PROGRAM} --silent --show-error --fail
+                               --retry ${ECBUILD_DOWNLOAD_RETRIES}
+                               --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+                               --output ${_p_OUT} ${_p_URL}
+                       WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+    else()
+
+      find_program( WGET_PROGRAM wget )
+
+      if( WGET_PROGRAM )
+
+        # wget takes the total number of tries, curl the number or retries
+        math( EXPR ECBUILD_DOWNLOAD_RETRIES ${ECBUILD_DOWNLOAD_RETRIES} + 1 )
+
+        execute_process( COMMAND ${WGET_PROGRAM} -nv -O ${_p_OUT}
+                                 -t ${ECBUILD_DOWNLOAD_RETRIES}
+                                 -T ${ECBUILD_DOWNLOAD_TIMEOUT} ${_p_URL}
+                         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+      else()
+        ecbuild_critical("Could not find curl or wget. Error downloading ${_p_URL}")
+      endif()
+
+    endif()
+
+    if(CMD_RESULT)
+      ecbuild_critical("Error downloading ${_p_URL}")
+    endif()
+
+  endif()
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_echo_targets.cmake b/ecbuild/cmake/ecbuild_echo_targets.cmake
new file mode 100644
index 0000000..61250ee
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_echo_targets.cmake
@@ -0,0 +1,233 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target_property
+# ============================
+#
+# Output a given property of a given target. ::
+#
+#   ecbuild_echo_target_property( <target> <property> )
+#
+##############################################################################
+
+function(ecbuild_echo_target_property tgt prop)
+
+  cmake_policy(PUSH)
+
+  if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD)
+  endif()
+
+  # v for value, d for defined, s for set
+  get_property(v TARGET ${tgt} PROPERTY ${prop})
+  get_property(d TARGET ${tgt} PROPERTY ${prop} DEFINED)
+  get_property(s TARGET ${tgt} PROPERTY ${prop} SET)
+
+  # only produce output for values that are set
+  if(s)
+    ecbuild_debug("tgt='${tgt}' prop='${prop}'")
+    ecbuild_debug("  value='${v}'")
+    ecbuild_debug("  defined='${d}'")
+    ecbuild_debug("  set='${s}'")
+    ecbuild_debug("")
+  endif()
+
+  cmake_policy(POP)
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target
+# ===================
+#
+# Output all possible target properties of a given target. ::
+#
+#   ecbuild_echo_target( <target> )
+#
+##############################################################################
+
+function(ecbuild_echo_target tgt)
+  if(NOT TARGET ${tgt})
+    ecbuild_debug("There is no target named '${tgt}'")
+    return()
+  endif()
+
+  set(props
+DEBUG_OUTPUT_NAME
+DEBUG_POSTFIX
+RELEASE_OUTPUT_NAME
+RELEASE_POSTFIX
+ARCHIVE_OUTPUT_DIRECTORY
+ARCHIVE_OUTPUT_DIRECTORY_DEBUG
+ARCHIVE_OUTPUT_DIRECTORY_RELEASE
+ARCHIVE_OUTPUT_NAME
+ARCHIVE_OUTPUT_NAME_DEBUG
+ARCHIVE_OUTPUT_NAME_RELEASE
+AUTOMOC
+AUTOMOC_MOC_OPTIONS
+BUILD_WITH_INSTALL_RPATH
+BUNDLE
+BUNDLE_EXTENSION
+COMPILE_DEFINITIONS
+COMPILE_DEFINITIONS_DEBUG
+COMPILE_DEFINITIONS_RELEASE
+COMPILE_FLAGS
+DEBUG_POSTFIX
+RELEASE_POSTFIX
+DEFINE_SYMBOL
+ENABLE_EXPORTS
+EXCLUDE_FROM_ALL
+EchoString
+FOLDER
+FRAMEWORK
+Fortran_FORMAT
+Fortran_MODULE_DIRECTORY
+GENERATOR_FILE_NAME
+GNUtoMS
+HAS_CXX
+IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
+IMPORTED
+IMPORTED_CONFIGURATIONS
+IMPORTED_IMPLIB
+IMPORTED_IMPLIB_DEBUG
+IMPORTED_IMPLIB_RELEASE
+IMPORTED_LINK_DEPENDENT_LIBRARIES
+IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG
+IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_LANGUAGES
+IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG
+IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE
+IMPORTED_LINK_INTERFACE_LIBRARIES
+IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG
+IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_MULTIPLICITY
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_DEBUG
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_RELEASE
+IMPORTED_LOCATION
+IMPORTED_LOCATION_DEBUG
+IMPORTED_LOCATION_RELEASE
+IMPORTED_NO_SONAME
+IMPORTED_NO_SONAME_DEBUG
+IMPORTED_NO_SONAME_RELEASE
+IMPORTED_SONAME
+IMPORTED_SONAME_DEBUG
+IMPORTED_SONAME_RELEASE
+IMPORT_PREFIX
+IMPORT_SUFFIX
+INCLUDE_DIRECTORIES
+INSTALL_NAME_DIR
+INSTALL_RPATH
+INSTALL_RPATH_USE_LINK_PATH
+INTERPROCEDURAL_OPTIMIZATION
+INTERPROCEDURAL_OPTIMIZATION_DEBUG
+INTERPROCEDURAL_OPTIMIZATION_RELEASE
+LABELS
+LIBRARY_OUTPUT_DIRECTORY
+LIBRARY_OUTPUT_DIRECTORY_DEBUG
+LIBRARY_OUTPUT_DIRECTORY_RELEASE
+LIBRARY_OUTPUT_NAME
+LIBRARY_OUTPUT_NAME_DEBUG
+LIBRARY_OUTPUT_NAME_RELEASE
+LINKER_LANGUAGE
+LINK_DEPENDS
+LINK_FLAGS
+LINK_FLAGS_DEBUG
+LINK_FLAGS_RELEASE
+LINK_INTERFACE_LIBRARIES
+LINK_INTERFACE_LIBRARIES_DEBUG
+LINK_INTERFACE_LIBRARIES_RELEASE
+LINK_INTERFACE_MULTIPLICITY
+LINK_INTERFACE_MULTIPLICITY_DEBUG
+LINK_INTERFACE_MULTIPLICITY_RELEASE
+LINK_SEARCH_END_STATIC
+LINK_SEARCH_START_STATIC
+LOCATION
+LOCATION_DEBUG
+LOCATION_RELEASE
+MACOSX_BUNDLE
+MACOSX_BUNDLE_INFO_PLIST
+MACOSX_FRAMEWORK_INFO_PLIST
+MAP_IMPORTED_CONFIG_DEBUG
+MAP_IMPORTED_CONFIG_RELEASE
+OSX_ARCHITECTURES
+OSX_ARCHITECTURES_DEBUG
+OSX_ARCHITECTURES_RELEASE
+OUTPUT_NAME
+OUTPUT_NAME_DEBUG
+OUTPUT_NAME_RELEASE
+POST_INSTALL_SCRIPT
+PREFIX
+PRE_INSTALL_SCRIPT
+PRIVATE_HEADER
+PROJECT_LABEL
+PUBLIC_HEADER
+RESOURCE
+RULE_LAUNCH_COMPILE
+RULE_LAUNCH_CUSTOM
+RULE_LAUNCH_LINK
+RUNTIME_OUTPUT_DIRECTORY
+RUNTIME_OUTPUT_DIRECTORY_DEBUG
+RUNTIME_OUTPUT_DIRECTORY_RELEASE
+RUNTIME_OUTPUT_NAME
+RUNTIME_OUTPUT_NAME_DEBUG
+RUNTIME_OUTPUT_NAME_RELEASE
+SKIP_BUILD_RPATH
+SOURCES
+SOVERSION
+STATIC_LIBRARY_FLAGS
+STATIC_LIBRARY_FLAGS_DEBUG
+STATIC_LIBRARY_FLAGS_RELEASE
+SUFFIX
+TYPE
+VERSION
+VS_DOTNET_REFERENCES
+VS_GLOBAL_WHATEVER
+VS_GLOBAL_KEYWORD
+VS_GLOBAL_PROJECT_TYPES
+VS_KEYWORD
+VS_SCC_AUXPATH
+VS_SCC_LOCALPATH
+VS_SCC_PROJECTNAME
+VS_SCC_PROVIDER
+VS_WINRT_EXTENSIONS
+VS_WINRT_REFERENCES
+WIN32_EXECUTABLE
+XCODE_ATTRIBUTE_WHATEVER
+)
+
+  ecbuild_debug("======================== ${tgt} ========================")
+  foreach(p ${props})
+    ecbuild_echo_target_property("${tgt}" "${p}")
+  endforeach()
+  ecbuild_debug("")
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_targets
+# ====================
+#
+# Output all possible target properties of the specified list-of-targets.
+# This is very useful for debugging. ::
+#
+#   ecbuild_echo_targets( <list-of-targets> )
+#
+##############################################################################
+
+function(ecbuild_echo_targets)
+  set(tgts ${ARGV})
+  foreach(t ${tgts})
+    ecbuild_echo_target("${t}")
+  endforeach()
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_enable_fortran.cmake b/ecbuild/cmake/ecbuild_enable_fortran.cmake
new file mode 100644
index 0000000..9d86aa7
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_enable_fortran.cmake
@@ -0,0 +1,89 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_fortran
+# ======================
+#
+# Enable the Fortran language. ::
+#
+#   ecbuild_enable_fortran( [ MODULE_DIRECTORY <directory> ] [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# MODULE_DIRECTORY : optional, defaults to ``${CMAKE_BINARY_DIR}/module``
+#   set the CMAKE_Fortran_MODULE_DIRECTORY
+#
+# REQUIRED : optional
+#   fail if no working Fortran compiler was detected
+#
+##############################################################################
+
+macro( ecbuild_enable_fortran )
+
+  set( options REQUIRED  )
+  set( single_value_args MODULE_DIRECTORY )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_enable_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT CMAKE_Fortran_COMPILER_LOADED )
+    enable_language( Fortran )
+    ecbuild_compiler_flags( Fortran )
+    if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+      set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -warn all" )
+      ecbuild_debug( "Fortran FLAG [-warn all] added" )
+    endif()
+  endif()
+
+  if( DEFINED _PAR_REQUIRED )
+    if( CMAKE_Fortran_COMPILER_FORCED )
+      set( CMAKE_Fortran_COMPILER_WORKS 1 )
+    endif()
+    if( NOT CMAKE_Fortran_COMPILER OR NOT CMAKE_Fortran_COMPILER_WORKS )
+      ecbuild_critical( "Fortran compiler required by project ${PROJECT_NAME} but does not seem to work" )
+    endif()
+  endif()
+
+  if( CMAKE_Fortran_COMPILER_LOADED )
+
+    include(CheckFortranFunctionExists)
+    if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+      include(FortranCInterface)
+    endif()
+    set( EC_HAVE_FORTRAN 1 )
+
+    # see issue ECBUILD-298
+    if( CMAKE_Fortran_COMPILER_ID MATCHES PGI )
+      unset( CMAKE_Fortran_COMPILE_OPTIONS_PIE )
+      unset( CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS )
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_MODULE_DIRECTORY )
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${_PAR_MODULE_DIRECTORY} )
+  else()
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module
+         CACHE PATH "directory for all fortran modules." )
+  endif()
+
+  file( MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  include_directories( ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  install( CODE "EXECUTE_PROCESS (COMMAND \"${CMAKE_COMMAND}\" -E copy_directory \"${CMAKE_Fortran_MODULE_DIRECTORY}/\${BUILD_TYPE}\" \"${INSTALL_INCLUDE_DIR}\")" )
+
+endmacro( ecbuild_enable_fortran )
diff --git a/ecbuild/cmake/ecbuild_features.cmake b/ecbuild/cmake/ecbuild_features.cmake
new file mode 100644
index 0000000..9406fd8
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_features.cmake
@@ -0,0 +1,57 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Internal macros to handle CMake features
+
+include( FeatureSummary )
+
+# Write list of enabled features to CMake variable ${OUT}
+macro( ecbuild_enabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY ENABLED_FEATURES )
+endmacro()
+
+# Write list of disabled features to CMake variable ${OUT}
+macro( ecbuild_disabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY DISABLED_FEATURES )
+endmacro()
+
+# Enable the feature ${_name} (add to enabled features, remove from disabled)
+function( ecbuild_enable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _disabled_features )
+    list( REMOVE_ITEM _disabled_features ${_name} )
+  endif()
+
+  list( APPEND _enabled_features ${_name} )
+  list( REMOVE_DUPLICATES _enabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
+
+# Disable the feature ${_name} (add to disabled features, remove from enabled)
+function( ecbuild_disable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _enabled_features )
+    list( REMOVE_ITEM _enabled_features ${_name} )
+  endif()
+
+  list( APPEND _disabled_features ${_name} )
+  list( REMOVE_DUPLICATES _disabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_find_fortranlibs.cmake b/ecbuild/cmake/ecbuild_find_fortranlibs.cmake
new file mode 100644
index 0000000..905d020
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_fortranlibs.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_fortranlibs
+# ========================
+#
+# Find the Fortran (static) link libraries. ::
+#
+#   ecbuild_find_fortranlibs( [ COMPILER gfortran|pgi|xlf|intel ]
+#                             [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPILER : optional, defaults to gfortran
+#   request a given Fortran compiler (``gfortran``, ``pgi``, ``xlf``, ``intel``)
+#
+# REQUIRED : optional
+#   fail if Fortran libraries were not found
+#
+##############################################################################
+
+macro( ecbuild_find_fortranlibs )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args COMPILER )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT FORTRANLIBS_FOUND ) # don't repeat search
+
+    if( _PAR_COMPILER )
+      set( __known_fcomp 0 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "gfortran" )
+      set( WITH_LIBGFORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "pgi" )
+      set( WITH_PGI_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "xlf" )
+      set( WITH_XL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "intel" )
+      set( WITH_INTEL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER AND NOT __known_fcomp )
+      ecbuild_critical( "unknown fortran compiler ${_PAR_COMPILER}" )
+    endif()
+
+    ### set path from environment variables
+
+    foreach( _fortran_lib PGI XLF LIBGFORTRAN INTEL )
+      if( NOT ${_fortran_lib}_PATH AND NOT "$ENV{${_fortran_lib}_PATH}" STREQUAL "" )
+        set( ${_fortran_lib}_PATH "$ENV{${_fortran_lib}_PATH}" )
+      endif()
+    endforeach()
+
+    set( _flibs_found 0 )
+
+    ### default is to search for gfortran
+
+    if( NOT (WITH_PGI_FORTRAN OR WITH_LIBGFORTRAN OR
+             WITH_XL_FORTRAN OR WITH_INTEL_FORTRAN)
+        AND NOT (DEFINED PGI_PATH OR DEFINED LIBGFORTRAN_PATH OR
+                 DEFINED XLF_PATH OR DEFINED INTEL_PATH) )
+      ecbuild_warn( "Finding fortran libs for unspecified Fortran compiler: default search [ gfortran ]" )
+      set( WITH_LIBGFORTRAN 1 )
+    endif()
+
+    ### actual search ...
+
+    if( WITH_PGI_FORTRAN OR DEFINED PGI_PATH )
+
+      find_package(PGIFortran)
+
+      if( LIBPGIFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "PGI" )
+      endif()
+
+    endif()
+
+    if( WITH_LIBGFORTRAN OR DEFINED LIBGFORTRAN_PATH )
+
+      find_package(LibGFortran)
+
+      if( LIBGFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${GFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "gfortran" )
+      endif()
+
+    endif()
+
+    if( WITH_XL_FORTRAN OR DEFINED XLF_PATH )
+
+      find_package(XLFortranLibs)
+
+      if( LIBXLFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${XLFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "XLF" )
+      endif()
+
+    endif()
+
+    if( WITH_INTEL_FORTRAN OR DEFINED INTEL_PATH )
+
+      find_package(LibIFort)
+
+      if( LIBIFORT_FOUND )
+        set( FORTRAN_LIBRARIES ${IFORT_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "Intel" )
+      endif()
+
+    endif()
+
+    ### set found
+
+    if( _flibs_found )
+      set( FORTRANLIBS_FOUND 1 CACHE INTERNAL "Fortran libraries found" )
+      set( FORTRANLIBS_NAME ${_flibs_txt}  CACHE INTERNAL "Fortran library name" )
+      set( FORTRAN_LIBRARIES ${FORTRAN_LIBRARIES} CACHE INTERNAL "Fortran libraries" )
+      ecbuild_info( "Found Fortran libraries: ${_flibs_txt}" )
+    else()
+      set( FORTRANLIBS_FOUND 0 )
+      if( _PAR_REQUIRED )
+        ecbuild_critical( "Failed to find Fortran libraries" )
+      else()
+        ecbuild_warn( "Failed to find Fortran libraries" )
+      endif()
+    endif()
+
+  endif( NOT FORTRANLIBS_FOUND )
+
+endmacro( ecbuild_find_fortranlibs )
diff --git a/ecbuild/cmake/ecbuild_find_lexyacc.cmake b/ecbuild/cmake/ecbuild_find_lexyacc.cmake
new file mode 100644
index 0000000..3dc8b61
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_lexyacc.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_lexyacc
+# ====================
+#
+# Find flex and bison (preferred) or lex and yacc.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables can set to skip search for bison or yacc:
+#
+# :SKIP_BISON: do not search for flex and bison
+# :SKIP_YACC:  do not search for lex and yacc
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if flex and bison were found:
+#
+# :FLEX_FOUND:       flex was found
+# :BISON_FOUND:      bison was found
+# :FLEX_EXECUTABLE:  path to the flex executable
+# :BISON_EXECUTABLE: path to the bison executable
+#
+# The following CMake variables are set if lex and yacc were found:
+#
+# :LEXYACC_FOUND:   Found suitable combination of bison, lex, yacc, flex
+# :LEX_FOUND:       lex was found
+# :YACC_FOUND:      yacc was found
+# :LEX_EXECUTABLE:  path to the lex executable
+# :YACC_EXECUTABLE: path to the yacc executable
+#
+##############################################################################
+
+macro( ecbuild_find_lexyacc )
+
+  # find preferably bison or else yacc
+
+  if( NOT SKIP_BISON )
+
+    find_package( BISON )
+    if(BISON_FOUND AND BISON_VERSION VERSION_LESS 2.3 )
+        ecbuild_critical( "Bison found with version ${BISON_VERSION} is less than 2.3.\nPlease define BISON_EXECUTABLE to an appropriate version or define SKIP_BISON to try finding Yacc instead" )
+    endif()
+    find_package( FLEX )
+
+  endif()
+
+  if( NOT BISON_FOUND AND NOT SKIP_YACC )
+
+    find_package( YACC )
+    find_package( LEX  )
+
+  endif()
+
+  set( LEXYACC_FOUND 1 )
+
+  if( NOT YACC_FOUND AND NOT BISON_FOUND ) # neither bison nor yacc were found
+    ecbuild_debug( "Neither bison or yacc were found - at least one is required (together with its lexical analyser" )
+    set( LEXYACC_FOUND 0 )
+  endif()
+
+  if( NOT YACC_FOUND ) # check for both bison & flex together
+    if( BISON_FOUND AND NOT FLEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - flex not found" )
+    endif()
+    if( FLEX_FOUND AND NOT BISON_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - bison not found" )
+    endif()
+  endif()
+
+  if( NOT BISON_FOUND ) # check for both yacc & lex together
+    if( YACC_FOUND AND NOT LEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - lex not found" )
+    endif()
+    if( LEX_FOUND AND NOT YACC_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - yacc not found" )
+    endif()
+  endif()
+
+endmacro( ecbuild_find_lexyacc )
diff --git a/ecbuild/cmake/ecbuild_find_mpi.cmake b/ecbuild/cmake/ecbuild_find_mpi.cmake
new file mode 100644
index 0000000..85ae5e1
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_mpi.cmake
@@ -0,0 +1,328 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_mpi
+# ================
+#
+# Find MPI and check if MPI compilers successfully compile C/C++/Fortran. ::
+#
+#   ecbuild_find_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+# Input variables
+# ---------------
+#
+# ECBUILD_FIND_MPI : optional, defaults to TRUE
+#   test C/C++/Fortran MPI compiler wrappers (assume working if FALSE)
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if MPI was found: ::
+#
+#   MPI_FOUND
+#   MPI_LIBRARY
+#   MPI_EXTRA_LIBRARY 
+#
+# The following CMake variables are set if C bindings were found: ::
+#
+#   MPI_C_FOUND
+#   MPI_C_COMPILER
+#   MPI_C_COMPILE_FLAGS
+#   MPI_C_INCLUDE_PATH
+#   MPI_C_LIBRARIES
+#   MPI_C_LINK_FLAGS
+#
+# The following CMake variables are set if C++ bindings were found: ::
+#
+#   MPI_CXX_FOUND
+#   MPI_CXX_COMPILER
+#   MPI_CXX_COMPILE_FLAGS
+#   MPI_CXX_INCLUDE_PATH
+#   MPI_CXX_LIBRARIES
+#   MPI_CXX_LINK_FLAGS
+#
+# The following CMake variables are set if Fortran bindings were found: ::
+#
+#   MPI_Fortran_FOUND
+#   MPI_Fortran_COMPILER
+#   MPI_Fortran_COMPILE_FLAGS
+#   MPI_Fortran_INCLUDE_PATH
+#   MPI_Fortran_LIBRARIES
+#   MPI_Fortran_LINK_FLAGS
+#
+##############################################################################
+
+macro( ecbuild_find_mpi )
+
+    # parse parameters
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    # if user defined compilers are MPI compliant, then we use them ...
+    if( NOT DEFINED ECBUILD_FIND_MPI )
+      set( ECBUILD_FIND_MPI TRUE )
+    endif()
+    if( ECBUILD_FIND_MPI )
+
+        # C compiler
+
+        if( CMAKE_C_COMPILER_LOADED AND NOT MPI_C_COMPILER )
+
+            include(CheckCSourceCompiles)
+
+            check_c_source_compiles("
+                #include <mpi.h>
+                int main(int argc, char* argv[])
+                {
+                int rank;
+                MPI_Init(&argc, &argv); 
+                MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
+                MPI_Finalize();
+                return 0;
+                }
+                "
+                C_COMPILER_SUPPORTS_MPI )
+
+            if( C_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C compiler supports MPI -- ${CMAKE_C_COMPILER}" )
+                set( MPI_C_COMPILER ${CMAKE_C_COMPILER} )
+            endif()
+
+        endif()
+
+        # CXX compiler
+
+        if( CMAKE_CXX_COMPILER_LOADED AND NOT MPI_CXX_COMPILER )
+
+            include(CheckCXXSourceCompiles)
+
+            check_cxx_source_compiles("
+                #include <mpi.h>
+                 #include <iostream>
+                 int main(int argc, char* argv[])
+                 {
+                   MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Finalize();
+                   return 0;
+                 }
+                 "
+                 CXX_COMPILER_SUPPORTS_MPI )
+
+            if( CXX_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C++ compiler supports MPI -- ${CMAKE_CXX_COMPILER}" )
+                set( MPI_CXX_COMPILER ${CMAKE_CXX_COMPILER} )
+            endif()
+
+        endif()
+
+        # Fortran compiler
+
+        if( CMAKE_Fortran_COMPILER_LOADED AND NOT MPI_Fortran_COMPILER )
+
+            include(CheckFortranSourceCompiles)
+
+            check_fortran_source_compiles("
+                program main
+                use MPI
+                integer ierr
+                call MPI_INIT( ierr )
+                call MPI_FINALIZE( ierr )
+                end
+                "
+            Fortran_COMPILER_SUPPORTS_MPI )
+
+            if( Fortran_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "Fortran compiler supports MPI (F90) -- ${CMAKE_Fortran_COMPILER}" )
+                set( MPI_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} )
+                set( MPI_Fortran_FOUND TRUE )
+            endif()
+
+        endif()
+
+        if( NOT _PAR_REQUIRED )
+            find_package( MPI QUIET )
+        else()
+            find_package( MPI QUIET REQUIRED )
+        endif()
+
+        if( C_COMPILER_SUPPORTS_MPI )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CXX_COMPILER_SUPPORTS_MPI )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( Fortran_COMPILER_SUPPORTS_MPI )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    else()
+
+        # find_package with Cray compiler did not send MPI_<lang>_FOUND
+        if( CMAKE_C_COMPILER_LOADED )
+            set( C_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CMAKE_CXX_COMPILER_LOADED )
+            set( CXX_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( CMAKE_Fortran_COMPILER_LOADED )
+            set( Fortran_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    endif( ECBUILD_FIND_MPI )
+
+    # hide these variables from UI
+
+    mark_as_advanced( MPI_LIBRARY MPI_EXTRA_LIBRARY )
+
+    if( NOT _PAR_COMPONENTS )
+      set( _PAR_COMPONENTS C )
+    endif()
+
+    set( MPI_FOUND TRUE )
+    foreach( _lang ${_PAR_COMPONENTS} )
+      if( NOT MPI_${_lang}_FOUND )
+        set( MPI_FOUND FALSE )
+      endif()
+    endforeach()
+
+endmacro( ecbuild_find_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_mpi
+# ==================
+#
+# Find MPI, add include directories and set compiler flags. ::
+#
+#   ecbuild_enable_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                       [ REQUIRED ] )
+#
+# For each MPI language binding found, set the corresponding compiler flags
+# and add the include directories.
+#
+# See ``ecbuild_find_mpi`` for input and output variables.
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+##############################################################################
+
+macro( ecbuild_enable_mpi )
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_COMPONENTS )
+      set (_PAR_COMPONENTS C )
+    endif()
+
+    if( NOT _PAR_REQUIRED )
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} )
+    else()
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} REQUIRED )
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_enable_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_include_mpi
+# ===================
+#
+# Add MPI include directories and set compiler flags, assuming MPI was found.
+#
+# For each MPI language binding found, set corresponding compiler flags and
+# add include directories. ``ecbuild_find_mpi`` must have been called before.
+#
+##############################################################################
+
+macro( ecbuild_include_mpi )
+
+    set( options )
+    set( single_value_args )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_c_source_return )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_cxx_source_return )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_include_mpi )
diff --git a/ecbuild/cmake/ecbuild_find_omp.cmake b/ecbuild/cmake/ecbuild_find_omp.cmake
new file mode 100644
index 0000000..3dc6fac
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_omp.cmake
@@ -0,0 +1,259 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for looking for openmp flags
+
+macro( lookup_omp_flags )
+  set(_OMP_FLAG_GNU        "-fopenmp")
+  set(_OMPSTUBS_FLAG_GNU   "-fno-openmp")
+
+  set(_OMP_FLAG_Cray       "-homp")
+  set(_OMPSTUBS_FLAG_Cray  "-hnoomp")
+
+  set(_OMP_FLAG_XL         "-qsmp=omp")
+  set(_OMPSTUBS_FLAG_XL    "-qsmp=noomp")
+
+  set(_OMP_FLAG_Intel      "-qopenmp")
+  set(_OMPSTUBS_FLAG_Intel "-qopenmp-stubs")
+
+  # sample C openmp source code to test
+  set(_OMP_C_TEST_SOURCE
+  "
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    #pragma omp parallel
+    {
+      (void)omp_get_thread_num();
+    }
+    return 0;
+  #else
+    breaks_on_purpose
+  #endif
+  }
+  ")
+  set( _OMP_CXX_TEST_SOURCE ${_OMP_C_TEST_SOURCE} )
+
+
+  # sample C openmp source code to test
+  set(_OMPSTUBS_C_TEST_SOURCE
+  "
+  // Include must be found
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    breaks_on_purpose
+  #else
+    #pragma omp parallel
+    {
+      // This pragma should have passed compilation
+      (void)0;
+    }
+    return 0;
+  #endif
+  }
+  ")
+  set( _OMPSTUBS_CXX_TEST_SOURCE ${_OMPSTUBS_C_TEST_SOURCE} )
+
+
+  # sample Fortran openmp source code to test
+  set(_OMP_Fortran_TEST_SOURCE
+  "
+  program main
+    use omp_lib
+  end program
+  ")
+  set( _OMPSTUBS_Fortran_TEST_SOURCE ${_OMP_Fortran_TEST_SOURCE} )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_omp
+# ================
+#
+# Find OpenMP. ::
+#
+#   ecbuild_find_omp( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ]
+#                     [ STUBS ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if OpenMP was not found
+#
+# STUBS : optional
+#   search for OpenMP stubs
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if OpenMP was found:
+#
+# :OMP_FOUND: OpenMP was found
+#
+# For each language listed in COMPONENTS, the following variables are set:
+#
+# :OMP_<LANG>_FOUND: OpenMP bindings for LANG were found
+# :OMP_<LANG>_FLAGS: OpenMP compiler flags for LANG
+#
+# If the STUBS option was given, all variables are also set with the OMPSTUBS
+# instead of the OMP prefix.
+#
+##############################################################################
+
+macro( ecbuild_find_omp )
+
+  set( options REQUIRED STUBS )
+  set( single_value_args )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT _PAR_COMPONENTS )
+    ecbuild_critical( "No COMPONENTS were specified, looking for OMP.\n Please find with COMPONENTS C CXX Fortran " )
+  endif()
+
+  set( _STUBS "" )
+  if( _PAR_STUBS )
+    set( _STUBS "STUBS" )
+  endif()
+
+  lookup_omp_flags()
+
+  set( OMP${_STUBS}_FOUND TRUE )
+
+  foreach( _LANG ${_PAR_COMPONENTS} )
+
+    if( NOT OMP${_STUBS}_${_LANG}_FLAGS )
+
+      if( DEFINED _OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID} )
+        set( _OMP${_STUBS}_${_LANG}_FLAG "${_OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID}}" )
+      endif()
+      if( CMAKE_${_LANG}_COMPILER_LOADED AND _OMP${_STUBS}_${_LANG}_FLAG )
+        set(SAVE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+        set(CMAKE_REQUIRED_FLAGS "${_OMP${_STUBS}_${_LANG}_FLAG}")
+        include(Check${_LANG}SourceCompiles)
+        set( _SOURCE ${_OMP${_STUBS}_${_LANG}_TEST_SOURCE} )
+        set( _FLAG ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS})
+        if( _LANG STREQUAL "C" )
+          check_c_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "CXX" )
+          check_cxx_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "Fortran" )
+          check_fortran_source_compiles("${_SOURCE}" ${_FLAG} SRC_EXT f90)
+        endif()
+        set(CMAKE_REQUIRED_FLAGS "${SAVE_CMAKE_REQUIRED_FLAGS}")
+      endif()
+
+      if( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} )
+        set( OMP${_STUBS}_${_LANG}_FLAGS ${_OMP${_STUBS}_${_LANG}_FLAG} )
+      endif()
+
+    else()
+      set( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} TRUE )
+    endif()
+
+
+    set( OMP${_STUBS}_${_LANG}_FIND_QUIETLY TRUE )
+    find_package_handle_standard_args( OMP${_STUBS}_${_LANG} REQUIRED_VARS ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS}  )
+
+    if( OMP${_STUBS}_FORTRAN_FOUND )
+      set( OMP${_STUBS}_Fortran_FOUND TRUE )
+    endif()
+
+    if( NOT OMP${_STUBS}_${_LANG}_FOUND )
+      set( OMP${_STUBS}_FOUND FALSE )
+    endif()
+
+    if( _PAR_STUBS )
+      set( OMP_${_LANG}_FOUND ${OMPSTUBS_${_LANG}_FOUND} )
+      set( OMP_${_LANG}_FLAGS ${OMPSTUBS_${_LANG}_FLAGS} )
+    endif()
+
+  endforeach()
+
+  if( _PAR_STUBS )
+    set( OMP_FOUND ${OMPSTUBS_FOUND} )
+  endif()
+
+endmacro( ecbuild_find_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_omp
+# ==================
+#
+# Find OpenMP for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP support was detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_omp )
+
+  ecbuild_debug("ecbuild_enable_omp: Trying to enable OpenMP")
+  ecbuild_find_omp( COMPONENTS C CXX Fortran )
+
+  ecbuild_debug_var("OMP_C_FOUND")
+  if( OMP_C_FOUND )
+    ecbuild_debug("Adding ${OMP_C_FLAGS} to CMAKE_C_FLAGS")
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMP_C_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_CXX_FOUND")
+  if( OMP_CXX_FOUND )
+    ecbuild_debug("Adding ${OMP_CXX_FLAGS} to CMAKE_CXX_FLAGS")
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMP_CXX_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_Fortran_FOUND")
+  if( OMP_Fortran_FOUND )
+    ecbuild_debug("Adding ${OMP_Fortran_FLAGS} to CMAKE_Fortran_FLAGS")
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMP_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_ompstubs
+# =======================
+#
+# Find OpenMP stubs for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP stubs were detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_ompstubs )
+
+  ecbuild_find_omp( COMPONENTS C CXX Fortran STUBS )
+
+  if( OMPSTUBS_C_FOUND )
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMPSTUBS_C_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_CXX_FOUND )
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMPSTUBS_CXX_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_Fortran_FOUND )
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMPSTUBS_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_ompstubs )
diff --git a/ecbuild/cmake/ecbuild_find_package.cmake b/ecbuild/cmake/ecbuild_find_package.cmake
new file mode 100644
index 0000000..71be044
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_package.cmake
@@ -0,0 +1,368 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_package
+# ====================
+#
+# Find a package and import its configuration. ::
+#
+#   ecbuild_find_package( NAME <name>
+#                         [ VERSION <version> [ EXACT ] ]
+#                         [ COMPONENTS <component1> [ <component2> ... ] ]
+#                         [ URL <url> ]
+#                         [ DESCRIPTION <description> ]
+#                         [ TYPE <type> ]
+#                         [ PURPOSE <purpose> ]
+#                         [ FAILURE_MSG <message> ]
+#                         [ REQUIRED ]
+#                         [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# COMPONENTS : optional
+#   list of package components to find (behaviour depends on the package)
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   literal string or name of CMake variable describing the package
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   literal string or name of CMake variable describing which functionality
+#   this package enables in the project
+#
+# FAILURE_MSG : optional
+#   literal string or name of CMake variable containing a message to be
+#   appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>`` is
+# the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :DEVELOPER_MODE: if enabled, discover projects parallel in the build tree
+# :<name>_PATH:    install prefix path of the package
+# :<NAME>_PATH:    install prefix path of the package
+# :<name>_DIR:     directory containing the ``<name>-config.cmake`` file
+#                  (usually ``<install-prefix>/share/<name>/cmake``)
+#
+# The environment variables ``<name>_PATH``, ``<NAME>_PATH``, ``<name>_DIR``
+# are taken into account only if the corresponding CMake variables are unset.
+#
+# Usage
+# -----
+#
+# The search proceeds as follows:
+#
+# 1.  If any paths have been specified by the user via CMake or environment
+#     variables as given above or a parallel build tree has been discovered in
+#     DEVELOPER_MODE:
+#
+#     * search for ``<name>-config.cmake`` in those paths only
+#     * search using ``Find<name>.cmake`` (which should respect those paths)
+#     * fail if the package was not found in any of those paths
+#
+# 2.  Search for ``<name>-config.cmake`` in the ``CMAKE_PREFIX_PATH`` and if
+#     DEVELOPER_MODE is enabled also in the user package registry.
+#
+# 3.  Search system paths for ``<name>-config.cmake``.
+#
+# 4.  Search system paths using ``Find<name>.cmake``.
+#
+# 5.  If the package was found, and a minimum version was requested, check if
+#     the version is acceptable and if not, unset ``<NAME>_FOUND``.
+#
+# 6.  Fail if the package was not found and is REQUIRED.
+#
+##############################################################################
+
+macro( ecbuild_find_package )
+
+  set( options REQUIRED QUIET EXACT )
+  set( single_value_args NAME VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_package(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_NAME  )
+    ecbuild_critical("The call to ecbuild_find_package() doesn't specify the NAME.")
+  endif()
+
+  if( _PAR_EXACT AND NOT _PAR_VERSION )
+    ecbuild_critical("Call to ecbuild_find_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _PAR_REQUIRED )
+    set( _PAR_TYPE REQUIRED )
+  endif()
+
+  # ecbuild_debug_var( _PAR_NAME )
+
+  string( TOUPPER ${_PAR_NAME} pkgUPPER )
+  string( TOLOWER ${_PAR_NAME} pkgLOWER )
+
+  set( _${pkgUPPER}_version "" )
+  if( _PAR_VERSION )
+    set( _${pkgUPPER}_version ${_PAR_VERSION} )
+    if( _PAR_EXACT )
+      set( _${pkgUPPER}_version ${_PAR_VERSION} EXACT )
+    endif()
+  endif()
+
+  # check developer mode (search in cmake cache )
+
+  if( NOT ${DEVELOPER_MODE} )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): Not in DEVELOPER_MODE - do not search package registry or recent GUI build paths")
+    set( NO_DEV_BUILD_DIRS NO_CMAKE_PACKAGE_REGISTRY NO_CMAKE_BUILDS_PATH )
+  endif()
+
+  # in DEVELOPER_MODE we give priority to projects parallel in the build tree
+  # so lets prepend a parallel build tree to the search path if we find it
+
+  if( DEVELOPER_MODE )
+    get_filename_component( _proj_bdir "${CMAKE_BINARY_DIR}/../${pkgLOWER}" ABSOLUTE )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - searching for ${pkgLOWER}-config.cmake in ${_proj_bdir}")
+    if( EXISTS ${_proj_bdir}/${pkgLOWER}-config.cmake )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - found parallel build tree in ${_proj_bdir}")
+      if( ${pkgUPPER}_PATH )
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - ${pkgUPPER}_PATH already set to ${${pkgUPPER}_PATH}, not modifying")
+      else()
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - setting ${pkgUPPER}_PATH to ${_proj_bdir}")
+        set( ${pkgUPPER}_PATH "${_proj_bdir}" )
+      endif()
+    endif()
+  endif()
+
+  # Read environment variables but ONLY if the corresponding CMake variables are unset
+
+  if( NOT DEFINED ${pkgUPPER}_PATH AND NOT "$ENV{${pkgUPPER}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${pkgUPPER}_PATH=${${pkgUPPER}_PATH} from environment")
+    set( ${pkgUPPER}_PATH "$ENV{${pkgUPPER}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_PATH AND NOT "$ENV{${_PAR_NAME}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH} from environment")
+    set( ${_PAR_NAME}_PATH "$ENV{${_PAR_NAME}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_DIR AND NOT "$ENV{${_PAR_NAME}_DIR}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR} from environment")
+    set( ${_PAR_NAME}_DIR "$ENV{${_PAR_NAME}_DIR}" )
+  endif()
+
+  # Find packages quietly unless in DEVELOPER_MODE or LOG_LEVEL is DEBUG
+
+  if( NOT DEVELOPER_MODE AND ( ECBUILD_LOG_LEVEL GREATER ${ECBUILD_DEBUG} ) )
+    set( _find_quiet QUIET )
+  endif()
+
+  # search user defined paths first
+
+  if( ${_PAR_NAME}_PATH OR ${pkgUPPER}_PATH OR ${_PAR_NAME}_DIR )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+
+    # 1) search using CONFIG mode -- try to locate a configuration file provided by the package (package-config.cmake)
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 1) search using CONFIG mode -- try to locate ${_PAR_NAME}-config.cmake")
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}):    using hints ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} NO_MODULE ${_find_quiet}
+        COMPONENTS ${_PAR_COMPONENTS}
+        HINTS ${${pkgUPPER}_PATH} ${${_PAR_NAME}_PATH} ${${_PAR_NAME}_DIR}
+        NO_DEFAULT_PATH )
+    endif()
+
+    # 2) search using a file Find<package>.cmake if it exists ( macro should itself take *_PATH into account )
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 2) search using a file Find${_PAR_NAME}.cmake if it exists")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} MODULE ${_find_quiet}
+                    COMPONENTS ${_PAR_COMPONENTS} )
+    endif()
+
+    # is <package>_PATH was given and we don't find anything then we FAIL
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      if( ${_PAR_NAME}_PATH )
+        ecbuild_critical( "${_PAR_NAME}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${_PAR_NAME}_PATH}'" )
+      endif()
+      if( ${pkgUPPER}_PATH )
+        ecbuild_critical( "${pkgUPPER}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${pkgUPPER}_PATH}'" )
+      endif()
+    endif()
+
+  endif()
+
+  # 3) search developer cache and recently configured packages in the CMake GUI if in DEVELOPER_MODE
+  #    otherwise only search CMAKE_PREFIX_PATH and <package>_PATH
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    if (NO_DEV_BUILD_DIRS)
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH")
+    else()
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH and package registry")
+    endif()
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      HINTS ENV ${pkgUPPER}_PATH
+      ${NO_DEV_BUILD_DIRS}
+      NO_CMAKE_ENVIRONMENT_PATH
+      NO_SYSTEM_ENVIRONMENT_PATH
+      NO_CMAKE_SYSTEM_PATH
+      NO_CMAKE_SYSTEM_PACKAGE_REGISTRY )
+
+  endif()
+
+  # 4) search system paths, for <package>-config.cmake
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 5) search system paths, for ${_PAR_NAME}-config.cmake")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      ${NO_DEV_BUILD_DIRS} )
+
+  endif()
+
+  # 5) search system paths, using Find<package>.cmake if it exists
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 6) search system paths, using Find${_PAR_NAME}.cmake if it exists")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} MODULE
+                  COMPONENTS ${_PAR_COMPONENTS} )
+
+  endif()
+
+  # check version found is acceptable
+
+  if( ${_PAR_NAME}_FOUND )
+    set( _version_acceptable 1 )
+    if( _PAR_VERSION )
+      if( ${_PAR_NAME}_VERSION )
+        if( _PAR_EXACT )
+          if( NOT ${_PAR_NAME}_VERSION VERSION_EQUAL _PAR_VERSION )
+            ecbuild_warn( "${PROJECT_NAME} requires (exactly) ${_PAR_NAME} = ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            set( _version_acceptable 0 )
+          endif()
+        else()
+          if( _PAR_VERSION VERSION_LESS ${_PAR_NAME}_VERSION OR _PAR_VERSION VERSION_EQUAL ${_PAR_NAME}_VERSION )
+            set( _version_acceptable 1 )
+          else()
+            if( NOT _PAR_QUIET )
+              ecbuild_warn( "${PROJECT_NAME} requires ${_PAR_NAME} >= ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            endif()
+            set( _version_acceptable 0 )
+          endif()
+        endif()
+      else()
+        if( NOT _PAR_QUIET )
+          ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but no version information, so cannot check if satisfies ${_PAR_VERSION}" )
+        endif()
+        set( _version_acceptable 0 )
+      endif()
+    endif()
+  endif()
+
+  if( ${_PAR_NAME}_FOUND )
+
+    if( _version_acceptable )
+      set( ${pkgUPPER}_FOUND ${${_PAR_NAME}_FOUND} )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but with unsuitable version" )
+      endif()
+      set( ${pkgUPPER}_FOUND 0 )
+      set( ${_PAR_NAME}_FOUND 0 )
+    endif()
+
+  endif()
+
+  ### final messages
+
+  if( ${_PAR_NAME}_FOUND OR ${pkgUPPER}_FOUND )
+
+    if( NOT _PAR_QUIET )
+      ecbuild_info( "[${_PAR_NAME}] (${${_PAR_NAME}_VERSION})" )
+      foreach( var in ITEMS INCLUDE_DIR INCLUDE_DIRS DEFINITIONS LIBRARY LIBRARIES )
+        if( ${pkgUPPER}_${var} )
+          ecbuild_info( "   ${pkgUPPER}_${var} : [${${pkgUPPER}_${var}}]" )
+        elseif( ${_PAR_NAME}_${var} )
+          ecbuild_info( "   ${_PAR_NAME}_${var} : [${${_PAR_NAME}_${var}}]" )
+        endif()
+      endforeach()
+    endif()
+
+    if( DEFINED ${_PAR_DESCRIPTION} )
+      set( _PAR_DESCRIPTION ${${_PAR_DESCRIPTION}} )
+    endif()
+    if( DEFINED ${_PAR_PURPOSE} )
+      set( _PAR_PURPOSE ${${_PAR_PURPOSE}} )
+    endif()
+    set_package_properties( ${_PAR_NAME} PROPERTIES
+                            URL "${_PAR_URL}"
+                            DESCRIPTION "${_PAR_DESCRIPTION}"
+                            TYPE "${_PAR_TYPE}"
+                            PURPOSE "${_PAR_PURPOSE}" )
+
+  else()
+
+    if( DEFINED ${_PAR_FAILURE_MSG} )
+      set( _PAR_FAILURE_MSG ${${_PAR_FAILURE_MSG}} )
+    endif()
+    set( _failed_message
+      "  ${PROJECT_NAME} FAILED to find package ${_PAR_NAME}\n"
+      "    Provide location with \"-D${pkgUPPER}_PATH=/...\" or \"-D${_PAR_NAME}_DIR=/...\" \n"
+      "    You may also export environment variables ${pkgUPPER}_PATH or ${_PAR_NAME}_DIR\n"
+      "  Values (note CAPITALISATION):\n"
+      "    ${pkgUPPER}_PATH should contain the path to the install prefix (as in <install>/bin <install>/lib <install>/include)\n"
+      "    ${_PAR_NAME}_DIR should be a directory containing a <package>-config.cmake file (usually <install>/share/<package>/cmake)\n"
+      )
+
+    if( _PAR_REQUIRED )
+      ecbuild_critical( "${_failed_message}!! ${PROJECT_NAME} requires package ${_PAR_NAME} !!\n${_PAR_FAILURE_MSG}" )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${_failed_message}\n${_PAR_FAILURE_MSG}" )
+      endif()
+    endif()
+
+  endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_find_perl.cmake b/ecbuild/cmake/ecbuild_find_perl.cmake
new file mode 100644
index 0000000..b6c1825
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_perl.cmake
@@ -0,0 +1,73 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_perl
+# =================
+#
+# Find perl executable and its version. ::
+#
+#   ecbuild_find_perl( [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# REQUIRED : optional
+#   fail if perl was not found
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if perl was found:
+#
+# :PERL_FOUND:          perl was found
+# :PERL_EXECUTABLE:     path to the perl executable
+# :PERL_VERSION:        perl version
+# :PERL_VERSION_STRING: perl version (same as ``PERL_VERSION``)
+#
+##############################################################################
+
+macro( ecbuild_find_perl )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_perl(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  find_package( Perl )
+
+  if( NOT PERL_EXECUTABLE AND _p_REQUIRED )
+    ecbuild_critical( "Failed to find Perl (REQUIRED)" )
+  endif()
+
+  if( PERL_EXECUTABLE )
+
+    execute_process( COMMAND ${PERL_EXECUTABLE} -V:version OUTPUT_VARIABLE  perl_version_output_variable  RESULT_VARIABLE  perl_version_return )
+    if( NOT perl_version_return )
+      string(REGEX REPLACE "version='([^']+)'.*" "\\1" PERL_VERSION ${perl_version_output_variable})
+    endif()
+
+    # from cmake 2.8.8 onwards
+    if( NOT PERL_VERSION_STRING )
+      set( PERL_VERSION_STRING ${PERL_VERSION} )
+    endif()
+
+    ecbuild_debug("ecbuild_find_perl: found perl version ${PERL_VERSION_STRING} as ${PERL_EXECUTABLE}")
+
+  endif()
+
+endmacro( ecbuild_find_perl )
diff --git a/ecbuild/cmake/ecbuild_find_python.cmake b/ecbuild/cmake/ecbuild_find_python.cmake
new file mode 100644
index 0000000..0e9adaa
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_find_python.cmake
@@ -0,0 +1,263 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_python
+# ===================
+#
+# Find Python interpreter, its version and the Python libraries. ::
+#
+#   ecbuild_find_python( [ VERSION <version> ] [ REQUIRED ] [ NO_LIBS ] )
+#
+# Options
+# -------
+#
+# VERSION : optional
+#   minimum required version
+#
+# REQUIRED : optional
+#   fail if Python was not found
+#
+# NO_LIBS : optional
+#   only search for the Python interpreter, not the libraries
+#
+# Unless ``NO_LIBS`` is set, the ``python-config`` utility, if found, is used
+# to determine the Python include directories, libraries and link line. Set the
+# CMake variable ``PYTHON_NO_CONFIG`` to use CMake's FindPythonLibs instead.
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if python was found:
+#
+# :PYTHONINTERP_FOUND:    Python interpreter was found
+# :PYTHONLIBS_FOUND:      Python libraries were found
+# :PYTHON_FOUND:          Python was found (both interpreter and libraries)
+# :PYTHON_EXECUTABLE:     Python executable
+# :PYTHON_VERSION_MAJOR:  major version number
+# :PYTHON_VERSION_MINOR:  minor version number
+# :PYTHON_VERSION_PATCH:  patch version number
+# :PYTHON_VERSION_STRING: Python version
+# :PYTHON_INCLUDE_DIRS:   Python include directories
+# :PYTHON_LIBRARIES:      Python libraries
+# :PYTHON_SITE_PACKAGES:  Python site packages directory
+#
+##############################################################################
+
+set( __test_python ${CMAKE_CURRENT_LIST_DIR}/pymain.c )
+
+function( ecbuild_find_python )
+
+    # parse parameters
+
+    set( options REQUIRED NO_LIBS )
+    set( single_value_args VERSION )
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+    if( _p_REQUIRED )
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter (required) ..." )
+      set( _p_REQUIRED REQUIRED )
+    else()
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter ..." )
+      unset( _p_REQUIRED )
+    endif()
+
+    # find python executable
+
+    # Search first without specifying the version, since doing so gives preference to the specified
+    # version even though a never version of the interpreter may be available
+    find_package( PythonInterp ${_p_REQUIRED} )
+
+    # If no suitable version was found, search again with the version specified
+    if( PYTHONINTERP_FOUND AND _p_VERSION )
+      if( _p_VERSION VERSION_GREATER "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}', however version '${_p_VERSION}' is required. Searching again..." )
+        unset( PYTHONINTERP_FOUND )
+        unset( PYTHON_EXECUTABLE )
+        unset( PYTHON_EXECUTABLE CACHE )
+        unset( PYTHON_VERSION_MAJOR )
+        unset( PYTHON_VERSION_MINOR )
+        unset( PYTHON_VERSION_PATCH )
+        unset( PYTHON_VERSION_STRING )
+        find_package( PythonInterp "${_p_VERSION}" ${_p_REQUIRED} )
+      endif()
+    endif()
+
+    set_package_properties( PythonInterp PROPERTIES
+                            URL http://python.org
+                            DESCRIPTION "Python interpreter" )
+
+    set( __required_vars PYTHONINTERP_FOUND )
+
+    if( PYTHONINTERP_FOUND )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}'" )
+
+        # find where python site-packages are ...
+
+        if( PYTHON_EXECUTABLE )
+            execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
+        endif()
+        ecbuild_debug( "ecbuild_find_python: PYTHON_SITE_PACKAGES=${PYTHON_SITE_PACKAGES}" )
+    else()
+        ecbuild_debug( "ecbuild_find_python: could NOT find Python interpreter!" )
+    endif()
+
+    if( PYTHONINTERP_FOUND AND _p_NO_LIBS )
+        ecbuild_debug( "ecbuild_find_python: NOT searching for Python libraries" )
+    elseif( PYTHONINTERP_FOUND )
+        list( APPEND __required_vars PYTHONLIBS_FOUND PYTHON_LIBS_WORKING )
+        ecbuild_debug( "ecbuild_find_python: Searching for Python libraries ..." )
+
+        # find python config
+
+        if( PYTHON_EXECUTABLE AND EXISTS ${PYTHON_EXECUTABLE}-config )
+            set(PYTHON_CONFIG_EXECUTABLE ${PYTHON_EXECUTABLE}-config CACHE PATH "" FORCE)
+        else()
+            get_filename_component( __python_bin_dir ${PYTHON_EXECUTABLE} PATH )
+            find_program( PYTHON_CONFIG_EXECUTABLE
+                          NO_CMAKE_PATH NO_CMAKE_SYSTEM_PATH
+                          NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH
+                          HINTS ${__python_bin_dir}
+                          NAMES python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}-config
+                                python${PYTHON_VERSION_MAJOR}-config
+                                python-config )
+        endif()
+
+        ecbuild_debug( "ecbuild_find_python: found python-config at '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+        # find python libs
+
+        # The OpenBSD python packages have python-config's
+        # that don't reliably report linking flags that will work.
+
+        if( PYTHON_CONFIG_EXECUTABLE AND NOT ( PYTHON_NO_CONFIG OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" ) )
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+            if( DEFINED PYTHON_LIBRARY )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY already set to '${PYTHON_LIBRARY}'" )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --prefix
+                              OUTPUT_VARIABLE PYTHON_PREFIX
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_PREFIX=${PYTHON_PREFIX}" )
+
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --ldflags
+                              OUTPUT_VARIABLE PYTHON_LIBRARY
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY=${PYTHON_LIBRARY}" )
+
+              # Prepend -L and and set the RPATH to the lib directory under the
+              # Python install prefix unless it is a standard system prefix path
+              if( PYTHON_LIBRARY AND PYTHON_PREFIX AND NOT CMAKE_SYSTEM_PREFIX_PATH MATCHES ${PYTHON_PREFIX} )
+                ecbuild_debug( "ecbuild_find_python: Python libraries not in CMAKE_SYSTEM_PREFIX_PATH, prepending PYTHON_PREFIX '${PYTHON_PREFIX}' to PYTHON_LIBRARY" )
+                set( PYTHON_LIBRARY "-L${PYTHON_PREFIX}/lib -Wl,-rpath,${PYTHON_PREFIX}/lib ${PYTHON_LIBRARY}" )
+              endif()
+            endif()
+
+            if( DEFINED PYTHON_INCLUDE_DIR )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR already set to '${PYTHON_INCLUDE_DIR}'" )
+            elseif(DEFINED PYTHON_INCLUDE_PATH AND NOT DEFINED PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_PATH already set to '${PYTHON_INCLUDE_PATH}'" )
+              ecbuild_deprecate( "ecbuild_find_python: PYTHON_INCLUDE_PATH is deprecated, use PYTHON_INCLUDE_DIR instead!" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_PATH}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --includes
+                              OUTPUT_VARIABLE PYTHON_INCLUDE_DIR
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+
+              string(REGEX REPLACE "^[-I]" "" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+              string(REGEX REPLACE "[ ]-I" " " PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+
+              separate_arguments(PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR}" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+
+            endif()
+
+            set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}")
+            set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}")
+
+            find_package_handle_standard_args( PythonLibs DEFAULT_MSG
+                                               PYTHON_INCLUDE_DIRS PYTHON_LIBRARIES )
+
+        else() # revert to finding pythonlibs the standard way (cmake macro)
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using find_package( PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH} ${_p_REQUIRED} )" )
+
+            find_package( PythonLibs "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" ${_p_REQUIRED} )
+
+            set_package_properties( PythonLibs PROPERTIES
+                                    URL http://python.org
+                                    DESCRIPTION "Python library and header" )
+
+        endif()
+
+        # Remove duplicate include directories
+        list(REMOVE_DUPLICATES PYTHON_INCLUDE_DIRS)
+
+        ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIRS=${PYTHON_INCLUDE_DIRS}" )
+        ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARIES=${PYTHON_LIBRARIES}" )
+
+        if( PYTHON_LIBRARIES AND PYTHON_INCLUDE_DIRS )
+            ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries ..." )
+            # Test if we can link against the Python libraries and include Python.h
+            try_compile( PYTHON_LIBS_WORKING ${CMAKE_CURRENT_BINARY_DIR}
+                         ${__test_python}
+                         CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PYTHON_INCLUDE_DIRS}"
+                         LINK_LIBRARIES ${PYTHON_LIBRARIES}
+                         OUTPUT_VARIABLE __try_compile_output )
+            if( PYTHON_LIBS_WORKING )
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries successful" )
+            else()
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries failed\n${__try_compile_output}" )
+            endif()
+
+        else()
+            ecbuild_debug( "ecbuild_find_python: Python library and include diretory not found" )
+        endif()
+
+    endif()
+
+    find_package_handle_standard_args( Python DEFAULT_MSG ${__required_vars} )
+
+    ecbuild_debug_var( PYTHONINTERP_FOUND )
+    ecbuild_debug_var( PYTHON_FOUND )
+    ecbuild_debug_var( PYTHON_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_CONFIG_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_VERSION_MAJOR )
+    ecbuild_debug_var( PYTHON_VERSION_MINOR )
+    ecbuild_debug_var( PYTHON_VERSION_PATCH )
+    ecbuild_debug_var( PYTHON_VERSION_STRING )
+    ecbuild_debug_var( PYTHON_INCLUDE_DIRS )
+    ecbuild_debug_var( PYTHON_LIBRARIES )
+    ecbuild_debug_var( PYTHON_SITE_PACKAGES )
+
+    set( PYTHONINTERP_FOUND    ${PYTHONINTERP_FOUND} PARENT_SCOPE )
+    set( PYTHONLIBS_FOUND      ${PYTHONLIBS_FOUND} PARENT_SCOPE )
+    set( PYTHON_FOUND          ${PYTHON_FOUND} PARENT_SCOPE )
+    set( PYTHON_EXECUTABLE     ${PYTHON_EXECUTABLE} PARENT_SCOPE )
+    set( PYTHON_VERSION_MAJOR  ${PYTHON_VERSION_MAJOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_MINOR  ${PYTHON_VERSION_MINOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_PATCH  ${PYTHON_VERSION_PATCH} PARENT_SCOPE )
+    set( PYTHON_VERSION_STRING ${PYTHON_VERSION_STRING} PARENT_SCOPE )
+    set( PYTHON_INCLUDE_DIRS   ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE )
+    set( PYTHON_LIBRARIES      ${PYTHON_LIBRARIES} PARENT_SCOPE )
+    set( PYTHON_SITE_PACKAGES  ${PYTHON_SITE_PACKAGES} PARENT_SCOPE )
+
+endfunction( ecbuild_find_python )
diff --git a/ecbuild/cmake/ecbuild_generate_config_headers.cmake b/ecbuild/cmake/ecbuild_generate_config_headers.cmake
new file mode 100644
index 0000000..8f2d44e
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_generate_config_headers.cmake
@@ -0,0 +1,63 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_config_headers
+# ===============================
+#
+# Generates the ecBuild configuration header for the project with the system
+# introspection done by CMake. ::
+#
+#   ecbuild_generate_config_headers( [ DESTINATION <directory> ] )
+#
+# Options
+# -------
+#
+# DESTINATION : optional
+#   installation destination directory
+#
+##############################################################################
+
+function( ecbuild_generate_config_headers )
+
+  # parse parameters
+
+  set( options )
+  set( single_value_args DESTINATION )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_config_headers(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # generate list of compiler flags
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${langs} )
+    set( EC_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+  endforeach()
+
+  configure_file( ${ECBUILD_MACROS_DIR}/ecbuild_config.h.in  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h   )
+
+  # install ecbuild configuration
+
+  set( _destination ${INSTALL_INCLUDE_DIR} )
+  if( _p_DESTINATION )
+    set( _destination ${_p_DESTINATION} )
+  endif()
+
+  install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h DESTINATION ${_destination} )
+
+endfunction( ecbuild_generate_config_headers )
diff --git a/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake b/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake
new file mode 100644
index 0000000..a127315
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake
@@ -0,0 +1,143 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_fortran_interfaces
+# ===================================
+#
+# Generates interfaces from the Fortran source files. ::
+#
+#   ecbuild_generate_fortran_interfaces()
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+##############################################################################
+
+function( ecbuild_generate_fortran_interfaces )
+
+  find_program( FCM_EXECUTABLE fcm REQUIRED DOC "Fortran interface generator" )
+
+  if( NOT FCM_EXECUTABLE )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: fcm executable not found." )
+  endif()
+
+  set( options )
+  set( single_value_args TARGET DESTINATION PARALLEL INCLUDE_DIRS GENERATED SOURCE_DIR FCM_CONFIG_FILE )
+  set( multi_value_args DIRECTORIES )
+
+  cmake_parse_arguments( P "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT DEFINED P_TARGET )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: TARGET argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DESTINATION )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DESTINATION argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DIRECTORIES )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DIRECTORIES argument missing" )
+  endif()
+
+  if( NOT DEFINED P_PARALLEL OR (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") )
+    set( P_PARALLEL 1 )
+  endif()
+
+  ecbuild_debug_var( P_PARALLEL )
+
+  if( NOT DEFINED P_SOURCE_DIR )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: SOURCE_DIR argument missing")
+  endif()
+
+  if( DEFINED P_FCM_CONFIG_FILE )
+    set( FCM_CONFIG_FILE ${P_FCM_CONFIG_FILE} )
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( PROJECT_FCM_CONFIG_FILE "${PROJECT_SOURCE_DIR}/cmake/fcm-make-interfaces.cfg" )
+    if( EXISTS ${PROJECT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${PROJECT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${PROJECT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${PROJECT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( DEFAULT_FCM_CONFIG_FILE "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg" )
+    if( EXISTS ${DEFAULT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${DEFAULT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  ecbuild_debug_var( FCM_CONFIG_FILE )
+
+  if( NOT EXISTS ${FCM_CONFIG_FILE} )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: needs fcm configuration in ${FCM_CONFIG_FILE}" )
+  endif()
+
+  foreach( _srcdir ${P_DIRECTORIES} )
+    if( _srcdir MATCHES "/$" )
+      ecbuild_critical("ecbuild_generate_fortran_interfaces: directory ${_srcdir} must not end with /")
+    endif()
+    ecbuild_list_add_pattern( LIST fortran_files SOURCE_DIR ${P_SOURCE_DIR} GLOB ${_srcdir}/*.F* )
+  endforeach()
+
+  string( REPLACE ";" " " _srcdirs "${P_DIRECTORIES}" )
+
+  set( _cnt 0 )
+  foreach( file ${_fortran_files} )
+    if( ${${SRC}/file} IS_NEWER_THAN ${${SRC}/file} )
+      set( run_fcm 1 )
+    endif()
+  endforeach()
+
+  foreach( fortran_file ${fortran_files} )
+    #list( APPEND fullpath_fortran_files ${CMAKE_CURRENT_SOURCE_DIR}/${fortran_file} )
+      get_filename_component(base ${fortran_file} NAME_WE)
+      set( interface_file "${CMAKE_CURRENT_BINARY_DIR}/interfaces/include/${base}.intfb.h" )
+      list( APPEND interface_files ${interface_file} )
+      set_source_files_properties( ${interface_file} PROPERTIES GENERATED TRUE )
+      math(EXPR _cnt "${_cnt}+1")
+  endforeach()
+
+  ecbuild_info("Target ${P_TARGET} will generate ${_cnt} interface files using FCM")
+
+
+
+  if( DEFINED P_GENERATED )
+    set( ${P_GENERATED} ${interface_files} PARENT_SCOPE )
+  endif()
+
+  set( include_dir ${CMAKE_CURRENT_BINARY_DIR}/${P_DESTINATION}/interfaces/include )
+  set( ${P_INCLUDE_DIRS} ${include_dir} PARENT_SCOPE )
+
+  execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${include_dir}
+                   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+
+    add_custom_command(
+      OUTPUT  "${P_DESTINATION}/${P_TARGET}.timestamp"
+      COMMAND ${FCM_EXECUTABLE} make -j ${P_PARALLEL} --config-file=${FCM_CONFIG_FILE} interfaces.ns-incl=${_srcdirs} interfaces.source=${P_SOURCE_DIR}
+      COMMAND touch "${P_TARGET}.timestamp"
+      DEPENDS ${fortran_files}
+      COMMENT "Generating ${_cnt} interface files for target ${P_TARGET}"
+      WORKING_DIRECTORY ${P_DESTINATION} VERBATIM )
+
+    add_custom_target( ${P_TARGET} DEPENDS ${P_DESTINATION}/${P_TARGET}.timestamp )
+
+
+endfunction( ecbuild_generate_fortran_interfaces )
diff --git a/ecbuild/cmake/ecbuild_generate_rpc.cmake b/ecbuild/cmake/ecbuild_generate_rpc.cmake
new file mode 100644
index 0000000..2eb8605
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_generate_rpc.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_rpc
+# ====================
+#
+# Process RPC (Remote Procedure Call) Language files using rpcgen. ::
+#
+#   ecbuild_generate_rpc( SOURCE <file>
+#                         [ TARGET_H <file> ]
+#                         [ TARGET_C <file> ]
+#                         [ DEPENDANT <file1> [ <file2> ... ] ] )
+#
+# Options
+# -------
+#
+# SOURCE : required
+#   RPC source file
+#
+# TARGET_H : optional (required if TARGET_C not given)
+#   name of header file to be generated
+#
+# TARGET_C : optional (required if TARGET_H not given)
+#   name of source file to be generated
+#
+# DEPENDANT : optional
+#  list of files which depend on the generated source and header files
+#
+##############################################################################
+
+macro( ecbuild_generate_rpc )
+
+  set( options )
+  set( single_value_args SOURCE TARGET_H TARGET_C )
+  set( multi_value_args DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_rpc(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SOURCE  )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the SOURCE file.")
+  endif()
+
+  # optional
+  #    if( NOT _PAR_DEPENDANT )
+  #      ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the DEPENDANT files.")
+  #    endif()
+
+  if( NOT DEFINED _PAR_TARGET_H AND NOT DEFINED _PAR_TARGET_C )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the _PAR_TARGET_H or _PAR_TARGET_C files.")
+  endif()
+
+  find_package( RPCGEN REQUIRED )
+
+  if( DEFINED _PAR_TARGET_H )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}
+      COMMAND ${RPCGEN_EXECUTABLE} -h -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}" )
+      endforeach()
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_TARGET_C )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}
+      COMMAND ${RPCGEN_EXECUTABLE} -c -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}" )
+      endforeach()
+    endif()
+
+  endif()
+
+endmacro( ecbuild_generate_rpc )
diff --git a/ecbuild/cmake/ecbuild_generate_yy.cmake b/ecbuild/cmake/ecbuild_generate_yy.cmake
new file mode 100644
index 0000000..ea7ec13
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_generate_yy.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_yy
+# ===================
+#
+# Process lex/yacc files. ::
+#
+#   ecbuild_generate_yy( YYPREFIX <prefix>
+#                        YACC <file>
+#                        LEX <file>
+#                        DEPENDANT <file1> [ <file2> ... ]
+#                        [ SOURCE_DIR <dir> ]
+#                        [ OUTPUT_DIRECTORY <dir> ]
+#                        [ YACC_TARGET <file> ]
+#                        [ LEX_TARGET <file> ]
+#                        [ YACC_FLAGS <flags> ]
+#                        [ LEX_FLAGS <flags> ]
+#                        [ BISON_FLAGS <flags> ]
+#                        [ FLEX_FLAGS <flags> ] )
+#
+# Options
+# -------
+#
+# YYPREFIX : required
+#   prefix to use for file and function names
+#
+# YACC : required
+#   base name of the yacc source file (without .y extension)
+#
+# LEX : required
+#   base name of the lex source file (without .l extension)
+#
+# DEPENDANT : required
+#  list of files which depend on the generated lex and yacc target files
+#  At least one should be an existing source file (not generated itself).
+#
+# SOURCE_DIR : optional, defaults to CMAKE_CURRENT_SOURCE_DIR
+#   directory where yacc and lex source files are located
+#
+# OUTPUT_DIRECTORY : optional, defaults to CMAKE_CURRENT_BINARY_DIR
+#   output directory for yacc and lex target files
+#
+# YACC_TARGET : optional, defaults to YACC
+#   base name of the generated yacc target file (without .c extension)
+#
+# LEX_TARGET : optional, defaults to LEX
+#   base name of the generated lex target file (without .c extension)
+#
+# YACC_FLAGS : optional, defaults to -t
+#   flags to pass to yacc executable
+#
+# LEX_FLAGS : optional
+#   flags to pass to lex executable
+#
+# BISON_FLAGS : optional, defaults to -t
+#   flags to pass to bison executable
+#
+# FLEX_FLAGS : optional, defaults to -l
+#   flags to pass to flex executable
+#
+##############################################################################
+
+macro( ecbuild_generate_yy )
+
+  ecbuild_find_lexyacc() # find [ yacc|byson ] and [ lex|flex ]
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args YYPREFIX YACC LEX SOURCE_DIR OUTPUT_DIRECTORY YACC_TARGET LEX_TARGET LEX_FLAGS YACC_FLAGS FLEX_FLAGS BISON_FLAGS )
+  set( multi_value_args  DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_yy(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_YYPREFIX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YYPREFIX.")
+  endif()
+
+  if( NOT _PAR_YACC  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YACC file.")
+  endif()
+
+  if( NOT _PAR_LEX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the LEX file.")
+  endif()
+
+  if( NOT _PAR_DEPENDANT )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the DEPENDANT files.")
+  endif()
+
+  set( BASE ${_PAR_YYPREFIX}_${_PAR_YACC} )
+
+  ## default flags
+
+  if( NOT _PAR_LEX_FLAGS )
+    set( _PAR_LEX_FLAGS "" )
+  endif()
+
+  if( NOT _PAR_FLEX_FLAGS )
+    set( _PAR_FLEX_FLAGS "-l" )
+  endif()
+
+  if( NOT _PAR_YACC_FLAGS )
+    set( _PAR_YACC_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_BISON_FLAGS )
+    set( _PAR_BISON_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_YACC_TARGET )
+    set ( _PAR_YACC_TARGET ${_PAR_YACC} )
+  endif()
+
+  if ( NOT _PAR_LEX_TARGET )
+    set ( _PAR_LEX_TARGET ${_PAR_LEX} )
+  endif()
+
+  if( NOT _PAR_SOURCE_DIR )
+    set( _PAR_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
+  endif()
+
+  if( NOT _PAR_OUTPUT_DIRECTORY )
+    set( _PAR_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+  else()
+    file( MAKE_DIRECTORY ${_PAR_OUTPUT_DIRECTORY} )
+  endif()
+
+  set( ${BASE}yy_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.c )
+  set( ${BASE}yh_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.h )
+  set( ${BASE}yl_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.tmp.c )
+
+  set( ${BASE}yy_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.c )
+  set( ${BASE}yh_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.h )
+  set( ${BASE}yl_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.c )
+
+  if( BISON_FOUND )
+    bison_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_BISON_FLAGS}" )
+  else()
+    yacc_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_YACC_FLAGS}" )
+  endif()
+
+  if( FLEX_FOUND )
+    flex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_FLEX_FLAGS}" )
+    add_flex_bison_dependency(${BASE}_scanner ${BASE}_parser)
+  else()
+    lex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_LEX_FLAGS}" )
+    add_lex_yacc_dependency(${BASE}_scanner ${BASE}_parser)
+  endif()
+
+  set_source_files_properties(${${BASE}yy_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_tmp_target} GENERATED)
+
+  add_custom_command(OUTPUT  ${${BASE}yy_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yy_tmp_target} ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yy_target}
+    DEPENDS ${${BASE}yy_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yh_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yh_tmp_target} ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.h/\\.h/g' ${${BASE}yh_target}
+    DEPENDS ${${BASE}yh_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yl_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yl_tmp_target} ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yl_target}
+    DEPENDS ${${BASE}yl_tmp_target}
+    )
+
+  set_source_files_properties(${${BASE}yy_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_target} GENERATED)
+
+  foreach( file ${_PAR_DEPENDANT} )
+    if( NOT IS_ABSOLUTE ${file})
+      set( file ${_PAR_SOURCE_DIR}/${file} )
+    endif()
+    set_source_files_properties( ${file} PROPERTIES
+        OBJECT_DEPENDS "${${BASE}yy_target};${${BASE}yh_target};${${BASE}yl_target}" )
+  endforeach()
+
+endmacro( ecbuild_generate_yy )
diff --git a/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake b/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake
new file mode 100644
index 0000000..acb0b21
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_cxx11_flags
+# =======================
+#
+# Set the CMake variable ``${CXX11_FLAGS}`` to the C++11 flags for the current
+# compiler (based on macros from https://github.com/UCL/GreatCMakeCookOff). ::
+#
+#   ecbuild_get_cxx11_flags( CXX11_FLAGS )
+#
+##############################################################################
+
+function( ecbuild_get_cxx11_flags CXX11_FLAGS )
+
+  include(CheckCXXCompilerFlag)
+
+  # On older cmake versions + newer compilers,
+  # the given version of CheckCXXCompilerFlags does not quite work.
+  if(CMAKE_VERSION VERSION_LESS 2.8.9)
+    macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+       set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+       set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+       CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+         # Some compilers do not fail with a bad flag
+         FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+         FAIL_REGEX "unrecognized .*option"                     # GNU
+         FAIL_REGEX "unknown .*option"                          # Clang
+         FAIL_REGEX "ignoring unknown option"                   # MSVC
+         FAIL_REGEX "warning D9002"                             # MSVC, any lang
+         FAIL_REGEX "option.*not supported"                     # Intel
+         FAIL_REGEX "invalid argument .*option"                 # Intel
+         FAIL_REGEX "ignoring option .*argument required"       # Intel
+         FAIL_REGEX "[Uu]nknown option"                         # HP
+         FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+         FAIL_REGEX "command option .* is not recognized"       # XL
+         FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+         FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+         FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+         )
+       set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+    endmacro ()
+  endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+  check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+  check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+  check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+  if(MINGW)
+    check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+    check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+  endif(MINGW)
+  if(has_std_gnupp11)
+    set(${CXX11_FLAGS} "-std=gnu++11" PARENT_SCOPE)
+  elseif(has_std_gnupp0x)
+    set(${CXX11_FLAGS} "-std=gnu++0x" PARENT_SCOPE)
+  elseif(has_hstd_cpp11)
+    set(${CXX11_FLAGS} "-hstd=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp11)
+    set(${CXX11_FLAGS} "-std=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp0x)
+    set(${CXX11_FLAGS} "-std=c++0x" PARENT_SCOPE)
+  else()
+    ecbuild_critical("Could not detect C++11 flags")
+  endif(has_std_gnupp11)
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_get_date.cmake b/ecbuild/cmake/ecbuild_get_date.cmake
new file mode 100644
index 0000000..0f94d7d
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_get_date.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_date
+# ================
+#
+# Set the CMake variable ``${DATE}`` to the current date in the form
+# YYYY.mm.DD. ::
+#
+#   ecbuild_get_date( DATE )
+#
+##############################################################################
+
+macro(ecbuild_get_date RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%d/%m/%Y" OUTPUT_VARIABLE ${RESULT})
+        string(REGEX REPLACE "(..)/(..)/(....).*" "\\3.\\2.\\1" ${RESULT} ${${RESULT}})
+    else()
+        ecbuild_error("date not implemented")
+    endif()
+endmacro(ecbuild_get_date)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_timestamp
+# =====================
+#
+# Set the CMake variable ``${TIMESTAMP}`` to the current date and time in the
+# form YYYYmmDDHHMMSS. ::
+#
+#   ecbuild_get_timestamp( TIMESTAMP )
+#
+##############################################################################
+
+macro(ecbuild_get_timestamp RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%Y/%m/%d/%H/%M/%S" OUTPUT_VARIABLE _timestamp)
+        string(REGEX REPLACE "(....)/(..)/(..)/(..)/(..)/(..).*" "\\1\\2\\3\\4\\5\\6" ${RESULT} ${_timestamp})
+    else()
+        ecbuild_warn("This is NOT UNIX - timestamp not implemented")
+    endif()
+endmacro(ecbuild_get_timestamp)
+
diff --git a/ecbuild/cmake/ecbuild_get_resources.cmake b/ecbuild/cmake/ecbuild_get_resources.cmake
new file mode 100644
index 0000000..d6c20e4
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_get_resources.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for adding a test
+##############################################################################
+
+macro( ecbuild_get_resources )
+
+    set( options                )
+    set( single_value_args TO_DIR  )
+    set( multi_value_args  LIST )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_get_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_LIST )
+        ecbuild_critical( "Missing parameter LIST of resources in macro ecbuild_get_resources()" )
+    endif()
+
+    if( NOT _PAR_TO_DIR )
+		set( _PAR_TO_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+	endif()
+
+    list( LENGTH _PAR_LIST _rsize )
+    math( EXPR _max "${_rsize}-1" )
+    foreach( i RANGE 0 ${_max} 2 )
+
+        math( EXPR in "${i}+1" )
+
+        list( GET _PAR_LIST ${i}  r  )
+        list( GET _PAR_LIST ${in} rh )
+
+#        ecbuild_debug_var( r  )
+#        ecbuild_debug_var( rh )
+
+        get_filename_component( rf ${r} NAME )
+
+        file( DOWNLOAD ${r} ${_PAR_TO_DIR}/${rf} EXPECTED_HASH SHA1=${rh} )
+
+    endforeach()
+
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_get_test_data.cmake b/ecbuild/cmake/ecbuild_get_test_data.cmake
new file mode 100644
index 0000000..4945b90
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_get_test_data.cmake
@@ -0,0 +1,427 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# function for downloading test data
+
+function( _download_test_data _p_NAME _p_DIRNAME )
+
+  # TODO: make that 'at ecmwf'
+  #if(1)
+  #unset(ENV{no_proxy})
+  #unset(ENV{NO_PROXY})
+  #set(ENV{http_proxy} "http://proxy.ecmwf.int:3333")
+  #endif()
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  find_program( CURL_PROGRAM curl )
+  mark_as_advanced(CURL_PROGRAM)
+
+  if( CURL_PROGRAM )
+
+    add_custom_command( OUTPUT ${_p_NAME}
+      COMMENT "(curl) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+      COMMAND ${CURL_PROGRAM} --silent --show-error --fail --output ${_p_NAME}
+              --retry ${ECBUILD_DOWNLOAD_RETRIES}
+              --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+              http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+  else()
+
+    find_program( WGET_PROGRAM wget )
+
+    if( WGET_PROGRAM )
+
+      # wget takes the total number of tries, curl the number or retries
+      math( EXPR ECBUILD_DOWNLOAD_RETRIES "${ECBUILD_DOWNLOAD_RETRIES} + 1" )
+
+      add_custom_command( OUTPUT ${_p_NAME}
+        COMMENT "(wget) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+        COMMAND ${WGET_PROGRAM} -nv -O ${_p_NAME}
+                -t ${ECBUILD_DOWNLOAD_RETRIES} -T ${ECBUILD_DOWNLOAD_TIMEOUT}
+                http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+    else()
+
+      if( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+        ecbuild_warn( "Couldn't find curl neither wget -- cannot download test data from server.\nPlease obtain the test data by other means and pleace it in the build directory." )
+        set( WARNING_CANNOT_DOWNLOAD_TEST_DATA 1 CACHE INTERNAL "Couldn't find curl neither wget -- cannot download test data from server" )
+        mark_as_advanced( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+      endif()
+
+    endif()
+
+  endif()
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_data
+# =====================
+#
+# Download a test data set at build time. ::
+#
+#   ecbuild_get_test_data( NAME <name>
+#                          [ TARGET <target> ]
+#                          [ DIRNAME <dir> ]
+#                          [ MD5 <hash> ]
+#                          [ EXTRACT ]
+#                          [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAME : required
+#   name of the test data file
+#
+# TARGET : optional, defaults to test_data_<name>
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# MD5 : optional, ignored if NOCHECK is given
+#   md5 checksum of the data set to verify. If not given and NOCHECK is *not*
+#   set, download the md5 checksum and verify
+#
+# EXTRACT : optional
+#   extract the downloaded file (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>/<NAME>``
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, the downloaded file is verified against an md5 checksum, either
+# given as the ``MD5`` argument or downloaded from the server otherwise. Use
+# the argument ``NOCHECK`` to disable this check.
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+# Examples
+# --------
+#
+# Do not verify the checksum: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib NOCHECK )
+#
+# Checksum agains remote md5 file: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib MD5 f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_data )
+
+    set( options NOCHECK EXTRACT )
+    set( single_value_args TARGET URL NAME DIRNAME MD5 SHA1)
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    file( RELATIVE_PATH currdir ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} )
+
+    ### check parameters
+
+    if( NOT _p_NAME )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAME")
+    endif()
+
+    if( NOT _p_TARGET )
+      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "test_data_${_p_NAME}")
+#      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "${_p_NAME}")
+#      set( _p_TARGET ${_p_NAME} )
+    endif()
+
+    if( NOT _p_DIRNAME )
+      set( _p_DIRNAME ${PROJECT_NAME}/${currdir} )
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_URL )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    # download the data
+
+    _download_test_data( ${_p_NAME} ${_p_DIRNAME} )
+
+    # perform the checksum if requested
+
+    set( _deps ${_p_NAME} )
+
+    if( NOT _p_NOCHECK )
+
+        if( NOT _p_MD5 AND NOT _p_SHA1) # use remote md5
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            _download_test_data( ${_p_NAME}.md5 ${_p_DIRNAME} )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 ${_p_NAME}.md5 )
+
+            list( APPEND _deps  ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+        if( _p_MD5 )
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            configure_file( "${ECBUILD_MACROS_DIR}/md5.in" ${_p_NAME}.md5 @ONLY )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 )
+
+            list( APPEND _deps ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+#        if( _p_SHA1 )
+
+#            find_program( SHASUM NAMES sha1sum shasum )
+#            if( SHASUM )
+#                add_custom_command( OUTPUT ${_p_NAME}.localsha1
+#                                    COMMAND ${SHASUM} ${_p_NAME} > ${_p_NAME}.localsha1 )
+
+#                add_custom_command( OUTPUT ${_p_NAME}.ok
+#                                    COMMAND diff ${_p_NAME}.sha1 ${_p_NAME}.localsha1 && touch ${_p_NAME}.ok )
+
+#                configure_file( "${ECBUILD_MACROS_DIR}/sha1.in" ${_p_NAME}.sha1 @ONLY )
+
+#                list( APPEND _deps ${_p_NAME}.localsha1 ${_p_NAME}.ok )
+#            endif()
+
+#        endif()
+
+    endif()
+
+    add_custom_target( ${_p_TARGET} DEPENDS ${_deps} )
+
+    if( _p_EXTRACT )
+      ecbuild_debug("ecbuild_get_test_data: extracting ${_p_NAME} (post-build for target ${_p_TARGET}")
+      add_custom_command( TARGET ${_p_TARGET} POST_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E tar xv ${_p_NAME} )
+    endif()
+
+endfunction(ecbuild_get_test_data)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_multidata
+# ==========================
+#
+# Download multiple test data sets at build time. ::
+#
+#   ecbuild_get_test_multidata( NAMES <name1> [ <name2> ... ]
+#                               TARGET <target>
+#                               [ DIRNAME <dir> ]
+#                               [ LABELS <label1> [<label2> ...] ]
+#                               [ EXTRACT ]
+#                               [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAMES : required
+#   list of names of the test data files
+#
+# TARGET : optional
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   Lower case project name and ``download_data`` are always added as labels.
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# EXTRACT : optional
+#   extract downloaded files (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>``
+# for each name given in the list of ``NAMES``. Each name may contain a
+# relative path, which is appended to ``DIRNAME`` and may be followed by an
+# md5 checksum, separated with a ``:`` (the name must not contain spaces).
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, each downloaded file is verified against an md5 checksum, either
+# given as part of the name as described above or a remote checksum downloaded
+# from the server. Use the argument ``NOCHECK`` to disable this check.
+#
+# Examples
+# --------
+#
+# Do not verify checksums: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir NOCHECK )
+#
+# Checksums agains remote md5 file: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data DIRNAME test/data/dir
+#                               NAMES msl.grib:f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_multidata )
+
+    set( options EXTRACT NOCHECK )
+    set( single_value_args TARGET DIRNAME )
+    set( multi_value_args  NAMES LABELS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    ### check parameters
+
+    if( NOT _p_NAMES )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAMES")
+    endif()
+
+    if( NOT _p_TARGET )
+      ecbuild_critical("ecbuild_get_test_data() expects a TARGET")
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    if( _p_EXTRACT )
+        set( _extract EXTRACT )
+    endif()
+
+    if( _p_NOCHECK )
+        set( _nocheck NOCHECK )
+    endif()
+
+    ### prepare file
+
+    set( _script ${CMAKE_CURRENT_BINARY_DIR}/get_data_${_p_TARGET}.cmake )
+
+    file( WRITE ${_script} "
+function(EXEC_CHECK)
+     execute_process(COMMAND \${ARGV} RESULT_VARIABLE CMD_RESULT)
+     if(CMD_RESULT)
+           message(FATAL_ERROR \"Error running ${CMD}\")
+     endif()
+endfunction()\n\n" )
+
+    foreach( _d ${_p_NAMES} )
+
+        string( REGEX MATCH "[^:]+" _f "${_d}" )
+
+        get_filename_component( _file ${_f} NAME )
+        get_filename_component( _dir  ${_f} PATH )
+
+        list( APPEND _path_comps ${_p_DIRNAME} ${_dir} )
+        join( _path_comps "/" _dirname )
+        if( _dirname )
+            set( _dirname DIRNAME ${_dirname} )
+        endif()
+        unset( _path_comps )
+
+        string( REPLACE "." "_" _name "${_file}" )
+        string( REGEX MATCH ":.*"  _md5  "${_d}" )
+        string( REPLACE ":" "" _md5 "${_md5}" )
+
+        if( _md5 )
+            set( _md5 MD5 ${_md5} )
+        endif()
+
+        #ecbuild_debug_var(_f)
+        #ecbuild_debug_var(_file)
+        #ecbuild_debug_var(_dirname)
+        #ecbuild_debug_var(_name)
+        #ecbuild_debug_var(_md5)
+
+        ecbuild_get_test_data(
+            TARGET __get_data_${_p_TARGET}_${_name}
+            NAME ${_file} ${_dirname} ${_md5} ${_extract} ${_nocheck} )
+
+        # The option /fast disables dependency checking on a target, see
+        # https://cmake.org/Wiki/CMake_FAQ#Is_there_a_way_to_skip_checking_of_dependent_libraries_when_compiling.3F
+        if( WIN32 )
+          set( _fast "\fast" )
+        else()
+          set( _fast "/fast" )
+        endif()
+        file( APPEND ${_script}
+              "exec_check( \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target __get_data_${_p_TARGET}_${_name}${_fast} )\n" )
+
+    endforeach()
+
+    if( ENABLE_TESTS )
+      add_test(  NAME ${_p_TARGET} COMMAND ${CMAKE_COMMAND} -P ${_script} )
+      set( _p_LABELS ${PROJECT_NAME_LOWCASE} download_data ${_p_LABELS} )
+      list( REMOVE_DUPLICATES _p_LABELS )
+      set_property( TEST ${_p_TARGET} APPEND PROPERTY LABELS "${_p_LABELS}" )
+    endif()
+
+endfunction(ecbuild_get_test_multidata)
diff --git a/ecbuild/cmake/ecbuild_git.cmake b/ecbuild/cmake/ecbuild_git.cmake
new file mode 100644
index 0000000..18aab19
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_git.cmake
@@ -0,0 +1,300 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( ECBUILD_GIT  ON  CACHE BOOL "Turn on/off ecbuild_git() function" )
+
+mark_as_advanced(ECBUILD_GIT)
+
+if( ECBUILD_GIT )
+
+  find_package(Git)
+
+  set( ECMWF_USER $ENV{USER} CACHE STRING "ECMWF git user" )
+  set( ECMWF_GIT  SSH        CACHE STRING "ECMWF git protocol" )
+
+  set( ECMWF_GIT_SSH   "ssh://git@software.ecmwf.int:7999"                  CACHE INTERNAL "ECMWF ssh address" )
+  set( ECMWF_GIT_HTTPS "https://${ECMWF_USER}@software.ecmwf.int/stash/scm" CACHE INTERNAL "ECMWF https address" )
+
+  if( ECMWF_GIT MATCHES "[Ss][Ss][Hh]" )
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_SSH} CACHE INTERNAL "" )
+  else()
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_HTTPS} CACHE INTERNAL "" )
+  endif()
+
+endif()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_git
+# ===========
+#
+# Manages an external Git repository. ::
+#
+#   ecbuild_git( PROJECT <name>
+#                DIR <directory>
+#                URL <giturl>
+#                [ BRANCH <gitbranch> | TAG <gittag> ]
+#                [ UPDATE | NOREMOTE ] )
+#                [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# URL : required
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecbuild_git )
+
+  set( options UPDATE NOREMOTE MANUAL )
+  set( single_value_args PROJECT DIR URL TAG BRANCH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _PAR_BRANCH AND DEFINED _PAR_TAG )
+    ecbuild_critical( "Cannot defined both BRANCH and TAG in macro ecbuild_git" )
+  endif()
+
+  if( _PAR_UPDATE AND _PAR_NOREMOTE )
+    ecbuild_critical( "Cannot pass both NOREMOTE and UPDATE in macro ecbuild_git" )
+  endif()
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_git(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( ECBUILD_GIT )
+
+    set( _needs_switch 0 )
+
+    get_filename_component( ABS_PAR_DIR "${_PAR_DIR}" ABSOLUTE )
+    get_filename_component( PARENT_DIR  "${_PAR_DIR}/.." ABSOLUTE )
+
+    ### clone if no directory
+
+    if( NOT EXISTS "${_PAR_DIR}" )
+
+      ecbuild_info( "Cloning ${_PAR_PROJECT} from ${_PAR_URL} into ${_PAR_DIR}...")
+      execute_process(
+        COMMAND ${GIT_EXECUTABLE} "clone" ${_PAR_URL} ${clone_args} ${_PAR_DIR} "-q"
+        RESULT_VARIABLE nok ERROR_VARIABLE error
+        WORKING_DIRECTORY "${PARENT_DIR}")
+      if(nok)
+        ecbuild_critical("${_PAR_DIR} git clone failed:\n  ${GIT_EXECUTABLE} clone ${_PAR_URL} ${clone_args} ${_PAR_DIR} -q\n  ${error}\n")
+      endif()
+      ecbuild_info( "${_PAR_DIR} retrieved.")
+      set( _needs_switch 1 )
+
+    endif()
+
+    ### check current tag and sha1
+
+    if( IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
+                       OUTPUT_VARIABLE _sha1 RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if(nok)
+        ecbuild_info("git rev-parse HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+                       OUTPUT_VARIABLE _current_branch RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if( nok OR _current_branch STREQUAL "" )
+        ecbuild_info("git rev-parse --abbrev-ref HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} describe --exact-match --abbrev=0
+                       OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE  ERROR_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+
+      if( error MATCHES "no tag exactly matches" OR error MATCHES "No names found" )
+        unset( _current_tag )
+      else()
+        if( nok )
+          ecbuild_info("git describe --exact-match --abbrev=0 on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+      if( NOT _current_tag ) # try nother method
+        execute_process( COMMAND ${GIT_EXECUTABLE} name-rev --tags --name-only ${_sha1}
+                         OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                         OUTPUT_STRIP_TRAILING_WHITESPACE
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+        if( nok OR _current_tag STREQUAL "" )
+          ecbuild_info("git name-rev --tags --name-only on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_BRANCH AND NOT "${_current_branch}" STREQUAL "${_PAR_BRANCH}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_TAG AND NOT "${_current_tag}" STREQUAL "${_PAR_TAG}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( DEFINED _PAR_BRANCH AND _PAR_UPDATE AND NOT _PAR_NOREMOTE )
+
+      add_custom_target( git_update_${_PAR_PROJECT}
+                         COMMAND "${GIT_EXECUTABLE}" pull -q
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}"
+                         COMMENT "git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR}" )
+
+      set( git_update_targets "git_update_${_PAR_PROJECT};${git_update_targets}" )
+
+    endif()
+
+    ### updates
+
+    if( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      if( DEFINED _PAR_BRANCH )
+        set ( _gitref ${_PAR_BRANCH} )
+        ecbuild_info("Updating ${_PAR_PROJECT} to head of BRANCH ${_PAR_BRANCH}...")
+      else()
+        ecbuild_info("Updating ${_PAR_PROJECT} to TAG ${_PAR_TAG}...")
+        set ( _gitref ${_PAR_TAG} )
+      endif()
+
+      # fetching latest tags and branches
+
+      if( NOT _PAR_NOREMOTE )
+
+        ecbuild_info("git fetch --all @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+        ecbuild_info("git fetch --all --tags @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all --tags -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all --tags in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      else()
+        ecbuild_info("${_PAR_DIR} marked NOREMOTE : Skipping git fetch")
+      endif()
+
+      # checking out gitref
+
+      ecbuild_info("git checkout ${_gitref} @ ${ABS_PAR_DIR}")
+      execute_process( COMMAND "${GIT_EXECUTABLE}" checkout -q "${_gitref}"
+                       RESULT_VARIABLE nok ERROR_VARIABLE error
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}")
+      if(nok)
+        ecbuild_critical("git checkout ${_gitref} on ${_PAR_DIR} failed:\n  ${GIT_EXECUTABLE} checkout -q ${_gitref}\n  ${error}")
+      endif()
+
+      if( DEFINED _PAR_BRANCH AND _PAR_UPDATE ) #############################################################################
+
+        # Use git pull --ff-only, we WANT this to fail on upstream rebase and
+        # we DON'T want merge commits here!
+        execute_process( COMMAND "${GIT_EXECUTABLE}" pull -q --ff-only
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_critical("git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      endif() ####################################################################################
+
+    endif( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+  endif( ECBUILD_GIT )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_stash
+# =============
+#
+# Manages an external Git repository on ECMWF Stash. ::
+#
+#   ecbuild_stash( PROJECT <name>
+#                  DIR <directory>
+#                  STASH <repository>
+#                  [ BRANCH <gitbranch> | TAG <gittag> ]
+#                  [ UPDATE | NOREMOTE ] )
+#                  [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# STASH : required
+#   Stash repository in the form <project>/<repository>
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecmwf_stash )
+
+  set( options )
+  set( single_value_args STASH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  ecbuild_git( URL "${ECMWF_GIT_ADDRESS}/${_PAR_STASH}.git" ${_PAR_UNPARSED_ARGUMENTS} )
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_install_project.cmake b/ecbuild/cmake/ecbuild_install_project.cmake
new file mode 100644
index 0000000..113e642
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_install_project.cmake
@@ -0,0 +1,437 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_install_project
+# =======================
+#
+# Set up packaging and export configuration. ::
+#
+#   ecbuild_install_project( NAME <name> [ DESCRIPTION <description> ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   project name used for packaging
+#
+# DESCRIPTION : optional
+#   project description used for packaging
+#
+# Usage
+# -----
+#
+# ``ecbuild_install_project`` should be called at the very end of any ecBuild
+# project (only followed by ``ecbuild_print_summary``), sets up packaging of
+# the project with cpack and exports the configuration and targets for other
+# projects to use.
+#
+# Unless ECBUILD_SKIP_<PNAME>_EXPORT is set, the following files are generated:
+#
+# :<project>-config.cmake:         default project configuration
+# :<project>-config-version.cmake: project version number
+# :<project>-import.cmake:         extra project configuration (optional)
+# :<project>-config.cmake.tpls:    3rd party project configurations
+# :<project>-targets.cmake:        exported targets
+#
+# For ``<project>-import.cmake`` to be exported to build and install tree,
+# ``<project>-import.cmake`` or ``<project>-import.cmake.in`` must exist in
+# the source tree. ``<project>-config.cmake.in`` and
+# ``<project>-config-version.cmake.in`` can be provided in the source tree to
+# override the default templates used to generate ``<project>-config.cmake``
+# and ``<project>-config-version.cmake``.
+#
+# In DEVELOPER_MODE, the build tree location is also added to the CMake user
+# package registry for top level projects.
+#
+# If the project is added as a subdirectory, the following CMake variables
+# are set in the parent scope:
+#
+# :<PROJECT>_FOUND:            set to ``TRUE``
+# :<project>_FOUND:            set to ``TRUE``
+# :<PROJECT>_VERSION:          version string
+# :<project>_VERSION:          version string
+# :<PROJECT>_INCLUDE_DIRS:     list of include directories
+# :<PROJECT>_LIBRARIES:        list of libraries
+# :<PROJECT>_DEFINITIONS:      list of compiler definitions
+# :<PROJECT>_TPLS:             list of 3rd party dependencies
+# :<PROJECT>_TPL_LIBRARIES:    libraries of 3rd party dependencies
+# :<PROJECT>_TPL_DEFINITIONS:  compiler definitions of 3rd party dependencies
+# :<PROJECT>_TPL_INCLUDE_DIRS: include directories of 3rd party dependencies
+# :<PROJECT>_FEATURES:         list of enabled features
+# :<PROJECT>_HAVE_<FEATURE>:   set to 1 for each enabled features
+#
+##############################################################################
+
+function( ecbuild_set_if_not_defined VAR VALUE )
+
+    if(NOT DEFINED ${VAR})
+        set( ${VAR} "${VALUE}" PARENT_SCOPE )
+        # ecbuild_info("Variable not defined, setting ${VAR} => ${VALUE}")
+    # else()
+        # ecbuild_info("${VAR} == ${${VAR}}")
+    endif()
+
+endfunction()
+
+macro( ecbuild_install_project )
+
+    set( options )
+    set( single_value_args NAME DESCRIPTION )
+    set( multi_value_args  COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_install_project(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_NAME  )
+      ecbuild_critical("The call to ecbuild_install_project() doesn't specify the NAME.")
+    endif()
+
+    ### EXTRA TARGETS #####################################################
+
+    # added here to avoid adding another macro call at the end of each project,
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        ecbuild_define_libs_and_execs_targets()
+        ecbuild_define_links_target()
+
+    endif()
+
+    ### PACKAGING ########################################################
+
+    set( PNAME ${PROJECT_NAME_CAPS} )
+    set( LNAME ${PROJECT_NAME_LOWCASE} )
+
+    # components
+
+    #    if( DEFINED _PAR_COMPONENTS )
+    #        set(CPACK_COMPONENTS_ALL   "${_PAR_COMPONENTS}")
+    #    else()
+    #        set(CPACK_COMPONENTS_ALL   "${PROJECT_NAME}")
+    #    endif()
+
+    # name, version, etc ...
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_NAME      "${_PAR_NAME}")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VERSION   "${${PNAME}_VERSION_STR}")
+    # Convert "/" to "-" for the case where the version string contains a "/"
+    string( REPLACE "/" "-" CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION} )
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_FILE_NAME   "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
+
+    #    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ECMWF") # required for DEB
+    #    set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON")
+    #    set(CPACK_RPM_COMPONENT_INSTALL "ON")
+
+    ecbuild_set_if_not_defined(CPACK_SOURCE_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VENDOR "ECMWF")
+
+    # short description
+
+    if( _PAR_DESCRIPTION )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_DESCRIPTION}" )
+    else()
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_NAME} misses a description" )
+    endif()
+
+    # long description
+
+    if( EXISTS ${PROJECT_SOURCE_DIR}/INSTALL )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/INSTALL")
+    endif()
+    if( EXISTS ${PROJECT_SOURCE_DIR}/LICENSE )
+        ecbuild_set_if_not_defined(CPACK_RESOURCE_FILE_LICENSE    "${PROJECT_SOURCE_DIR}/LICENSE")
+    endif()
+
+    # set(CPACK_PACKAGE_EXECUTABLES ${ECBUILD_ALL_EXES})
+
+    list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES
+          "${PROJECT_SOURCE_DIR}" "."
+          "${ECBUILD_MACROS_DIR}" "cmake/" )
+
+    # what to pack and not
+
+    set(CPACK_SOURCE_IGNORE_FILES
+        /build/
+        /\\\\.git/
+        /\\\\.svn/
+        CMakeLists.txt.user
+        \\\\.swp$
+        p4config
+    )
+
+    # skip the files that were declared as DONT_PACK
+
+    list( APPEND CPACK_SOURCE_IGNORE_FILES ${ECBUILD_DONT_PACK_FILES} )
+
+    # Find the ecbuild toolchain files and include in the source package if found
+    find_path( ECBUILD_TOOLCHAIN_DIR ecmwf-XC30-GNU.cmake
+               PATHS ${ECBUILD_MACROS_DIR}/../toolchains
+                     ${ECBUILD_MACROS_DIR}/../share/ecbuild/toolchains )
+
+    mark_as_advanced( ECBUILD_TOOLCHAIN_DIR )
+
+    if( ECBUILD_TOOLCHAIN_DIR )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_TOOLCHAIN_DIR}" "share/ecbuild/toolchains/" )
+    endif()
+
+    # Find the ecbuild bin directory and include in the source package if found
+    find_program( ECBUILD_SCRIPT ecbuild
+                  PATHS ${ECBUILD_MACROS_DIR}/../bin
+                        ${ECBUILD_MACROS_DIR}/../../../bin )
+
+    mark_as_advanced( ECBUILD_SCRIPT )
+
+    if( ECBUILD_SCRIPT )
+      get_filename_component( ECBUILD_BIN_DIR ${ECBUILD_SCRIPT} PATH )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_BIN_DIR}" "bin/" )
+    endif()
+
+    # cpack config file
+
+    # set(CPACK_INSTALL_CMAKE_PROJECTS "${${PROJECT_NAME}_BINARY_DIR}" "${PROJECT_NAME}" "${CPACK_COMPONENTS_ALL}" "*" )
+
+    include( CPack )
+
+    ### EXPORTS ########################################################
+
+    ecbuild_enabled_features( ${PROJECT_NAME_CAPS}_FEATURES )
+    foreach( _f ${${PNAME}_FEATURES} )
+        set( ${PNAME}_HAVE_${_f} 1 )
+    endforeach()
+
+    ecbuild_info( "${PROJECT_NAME_CAPS}_TPLS: ${${PROJECT_NAME_CAPS}_TPLS}" )
+
+    foreach( _tpl ${${PNAME}_TPLS} )
+        string( TOUPPER ${_tpl} _TPL )
+
+        if( ${_tpl}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIRS} )
+        elseif( ${_tpl}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIR} )
+        elseif( ${_TPL}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIRS} )
+        elseif( ${_TPL}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIR} )
+        endif()
+
+        if( ${_tpl}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARIES} )
+        elseif( ${_tpl}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARY} )
+        elseif( ${_TPL}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARIES} )
+        elseif( ${_TPL}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARY} )
+        endif()
+
+        if( ${_tpl}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_tpl}_DEFINITIONS} )
+        elseif( ${_TPL}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_TPL}_DEFINITIONS} )
+        endif()
+    endforeach()
+
+    # Deduplicate TPL includes, libs and definitions
+    # The same TPL may indirectly be pulled in multiple times!
+    if( ${PNAME}_TPL_INCLUDE_DIRS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_INCLUDE_DIRS )
+    endif()
+    if( ${PNAME}_TPL_LIBRARIES )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_LIBRARIES )
+    endif()
+    if( ${PNAME}_TPL_DEFINITIONS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_DEFINITIONS )
+    endif()
+
+    # Generate the project .cmake config files
+    # All variables here must be (sub)project specific in order to work within bundles
+    if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+        set( _template_config "${ECBUILD_MACROS_DIR}/project-config.cmake.in" )
+        if( EXISTS ${LNAME}-config.cmake.in )
+            set( _template_config "${LNAME}-config.cmake.in" )
+        endif()
+
+        set( _template_config_version "${ECBUILD_MACROS_DIR}/project-config-version.cmake.in" )
+        if( EXISTS ${LNAME}-config-version.cmake.in )
+            set( _template_config_version "${LNAME}-config-version.cmake.in" )
+        endif()
+
+        # project-config-version.cmake -- format ([0-9]+).([0-9]+).([0-9]+)
+
+        set( PACKAGE_VERSION        "${${PNAME}_VERSION}" )
+        set( PACKAGE_GIT_SHA1       "${${PNAME}_GIT_SHA1}" )
+        set( PACKAGE_GIT_SHA1_SHORT "${${PNAME}_GIT_SHA1_SHORT}" )
+
+        configure_file( "${_template_config_version}" "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" @ONLY )
+
+        install( FILES "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # prepare imutable variables (don't depend on install path)
+
+        if( ${PNAME}_FEATURES )
+          set( CONF_FEATURES ${${PNAME}_FEATURES} )
+        endif()
+
+        set( CONF_LIBRARIES ${${PROJECT_NAME}_ALL_LIBS} )
+        if( ${PNAME}_LIBRARIES )
+            set( CONF_LIBRARIES ${${PNAME}_LIBRARIES} )
+        endif()
+
+        set( CONF_DEFINITIONS "" )
+        if( ${PNAME}_DEFINITIONS )
+           set( CONF_DEFINITIONS ${${PNAME}_DEFINITIONS} )
+        endif()
+
+        set( CONF_TPL_LIBRARIES   "" )
+        if( ${PNAME}_TPL_LIBRARIES )
+           set( CONF_TPL_LIBRARIES ${${PNAME}_TPL_LIBRARIES} )
+        endif()
+
+        set( CONF_TPLS ${${PNAME}_TPLS} )
+
+        set( CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" )
+        if( ${PNAME}_INCLUDE_DIRS )
+            set( CONF_INCLUDE_DIRS ${${PNAME}_INCLUDE_DIRS} )
+        endif()
+
+        set( CONF_TPL_INCLUDE_DIRS "" )
+        if( ${PNAME}_TPL_INCLUDE_DIRS )
+            set( CONF_TPL_INCLUDE_DIRS ${${PNAME}_TPL_INCLUDE_DIRS} )
+        endif()
+
+        # Generate <project>-import.cmake (if it exists)
+
+        set( CONF_IMPORT_FILE "${LNAME}-import.cmake" )
+
+        # If <project>-import.cmake.in exist in source tree, configure it to
+        # the build tree and install the configured version
+        if( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in - configuring to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" @ONLY )
+          install( FILES "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        # Otherwise, if <project>-import.cmake exist in source tree, copy it to
+        # the build tree and install it
+        elseif( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE} - copying to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" COPYONLY )
+          install( FILES "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        else()
+          ecbuild_debug( "No ${CONF_IMPORT_FILE} found in ${PROJECT_SOURCE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use from the build tree
+
+        set( _lname_config "${PROJECT_BINARY_DIR}/${LNAME}-config.cmake")
+
+        # Include directories (may) reference source and build tree and the
+        # config file is marked as coming from a build tree
+        set( _is_build_dir_export ON )
+        configure_file( "${_template_config}" "${_lname_config}" @ONLY )
+
+        # Generate <project>-config.cmake.tpls (if there are any TPLs)
+
+        file( REMOVE ${_lname_config}.tpls.in )
+
+        foreach( _tpl ${${PNAME}_TPLS} )
+
+            string( TOUPPER ${_tpl} TPL )
+
+            if( ${TPL}_IMPORT_FILE ) # ecBuild packages should trigger this if they export themselves
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_IMPORT_FILE}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_IMPORT_FILE )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            elseif( ${TPL}_CONFIG ) # cmake built packages (e.g. CGAL) may have exported their targets
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_CONFIG}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_CONFIG )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    set( ${TPL}_CONFIG \"${__import_file}\" )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            endif()
+
+        endforeach()
+
+        if( EXISTS "${_lname_config}.tpls.in" )
+            configure_file( "${_lname_config}.tpls.in" "${_lname_config}.tpls" @ONLY )
+            install( FILES "${_lname_config}.tpls" DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use in the install tree
+
+        # Compute path to the include dir relative to the project's CMake dir
+        # where <project>-config.cmake is installed to
+        file( RELATIVE_PATH REL_INCLUDE_DIR "${${PNAME}_FULL_INSTALL_CMAKE_DIR}" "${${PNAME}_FULL_INSTALL_INCLUDE_DIR}" )
+        set( CONF_INCLUDE_DIRS "\${${PNAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" )
+
+        set( _is_build_dir_export OFF )
+        configure_file( "${_template_config}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" @ONLY )
+        install( FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # install the export
+
+        if( ${PROJECT_NAME}_ALL_EXES OR ${PROJECT_NAME}_ALL_LIBS )
+            install( EXPORT ${PROJECT_NAME}-targets
+                     DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+    endif()  # if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+    # exports the package for use from the build-tree but only in DEVELOPER_MODE
+    # inserts <package> into the CMake user package registry
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        if( DEVELOPER_MODE )
+            export( PACKAGE ${PROJECT_NAME} )
+        endif()
+
+    else()
+
+    # export variables for upper projects
+
+        set( ${PNAME}_FOUND             TRUE                          PARENT_SCOPE )
+        set( ${PROJECT_NAME}_FOUND      TRUE                          PARENT_SCOPE )
+        set( ${PNAME}_VERSION           ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1          ${${PNAME}_GIT_SHA1}          PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1_SHORT    ${${PNAME}_GIT_SHA1_SHORT}    PARENT_SCOPE )
+        set( ${PROJECT_NAME}_VERSION    ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_INCLUDE_DIRS      ${${PNAME}_INCLUDE_DIRS}      PARENT_SCOPE )
+        set( ${PNAME}_LIBRARIES         ${${PNAME}_LIBRARIES}         PARENT_SCOPE )
+        set( ${PNAME}_DEFINITIONS       ${${PNAME}_DEFINITIONS}       PARENT_SCOPE )
+        set( ${PNAME}_PACKAGES          ${${PNAME}_PACKAGES}          PARENT_SCOPE )
+        set( ${PNAME}_TPLS              ${${PNAME}_TPLS}              PARENT_SCOPE )
+        set( ${PNAME}_TPL_LIBRARIES     ${${PNAME}_TPL_LIBRARIES}     PARENT_SCOPE )
+        set( ${PNAME}_TPL_DEFINITIONS   ${${PNAME}_TPL_DEFINITIONS}   PARENT_SCOPE )
+        set( ${PNAME}_TPL_INCLUDE_DIRS  ${${PNAME}_TPL_INCLUDE_DIRS}  PARENT_SCOPE )
+        set( ${PNAME}_FEATURES          ${${PNAME}_FEATURES}          PARENT_SCOPE )
+        foreach( _f ${${PNAME}_FEATURES} )
+            set( ${PNAME}_HAVE_${_f} ${${PNAME}_HAVE_${_f}} PARENT_SCOPE )
+        endforeach()
+
+    endif()
+
+endmacro( ecbuild_install_project )
diff --git a/ecbuild/cmake/ecbuild_list_add_pattern.cmake b/ecbuild/cmake/ecbuild_list_add_pattern.cmake
new file mode 100644
index 0000000..a0f9f34
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_list_add_pattern.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_add_pattern
+# ========================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_add_pattern( LIST <input_list>
+#                             GLOB <pattern1> [ <pattern2> ... ]
+#                             [ SOURCE_DIR <source_dir> ]
+#                             [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be appended to
+#
+# GLOB : required
+#   Regex pattern of exclusion
+#
+# SOURCE_DIR : optional
+#   Directory from where to start search
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_add_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST SOURCE_DIR )
+  set( multi_value_args  GLOB )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_add_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_GLOB )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the GLOB.")
+  endif()
+
+  #####
+
+  set( input_list ${${_p_LIST}} )
+  unset( matched_files )
+
+  foreach( pattern ${_p_GLOB} )
+
+    if( IS_ABSOLUTE ${pattern} )
+      ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern}" )
+      file( GLOB_RECURSE matched_files ${pattern} )
+    else()
+
+      if(_p_SOURCE_DIR)
+        if( IS_ABSOLUTE ${_p_SOURCE_DIR} )
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files ${_p_SOURCE_DIR}/${pattern} )
+        else()
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_p_SOURCE_DIR}/${pattern} )
+        endif()
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern} ")
+        file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${pattern} )
+      endif()
+
+    endif()
+
+    if(matched_files)
+      ecbuild_debug( "ecbuild_list_add_pattern: Found ${matched_files}" )
+      list( APPEND input_list ${matched_files} )
+      list( REMOVE_DUPLICATES input_list )
+      set( ${_p_LIST} ${input_list} PARENT_SCOPE )
+    else()
+      if(NOT _p_QUIET)
+        ecbuild_warn( "ecbuild_list_add_pattern: no matches found for patterns ${pattern}" )
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern:no matches found for patterns ${pattern}" )
+      endif()
+    endif()
+
+  endforeach()
+
+
+endfunction(ecbuild_list_add_pattern)
diff --git a/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake b/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake
new file mode 100644
index 0000000..bfb6ee8
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake
@@ -0,0 +1,88 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_exclude_pattern
+# ============================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_exclude_pattern( LIST <input_list>
+#                                 REGEX <regex1> [ <regex2> ... ]
+#                                 [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be cleaned
+#
+# REGEX : required
+#   Regex pattern of exclusions
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_exclude_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST )
+  set( multi_value_args  REGEX )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_exclude_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_REGEX )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the REGEX.")
+  endif()
+
+  #####
+
+  set( result "" )
+  set( matches_found 0 )
+
+  # ecbuild_debug_var(_p_REGEX)
+
+  foreach( item ${${_p_LIST}} )
+
+    set( _keep 1 )
+
+    foreach( pattern ${_p_REGEX} )
+        if( ${item} MATCHES ${pattern} )
+            set( _keep 0 )
+            set( matches_found 1 )
+        endif()
+    endforeach()
+    if( _keep )
+        list( APPEND result ${item} )
+#    else()
+#      ecbuild_warn( "removing ${item}" )
+    endif()
+
+  endforeach()
+
+  if( matches_found )
+      set( ${_p_LIST} ${result} PARENT_SCOPE )
+  else()
+    if( NOT _p_QUIET )
+        ecbuild_warn( "ecbuild_list_exclude_pattern: no matches found for patterns ${_p_REGEX} in ${_p_LIST}" )
+    endif()
+  endif()
+
+endfunction(ecbuild_list_exclude_pattern)
diff --git a/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake b/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake
new file mode 100644
index 0000000..1ba31bc
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake
@@ -0,0 +1,81 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+#
+# macro for adding search paths for a package to a given CMake variable
+#
+# usage: ecbuild_list_extra_search_paths( netcdf4 VARIABLE )
+
+function( ecbuild_list_extra_search_paths pkg var )
+
+  ecbuild_deprecate( " ecbuild_list_extra_search_paths should no longer be"
+                     " used and is going to be removed in a future version of ecBuild." )
+
+	# ecbuild_debug_var( pkg )
+	# ecbuild_debug_var( var )
+
+	string( TOUPPER ${pkg} _PKG )
+
+	# PKG_PATH (upper case)
+
+	if( DEFINED ${_PKG}_PATH AND EXISTS ${${_PKG}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${_PKG}_PATH = ${${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} ${${_PKG}_PATH} )
+	endif()
+
+	# ENV PKG_PATH (upper case)
+
+	if( DEFINED ENV{${_PKG}_PATH} AND EXISTS $ENV{${_PKG}_PATH}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_PATH = $ENV{${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_PATH} )
+	endif()
+
+	# pkg_PATH (lower case)
+
+	if( DEFINED ${pkg}_PATH AND EXISTS ${${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${pkg}_PATH = ${${pkg}_PATH} to ${var}")
+		list( APPEND ${var} ${${pkg}_PATH} )
+	endif()
+
+	# ENV pkg_PATH (lower case)
+
+  if( DEFINED ${pkg}_PATH AND EXISTS $ENV{${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_PATH = $ENV{${pkg}_PATH} to ${var}")
+    list( APPEND ${var} $ENV{${pkg}_PATH} )
+	endif()
+
+	# ENV PKG_DIR (upper case)
+
+	if( DEFINED ENV{${_PKG}_DIR} AND EXISTS $ENV{${_PKG}_DIR}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_DIR = $ENV{${_PKG}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_DIR} )
+	endif()
+
+	# ENV pkg_DIR (lower case)
+
+	if( DEFINED ENV{${pkg}_DIR} AND EXISTS $ENV{${pkg}_DIR} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_DIR = $ENV{${pkg}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${pkg}_DIR} )
+	endif()
+
+	# sanitize the list
+
+	if( ${var} )
+		list( REMOVE_DUPLICATES ${var} )
+	endif()
+
+	# define it out of the function
+
+  ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): setting ${var} to ${${var}}")
+	set( ${var} ${${var}} PARENT_SCOPE )
+
+# ecbuild_debug_var( ${var} )
+
+endfunction()
+
diff --git a/ecbuild/cmake/ecbuild_list_macros.cmake b/ecbuild/cmake/ecbuild_list_macros.cmake
new file mode 100644
index 0000000..232e5e8
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_list_macros.cmake
@@ -0,0 +1,58 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# function for concatenating list into a string
+#
+# examples:
+#
+#   set( _paths "foo" "bar" )
+#   join( _paths "/" _mypath )
+#
+#   message( "${_mpath}" ) #  produces "foo/bar"
+
+function( JOIN _listname _glue _output )
+
+	set( _ret "" )
+
+	foreach( _v ${${_listname}} )
+		if( _ret )
+			set(_ret "${_ret}${_glue}${_v}") # append
+		else()
+			set(_ret "${_v}") # init
+		endif()
+	endforeach()
+
+	set(${_output} "${_ret}" PARENT_SCOPE)
+
+endfunction()
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_insert( "mymap" "foo" "bar" )
+#
+
+function( MAP_INSERT _map _key _value )
+	set( "_${_map}_${_key}" "${_value}" PARENT_SCOPE )
+endfunction(MAP_INSERT)
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_get( "mymap" "foo" VAR )
+#
+
+function( MAP_GET _map _key _var )
+	set( ${_var} "${_${_map}_${_key}}" PARENT_SCOPE )
+endfunction(MAP_GET)
+
diff --git a/ecbuild/cmake/ecbuild_log.cmake b/ecbuild/cmake/ecbuild_log.cmake
new file mode 100644
index 0000000..57ebf0d
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_log.cmake
@@ -0,0 +1,249 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# Logging
+# =======
+#
+# ecBuild provides functions for logging based on a log level set by the user,
+# similar to the Python logging module:
+#
+# :ecbuild_debug:     logs a ``STATUS`` message if log level <= ``DEBUG``
+# :ecbuild_info:      logs a ``STATUS`` message if log level <= ``INFO``
+# :ecbuild_warn:      logs a ``WARNING`` message if log level <= ``WARN``
+# :ecbuild_error:     logs a ``SEND_ERROR`` message if log level <= ``ERROR``
+# :ecbuild_critical:  logs a ``FATAL_ERROR`` message if log level <= ``CRITICAL``
+# :ecbuild_deprecate: logs a ``DEPRECATION`` message as a warning
+#                     enable CMAKE_ERROR_DEPRECATED to raise an error instead
+#                     disable CMAKE_WARN_DEPRECATED to hide deprecations
+#
+# Furthermore there are auxilliary functions for outputting CMake variables,
+# CMake lists and environment variables if the log level is ``DEBUG``:
+#
+# :ecbuild_debug_var:      logs given CMake variables if log level <= ``DEBUG``
+# :ecbuild_debug_list:     logs given CMake lists if log level <= ``DEBUG``
+# :ecbuild_debug_env_var:  logs given environment variables if log level <= ``DEBUG``
+# :ecbuild_debug_property: logs given global CMake property if log level <= ``DEBUG``
+#
+# To log a message to the ecBuild log file only at a given log level, use ::
+#
+#   ecbuild_log( <level> <msg> )
+#
+# Input variables
+# ---------------
+#
+# CMake variables controlling logging behaviour:
+#
+# ECBUILD_LOG_FILE : path
+#   set the log file, defaults to ``${CMAKE_BINARY_DIR}/ecbuild.log``
+#
+#   All ecBuild log functions write their messages to this log file with a time
+#   stamp. Messages emitted by CMake directly cannot be logged to file.
+#
+# ECBUILD_LOG_LEVEL : string, one of DEBUG, INFO, WARN, ERROR, CRITICAL, OFF
+#   desired log level, defaults to ``INFO``, ``OFF`` to disable logging
+#
+# ECBUILD_NO_COLOUR : bool
+#   if set, does not colour log output (by default log output is coloured)
+#
+# Usage
+# -----
+#
+# The functions ``ecbuild_debug`` and ``ecbuild_info`` can be used to output
+# messages which are not printed by default. Many ecBuild macros use this
+# facility to log debugging hints. When debugging a CMake run, users can use
+# ``-DECBUILD_LOG_LEVEL=DEBUG`` to get detailed diagnostics.
+#
+##############################################################################
+
+# Define colour escape sequences (not available on Windows)
+if(NOT (WIN32 OR ECBUILD_NO_COLOUR))
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
+
+set(ECBUILD_DEBUG    10)
+set(ECBUILD_INFO     20)
+set(ECBUILD_WARN     30)
+set(ECBUILD_ERROR    40)
+set(ECBUILD_CRITICAL 50)
+
+if( NOT DEFINED ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( NOT ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL 60)
+elseif( ECBUILD_LOG_LEVEL STREQUAL "DEBUG" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_DEBUG})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "INFO" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "WARN" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "ERROR" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_ERROR})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "CRITICAL" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_CRITICAL})
+else()
+  message(WARNING "Unknown log level ${ECBUILD_LOG_LEVEL} (valid are DEBUG, INFO, WARN, ERROR, CRITICAL) - using WARN")
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+endif()
+
+if( NOT DEFINED ECBUILD_LOG_FILE )
+  set( ECBUILD_LOG_FILE ${CMAKE_BINARY_DIR}/ecbuild.log )
+endif()
+if( NOT DEFINED CMAKE_ERROR_DEPRECATED AND NOT DEFINED CMAKE_WARN_DEPRECATED )
+  set( CMAKE_WARN_DEPRECATED ON )
+endif()
+
+##############################################################################
+
+function( ecbuild_log LEVEL )
+  string( REPLACE ";" " " MSG "${ARGN}" )
+  string( TIMESTAMP _time )
+  file( APPEND ${ECBUILD_LOG_FILE} "${_time} - ${PROJECT_NAME} - ${LEVEL} - ${MSG}\n" )
+endfunction( ecbuild_log )
+
+##############################################################################
+
+function( ecbuild_debug )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(DEBUG "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    message(STATUS "${Blue}DEBUG - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_debug )
+
+##############################################################################
+
+function( ecbuild_info )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(INFO "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 21)
+    message(STATUS "${MSG}")
+  endif()
+endfunction( ecbuild_info )
+
+##############################################################################
+
+function( ecbuild_warn )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(WARNING "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 31)
+    message(WARNING "${Yellow}WARN - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_warn )
+
+##############################################################################
+
+function( ecbuild_error )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 41)
+    message(SEND_ERROR "${BoldRed}ERROR - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_error )
+
+##############################################################################
+
+function( ecbuild_deprecate )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(DEPRECATION "${MSG}")
+  # DEPRECATION message type was only introduced in CMake 3.0, provide
+  # consistent behaviour for CMake < 3.0
+  if( CMAKE_VERSION VERSION_LESS 3.0 )
+    if( CMAKE_ERROR_DEPRECATED )
+      message(FATAL_ERROR "${BoldRed}DEPRECATION - ${MSG}${ColourReset}")
+    elseif( CMAKE_WARN_DEPRECATED )
+      message(WARNING "${Yellow}DEPRECATION - ${MSG}${ColourReset}")
+    endif()
+  else()
+    message(DEPRECATION "${BoldRed}${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_deprecate )
+
+##############################################################################
+
+function( ecbuild_critical )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(FATAL_ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 51)
+    message(FATAL_ERROR "${BoldMagenta}CRITICAL - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_critical )
+
+##############################################################################
+# function for debugging CMake variables
+
+function( ecbuild_debug_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ${VAR} : ${${VAR}}${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging CMake lists
+
+function( ecbuild_debug_list )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    foreach( _elem ${${VAR}} )
+      ecbuild_log( DEBUG "  ${_elem}" )
+    endforeach()
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message( STATUS "${Blue}DEBUG - ${VAR}" )
+      foreach( _elem ${${VAR}} )
+        message( STATUS "  ${_elem}" )
+      endforeach()
+      message(STATUS "${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging environment variables
+
+function( ecbuild_debug_env_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "ENV ${VAR} : $ENV{${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ENV ${VAR} [$ENV{${VAR}}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging a CMake global property
+
+function( ecbuild_debug_property )
+  foreach( VAR ${ARGV} )
+    get_property( __prop GLOBAL PROPERTY ${VAR} )
+    ecbuild_log(DEBUG "PROPERTY ${VAR} : ${__prop}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - PROPERTY ${VAR} [${__prop}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_pkgconfig.cmake b/ecbuild/cmake/ecbuild_pkgconfig.cmake
new file mode 100644
index 0000000..5ae273e
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_pkgconfig.cmake
@@ -0,0 +1,421 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# Write transitive list of library dependencies of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_library_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    unset( _location )
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      set( _imported 0 )
+      get_property( _imported TARGET ${_lib} PROPERTY IMPORTED )
+
+      unset( _deps )
+
+      if( _imported )
+
+        get_property( _location TARGET ${_lib} PROPERTY LOCATION )
+        get_property( _configs   TARGET ${_lib} PROPERTY IMPORTED_CONFIGURATIONS )
+        list( REVERSE _configs )
+        list( GET _configs 0 _config)
+        get_property( _deps     TARGET ${_lib} PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES_${_config} )
+        get_property( _locimp   TARGET ${_lib} PROPERTY IMPORTED_LOCATION_${_config} )
+
+      else()
+
+        list( APPEND _location ${_lib} )
+        get_property( _deps TARGET ${_lib} PROPERTY LINK_LIBRARIES )
+
+      endif()
+
+      ecbuild_library_dependencies( _deps_location _deps )
+      list( APPEND _location ${_deps_location} )
+
+    else()
+
+      set( _location ${_lib} )
+
+    endif()
+
+    list( APPEND _dependencies ${_location} )
+
+  endforeach()
+
+  if( _dependencies )
+    list( REVERSE           _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    list( REVERSE           _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_library_dependencies)
+
+##############################################################################
+
+# Write list of include directories of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_include_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      get_property( _include_dirs TARGET ${_lib} PROPERTY INCLUDE_DIRECTORIES )
+      list( APPEND _dependencies ${_include_dirs} )
+
+    endif()
+
+  endforeach()
+
+  if( _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_include_dependencies)
+
+##############################################################################
+
+# Transform list of libraries in ${libraries}, ignoring any in ${ignore_libs},
+# and write pkg-config compatible string to CMake variable ${pkgconfig_libs}
+function( ecbuild_pkgconfig_libs pkgconfig_libs libraries ignore_libs )
+
+  set( _libraries ${${libraries}} )
+  set( _ignore_libs ${${ignore_libs}} )
+
+  foreach( _lib ${_libraries} )
+
+    unset( _name )
+    unset( _dir  )
+
+    if( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+      get_filename_component( _name ${_lib} NAME_WE )
+      list( APPEND _pkgconfig_libs "-framework ${_name}" )
+
+    else()
+
+      if( ${_lib} MATCHES "-l.+" )
+
+        string( REGEX REPLACE "^-l" "" _name ${_lib} )
+
+      else()
+
+
+        get_filename_component( _name ${_lib} NAME_WE )
+        get_filename_component( _dir  ${_lib} PATH )
+
+        if( TARGET ${_lib} )
+          get_target_property( _name ${_lib} OUTPUT_NAME )
+        endif()
+        if( NOT _name )
+          set( _name ${_lib} )
+        endif()
+
+        string( REGEX REPLACE "^lib" "" _name ${_name} )
+
+        if( "${_dir}" STREQUAL "/usr/lib" )
+          unset( _dir )
+        endif()
+        if( "${_dir}" STREQUAL "/usr/lib64" )
+          unset( _dir )
+        endif()
+
+      endif()
+
+      set( _set_append TRUE )
+        foreach( _ignore ${_ignore_libs} )
+          if( "${_name}" STREQUAL "${_ignore}" )
+            set( _set_append FALSE )
+          endif()
+      endforeach()
+
+      if( _set_append )
+
+        if( _dir )
+          list( APPEND _pkgconfig_libs "-L${_dir}" "-l${_name}" )
+        else()
+          list( APPEND _pkgconfig_libs "-l${_name}" )
+        endif()
+
+      endif()
+
+    endif( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+  endforeach( _lib ${_libraries} )
+
+  if( _pkgconfig_libs )
+    list( REMOVE_DUPLICATES _pkgconfig_libs )
+    string( REPLACE ";" " " _pkgconfig_libs "${_pkgconfig_libs}" )
+
+    set( ${pkgconfig_libs} ${_pkgconfig_libs} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_libs)
+
+##############################################################################
+
+# Transform list of include directories in ${INCLUDE_DIRS}, ignoring any in
+# ${ignore_includes} and ${${PNAME}_INCLUDE_DIRS}, and write pkg-config
+# compatible string to CMake variable ${INCLUDE}
+function( ecbuild_pkgconfig_include INCLUDE INCLUDE_DIRS ignore_includes )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  set( _ignore_includes ${${ignore_includes}} )
+
+  list( APPEND ignore_include_dirs
+    "/usr/include"
+     ${${PNAME}_INCLUDE_DIRS} # These are build-directory includes
+     ${CMAKE_SOURCE_DIR}  # Ignore private includes referencing source tree
+     ${CMAKE_BINARY_DIR}  # Ignore private includes referencing build tree
+     ${_ignore_includes}
+  )
+
+  foreach( _incdir ${${INCLUDE_DIRS}} )
+
+    foreach( _ignore ${ignore_include_dirs} )
+      if( "${_incdir}" MATCHES "${_ignore}" )
+        unset( _incdir )
+        break()
+      endif()
+    endforeach()
+
+    if( _incdir )
+      list( APPEND _include "-I${_incdir}")
+    endif()
+
+  endforeach()
+
+  if( _include )
+    list( REMOVE_DUPLICATES _include)
+    string( REPLACE ";" " " _include "${_include}")
+    set( ${INCLUDE} ${_include} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_include)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_pkgconfig
+# =================
+#
+# Create a pkg-config file for the current project. ::
+#
+#   ecbuild_pkgconfig( [ NAME <name> ]
+#                      [ FILENAME <filename> ]
+#                      [ TEMPLATE <template> ]
+#                      [ URL <url> ]
+#                      [ DESCRIPTION <description> ]
+#                      [ LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ IGNORE_INCLUDE_DIRS <dir1> [ <dir2> ... ] ]
+#                      [ IGNORE_LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ LANGUAGES <language1> [ <language2> ... ] ]
+#                      [ VARIABLES <variable1> [ <variable2> ... ] ]
+#                      [ NO_PRIVATE_INCLUDE_DIRS ] )
+#
+# Options
+# -------
+#
+# NAME : optional, defaults to lower case name of the project
+#   name to be given to the package
+#
+# FILENAME : optional, defaults to ``<NAME>.pc``
+#   file to be generated, including .pc extension
+#
+# TEMPLATE : optional, defaults to ``${ECBUILD_CMAKE_DIR}/pkg-config.pc.in``
+#   template configuration file to use
+#
+#   This is useful to create customised pkg-config files.
+#
+# URL : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_URL``
+#   url of the package
+#
+# DESCRIPTION : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_DESCRIPTION``
+#   description of the package
+#
+# LIBRARIES : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_LIBRARIES``
+#   list of package libraries
+#
+# IGNORE_INCLUDE_DIRS : optional
+#   list of include directories to ignore
+#
+# IGNORE_LIBRARIES : optional
+#   list of libraries to ignore i.e. those are removed from ``LIBRARIES``
+#
+# VARIABLES : optional
+#   list of additional CMake variables to export to the pkg-config file
+#
+# LANGUAGES : optional, defaults to all loaded languages
+#   list of languages to use. Accepted languages: C CXX Fortran
+#
+# NO_PRIVATE_INCLUDE_DIRS
+#   do not add include directories of dependencies to Cflags
+#
+#   This is mainly useful for Fortran only packages, when only modules need
+#   to be added to Cflags.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are used as default values for some of the
+# options listed above, where ``PNAME`` is the project name in upper case:
+#
+# :<PNAME>_LIBRARIES:    list of libraries to export
+# :<PNAME>_DESCRIPTION:  package description
+# :<PNAME>_URL:          package URL
+# :<PNAME>_VERSION:      package version
+# :<PNAME>_GIT_SHA1:     Git revision
+#
+# Usage
+# -----
+#
+# It is good practice to provide a separate pkg-config file for each library a
+# package exports. This can be achieved as follows: ::
+#
+#   foreach( _lib ${${PNAME}_LIBRARIES} )
+#     if( TARGET ${_lib} )
+#       ecbuild_pkgconfig( NAME ${_lib}
+#                          DESCRIPTION "..."
+#                          URL "..."
+#                          LIBRARIES ${_lib} )
+#     endif()
+#   endforeach()
+#
+##############################################################################
+
+function( ecbuild_pkgconfig )
+
+  set( options REQUIRES NO_PRIVATE_INCLUDE_DIRS )
+  set( single_value_args FILENAME NAME TEMPLATE URL DESCRIPTION )
+  set( multi_value_args LIBRARIES IGNORE_INCLUDE_DIRS IGNORE_LIBRARIES VARIABLES LANGUAGES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+  string( TOLOWER ${PROJECT_NAME} LNAME )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  unset( PKGCONFIG_LANGUAGES )
+  if( NOT _PAR_LANGUAGES )
+    if( CMAKE_C_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES C )
+    endif()
+    if( CMAKE_CXX_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES CXX )
+    endif()
+    if( CMAKE_Fortran_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES Fortran )
+    endif()
+  else()
+    foreach( _lang ${_PAR_LANGUAGES} )
+      if( CMAKE_${_lang}_COMPILER_LOADED )
+        list( APPEND PKGCONFIG_LANGUAGES ${_lang} )
+      endif()
+    endforeach()
+  endif()
+
+  foreach( _lang ${PKGCONFIG_LANGUAGES} )
+    set( PKGCONFIG_HAVE_${_lang} 1 )
+  endforeach()
+
+  set( LIBRARIES ${${PNAME}_LIBRARIES} )
+  if( _PAR_LIBRARIES )
+    set( LIBRARIES ${_PAR_LIBRARIES} )
+  endif()
+
+  if( CMAKE_CXX_COMPILER_LOADED )
+   set( _linker_lang CXX )
+  elseif( CMAKE_C_COMPILER_LOADED )
+   set( _linker_lang C )
+  elseif( CMAKE_Fortran_COMPILER_LOADED )
+   set( _linker_lang Fortran )
+  endif()
+
+  set( RPATH_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_${_linker_lang}_FLAG} )
+
+  set( PKGCONFIG_MOD_FLAG ${CMAKE_Fortran_MODPATH_FLAG} )
+
+  if( NOT PKGCONFIG_MOD_FLAG )
+    set( PKGCONFIG_MOD_FLAG "-I" )
+  endif()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS LIBRARIES _PAR_IGNORE_LIBRARIES )
+
+  ecbuild_library_dependencies( _libraries LIBRARIES )
+  foreach( _lib ${LIBRARIES} )
+    list( REMOVE_ITEM _libraries ${_lib} )
+  endforeach()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS_PRIVATE _libraries _PAR_IGNORE_LIBRARIES )
+
+  if( NOT _PAR_NO_PRIVATE_INCLUDE_DIRS )
+    ecbuild_include_dependencies( _include_dirs LIBRARIES )
+    ecbuild_pkgconfig_include( PKGCONFIG_CFLAGS _include_dirs _PAR_IGNORE_INCLUDE_DIRS )
+  endif()
+
+  set( PKGCONFIG_INCLUDE "-I\${includedir}" )
+  if( PKGCONFIG_HAVE_Fortran )
+    set( PKGCONFIG_INCLUDE "${PKGCONFIG_INCLUDE} ${PKGCONFIG_MOD_FLAG}\${fmoddir}" )
+  endif()
+
+  if( NOT _PAR_TEMPLATE )
+    set( _PAR_TEMPLATE "${ECBUILD_MACROS_DIR}/pkg-config.pc.in" )
+  endif()
+
+  set( PKGCONFIG_NAME ${LNAME} )
+  if( _PAR_NAME )
+    set( PKGCONFIG_NAME ${_PAR_NAME} )
+  endif()
+
+  if( NOT _PAR_FILENAME )
+    set( _PAR_FILENAME "${PKGCONFIG_NAME}.pc" )
+  endif()
+
+  set( PKGCONFIG_DESCRIPTION ${${PNAME}_DESCRIPTION} )
+  if( _PAR_DESCRIPTION )
+    set( PKGCONFIG_DESCRIPTION ${_PAR_DESCRIPTION} )
+  endif()
+
+  set( PKGCONFIG_URL ${${PNAME}_URL} )
+  if( _PAR_URL )
+    set( PKGCONFIG_URL ${_PAR_URL} )
+  endif()
+
+  set( PKGCONFIG_VERSION ${${PNAME}_VERSION} )
+  set( PKGCONFIG_GIT_TAG ${${PNAME}_GIT_SHA1} )  # For now set it to a commit id
+
+  if( _PAR_VARIABLES )
+    set( PKGCONFIG_VARIABLES "\n### Features:\n\n")
+    foreach( _var ${_PAR_VARIABLES} )
+      set( PKGCONFIG_VARIABLES "${PKGCONFIG_VARIABLES}${_var}=${${_var}}\n" )
+    endforeach()
+  endif()
+
+  configure_file( ${_PAR_TEMPLATE} "${CMAKE_BINARY_DIR}/${_PAR_FILENAME}" @ONLY )
+  ecbuild_info( "pkg-config file created: ${_PAR_FILENAME}" )
+
+  install( FILES ${CMAKE_BINARY_DIR}/${_PAR_FILENAME}
+           DESTINATION ${INSTALL_LIB_DIR}/pkgconfig/
+           COMPONENT utilities )
+
+endfunction(ecbuild_pkgconfig)
diff --git a/ecbuild/cmake/ecbuild_policies.cmake b/ecbuild/cmake/ecbuild_policies.cmake
new file mode 100644
index 0000000..df2b40f
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_policies.cmake
@@ -0,0 +1,67 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#
+# ecBuild Policies
+# ================
+#
+# NOTE: This file needs to be included with NO_POLICY_SCOPE or it will have no
+#       effect!
+# NOTE: Policies 1 through 17 will be set to NEW by requiring CMake 2.8.4 i.e.
+#       calling cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+#
+##############################################################################
+
+# allow for empty spaces around library names 
+if( POLICY CMP0004 )
+    cmake_policy( SET CMP0004 OLD )
+endif()
+
+# Allow use of the LOCATION target property.
+if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD )
+endif()
+
+# for macosx use @rpath in a target’s install name
+if( POLICY CMP0042 )
+    cmake_policy( SET CMP0042 NEW )
+    set( CMAKE_MACOSX_RPATH ON )
+endif()
+
+# Error on non-existent target in get_target_property
+if( POLICY CMP0045 )
+    cmake_policy( SET CMP0045 NEW )
+endif()
+
+# Error on non-existent dependency in add_dependencies
+if( POLICY CMP0046 )
+    cmake_policy( SET CMP0046 NEW )
+endif()
+
+# Do not manage VERSION variables in project command
+if( POLICY CMP0048 )
+  cmake_policy( SET CMP0048 OLD )
+endif()
+
+# Disallow add_custom_command SOURCE signatures
+if( POLICY CMP0050 )
+    cmake_policy( SET CMP0050 NEW )
+endif()
+
+# Reject source and build dirs in installed INTERFACE_INCLUDE_DIRECTORIES
+if( POLICY CMP0052 )
+    cmake_policy( SET CMP0052 NEW )
+endif()
+
+# inside if() don't dereference variables if they are quoted
+# e.g. "VAR" is not dereferenced
+#      "${VAR}" is dereference only once
+if( POLICY CMP0054 )
+    cmake_policy( SET CMP0054 NEW )
+endif()
diff --git a/ecbuild/cmake/ecbuild_print_summary.cmake b/ecbuild/cmake/ecbuild_print_summary.cmake
new file mode 100644
index 0000000..c863e60
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_print_summary.cmake
@@ -0,0 +1,116 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_print_summary
+# =====================
+#
+# Print a summary of the project, build environment and enabled features. ::
+#
+#   ecbuild_print_summary()
+#
+# If ``project_summary.cmake`` exist in the source root directory, a project
+# summary is printed by including this file.
+#
+# For a top level project, a summary of the build environment and a feature
+# summary are also printed.
+#
+##############################################################################
+
+macro( ecbuild_print_summary )
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Project ${PROJECT_NAME} summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    include( ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+  endif()
+
+  if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    if( NOT ${DEVELOPER_MODE} )
+      ecbuild_info( "Build summary" )
+    else()
+      ecbuild_info( "Build summary -- ( DEVELOPER_MODE )" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    ecbuild_info( "system : [${BUILD_SITE}] [${CMAKE_SYSTEM}] [${EC_OS_NAME}.${EC_OS_BITS}]" )
+    ecbuild_info( "processor        : [${CMAKE_SYSTEM_PROCESSOR}]" )
+    if( EC_BIG_ENDIAN )
+      ecbuild_info( "endiness         : Big Endian -- IEEE [${IEEE_BE}]" )
+    endif()
+    if( EC_LITTLE_ENDIAN )
+      ecbuild_info( "endiness         : Little Endian -- IEEE [${IEEE_LE}]" )
+    endif()
+    ecbuild_info( "build type       : [${CMAKE_BUILD_TYPE}]" )
+    ecbuild_info( "timestamp        : [${EC_BUILD_TIMESTAMP}]" )
+    ecbuild_info( "install prefix   : [${CMAKE_INSTALL_PREFIX}]" )
+    ecbuild_info( "  bin dir        : [${${PNAME}_FULL_INSTALL_BIN_DIR}]" )
+    ecbuild_info( "  lib dir        : [${${PNAME}_FULL_INSTALL_LIB_DIR}]" )
+    ecbuild_info( "  include dir    : [${${PNAME}_FULL_INSTALL_INCLUDE_DIR}]" )
+    ecbuild_info( "  data dir       : [${${PNAME}_FULL_INSTALL_DATA_DIR}]" )
+    ecbuild_info( "  cmake dir      : [${${PNAME}_FULL_INSTALL_CMAKE_DIR}]" )
+    if( EC_LINK_DIR )
+      ecbuild_info( "links prefix     : [${EC_LINK_DIR}]" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    foreach( lang ${langs} )
+      ecbuild_info( "${lang} -- ${CMAKE_${lang}_COMPILER_ID} ${CMAKE_${lang}_COMPILER_VERSION}"  )
+      ecbuild_info( "    compiler   : ${CMAKE_${lang}_COMPILER}" )
+      ecbuild_info( "    flags      : ${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} ${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+      ecbuild_info( "    link flags : ${CMAKE_${lang}_LINK_FLAGS}" )
+    endforeach()
+
+    ecbuild_info( "linker : ${CMAKE_LINKER}")
+    ecbuild_info( "ar     : ${CMAKE_AR}")
+    ecbuild_info( "ranlib : ${CMAKE_RANLIB}")
+    ecbuild_info( "link flags" )
+    ecbuild_info( "    executable [${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    shared lib [${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    static lib [${CMAKE_MODULE_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "install rpath  : ${CMAKE_INSTALL_RPATH}" )
+
+    get_directory_property( defs COMPILE_DEFINITIONS )
+
+    ecbuild_info( "common definitions: ${defs}" )
+
+    ### FEATURE SUMMARY
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Feature summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8.6" )
+      set( __what ALL )
+    else()
+      set( __what ALL INCLUDE_QUIET_PACKAGES )
+    endif()
+
+    # Print feature summary
+    feature_summary( WHAT ${__what} )
+    # Write feature summary to ecbuild.log
+    feature_summary( WHAT ${__what} FILENAME ${ECBUILD_LOG_FILE} APPEND )
+
+    ### WARNINGS
+
+    # issue warnings / errors in case there are unused project files
+    ecbuild_warn_unused_files()
+
+  endif()
+
+endmacro( ecbuild_print_summary )
diff --git a/ecbuild/cmake/ecbuild_project_files.cmake b/ecbuild/cmake/ecbuild_project_files.cmake
new file mode 100644
index 0000000..12c07c2
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_project_files.cmake
@@ -0,0 +1,75 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# resert the variable on each configure
+set( EC_UNUSED_FILES "" CACHE INTERNAL "unused files" )
+
+##############################################################################
+# finds project files and adds them to the passed variable
+
+macro( ecbuild_find_files_recursive aFileList )
+
+list( APPEND ecbuild_project_extensions c cc cpp cxx ) # for the moment skip ( h hh )
+
+foreach( aExt ${ecbuild_project_extensions} )
+  set( globPatterns ${globPatterns} *.${aExt} )
+endforeach()
+
+# This globs for only one pattern at a time
+# Shell extglob patterns are unfortunately not supported.
+file( GLOB_RECURSE ${aFileList} ${globPatterns} )
+
+endmacro()
+
+##############################################################################
+# finds the unused files on all the project
+function( ecbuild_find_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    ecbuild_find_files_recursive( cwdFiles )
+
+    # this list will be kept
+    set( EC_PROJECT_FILES ${EC_PROJECT_FILES} ${cwdFiles} CACHE INTERNAL "" )
+    # this list will be progressevely emptied
+    set( EC_UNUSED_FILES  ${EC_UNUSED_FILES}  ${cwdFiles} CACHE INTERNAL "" )
+  endif()
+
+endfunction()
+
+##############################################################################
+# removed used files from unused list
+macro( ecbuild_declare_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    foreach( _afile ${ARGV} )
+
+      # ecbuild_debug_var( _afile )
+
+      get_property( _src_gen SOURCE ${_afile} PROPERTY GENERATED )
+
+      if( NOT _src_gen )
+
+        get_filename_component( _abspath ${_afile} ABSOLUTE )
+
+        # check for existance of all declared files
+        if( EXISTS ${_abspath} )
+            list( REMOVE_ITEM EC_UNUSED_FILES ${_abspath} )
+        else()
+        ecbuild_critical( "In directory ${CMAKE_CURRENT_SOURCE_DIR} file ${_afile} was declared in CMakeLists.txt but not found" )
+        endif()
+      endif()
+
+    endforeach()
+
+    # rewrite the unused file list in cache
+    set( EC_UNUSED_FILES ${EC_UNUSED_FILES} CACHE INTERNAL "unused files" )
+  endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake b/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake
new file mode 100644
index 0000000..93aab17
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake
@@ -0,0 +1,64 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_remove_fortran_flags
+# ============================
+#
+# Remove Fortran compiler flags from ``CMAKE_Fortran_FLAGS``. ::
+#
+#   ecbuild_remove_fortran_flags( <flag1> [ <flag2> ... ] [ BUILD <build> ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   remove flags from ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_remove_fortran_flags m_flags )
+
+  set( _flags ${m_flags} )
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( single_value_args BUILD )
+    set( multi_value_args )
+    cmake_parse_arguments( _PAR "" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+    string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+    if( _PAR_BUILD )
+      string( TOUPPER ${_PAR_BUILD} _PAR_BUILD_CAPS )
+    endif()
+
+    if( _PAR_BUILD AND (CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}") )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${_PAR_BUILD} ${CMAKE_Fortran_FLAGS_${_PAR_BUILD}})
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed from build type ${_PAR_BUILD}" )
+      endforeach()
+
+    elseif( NOT _PAR_BUILD )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS} ${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS ${CMAKE_Fortran_FLAGS} )
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed" )
+      endforeach()
+
+    endif()
+
+  endif()
+  unset( _flags )
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_requires_macro_version.cmake b/ecbuild/cmake/ecbuild_requires_macro_version.cmake
new file mode 100644
index 0000000..8ff5617
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_requires_macro_version.cmake
@@ -0,0 +1,27 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_requires_macro_version
+# ==============================
+#
+# Check that the ecBuild version satisfied a given minimum version or fail. ::
+#
+#   ecbuild_requires_macro_version( <minimum-version> )
+#
+##############################################################################
+
+macro( ecbuild_requires_macro_version req_vrs )
+
+	if( ECBUILD_MACRO_VERSION VERSION_LESS ${req_vrs} )
+		ecbuild_critical( "${PROJECT_NAME} needs ecbuild macro version >= ${req_vrs}" )
+	endif()
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_separate_sources.cmake b/ecbuild/cmake/ecbuild_separate_sources.cmake
new file mode 100644
index 0000000..69b6810
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_separate_sources.cmake
@@ -0,0 +1,103 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_separate_sources
+# ========================
+#
+# Separate a given list of sources according to language. ::
+#
+#   ecbuild_separate_sources( TARGET <name>
+#                             SOURCES <source1> [ <source2> ... ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   base name for the CMake output variables to set
+#
+# SOURCES : required
+#   list of source files to separate
+#
+# Output variables
+# ----------------
+#
+# If any file of the following group of extensions is present in the list of
+# sources, the corresponding CMake variable is set:
+#
+# :<target>_h_srcs:       source files with extension .h, .hxx, .hh, .hpp, .H
+# :<target>_c_srcs:       source files with extension .c
+# :<target>_cxx_srcs:     source files with extension .cc, .cxx, .cpp, .C
+# :<target>_fortran_srcs: source files with extension .f, .F, .for, f77, .f90,
+#                                                     .f95, .F77, .F90, .F95
+# :<target>_cuda_srcs:    source files with extension .cu
+#
+##############################################################################
+
+function( ecbuild_separate_sources )
+
+	set( options )
+	set( single_value_args TARGET  )
+	set( multi_value_args  SOURCES )
+
+	cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+	if(_PAR_UNPARSED_ARGUMENTS)
+	  ecbuild_critical("Unknown keywords given to ecbuild_separate_sources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+	endif()
+
+	if( NOT _PAR_TARGET  )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the TARGET.")
+	endif()
+
+	if( NOT _PAR_SOURCES )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the SOURCES.")
+	endif()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.h$|\\.hxx$|\\.hh$|\\.hpp$|\\.H$)")
+			list( APPEND ${_PAR_TARGET}_h_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.c$)")
+			list( APPEND ${_PAR_TARGET}_c_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.cc$|\\.cxx$|\\.cpp$|\\.C$)")
+			list( APPEND ${_PAR_TARGET}_cxx_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.f$|\\.F$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f03$|\\.f08$|\\.F77$|\\.F90$|\\.F95$|\\.F03$|\\.F08$)")
+			list( APPEND ${_PAR_TARGET}_fortran_srcs ${src} )
+		endif()
+	endforeach()
+
+    foreach( src ${_PAR_SOURCES} )
+        if(${src} MATCHES "(\\.cu$)")
+            list( APPEND ${_PAR_TARGET}_cuda_srcs ${src} )
+        endif()
+    endforeach()
+
+    set_source_files_properties( ${${_PAR_TARGET}_fortran_srcs} PROPERTIES LANGUAGE Fortran )
+
+    set( ${_PAR_TARGET}_h_srcs       "${${_PAR_TARGET}_h_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_c_srcs       "${${_PAR_TARGET}_c_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cxx_srcs     "${${_PAR_TARGET}_cxx_srcs}"     PARENT_SCOPE )
+    set( ${_PAR_TARGET}_fortran_srcs "${${_PAR_TARGET}_fortran_srcs}" PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cuda_srcs    "${${_PAR_TARGET}_cuda_srcs}"    PARENT_SCOPE )
+
+
+endfunction( ecbuild_separate_sources )
diff --git a/ecbuild/cmake/ecbuild_setup_test_framework.cmake b/ecbuild/cmake/ecbuild_setup_test_framework.cmake
new file mode 100644
index 0000000..b4b3ccb
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_setup_test_framework.cmake
@@ -0,0 +1,72 @@
+ecbuild_add_option( FEATURE TESTS
+                    DEFAULT ON
+                    DESCRIPTION "Enable the unit tests" )
+
+if( ENABLE_TESTS AND CMAKE_CXX_COMPILER_LOADED )
+
+  # Try to find compiled boost
+
+  # BOOST_ROOT or BOOSTROOT should take precedence on the search for location
+  if( BOOST_ROOT OR BOOSTROOT OR DEFINED ENV{BOOST_ROOT} OR DEFINED ENV{BOOSTROOT} )
+    set( CMAKE_PREFIX_PATH ${BOOST_ROOT} ${BOOSTROOT} $ENV{BOOST_ROOT} $ENV{BOOSTROOT} ${CMAKE_PREFIX_PATH} )
+  endif()
+
+  set( Boost_USE_MULTITHREADED  ON )
+  #   set( Boost_DEBUG              ON )
+
+  find_package( Boost 1.47.0 COMPONENTS unit_test_framework )
+
+  set( ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/include" )
+
+  if( Boost_FOUND AND Boost_UNIT_TEST_FRAMEWORK_LIBRARY )
+
+    set( HAVE_BOOST_UNIT_TEST 1 )
+    set( BOOST_UNIT_TEST_FRAMEWORK_LINKED 1 )
+
+    ecbuild_info( "Using Boost for unit tests:\n    INC [${Boost_INCLUDE_DIRS}]\n    LIB [${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}]" )
+
+  else()
+
+    ecbuild_info( "Boost unit test framework -- NOT FOUND" )
+
+    set( HAVE_BOOST_UNIT_TEST 0 )
+
+    # set( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY 1 )
+    # comment out this when ecbuild packs boost unit test inside...
+    # list( APPEND ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/contrib/boost-1.55/include" )
+    # set( HAVE_BOOST_UNIT_TEST 1 )
+
+  endif()
+
+endif()
+
+if( ENABLE_TESTS )
+
+  # CTest has built-in support for running with memcheck
+  # (https://cmake.org/cmake/help/latest/manual/ctest.1.html#ctest-memcheck-step)
+  # via `ctest -T memcheck`, however by default memcheck does not exit with a
+  # non-zero error code if any issues are found.
+  #
+  # CTest will run ${MEMORYCHECK_COMMAND} with ${MEMORYCHECK_COMMAND_OPTIONS}.
+  # Suppressions are read from ${MEMORYCHECK_SUPPRESSIONS_FILE} if given.
+
+  find_program( MEMORYCHECK_COMMAND valgrind )
+  ecbuild_debug_var( MEMORYCHECK_COMMAND )
+
+  if( NOT MEMORYCHECK_COMMAND_OPTIONS )
+    set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --error-exitcode=1"
+         CACHE STRING "Options passed to memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_COMMAND_OPTIONS )
+
+  if( NOT MEMORYCHECK_SUPPRESSIONS_FILE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/valgrind_suppress.txt" )
+    set( MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.supp"
+         CACHE FILEPATH "Suppressions file to be used with memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_SUPPRESSIONS_FILE )
+
+else()
+
+  ecbuild_info("Tests have been disabled")
+
+endif()
diff --git a/ecbuild/cmake/ecbuild_source_flags.cmake b/ecbuild/cmake/ecbuild_source_flags.cmake
new file mode 100644
index 0000000..bd63258
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_source_flags.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( __gen_source_flags ${CMAKE_CURRENT_LIST_DIR}/gen_source_flags.py )
+
+# Calls gen_source_flags.py to generate a CMake file with the per
+# source file flags for a given target.
+function( ecbuild_source_flags OUT TARGET DEFAULT_FLAGS SOURCES )
+
+  if( NOT PYTHONINTERP_FOUND OR PYTHON_VERSION VERSION_LESS 2.7 )
+    find_package( PythonInterp 2.7 REQUIRED )
+  endif()
+
+  set( OUTFILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_source_flags.cmake )
+
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    set( __debug "--debug" )
+  endif()
+  execute_process( COMMAND ${PYTHON_EXECUTABLE} ${__gen_source_flags}
+                           ${ECBUILD_SOURCE_FLAGS} ${OUTFILE} "${DEFAULT_FLAGS}"
+                           ${SOURCES} "${__debug}"
+                   RESULT_VARIABLE __res )
+
+  if( __res GREATER 0 )
+    ecbuild_error( "ecbuild_source_flags: failed generating source flags for target ${TARGET} from ${ECBUILD_SOURCE_FLAGS}" )
+  endif()
+  set( ${OUT} ${OUTFILE} PARENT_SCOPE )
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_system.cmake b/ecbuild/cmake/ecbuild_system.cmake
new file mode 100644
index 0000000..16c0299
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_system.cmake
@@ -0,0 +1,277 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+########################################################################################################
+# disallow in-source build
+
+if( EXISTS ${CMAKE_SOURCE_DIR}/CMakeCache.txt ) # check for failed attempts to build within the source tree
+    message( FATAL_ERROR "Project ${PROJECT_NAME} contains a CMakeCache.txt inside source tree [${CMAKE_SOURCE_DIR}/CMakeCache.txt].\n Please remove it and
+    make sure that source tree is prestine and clean of unintended files, before retrying." )
+endif()
+
+get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+if(${srcdir} STREQUAL ${bindir})
+    message("######################################################")
+    message("You are attempting to build in your source directory (${srcdir}).")
+    message("You must run cmake from a different build directory.")
+    message("######################################################")
+    message( FATAL_ERROR "${PROJECT_NAME} requires an out of source build.\n Please create a separate build directory and run 'cmake path/to/project [options]' from there.")
+endif()
+
+########################################################################################################
+# ecbuild versioning support
+
+set( ECBUILD_CMAKE_MINIMUM "2.8.10" )
+if( ${CMAKE_VERSION} VERSION_LESS ${ECBUILD_CMAKE_MINIMUM} )
+    message(FATAL_ERROR "${PROJECT_NAME} requires at least CMake ${ECBUILD_CMAKE_MINIMUM} -- you are using ${CMAKE_COMMAND} [${CMAKE_VERSION}]\n Please, get a newer version of CMake @ www.cmake.org" )
+endif()
+
+set( ECBUILD_MACROS_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "where ecbuild system is" )
+
+include( "${ECBUILD_MACROS_DIR}/VERSION.cmake" )
+
+set( ecbuild_VERSION_STR "${ECBUILD_VERSION_STR}" )
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+# set capitalised project name
+
+string( TOUPPER ${PROJECT_NAME} PROJECT_NAME_CAPS )
+string( TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWCASE )
+
+########################################################################################################
+# include our cmake macros, but only do so if this is the top project
+if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    # hostname of where we build
+
+    site_name( BUILD_SITE )
+    mark_as_advanced( BUILD_SITE )
+    mark_as_advanced( BUILD_TESTING )
+
+    set( ECBUILD_PROJECTS  "" CACHE INTERNAL "list of ecbuild (sub)projects that use ecbuild" )
+
+    # Include log macros since these are used right away
+    include( ecbuild_log )
+
+    execute_process( COMMAND env OUTPUT_VARIABLE __env )
+    ecbuild_debug( "---------------------------------------------------------" )
+    ecbuild_debug( "Environment:" )
+    ecbuild_debug( "---------------------------------------------------------\n${__env}" )
+    ecbuild_debug( "---------------------------------------------------------" )
+
+    ecbuild_info( "ecbuild   ${ecbuild_VERSION_STR}\t${ECBUILD_MACROS_DIR}" )
+    ecbuild_info( "cmake     ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}\t${CMAKE_COMMAND}" )
+
+    if( CMAKE_TOOLCHAIN_FILE )
+      ecbuild_info( "toolchain ${CMAKE_TOOLCHAIN_FILE}" )
+    endif()
+
+    if( ECBUILD_CONFIG )
+      ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+    endif()
+
+    if( ECBUILD_CACHE )
+      include( ${ECBUILD_CACHE} )
+      ecbuild_info( "cache     ${ECBUILD_CACHE}" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+    # clear the build dir exported targets file (only on the top project)
+
+    set( TOP_PROJECT_TARGETS_FILE "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-targets.cmake" CACHE INTERNAL "" )
+    file( REMOVE ${TOP_PROJECT_TARGETS_FILE} )
+
+    # add backport support for versions up too 2.8.4
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8" )
+    set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/2.8" ${CMAKE_MODULE_PATH} )
+    endif()
+
+    # add extra macros from external contributions
+    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib" )
+
+    # would bring FindEigen in, so for the moment keep it out
+    # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib/GreatCMakeCookOff" )
+
+    ############################################################################################
+    # define valid build types
+
+    include(ecbuild_define_build_types)
+
+    ############################################################################################
+    # add cmake macros
+
+    include(AddFileDependencies)
+
+    include(CheckTypeSize)
+    include(CheckIncludeFile)
+    include(CheckIncludeFiles)
+
+    include(CheckFunctionExists)
+    include(CheckSymbolExists)
+
+    include(CheckCCompilerFlag)
+    include(CheckCSourceCompiles)
+    include(CheckCSourceRuns)
+
+    include(CMakeParseArguments)
+
+    # include(CMakePrintSystemInformation) # available in cmake 2.8.4
+
+    if( CMAKE_CXX_COMPILER_LOADED )
+        include(CheckIncludeFileCXX)
+        include(CheckCXXCompilerFlag)
+        include(CheckCXXSourceCompiles)
+        include(CheckCXXSourceRuns)
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_LOADED )
+        set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module CACHE PATH "directory for all fortran modules." )
+        include(CheckFortranFunctionExists)
+        if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+            include(FortranCInterface)
+        endif()
+        set( EC_HAVE_FORTRAN 1 )
+    endif()
+
+    include(FeatureSummary) # support features in cmake
+
+    include(TestBigEndian)
+
+    ############################################################################################
+    # backport of cmake > 2.8.4 functions
+
+    if( "${CMAKE_VERSION}" VERSION_LESS "2.8.6" )
+        include( ${CMAKE_CURRENT_LIST_DIR}/2.8/CMakePushCheckState.cmake )
+    else()
+        include(CMakePushCheckState)
+    endif()
+
+    ############################################################################################
+    # add our macros
+
+    include( ecbuild_list_macros )
+    include( ecbuild_list_add_pattern )
+    include( ecbuild_list_exclude_pattern )
+
+    include( ecbuild_check_c_source_return )
+    include( ecbuild_check_cxx_source_return )
+    include( ecbuild_check_cxx11 )
+    include( ecbuild_check_fortran_source_return )
+
+    include( ecbuild_requires_macro_version )
+    include( ecbuild_get_date )
+    include( ecbuild_add_persistent )
+    include( ecbuild_generate_config_headers )
+    include( ecbuild_generate_rpc )
+    include( ecbuild_generate_yy )
+    include( ecbuild_generate_fortran_interfaces )
+    include( ecbuild_echo_targets )
+    include( ecbuild_features )
+    include( ecbuild_add_option )
+    include( ecbuild_add_library )
+    include( ecbuild_add_executable )
+    include( ecbuild_append_to_rpath )
+    include( ecbuild_download_resource )
+    include( ecbuild_get_test_data )
+    include( ecbuild_add_c_flags )
+    include( ecbuild_add_cxx_flags )
+    include( ecbuild_add_cxx11_flags )
+    include( ecbuild_get_cxx11_flags )
+    include( ecbuild_check_fortran )
+    include( ecbuild_add_fortran_flags )
+    include( ecbuild_add_test )
+    include( ecbuild_add_resources )
+    include( ecbuild_get_resources )
+    include( ecbuild_dont_pack )
+    include( ecbuild_project_files )
+    include( ecbuild_declare_project )
+    include( ecbuild_install_project )
+    include( ecbuild_separate_sources )
+    include( ecbuild_find_package )
+    include( ecbuild_use_package )
+    include( ecbuild_list_extra_search_paths )
+    include( ecbuild_add_extra_search_paths )
+    include( ecbuild_print_summary )
+    include( ecbuild_warn_unused_files )
+    include( ecbuild_find_mpi )
+    include( ecbuild_find_omp )
+    include( ecbuild_find_perl )
+    include( ecbuild_find_python )
+    include( ecbuild_find_lexyacc )
+    include( ecbuild_find_fortranlibs )
+    include( ecbuild_git )
+    include( ecbuild_enable_fortran )
+    include( ecbuild_source_flags )
+    include( ecbuild_target_flags )
+    include( ecbuild_bundle )
+    include( ecbuild_pkgconfig )
+    include( ecbuild_cache )
+    include( ecbuild_remove_fortran_flags )
+
+    include( ${CMAKE_CURRENT_LIST_DIR}/contrib/GetGitRevisionDescription.cmake )
+
+    ############################################################################################
+    # kickstart the build system
+
+    if( ECBUILD_CONFIG )
+      include( ${ECBUILD_CONFIG} )
+    endif()
+
+    ecbuild_prepare_cache()
+
+    include( ecbuild_define_options )               # define build options
+    include( ecbuild_compiler_flags )               # compiler flags
+    include( ecbuild_check_compiler )               # check for compiler characteristics
+    include( ecbuild_check_os )                     # check for os characteristics
+    include( ecbuild_check_functions )              # check for available functions
+    include( ecbuild_define_paths )                 # defines installation paths
+    include( ecbuild_define_libs_and_execs_target ) # defines the top level execs and libs
+    include( ecbuild_define_links_target )          # defines the links target
+    include( ecbuild_setup_test_framework )         # setup test framework
+    include( ecbuild_define_uninstall )             # define uninstall target
+
+    ecbuild_flush_cache()
+
+    ############################################################################################
+    # Testing
+
+    include(CTest)                 # add cmake testing support
+    enable_testing()
+
+    # keep this until we modify the meaning to 'check' if installation worked
+    add_custom_target( check COMMAND ${CMAKE_CTEST_COMMAND} )
+
+    ############################################################################################
+    # define the build timestamp, unless the user provided one via EC_BUILD_TIMESTAMP
+
+    if( NOT DEFINED EC_BUILD_TIMESTAMP )
+        ecbuild_get_timestamp( EC_BUILD_TIMESTAMP )
+        set( EC_BUILD_TIMESTAMP  "${EC_BUILD_TIMESTAMP}" CACHE INTERNAL "Build timestamp" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+else()
+
+    # Allow subprojects with different compilation flags. This could be done by defining
+    #     set( ECBUILD_C_FLAGS_DEBUG "-O0" )
+    # or
+    #     set( ECBUILD_CONFIG "<subproject-config>.cmake" )
+    if( ECBUILD_CONFIG )
+        ecbuild_info( "---------------------------------------------------------" )
+        ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+        include( ${ECBUILD_CONFIG} )
+    endif()
+    include( ecbuild_compiler_flags )
+
+endif()
diff --git a/ecbuild/cmake/ecbuild_target_flags.cmake b/ecbuild/cmake/ecbuild_target_flags.cmake
new file mode 100644
index 0000000..b6527d1
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_target_flags.cmake
@@ -0,0 +1,91 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_target_flags
+# ====================
+#
+# Override compiler flags for a given target. ::
+#
+#   ecbuild_target_flags( <target> <c_flags> <cxx_flags> <fortran_flags> )
+#
+# Required arguments:
+#
+# :target:        Target name
+# :c_flags:       Target specific C flags (can be empty)
+# :cxx_flags:     Target specific CXX flags (can be empty)
+# :fortran_flags: Target specific Fortran flags (can be empty)
+#
+# There are 3 cases, only the first applicable case takes effect:
+#
+# 1.  Use custom rules from user specified ``ECBUILD_COMPILE_FLAGS`` file and
+#     append target specific flags.
+#
+# 2.  Use JSON rules from user specified ``ECBUILD_SOURCE_FLAGS`` file and
+#     append target specific flags.
+#
+# 3.  Only the target specific flags are applied to all matching source files.
+#
+##############################################################################
+
+function( ecbuild_target_flags target c_flags cxx_flags fortran_flags )
+
+  get_property( languages GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${languages} )
+
+    string( TOLOWER ${lang} l )
+
+    if( ${target}_${l}_srcs )
+
+      # 1) Override compile flags from user specified CMake file
+      if( ECBUILD_COMPILE_FLAGS )
+
+        # Project specific flags for current language and optionally build type
+        set( pflags "${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+
+        foreach( src ${${target}_${l}_srcs} )
+          get_property( oflags SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS )
+          get_property( oflags_btype SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+          # Override compile flags for source file?
+          if( oflags OR oflags_btype )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${oflags} ${oflags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): overriding flags for ${src} with '${oflags} ${oflags_btype}'" )
+          # Otherwise append source file specific flags to project specific and target specific flags
+          else()
+            get_property( flags SOURCE ${src} PROPERTY COMPILE_FLAGS )
+            get_property( flags_btype SOURCE ${src} PROPERTY COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${pflags} ${${l}_flags} ${flags} ${flags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): setting flags for ${src} to '${pflags} ${${l}_flags} ${flags} ${flags_btype}'" )
+          endif()
+        endforeach()
+
+      # 2) Override compile flags from user specified JSON file
+      elseif( ECBUILD_SOURCE_FLAGS )
+        ecbuild_source_flags( ${target}_${lang}_SOURCE_FLAGS
+                              ${target}_${l}
+                              "${${l}_flags}"
+                              "${${target}_${l}_srcs}" )
+
+        ecbuild_debug("ecbuild_target_flags(${target}): setting source file ${lang} flags from ${${target}_${lang}_SOURCE_FLAGS}")
+        include( ${${target}_${lang}_SOURCE_FLAGS} )
+
+      # 3) Use target specific compile flags
+      elseif( ${l}_flags )
+
+        set_source_files_properties( ${${target}_${l}_srcs} PROPERTIES COMPILE_FLAGS "${${l}_flags}" )
+        ecbuild_debug("ecbuild_target_flags(${target}): setting flags for '${${target}_${l}_srcs}' to '${${l}_flags}'")
+
+      endif()
+    endif()
+
+  endforeach()
+
+endfunction()
diff --git a/ecbuild/cmake/ecbuild_uninstall.cmake.in b/ecbuild/cmake/ecbuild_uninstall.cmake.in
new file mode 100644
index 0000000..33b1949
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_uninstall.cmake.in
@@ -0,0 +1,28 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+
+if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt")
+  file(READ "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt" __files)
+  string(REGEX REPLACE "\n" ";" __files "${__files}")
+  list(APPEND files ${__files})
+endif()
+
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif()
+  else()
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif()
+endforeach(file)
diff --git a/ecbuild/cmake/ecbuild_use_package.cmake b/ecbuild/cmake/ecbuild_use_package.cmake
new file mode 100644
index 0000000..d54e459
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_use_package.cmake
@@ -0,0 +1,341 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_use_package
+# ===================
+#
+# Add a project from a source directory, a subdirectory or search for it. ::
+#
+#   ecbuild_use_package( PROJECT <name>
+#                        [ VERSION <version> [ EXACT ] ]
+#                        [ URL <url> ]
+#                        [ DESCRIPTION <description> ]
+#                        [ TYPE <type> ]
+#                        [ PURPOSE <purpose> ]
+#                        [ FAILURE_MSG <message> ]
+#                        [ REQUIRED ]
+#                        [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   string describing the package (shown in summary and stored in the cache)
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   string describing which functionality this package enables in the project
+#
+# FAILURE_MSG : optional
+#   string to be appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>``
+# is the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :<NAME>_SOURCE:    path to source directory for package
+# :SUBPROJECT_DIRS:  list of additional paths to search for package source
+#
+# See also ``ecbuild_find_package`` for additional CMake variables relevant
+# when search for the package (step 6 below).
+#
+# Usage
+# -----
+#
+# Use another CMake project as a dependency by either building it from source
+# i.e. adding its source directory as a subdirectory or searching for it. This
+# transparently deals with the case where the project has already been included
+# e.g. because multiple projects with shared dependencies are built together.
+#
+# The search proceeds as follows:
+#
+# 1.  If ``SUBPROJECT_DIRS`` is set, each directory in the list is searched
+#     for a subdirectory <name> and ``<NAME>_SOURCE`` is set to the first one
+#     found (if any).
+#
+# 2.  If ``<NAME>_SOURCE`` is set, check if this directory is a CMake project
+#     (contains ``CMakeLists.txt`` and fail if not.
+#
+# 3.  Otherwise, check if the current directory has a ``<name>`` subdirectory.
+#
+# 4.  If the project has not been previously marked as found or added as a
+#     subdirectory and a project source directory has been found in steps 1-3
+#     add this subdirectory.
+#
+# 5.  If the project has been marked as found, check the version.
+#
+# 6.  Otherwise, search for the project using ``ecbuild_find_package``.
+#
+##############################################################################
+
+macro( ecbuild_use_package )
+
+  set( options            REQUIRED QUIET EXACT )
+  set( single_value_args  PROJECT VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_use_package(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_PROJECT  )
+    ecbuild_critical("The call to ecbuild_use_package() doesn't specify the PROJECT.")
+  endif()
+
+  if( _p_EXACT AND NOT _p_VERSION )
+    ecbuild_critical("Call to ecbuild_use_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _p_REQUIRED )
+    set( _p_TYPE REQUIRED )
+  endif()
+
+  # try to find the package as a subproject and build it
+
+  string( TOUPPER ${_p_PROJECT} pkgUPPER )
+
+  # user defined dir with subprojects
+
+  if( NOT DEFINED ${pkgUPPER}_SOURCE AND DEFINED SUBPROJECT_DIRS )
+    ecbuild_warn("ecbuild_use_package(): setting SUBPROJECT_DIRS is deprecated")
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): scanning subproject directories ${SUBPROJECT_DIRS}")
+    foreach( dir ${SUBPROJECT_DIRS} )
+      if( EXISTS ${dir}/${_p_PROJECT} AND EXISTS ${dir}/${_p_PROJECT}/CMakeLists.txt )
+        ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):   setting ${pkgUPPER}_SOURCE to ${dir}/${_p_PROJECT}")
+        set( ${pkgUPPER}_SOURCE "${dir}/${_p_PROJECT}" )
+      endif()
+    endforeach()
+  endif()
+
+  # user defined path to subproject
+
+  if( DEFINED ${pkgUPPER}_SOURCE )
+
+    if( NOT EXISTS ${${pkgUPPER}_SOURCE} OR NOT EXISTS ${${pkgUPPER}_SOURCE}/CMakeLists.txt )
+      ecbuild_critical("User defined source directory '${${pkgUPPER}_SOURCE}' for project '${_p_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    set( ${pkgUPPER}_subproj_dir_ "${${pkgUPPER}_SOURCE}" )
+
+  else() # default is 'dropped in' subdirectory named as project
+
+    if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT} AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}/CMakeLists.txt )
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): found ${_p_PROJECT} in subdirectory ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}")
+      set( ${pkgUPPER}_subproj_dir_ "${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}" )
+    endif()
+
+  endif()
+
+  # check if was already added as subproject ...
+
+  set( _just_added 0 )
+  set( _do_version_check 0 )
+  set( _source_description "" )
+
+  list( FIND ECBUILD_PROJECTS ${_p_PROJECT} _ecbuild_project_${pkgUPPER} )
+
+  if( NOT _ecbuild_project_${pkgUPPER} EQUAL "-1" )
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 1 )
+  else()
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was not previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 0 )
+  endif()
+
+  # solve capitalization issues
+
+  if( ${_p_PROJECT}_FOUND AND NOT ${pkgUPPER}_FOUND )
+    set( ${pkgUPPER}_FOUND 1 )
+  endif()
+  if( ${pkgUPPER}_FOUND AND NOT ${_p_PROJECT}_FOUND )
+    set( ${_p_PROJECT}_FOUND 1 )
+  endif()
+
+  # Case 1) project was NOT previously added as subproject and is NOT already FOUND
+
+  if( NOT ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ )
+
+    # check if SUBPROJDIR is set
+
+    if( DEFINED ${pkgUPPER}_subproj_dir_ )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 1) project was NOT previously added as subproject and is NOT already FOUND")
+
+      # check version is acceptable
+      set( _just_added 1 )
+      set( _do_version_check 1 )
+      set( _source_description "sub-project ${_p_PROJECT} (sources)" )
+
+      # add as a subproject
+
+      set( ${pkgUPPER}_subproj_dir_ ${${pkgUPPER}_subproj_dir_} CACHE PATH "Path to ${_p_PROJECT} source directory" )
+      mark_as_advanced( ${pkgUPPER}_subproj_dir_ )
+
+      set( ECBUILD_PROJECTS ${ECBUILD_PROJECTS} ${_p_PROJECT} CACHE INTERNAL "" )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):    ${_p_PROJECT} found in subdirectory ${${pkgUPPER}_subproj_dir_}")
+      add_subdirectory( ${${pkgUPPER}_subproj_dir_} ${_p_PROJECT} )
+
+      set( ${_p_PROJECT}_BASE_DIR ${CMAKE_BINARY_DIR} )
+
+      set( ${pkgUPPER}_FOUND 1 )
+      set( ${_p_PROJECT}_VERSION ${${pkgUPPER}_VERSION} )
+
+      list( APPEND ${pkgUPPER}_INCLUDE_DIRS ${${pkgUPPER}_TPL_INCLUDE_DIRS} )
+
+    endif()
+
+  endif()
+
+  # Case 2) project was already added as subproject, so is already FOUND -- BUT must check version acceptable
+
+  if( ${pkgUPPER}_previous_subproj_ )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 2) project was already added as subproject, check version is acceptable")
+
+    if( NOT ${pkgUPPER}_FOUND )
+      ecbuild_critical( "${_p_PROJECT} was already included as sub-project but ${pkgUPPER}_FOUND isn't set -- this is likely a BUG in ecbuild" )
+    endif()
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "already existing sub-project ${_p_PROJECT} (sources)" )
+
+  endif()
+
+  # Case 3) project was NOT added as subproject, but is FOUND -- so it was previously found as a binary ( either build or install tree )
+
+  if( ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ AND NOT _just_added )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 3) project was NOT previously added as subproject, but is FOUND")
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "previously found package ${_p_PROJECT} (binaries)" )
+
+  endif()
+
+  # test version for Cases 1,2,3
+
+  # ecbuild_debug_var( _p_PROJECT )
+  # ecbuild_debug_var( _p_VERSION )
+  # ecbuild_debug_var( ${pkgUPPER}_VERSION )
+  # ecbuild_debug_var( ${_p_PROJECT}_VERSION )
+  # ecbuild_debug_var( _just_added )
+  # ecbuild_debug_var( _do_version_check )
+  # ecbuild_debug_var( _source_description )
+  # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+  # ecbuild_debug_var( ${pkgUPPER}_previous_subproj_ )
+
+  if( _p_VERSION AND _do_version_check )
+    if( _p_EXACT )
+      if( NOT ${_p_PROJECT}_VERSION VERSION_EQUAL _p_VERSION )
+        ecbuild_critical( "${PROJECT_NAME} requires (exactly) ${_p_PROJECT} = ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    else()
+      if( _p_VERSION VERSION_LESS ${_p_PROJECT}_VERSION OR _p_VERSION VERSION_EQUAL ${_p_PROJECT}_VERSION )
+        ecbuild_info( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      else()
+        ecbuild_critical( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected only ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    endif()
+  endif()
+
+  # Case 4) is NOT FOUND so far, NOT as sub-project (now or before), and NOT as binary neither
+  #         so try to find precompiled binaries or a build tree
+
+  if( ${pkgUPPER}_FOUND )
+    # Only set package properties if ecbuild_find_package, which itself calls
+    # set_package_properties, is not subsequently called since doing so would
+    # duplicate the purpose
+    set_package_properties( ${_p_PROJECT} PROPERTIES
+                            URL "${_p_URL}"
+                            DESCRIPTION "${_p_DESCRIPTION}"
+                            TYPE "${_p_TYPE}"
+                            PURPOSE "${_p_PURPOSE}" )
+  else()
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 4) project has NOT been added as a subproject and is NOT already FOUND")
+
+    set( _opts )
+    if( _p_VERSION )
+      list( APPEND _opts VERSION ${_p_VERSION} )
+    endif()
+    if( _p_EXACT )
+      list( APPEND _opts EXACT )
+    endif()
+    if( _p_REQUIRED )
+      list( APPEND _opts REQUIRED )
+    endif()
+    if( _p_URL )
+      list( APPEND _opts URL ${_p_URL} )
+    endif()
+    if( _p_DESCRIPTION )
+      list( APPEND _opts DESCRIPTION "${_p_DESCRIPTION}" )
+    endif()
+    if( _p_TYPE )
+      list( APPEND _opts TYPE ${_p_TYPE} )
+    endif()
+    if( _p_PURPOSE )
+      list( APPEND _opts PURPOSE "${_p_PURPOSE}" )
+    endif()
+    if( _p_FAILURE_MSG )
+      ecbuild_debug_var( _p_FAILURE_MSG )
+      list( APPEND _opts FAILURE_MSG "${_p_FAILURE_MSG}" )
+    endif()
+
+    ecbuild_find_package( NAME ${_p_PROJECT} ${_opts} )
+
+    if( ${_p_PROJECT}_FOUND )
+      set( ${pkgUPPER}_FOUND ${${_p_PROJECT}_FOUND} )
+    endif()
+
+  endif()
+
+  if( ${pkgUPPER}_FOUND )
+    list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${_p_PROJECT} )
+    list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+  endif()
+
+  ### for when we change this macro to a function()
+  # set_parent_scope( ${pkgUPPER}_FOUND )
+  # set_parent_scope( ${_p_PROJECT}_FOUND )
+  # set_parent_scope( ${pkgUPPER}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_BINARY_DIR )
+
+endmacro()
diff --git a/ecbuild/cmake/ecbuild_version.h.in b/ecbuild/cmake/ecbuild_version.h.in
new file mode 100644
index 0000000..89b0e33
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_version.h.in
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ecbuild_version_h
+#define ecbuild_version_h
+
+#define ECBUILD_VERSION "@ECBUILD_VERSION@"
+
+#define ECBUILD_MAJOR_VERSION @ECBUILD_MAJOR_VERSION@
+#define ECBUILD_MINOR_VERSION @ECBUILD_MINOR_VERSION@
+#define ECBUILD_PATCH_VERSION @ECBUILD_PATCH_VERSION@
+
+#endif // ecbuild_version_h
diff --git a/ecbuild/cmake/ecbuild_warn_unused_files.cmake b/ecbuild/cmake/ecbuild_warn_unused_files.cmake
new file mode 100644
index 0000000..7d330d7
--- /dev/null
+++ b/ecbuild/cmake/ecbuild_warn_unused_files.cmake
@@ -0,0 +1,77 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_warn_unused_files
+# =========================
+#
+# Print warnings about unused source files in the project. ::
+#
+#   ecbuild_warn_unused_files()
+#
+# If the CMake variable ``CHECK_UNUSED_FILES`` is set, ecBuild will keep track
+# of any source files (.c, .cc, .cpp, .cxx) which are not part of a CMake
+# target. If set, this macro reports unused files if any have been found. This
+# is considered a fatal error unless ``UNUSED_FILES_LEVEL`` is set to a value
+# different from ``ERROR``.
+#
+# .. note ::
+#
+#   Enabling ``CHECK_UNUSED_FILES`` can slow down the CMake configure time
+#   considerably!
+#
+##############################################################################
+
+macro( ecbuild_warn_unused_files )
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME ) # only for top level project
+    
+      # if cache file with unused files exists remove it
+      set( UNUSED_FILE "${CMAKE_BINARY_DIR}/UnusedFiles.txt" )
+      if( EXISTS ${UNUSED_FILE} )
+              file( REMOVE ${UNUSED_FILE} )
+      endif()
+    
+      if( CHECK_UNUSED_FILES ) # to check or not to check...
+    
+          if( NOT DEFINED UNUSED_FILES_LEVEL ) # to err or not...
+              set( UNUSED_FILES_LEVEL "ERROR" )
+          endif()
+    
+          # if unused files where found, put the list on the file
+          if( EC_UNUSED_FILES )
+    
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+            ecbuild_info(" Unused source files found:")
+            foreach( AFILE ${EC_UNUSED_FILES} )
+              ecbuild_info("     ${AFILE}")
+              file( APPEND ${UNUSED_FILE} "${AFILE}\n" )
+            endforeach()
+            ecbuild_info("")
+            ecbuild_info(" List dumped to ${UNUSED_FILE}")
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+    
+            if( UNUSED_FILES_LEVEL STREQUAL "ERROR" )
+              ecbuild_critical( "\n Aborted build system configuration. \n Add unused files to the build system or remove them." )
+            endif()
+    
+          endif()
+    
+      endif()
+    
+    endif()
+
+endmacro( ecbuild_warn_unused_files )
diff --git a/ecbuild/cmake/fcm-make-interfaces.cfg b/ecbuild/cmake/fcm-make-interfaces.cfg
new file mode 100644
index 0000000..a73363a
--- /dev/null
+++ b/ecbuild/cmake/fcm-make-interfaces.cfg
@@ -0,0 +1,31 @@
+# FCM configuration file used to auto-generate interface files
+# for F77 and F90 files.
+# Interface files will have the extention ".intfb.h"
+# Results will be in a directory "interfaces/include" relative to cwd
+
+# Usage: fcm make --config-file=<path -to-this-file> \
+#                 interfaces.ns-incl="<space-sep-list-of-dirs>"
+
+$SRC{?}  = $HERE
+
+step.class[interfaces] = build
+steps  = interfaces
+
+interfaces.target{task}     = ext-iface
+interfaces.target{category} = include
+
+interfaces.source = $SRC
+
+# Exclude all
+interfaces.ns-excl = /
+
+# Include some
+# interfaces.ns-incl = <list of dirs passed at command-line>
+
+# Extention of interface files
+interfaces.prop{file-ext.f90-interface} = .intfb.h
+
+# Do not follow includes
+interfaces.prop{no-dep.f.module} = *
+interfaces.prop{no-dep.include} = *
+
diff --git a/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake b/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake
new file mode 100644
index 0000000..992964f
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake
@@ -0,0 +1,167 @@
+###############################################################################
+# checks
+set(Fortran_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "fortran file directory")
+
+MACRO(fortran_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/fortran_feature_tests/fortran_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking Fortran support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.F90")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.F90")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.F90")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "Fortran support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(fortran_check_single_feature)
+
+# Find list of all features
+function(fortran_find_all_features outvar)
+  FILE(GLOB ALL_Fortran_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/*.F90")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_Fortran_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_fortran_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/${current_feature}*.F90")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[Fortran] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+  fortran_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_fortran_feature)
+
+function(fortran_feature_check)
+
+  # find all features
+  fortran_find_all_features(ALL_Fortran_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_Fortran_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[Fortran] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_fortran_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_fortran_feature(${current_feature})
+    set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[Fortran] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(fortran_feature_check)
diff --git a/ecbuild/cmake/fortran_features/c_size_t.F90 b/ecbuild/cmake/fortran_features/c_size_t.F90
new file mode 100644
index 0000000..3c47136
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/c_size_t.F90
@@ -0,0 +1,8 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_size_t, c_int, c_long
+
+write(0,*) "c_int    = ",c_int
+write(0,*) "c_long   = ",c_long
+write(0,*) "c_size_t = ",c_size_t
+
+end program
\ No newline at end of file
diff --git a/ecbuild/cmake/fortran_features/c_sizeof.F90 b/ecbuild/cmake/fortran_features/c_sizeof.F90
new file mode 100644
index 0000000..fc1be41
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/c_sizeof.F90
@@ -0,0 +1,3 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_sizeof
+end program
\ No newline at end of file
diff --git a/ecbuild/cmake/fortran_features/derivedtype_interface.F90 b/ecbuild/cmake/fortran_features/derivedtype_interface.F90
new file mode 100644
index 0000000..d59a1c3
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/derivedtype_interface.F90
@@ -0,0 +1,54 @@
+module constructor
+
+implicit none
+
+TYPE :: AnimalType
+  private
+  integer :: m_age
+contains
+  procedure :: age
+  procedure :: speak
+ENDTYPE
+
+! Declare constructor as interface with same name as type
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+contains
+
+function AnimalType__ctor(age) result(self)
+  type(AnimalType) :: self
+  integer :: age
+  write(0,'(A)') "Constructor Animal"
+  self%m_age = age
+end function
+
+function age(self)
+  class(AnimalType), intent(inout) :: self
+  integer :: age
+  age = self%m_age
+end function
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(A)') "Animal::speak not overridden"
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_constructor
+use constructor
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8)
+
+  write(0,'(A,I0)') "age = ",animal%age()
+
+  call animal%speak()
+
+end program
diff --git a/ecbuild/cmake/fortran_features/derivedtype_io.F90 b/ecbuild/cmake/fortran_features/derivedtype_io.F90
new file mode 100644
index 0000000..47a98b0
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/derivedtype_io.F90
@@ -0,0 +1,42 @@
+module write_module
+
+implicit none
+
+TYPE :: AnimalType
+  integer :: m_age
+  integer :: m_paws
+contains
+  procedure :: writetype
+  generic :: write(formatted) => writetype
+ENDTYPE
+
+contains
+
+subroutine writetype(animal, unit, iotype, v_list, iostat, iomsg)
+  ! Argument names here from the std, but you can name them differently.
+  class(AnimalType), intent(in) :: animal ! Object to write.
+  integer, intent(in) :: unit             ! Internal unit to write to.
+  character(*), intent(in) :: iotype      ! LISTDIRECTED or DTxxx
+  integer, intent(in) :: v_list(:)        ! parameters from fmt spec.
+  integer, intent(out) :: iostat          ! non zero on error, etc.
+  character(*), intent(inout) :: iomsg    ! define if iostat non zero.
+
+  write (unit, "(A)", IOSTAT=iostat, IOMSG=iomsg) &
+      "I am a dog"
+end subroutine writetype
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_write
+use write_module
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8,4)
+
+  write(0,'(A,DT)') 'Custom writing: ',animal
+
+end program
diff --git a/ecbuild/cmake/fortran_features/finalization.F90 b/ecbuild/cmake/fortran_features/finalization.F90
new file mode 100644
index 0000000..5bacd5f
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/finalization.F90
@@ -0,0 +1,141 @@
+module final_module
+
+implicit none
+
+integer :: final_counted = 0
+integer :: destroy_counted = 0
+
+TYPE :: AnimalType
+  character(len=20), private :: m_kind = "unidentified"
+  logical :: constructed = .false.
+contains
+  procedure :: speak
+  final :: AnimalType__dtor
+ENDTYPE
+
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+interface assignment(=)
+  module procedure AnimalType__assignment
+end interface
+
+contains
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(2A)') "I am a ",self%m_kind
+end subroutine
+
+subroutine AnimalType__dtor(self)
+  type(AnimalType), intent(inout) :: self
+
+  write(0,'(2A)') "Final animal ",self%m_kind
+  final_counted = final_counted + 1
+
+  ! Destruction guard needed for portability
+  if( self%constructed ) then
+    write(0,'(2A)') "    Destroy animal ",self%m_kind
+    destroy_counted = destroy_counted + 1
+  endif
+end subroutine
+
+function AnimalType__ctor(animaltype_) result(self)
+  type(AnimalType) :: self
+  character(len=*) :: animaltype_
+  self%m_kind = animaltype_
+  write(0,'(3A,I0)') "Constructing animal ",self%m_kind, " -- address = ",loc(self)
+  self%constructed = .true.
+end function
+
+subroutine AnimalType__assignment(animal_out,animal_in)
+  type(AnimalType), intent(out) :: animal_out
+  class(AnimalType), intent(in) :: animal_in
+  write(0,'(3A,I0,A,I0)') '   Copying ',animal_in%m_kind, " -- from address ", loc(animal_in), " to address ", loc(animal_out)
+  animal_out%m_kind = animal_in%m_kind
+  animal_out%constructed = animal_in%constructed
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+subroutine scope_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: cat
+
+  dog = AnimalType("dog")  ! Cray       : final called on temporary AnimalType("dog"); missing final call on dog before assignment
+                           ! Intel      : final called on dog before assignment; and on temporary AnimalType("dog")
+                           ! PGI 14.4   : final NOT called at all, possibly compiler bug
+                           ! GNU 4.9    : final called on dog before assignment; missing call on temporary AnimalType("dog")
+  call dog%speak()
+
+  ! final called on dog when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+subroutine assignment_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: animal
+
+  dog = AnimalType("dog")    ! final called on dog before assignment
+  call dog%speak()
+  write(0,'(A)') "-- animal = dog"
+  animal = dog               ! final called on animal before assignment
+  call animal%speak()
+
+  ! final called on dog when out of scope
+  ! final called on animal when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+program test_final
+use final_module
+implicit none
+  logical :: test_failed = .false.
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin scope_test"
+  call scope_test
+  write(0,'(A)') "<<<<<< end scope_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 1 ) then
+    test_failed = .true.
+    write(0,'(A)') "ASSERTION FAILED: destroy_counted < 1"
+  endif
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin assignment_test"
+  call assignment_test
+  write(0,'(A)') "<<<<<< end assignment_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 2 ) then
+    test_failed = .true.
+    write(0,*) "ASSERTION FAILED: destroy_counted < 2"
+  endif
+  if( test_failed ) STOP 1
+
+end program
diff --git a/ecbuild/cmake/fortran_features/submodules.F90 b/ecbuild/cmake/fortran_features/submodules.F90
new file mode 100644
index 0000000..3a2261f
--- /dev/null
+++ b/ecbuild/cmake/fortran_features/submodules.F90
@@ -0,0 +1,35 @@
+module sb_module
+implicit none
+integer :: a = 1
+
+interface
+  module subroutine sb
+  end subroutine
+end interface
+
+contains
+end module sb_module
+
+! -------------------------------------------------------
+
+submodule (sb_module) sb_submod1
+implicit none
+integer :: b = 2
+
+contains
+
+module subroutine sb()
+  a = b
+end subroutine
+
+end submodule sb_submod1
+
+! -------------------------------------------------------
+
+program test_submodule
+use sb_module
+implicit none
+write(0,*) a
+call sb()
+write(0,*) a
+end program
\ No newline at end of file
diff --git a/ecbuild/cmake/gen_source_flags.py b/ecbuild/cmake/gen_source_flags.py
new file mode 100644
index 0000000..c08ad08
--- /dev/null
+++ b/ecbuild/cmake/gen_source_flags.py
@@ -0,0 +1,84 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+"""
+Generate .cmake file to set source-file specific compiler flags based on
+rules defined in a JSON file.
+"""
+
+from argparse import ArgumentParser
+from fnmatch import fnmatch
+import logging
+from json import JSONDecoder
+from os import path
+
+log = logging.getLogger('gen_source_flags')
+
+
+def match(source, pattern, op, flags, indent=0):
+    if fnmatch(source, pattern):
+
+        suff = '' if op[0] in ('+', '=', '/') else ' (nested pattern)'
+        log.debug('%s-> pattern "%s" matches "%s"%s',
+                  ' ' * (indent + 1), pattern, source, suff)
+
+        if op[0] == "+":
+            flags += [flag for flag in op[1:] if flag not in flags]
+            log.debug('%sappending %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "=":
+            flags = op[1:]
+            log.debug('%ssetting %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "/":
+            flags = [flag for flag in flags if flag not in op[1:]]
+            log.debug('%sremoving %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        else:  # Nested rule
+            log.debug('%sapplying nested rules for "%s" (flags: %s)',
+                      ' ' * (indent + 2), pattern, flags)
+            for nested_pattern, nested_op in op:
+                flags = match(source, nested_pattern, nested_op, flags, indent + 2)
+
+    return flags
+
+
+def generate(rules, out, default_flags, sources, debug=False):
+    logging.basicConfig(level=logging.DEBUG if debug else logging.INFO,
+                        format='-- %(levelname)s - %(name)s: %(message)s')
+
+    with open(path.expanduser(rules)) as f:
+        rules = JSONDecoder(object_pairs_hook=list).decode(f.read())
+
+    with open(path.expanduser(out), 'w') as f:
+        for source in sources:
+            log.debug('%s (default flags: "%s")', source, default_flags)
+            flags = default_flags.split()
+            for pattern, op in rules:
+                flags = match(source, pattern, op, flags)
+
+            if flags:
+                log.debug(' ==> setting flags for %s to %s', source, ' '.join(flags))
+                f.write('set_source_files_properties(%s PROPERTIES COMPILE_FLAGS "%s")\n'
+                        % (source, ' '.join(flags)))
+            else:
+                log.debug(' ==> flags for %s empty', source)
+
+
+def main():
+    """Parse arguments"""
+    parser = ArgumentParser(description=__doc__)
+    parser.add_argument('rules', metavar='RULES.json', help='JSON rules file')
+    parser.add_argument('out', metavar='OUT.cmake', help='CMake script to generate')
+    parser.add_argument('default_flags', help='Default compiler flags to use')
+    parser.add_argument('sources', metavar='file', nargs='+', help='Path to file to apply rules to')
+    parser.add_argument('--debug', '-d', action='store_true', help='Log debug messages')
+    generate(**vars(parser.parse_args()))
+
+if __name__ == '__main__':
+    main()
diff --git a/ecbuild/cmake/include/ecbuild/boost_test_framework.h b/ecbuild/cmake/include/ecbuild/boost_test_framework.h
new file mode 100644
index 0000000..ba48689
--- /dev/null
+++ b/ecbuild/cmake/include/ecbuild/boost_test_framework.h
@@ -0,0 +1,17 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifdef BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#include <boost/test/included/unit_test.hpp>
+#else
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+#endif
+
diff --git a/ecbuild/cmake/md5.in b/ecbuild/cmake/md5.in
new file mode 100644
index 0000000..bf8aeb0
--- /dev/null
+++ b/ecbuild/cmake/md5.in
@@ -0,0 +1 @@
+ at _p_MD5@  @_p_NAME@
diff --git a/ecbuild/cmake/pkg-config.pc.in b/ecbuild/cmake/pkg-config.pc.in
new file mode 100644
index 0000000..e6d903d
--- /dev/null
+++ b/ecbuild/cmake/pkg-config.pc.in
@@ -0,0 +1,35 @@
+# This pkg-config file is generated by ecbuild_pkgconfig()
+# with template ecbuild/cmake/pkg-config.pc.in
+
+git_tag=@PKGCONFIG_GIT_TAG@
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@INSTALL_LIB_DIR@
+includedir=${prefix}/@INSTALL_INCLUDE_DIR@
+bindir=${prefix}/@INSTALL_BIN_DIR@
+fmoddir=${prefix}/@INSTALL_INCLUDE_DIR@
+
+CC=@CMAKE_C_COMPILER@
+CXX=@CMAKE_CXX_COMPILER@
+FC=@CMAKE_Fortran_COMPILER@
+
+rpath=@RPATH_FLAG@${libdir}
+
+libs=-L${libdir} ${rpath} @PKGCONFIG_LIBS@
+
+libs_private=@PKGCONFIG_LIBS_PRIVATE@
+
+cflags=@PKGCONFIG_INCLUDE@ @PKGCONFIG_CFLAGS@
+ at PKGCONFIG_VARIABLES@
+#====================================================================
+Name: @PKGCONFIG_NAME@
+Description: @PKGCONFIG_DESCRIPTION@
+URL: @PKGCONFIG_URL@
+Version: @PKGCONFIG_VERSION@
+Libs: ${libs}
+Libs.private: ${libs_private}
+Requires: @PKGCONFIG_REQUIRES@
+Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@
+Cflags: ${cflags}
+#====================================================================
diff --git a/ecbuild/cmake/project-config-version.cmake.in b/ecbuild/cmake/project-config-version.cmake.in
new file mode 100644
index 0000000..802a2fe
--- /dev/null
+++ b/ecbuild/cmake/project-config-version.cmake.in
@@ -0,0 +1,12 @@
+set(PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# check whether the requested PACKAGE_FIND_VERSION is compatible
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+  set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+  set(PACKAGE_VERSION_COMPATIBLE TRUE)
+  if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+    set(PACKAGE_VERSION_EXACT TRUE)
+  endif()
+endif()
\ No newline at end of file
diff --git a/ecbuild/cmake/project-config.cmake.in b/ecbuild/cmake/project-config.cmake.in
new file mode 100644
index 0000000..0e1e83b
--- /dev/null
+++ b/ecbuild/cmake/project-config.cmake.in
@@ -0,0 +1,97 @@
+# Config file for the @PROJECT_NAME@ package
+# Defines the following variables:
+#
+#  @PNAME at _INCLUDE_DIRS   - include directories
+#  @PNAME at _DEFINITIONS    - preprocessor definitions
+#  @PNAME at _LIBRARIES      - libraries to link against
+#  @PNAME at _FEATURES       - list of enabled features
+#  @PNAME at _VERSION        - version of the package
+#  @PNAME at _GIT_SHA1       - Git revision of the package
+#  @PNAME at _GIT_SHA1_SHORT - short Git revision of the package
+#
+# Also defines @PROJECT_NAME@ third-party library dependencies:
+#  @PNAME at _TPLS             - package names of  third-party library dependencies
+#  @PNAME at _TPL_INCLUDE_DIRS - include directories
+#  @PNAME at _TPL_DEFINITIONS  - preprocessor definitions
+#  @PNAME at _TPL_LIBRARIES    - libraries to link against
+
+### compute paths
+
+get_filename_component(@PNAME at _CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+
+set( @PNAME at _SELF_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@" )
+set( @PNAME at _SELF_DEFINITIONS  "@CONF_DEFINITIONS@" )
+set( @PNAME at _SELF_LIBRARIES    "@CONF_LIBRARIES@" )
+
+set( @PNAME at _TPLS              "@CONF_TPLS@" )
+set( @PNAME at _TPL_INCLUDE_DIRS  "@CONF_TPL_INCLUDE_DIRS@" )
+set( @PNAME at _TPL_DEFINITIONS   "@CONF_TPL_DEFINITIONS@" )
+set( @PNAME at _TPL_LIBRARIES     "@CONF_TPL_LIBRARIES@" )
+
+set( @PNAME at _VERSION           "@PACKAGE_VERSION@" )
+set( @PNAME at _GIT_SHA1          "@PACKAGE_GIT_SHA1@" )
+set( @PNAME at _GIT_SHA1_SHORT    "@PACKAGE_GIT_SHA1_SHORT@" )
+
+### export include paths as absolute paths
+
+set( @PNAME at _INCLUDE_DIRS "" )
+foreach( path ${@PNAME at _SELF_INCLUDE_DIRS} )
+  get_filename_component( abspath ${path} ABSOLUTE )
+  list( APPEND @PNAME at _INCLUDE_DIRS ${abspath} )
+endforeach()
+list( APPEND @PNAME at _INCLUDE_DIRS ${@PNAME at _TPL_INCLUDE_DIRS} )
+
+### export definitions
+
+set( @PNAME at _DEFINITIONS      ${@PNAME at _SELF_DEFINITIONS} ${@PNAME at _TPL_DEFINITIONS} )
+
+### export list of all libraries
+
+set( @PNAME at _LIBRARIES        ${@PNAME at _SELF_LIBRARIES}   ${@PNAME at _TPL_LIBRARIES}   )
+
+### export the features provided by the package
+
+set( @PNAME at _FEATURES    "@CONF_FEATURES@" )
+foreach( _f ${@PNAME at _FEATURES} )
+  set( @PNAME at _HAVE_${_f} 1 )
+endforeach()
+
+# Has this configuration been exported from a build tree?
+set( @PNAME at _IS_BUILD_DIR_EXPORT @_is_build_dir_export@ )
+
+if( EXISTS ${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@ )
+  set( @PNAME at _IMPORT_FILE "${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@" )
+  include( ${@PNAME at _IMPORT_FILE} )
+endif()
+
+# here goes the imports of the TPL's
+
+include( ${CMAKE_CURRENT_LIST_FILE}.tpls OPTIONAL )
+
+# insert definitions for IMPORTED targets
+
+if( NOT @PROJECT_NAME at _BINARY_DIR )
+
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    include( "@TOP_PROJECT_TARGETS_FILE@" OPTIONAL )
+  else()
+    include( "${@PNAME at _CMAKE_DIR}/@PROJECT_NAME at -targets.cmake" OPTIONAL )
+  endif()
+
+endif()
+
+# publish this file as imported
+
+set( @PNAME at _IMPORT_FILE ${CMAKE_CURRENT_LIST_FILE} )
+mark_as_advanced( @PNAME at _IMPORT_FILE )
+
+# set @PROJECT_NAME at _BASE_DIR for final installations or build directories
+
+if( NOT @PROJECT_NAME@ )
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    set( @PROJECT_NAME at _BASE_DIR @CMAKE_BINARY_DIR@ )
+  else()
+    get_filename_component( abspath ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE )
+    set( @PROJECT_NAME at _BASE_DIR ${abspath} )
+  endif()
+endif()
diff --git a/ecbuild/cmake/pymain.c b/ecbuild/cmake/pymain.c
new file mode 100644
index 0000000..823d57d
--- /dev/null
+++ b/ecbuild/cmake/pymain.c
@@ -0,0 +1,5 @@
+#include <Python.h>
+
+int main() {
+  return 0;
+}
diff --git a/ecbuild/cmake/sg.pl b/ecbuild/cmake/sg.pl
new file mode 100755
index 0000000..f8c8e31
--- /dev/null
+++ b/ecbuild/cmake/sg.pl
@@ -0,0 +1,573 @@
+#!/usr/bin/perl
+#!/usr/local/share/perl56
+
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+use strict;
+
+#use Data::Dumper;
+use File::Basename;
+
+#$Data::Dumper::Indent = 1;
+# $ARGV[0] = "test.cc";
+# $ARGV[0] = "/usr/include/g++-3/stl_pair.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_vector.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_list.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_map.h";
+# $ARGV[0] = "x.cc";
+# $ARGV[0] = "/usr/include/g++-3/std/bastring.h";
+
+# script takes 3 parameters:
+# (1) file to process
+my $file = $ARGV[0];
+# (2) [optional] directory to place the generated .b file
+my $base = $ARGV[1];
+# (3) [optional] c++ namespace 
+my $namespace = $ARGV[2];
+
+# no argv[1] passed, take basedir from file
+if( $base eq "" )
+{
+	$base = dirname($file);
+}
+
+# no argv[1] passed, take basedir from file
+if( $namespace eq "" )
+{
+    $namespace = "eclib"
+}
+
+my @c = parser::parse($file);
+#print Dumper(\@c);
+
+
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(STDOUT,">$base/$n.b") || die "$base/$n.b: $!";
+
+	my @init1;
+	push @init1, map { "$_(b)" } $c->super;
+	push @init1, map { "$_(b(\&$_))" } $c->members;
+
+	my $col1;
+	$col1=":\n" if(@init1);
+	my $init1 = join(",\n",map {"\t$_"} @init1);
+
+	my @init2;
+	push @init2, map { "$_(b(\"$n\"))" } $c->super;
+	push @init2, map { "$_(b(\"$n\",\"$_\"))" } $c->members;
+
+	my $col2;
+	$col2=":\n" if(@init2);
+	my $init2 = join(",\n",map {"\t$_"} @init2);
+
+
+	my @s = map { "${_}::describe(s,depth+1)"      } $c->super;
+	my @m = map { "${namespace}::_describe(s,depth+1,\"$_\",$_)" } $c->members;
+	my $d = join(";\n\t","${namespace}::_startClass(s,depth,specName())", at s, at m,"${namespace}::_endClass(s,depth,specName())");
+
+	my @s = map { "${_}::_export(h)"      } $c->super;
+	my @m = map { "${namespace}::_export(h,\"$_\",$_)" } $c->members;
+	my $D = join(";\n\t","${namespace}::_startClass(h,\"$n\")", at s, at m,"${namespace}::_endClass(h,\"$n\")");
+
+	my $spec = "\"$n\"";
+	my @tmpl = $c->template;
+
+	my $spec_type = "const char*";
+
+	if(@tmpl)
+	{
+		$spec_type = "std::string";
+		my $x = join("+ ',' + ",  map { "Traits<$_>::name()"; } @tmpl);
+		$spec = <<"EOS";
+        std::string("$n<\") + $x + ">"
+EOS
+		$spec =~ s/\n/ /g;
+	}
+
+	my $isa = "${namespace}::Isa::add(t,specName());";
+	foreach my $s ( $c->super )
+	{
+		$isa = "${s}::isa(t);$isa";
+	}
+
+	my $schema;
+	@s = map { "${_}::schema(s)"      } $c->super;
+	@m = map { $a=$_->[0]; $b=$_->[1]; "s.member(\"$a\",member_size($n,$a),member_offset($n,$a),\"$b\")" } $c->members_types;
+	$schema = join(";\n\t","s.start(specName(),sizeof($n))", at s, at m,"s.end(specName())");
+
+	print <<"EOF";
+
+${n}(${namespace}::Bless& b)$col1$init1
+{
+}
+
+${n}(${namespace}::Evolve b)$col2$init2
+{
+}
+
+static ${spec_type} specName()      { return ${spec}; }
+static void isa(TypeInfo* t)  { ${isa} }
+static ${namespace}::Isa* isa()             { return ${namespace}::Isa::get(specName());  }
+
+static void schema(${namespace}::Schema& s)
+{
+	$schema;
+}
+
+EOF
+
+if(!$c->has_method("describe"))
+{
+print <<"EOF";
+
+void describe(std::ostream& s,int depth = 0) const {
+	$d;
+}
+
+
+EOF
+}
+
+print <<"EOF";
+
+void _export(${namespace}::Exporter& h) const { 
+	$D;
+}
+
+
+EOF
+
+}
+if(0)
+{
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(OUT,">${n}.b");
+	select OUT;
+	print "static void schema(${namespace}::Schema& s) {\n";
+	foreach my $x ( $c->super )
+	{
+		print "${x}::schema(s);\n";
+		#print "s(\"$x\", 0,sizeof($x));\n";
+	}
+	foreach my $x ( $c->members )
+	{
+		print "s(\"${n}::$x\",offsetof($n,$x),sizeof(&(($n*)0)->$x));\n";
+	}
+	print "}\n";
+}
+}
+package parser;
+use Carp;
+my @TOKENS;
+sub parse {
+	my ($file) = @_;
+	local $/ = undef;
+	open(IN,"<$file") || croak "$file: $!";
+	my $x = <IN>;
+	close(IN);
+	$x =~ s/^#.*$//mg;
+	$x =~ s/\/\/.*$//mg;
+	@TOKENS =
+		grep { length($_);                }
+		map  { /\W/ ? split('',$_) :  $_; }
+		map  { s/\s//g; $_;               }
+		split(/\b/, $x );
+
+	my @c;
+	my $x;
+	while($x = consume_until("(typedef|template|class|struct)"))
+	{
+		if($x eq 'typedef')
+		{
+			consume_until(";");
+			next;
+		}
+
+		if($x eq 'template')
+		{
+			push @c, parse_template();
+		}
+		else
+		{
+			push @c, parse_class();
+		}
+	}
+	return grep { defined $_; } @c;
+}
+
+sub parse_template {
+	my @tmp = template_args();
+	return parse_class(@tmp) if(next_is("(class|struct)"));
+}
+sub template_args {
+	my @tmp;
+	expect_next("<");
+	for(;;)
+	{
+		expect_next("(class|bool|int)");
+		push @tmp, next_ident();
+		if(next_is("="))
+		{
+			my $x = consume_until('(,|\>|\<)');
+			unshift @TOKENS,$x;
+			while($x eq '<')
+			{
+				consume_block('<','>');
+				$x = consume_until('(,|\>|\<)');
+				unshift @TOKENS,$x;
+			}
+		}
+		last unless(next_is(","));
+	}
+	expect_next(">");
+	return @tmp;
+}
+sub parse_class {
+	my (@tmp) = @_;
+	my $self = {};
+	my $name = next_ident();
+	$self->{name}     = $name;
+	$self->{template} = \@tmp if(@tmp);
+	# Foreward declaration
+	return if(next_is(";"));
+	if(next_is(":"))
+	{
+		for(;;)
+		{
+			ignore_while("(public|private|protected|virtual)");
+			push @{$self->{super}}, next_ident();
+			last unless(next_is(","));
+		}
+	}
+	expect_next('{');
+	while(!peek_next('}'))
+	{
+		# print "... : $TOKENS[0], $TOKENS[1], ... \n";
+		if(next_is('\/'))
+		{
+			if(next_is('\*'))
+			{
+				while(!next_is('\/'))
+				{
+					consume_until('\*');
+				}
+				next;
+			}
+			else
+			{
+				unshift @TOKENS, "/";	
+			}
+
+		}
+
+		if(next_is("(public|private|protected)"))
+		{
+			expect_next(":");
+			next;
+		}
+
+		if(next_is("friend"))
+		{
+			my $x = consume_until("(;|{)");
+			if($x eq "{")
+			{
+				unshift @TOKENS, $x;
+				consume_block('{','}');
+			}
+			next;
+		}
+
+		# next_is("explicit");
+		if(next_is("(typedef|using|typename|enum)"))
+		{
+			consume_until(";");
+			next;
+		}
+		if(next_is("(class|struct)"))
+		{
+			push @{$self->{classes}}, parse_class();
+			next;
+		}
+		my %m;
+		while(next_is("template"))
+		{
+			push @{ $m{template} } , template_args();
+		}
+
+		my @x;
+#		push @x,"~" while(next_is('\~'));
+
+		$m{explicit} = 1 if(next_is("explicit"));
+		$m{static}   = 1 if(next_is("static"));
+		$m{virtual}  = 1 if(next_is("virtual"));
+		my $x;
+		while($x = next_is_ident())
+		{
+			# print "--- : $x\n";
+			push @x, $x;
+			push @x,'*' while(next_is('\*'));
+			push @x,'&' while(next_is('\&'));
+			$m{name} = $x;
+			# int a,b,*c; does not work
+			my $s;
+			if($s = next_is('(,|;|=)'))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				if(exists $m{static})
+				{
+					push @{$self->{class_members}}, \%m;
+				}
+				else
+				{
+					push @{$self->{members}}, \%m;
+				}
+				consume_until(";") if($s eq '=');
+				last;
+			}
+			if(peek_next('\('))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				my @args = consume_block('(',')');
+				shift @args;
+				pop @args;
+				my @a;
+				my $n = 0;
+				my @z;
+				foreach my $a ( @args )
+				{
+					if($a eq ',' && $n == 0)
+					{
+						push @a, make_type(@z);
+						@z = ();
+						next;
+					}
+					$n++ if($a eq '<');	
+					$n++ if($a eq '(');	
+					$n-- if($a eq ')');	
+					$n-- if($a eq '>');	
+					push @z,$a;
+				}
+				push @a, make_type(@z) if(@z);
+				$m{const} = 1 if(next_is("const"));
+				$m{args}  = \@a;
+				if(exists $m{static})
+				{
+					push @{$self->{class_methods}}, \%m;
+				}
+				else
+				{
+					push @{$self->{methods}}, \%m;
+				}
+				# print "f: $x\n";
+
+				if(next_is(':'))
+				{
+					# print "{: $TOKENS[0]\n";
+					consume_until('\{');
+					unshift @TOKENS, '{';
+					consume_block('{','}');
+					# print "}: $TOKENS[0]\n";
+				}
+				else
+				{
+					if(peek_next('\{'))
+					{
+						consume_block('{','}');
+					}
+					else
+					{
+						if(next_is("="))
+						{
+							expect_next("0");
+							$m{abstract} = 1;
+						}
+						expect_next(";");
+					}
+				}
+				last;
+			}
+		}
+
+	}
+	expect_next("}");
+	expect_next(";");
+	return bless($self,"class");
+}
+sub consume_until {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		return $x if($x =~ /^$r$/);
+	}
+	return undef;
+}
+sub consume_block {
+	my ($bra,$ket) = @_;
+	my $n = 0;
+	my @x;
+	croak "@TOKENS" unless($bra eq $TOKENS[0]);
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		$n++ if($x eq $bra);
+		$n-- if($x eq $ket);
+		push @x,$x;
+		return @x if($n == 0);
+	}
+}
+sub ignore_while {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		return unless($TOKENS[0] =~ /^$r$/);
+		shift @TOKENS;
+	}
+}
+sub expect_next {
+	my ($r) = @_;
+	my $ident = shift @TOKENS;
+	croak "$ident is not $r" unless($ident =~ /^$r$/);
+	return $ident;
+}
+sub next_ident {
+	my $x = next_is_ident();
+	croak "not an ident " unless($x);
+	return $x;
+}
+sub next_is {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return shift @TOKENS;
+	}
+	return undef;
+}
+sub next_is_ident {
+	my $op = next_is("operator");
+	if($op)
+	{
+		my $x;
+		if($x = next_is("(new|delete)"))
+		{
+			$op .= " $x";
+			if(next_is('\['))
+			{
+				expect_next('\]');
+				$op .= "[]";
+			}
+			return $op;
+		}
+
+		if(next_is('\('))
+		{
+			expect_next('\)');
+			$op .= "()";
+		}
+		my $z;
+		while($z = next_is('[\-+/\*\[\]<>=!]'))
+		{
+			$op .= $z;
+		}
+		return $op;
+	}
+	my $y = next_is('\~');
+	my $x = next_is('\w+');
+	if($x)
+	{
+		$x = "$y$x";
+		if(peek_next("<"))
+		{
+			my @x = consume_block("<",">");
+			$x .= join("", at x);
+		}
+
+		if(next_is(":"))
+		{
+			if(next_is(":"))
+			{
+				my $z = next_is_ident();
+				return "${x}::${z}";
+			}
+			else
+			{
+				unshift @TOKENS, ":";
+			}
+		}
+	}
+	return $x;
+}
+sub peek_next {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return 1;
+	}
+	return 0;
+}
+sub make_type {
+	my (@a) = @_;
+	my $p;
+	my @x;
+	foreach my $a ( @a )
+	{
+		push @x, " " if($p =~ /^(\w+|\&|\*)$/ && $a =~ /^\w+$/);
+		push @x, $a;
+		$p = $a;
+	}
+	my $s = join('', at x);
+	$s =~ s/>>/> >/g;
+	return $s;
+}
+package class;
+sub name {
+	my ($self) = @_;
+	return $self->{name};
+}
+sub super {
+	my ($self) = @_;
+	return $self->{super} ? @{$self->{super}} : ();
+}
+
+sub members {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub methods {
+	my ($self) = @_;
+	my @x = $self->{methods} ? @{$self->{methods}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub has_method {
+	my ($self,$name) = @_;
+	return grep { $_ eq $name } $self->methods;
+}
+
+sub members_types {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { [ $_->{name}, $_->{type} ]  } @x;
+}
+
+sub template {
+	my ($self) = @_;
+	return $self->{template} ? @{$self->{template}} : ();
+}
+1;
+
+
diff --git a/ecbuild/cmake/sha1.in b/ecbuild/cmake/sha1.in
new file mode 100644
index 0000000..8e744a7
--- /dev/null
+++ b/ecbuild/cmake/sha1.in
@@ -0,0 +1 @@
+ at _p_SHA1@  @_p_NAME@
diff --git a/ecbuild/doc/CMakeLists.txt b/ecbuild/doc/CMakeLists.txt
new file mode 100644
index 0000000..083ffbc
--- /dev/null
+++ b/ecbuild/doc/CMakeLists.txt
@@ -0,0 +1,22 @@
+find_package( PythonInterp )
+
+if( PYTHONINTERP_FOUND )
+
+  if( DEFINED ENV{CONFLUENCE_PASSWORD} )
+
+    file( GLOB ECBUILD_MODULE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+          ${CMAKE_SOURCE_DIR}/cmake/ecbuild_*.cmake )
+
+    add_custom_target( upload_docs COMMENT "Uploading documentation" )
+    add_custom_command( TARGET upload_docs POST_BUILD
+                        COMMAND ${PYTHON_EXECUTABLE} upload.py --logfile "${CMAKE_CURRENT_BINARY_DIR}/upload.log" ${ECBUILD_MODULE}
+                        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+                        COMMENT "Uploading documentation" )
+
+  else()
+
+    add_custom_target( upload_docs COMMENT "Failed to upload documentation: the Confluence password must be exported as environment variable CONFLUENCE_PASSWORD" )
+
+  endif()
+
+endif()
diff --git a/ecbuild/doc/upload.py b/ecbuild/doc/upload.py
new file mode 100644
index 0000000..b246d10
--- /dev/null
+++ b/ecbuild/doc/upload.py
@@ -0,0 +1,148 @@
+"""Uplodad documentation for a given list of ecBuild macros to Confluence.
+
+The Confluence password needs be be export as environment variable
+``CONFLUENCE_PASSWORD``.
+"""
+
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+import json
+import logging
+from os import environ, path
+
+from docutils.core import publish_string
+
+import requests
+
+from rst2confluence import confluence
+
+API_URL = 'https://software.ecmwf.int/wiki/rest/api/content'
+AUTH = (environ['USER'], environ['CONFLUENCE_PASSWORD'])
+
+log = logging.getLogger('upload')
+log.setLevel(logging.DEBUG)
+
+
+def extract(fname):
+    "Extract rST documentation from CMake module ``fname``."
+    with open(fname) as f:
+        rst = False
+        lines = []
+        for line in f:
+            line = line.strip()
+            # Only consider comments
+            if not line.startswith('#'):
+                rst = False
+                continue
+            # Lines with the magic cooke '.rst:' start an rST block
+            if line.endswith('.rst:'):
+                rst = True
+                continue
+            # Only add lines in an rST block
+            if rst:
+                line = line.lstrip('#')
+                # Blank lines are syntactically relevant
+                lines.append(line[1:] if line else line)
+        return lines
+
+
+def pp(res):
+    "Pretty-print JSON response"
+    return '{} {}\n'.format(json.dumps(res.json(), sort_keys=True, indent=2,
+                            separators=(',', ': ')), res)
+
+
+def upload(fname, space, parent_id):
+    """Upload documentation for CMake module ``fname`` as child of page with
+    id ``parent_id``."""
+    pagename = path.splitext(path.basename(fname))[0]
+    rst = '\n'.join(extract(fname))
+    if not rst:
+        return
+    log.debug('=' * 79)
+    log.info('Uploading %s', pagename)
+    log.debug('=' * 79)
+    log.debug('rST')
+    log.debug('-' * 79)
+    log.debug(rst)
+    log.debug('-' * 79)
+    log.debug('Confluence')
+    log.debug('-' * 79)
+    cwiki = publish_string(rst, writer=confluence.Writer())
+    log.debug(cwiki)
+    page = {'type': 'page',
+            'title': pagename,
+            'space': {'key': space},
+            'body': {'storage': {'value': cwiki, 'representation': 'wiki'}},
+            'ancestors': [{'type': 'page', 'id': parent_id}]}
+    create_or_update(page, space)
+
+
+def create_or_update(page, space):
+    "Update Confluence ``page`` if it exists, otherwise create it."
+    # Check if page exists
+    r = requests.get(API_URL,
+                     params={'spaceKey': space,
+                             'title': page['title'],
+                             'expand': 'version'},
+                     auth=AUTH,
+                     headers=({'Content-Type': 'application/json'}))
+    if len(r.json()['results']):
+        p = r.json()['results'][0]
+        page['version'] = {'number': p['version']['number'] + 1}
+        r = requests.put('/'.join([API_URL, p['id']]),
+                         data=json.dumps(page),
+                         auth=AUTH,
+                         headers=({'Content-Type': 'application/json'}))
+    else:
+        r = requests.post(API_URL,
+                          data=json.dumps(page),
+                          auth=AUTH,
+                          headers=({'Content-Type': 'application/json'}))
+    log.debug(pp(r))
+    if not r.ok:
+        log.warn('  Uploading to space %s failed: %d (%s)\n    %s',
+                 space, r.status_code, r.reason, r.json()['message'])
+
+
+def main():
+    parser = ArgumentParser(description=__doc__,
+                            formatter_class=ArgumentDefaultsHelpFormatter)
+    parser.add_argument('--space', default='ECBUILD',
+                        help='Confluence space to upload to')
+    parser.add_argument('--page', default='ecBuild Macros',
+                        help='Page to attach documentation to')
+    parser.add_argument('--logfile', default='upload.log',
+                        help='Path to log file')
+    parser.add_argument('macro', nargs='+',
+                        help='list of paths to ecBuild macros')
+    args = parser.parse_args()
+
+    # Log to file with level DEBUG
+    fh = logging.FileHandler(args.logfile)
+    fh.setLevel(logging.DEBUG)
+    fmt = logging.Formatter('%(asctime)s %(name)s %(levelname)-5s - %(message)s')
+    fh.setFormatter(fmt)
+    log.addHandler(fh)
+    # Log to console with level INFO
+    ch = logging.StreamHandler()
+    ch.setLevel(logging.INFO)
+    log.addHandler(ch)
+    # Also log requests at debug level to file
+    logging.getLogger('requests').addHandler(fh)
+    logging.getLogger('requests').setLevel(logging.DEBUG)
+
+    # Get id of parent page
+    r = requests.get(API_URL,
+                     params={'spaceKey': args.space, 'title': args.page},
+                     auth=AUTH,
+                     headers=({'Content-Type': 'application/json'}))
+    parent_id = r.json()['results'][0]['id']
+    log.info('====== Start uploading documentation to "%s" in space %s ======',
+             args.page, args.space)
+    log.info('Logging to file %s', args.logfile)
+    for f in args.macro:
+        upload(f, args.space, parent_id)
+    log.info('====== Finished uploading documentation ======')
+
+if __name__ == '__main__':
+    main()
diff --git a/ecbuild/ecbuild.sublime-project b/ecbuild/ecbuild.sublime-project
new file mode 100644
index 0000000..97517c5
--- /dev/null
+++ b/ecbuild/ecbuild.sublime-project
@@ -0,0 +1,29 @@
+{
+	"folders":
+	[
+		{
+            "file_exclude_patterns": [".tags", ".tags_sorted_by_file", ".gemtags","CMakeLists.txt.user*"],
+			"follow_symlinks": true,
+			"path": "."
+		}
+	],
+	"build_systems": [
+        {
+            "working_dir": "${project_path}/../../build/ecbuild",
+            "cmd": [
+                "make"
+            ],
+            "file_regex": "([/\\w\\-\\.]+):(\\d+):(\\d+:)?",
+            "name": "ecbuild"
+        }
+    ],
+    "SublimeLinter":
+    {
+        "linters":
+        {
+            "cpplint": {
+                "filter": "-whitespace/line_length,-whitespace/blank_line,-runtime/references"
+            },
+        }
+    }
+}
diff --git a/ecbuild/examples/boost-python-lib/CMakeLists.txt b/ecbuild/examples/boost-python-lib/CMakeLists.txt
new file mode 100644
index 0000000..0eecb13
--- /dev/null
+++ b/ecbuild/examples/boost-python-lib/CMakeLists.txt
@@ -0,0 +1,60 @@
+
+cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+
+project( mypython CXX )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" )
+
+include( ecbuild_system )
+
+ecbuild_declare_project()
+
+###############################################################################
+# find extra packages
+
+# Python
+# Can specify non-default distribution with CMake variable PYTHON_EXECUTABLE
+ecbuild_find_python()
+ecbuild_info("PYTHON_EXECUTABLE    : ${PYTHON_EXECUTABLE}")
+ecbuild_info("PYTHON_INCLUDE_DIRS  : ${PYTHON_INCLUDE_DIRS}")
+ecbuild_info("PYTHON_LIBRARIES     : ${PYTHON_LIBRARIES}")
+
+# Boost
+# Can specify non-default distribution with CMake variable BOOST_ROOT
+ecbuild_add_extra_search_paths( boost )
+set( Boost_MINIMUM_VERSION "1.47" )
+find_package( Boost ${Boost_MINIMUM_VERSION} REQUIRED
+              COMPONENTS python )
+ecbuild_info("Boost_LIBRARIES     : ${Boost_LIBRARIES}" )
+
+###############################################################################
+# Contents
+
+ecbuild_add_library( TARGET       mypython
+                     INCLUDES     ${Boost_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}
+                     LIBS         ${Boost_LIBRARIES} ${PYTHON_LIBRARIES}
+                     SOURCES      pythonlib.hpp pythonlib.cpp )
+configure_file( mypython.py       ${CMAKE_BINARY_DIR}/lib COPYONLY )
+
+############################################################################################
+# Installation 
+
+ecbuild_install_project( NAME python_project )
+
+###############################################################################
+# Summary
+
+ecbuild_print_summary()
+
+
+
+ecbuild_info( "" )
+ecbuild_info( "To test the library:" )
+ecbuild_info( "--------------------" )
+ecbuild_info( "make" )
+ecbuild_info( "cd ${CMAKE_CURRENT_BINARY_DIR}/lib" )
+ecbuild_info( "python" )
+ecbuild_info( ">>> import libmypython" )
+ecbuild_info( ">>> help(libmypython)" )
+ecbuild_info( "" )
+ecbuild_info( "---------------------------------------------------------" )
diff --git a/ecbuild/examples/boost-python-lib/VERSION.cmake b/ecbuild/examples/boost-python-lib/VERSION.cmake
new file mode 100644
index 0000000..caf4027
--- /dev/null
+++ b/ecbuild/examples/boost-python-lib/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.10.0")
diff --git a/ecbuild/examples/boost-python-lib/mypython.py b/ecbuild/examples/boost-python-lib/mypython.py
new file mode 100644
index 0000000..d6f6a1b
--- /dev/null
+++ b/ecbuild/examples/boost-python-lib/mypython.py
@@ -0,0 +1,10 @@
+"""
+API for the C++ module using boost::python
+
+mypython.py:
+    This file can be used to do some python actions at import
+
+Module description:
+    ...
+"""
+from libmypython import *
\ No newline at end of file
diff --git a/ecbuild/examples/boost-python-lib/pythonlib.cpp b/ecbuild/examples/boost-python-lib/pythonlib.cpp
new file mode 100644
index 0000000..ba32ffb
--- /dev/null
+++ b/ecbuild/examples/boost-python-lib/pythonlib.cpp
@@ -0,0 +1,6 @@
+#include "pythonlib.hpp"
+
+BOOST_PYTHON_MODULE(libmypython)
+{
+  boost::python::scope().attr("__doc__") = "Python API for my python project";
+}
diff --git a/ecbuild/examples/boost-python-lib/pythonlib.hpp b/ecbuild/examples/boost-python-lib/pythonlib.hpp
new file mode 100644
index 0000000..349d581
--- /dev/null
+++ b/ecbuild/examples/boost-python-lib/pythonlib.hpp
@@ -0,0 +1,6 @@
+#ifndef pythonlib_hpp
+#define pythonlib_hpp
+
+#include <boost/python.hpp>
+
+#endif
\ No newline at end of file
diff --git a/ecbuild/examples/config-bundle/.gitignore b/ecbuild/examples/config-bundle/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/ecbuild/examples/config-bundle/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/ecbuild/examples/config-bundle/CMakeLists.txt b/ecbuild/examples/config-bundle/CMakeLists.txt
new file mode 100644
index 0000000..ed213a2
--- /dev/null
+++ b/ecbuild/examples/config-bundle/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" ${CMAKE_MODULE_PATH} )
+
+include( ecbuild_bundle )
+project( config_bundle C )
+
+ecbuild_bundle_initialize()
+
+# Use command "make update" to update branches to latest develop version
+ecbuild_bundle( PROJECT subproj1 )
+ecbuild_bundle( PROJECT subproj2 )
+
+# Finalize
+ecbuild_bundle_finalize()
diff --git a/ecbuild/examples/config-bundle/README.md b/ecbuild/examples/config-bundle/README.md
new file mode 100644
index 0000000..d811064
--- /dev/null
+++ b/ecbuild/examples/config-bundle/README.md
@@ -0,0 +1,7 @@
+# Config-bundle with separate configuration for subproj2
+
+    SRC_DIR=$(pwd)
+    BUILD_DIR=$(pwd)/build
+    mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
+
+    ../../../bin/ecbuild --build=debug --log=debug --config=${SRC_DIR}/general_config.cmake -- -DSUBPROJ2_CONFIG=${SRC_DIR}/subproj2_config.cmake ${SRC_DIR}
diff --git a/ecbuild/examples/config-bundle/general_config.cmake b/ecbuild/examples/config-bundle/general_config.cmake
new file mode 100644
index 0000000..0c9fa5f
--- /dev/null
+++ b/ecbuild/examples/config-bundle/general_config.cmake
@@ -0,0 +1,6 @@
+set( ECBUILD_TRUST_FLAGS TRUE )
+set( ECBUILD_C_FLAGS "-common" )
+set( ECBUILD_C_FLAGS_DEBUG "-debugopts" )
+
+set( ENABLE_OS_TESTS OFF CACHE BOOL "" )
+set( ENABLE_LARGE_FILE_SUPPORT OFF CACHE BOOL "" )
diff --git a/ecbuild/examples/config-bundle/subproj1/CMakeLists.txt b/ecbuild/examples/config-bundle/subproj1/CMakeLists.txt
new file mode 100644
index 0000000..ba25687
--- /dev/null
+++ b/ecbuild/examples/config-bundle/subproj1/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+project( subproj_1 C )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH} )
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+ecbuild_debug_var( CMAKE_C_FLAGS )
+ecbuild_debug_var( CMAKE_C_FLAGS_DEBUG )
diff --git a/ecbuild/examples/config-bundle/subproj2/CMakeLists.txt b/ecbuild/examples/config-bundle/subproj2/CMakeLists.txt
new file mode 100644
index 0000000..bf5cf51
--- /dev/null
+++ b/ecbuild/examples/config-bundle/subproj2/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+project( subproj_2 C )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake" ${CMAKE_MODULE_PATH} )
+
+if( SUBPROJ2_CONFIG )
+    set( ECBUILD_CONFIG ${SUBPROJ2_CONFIG} )
+endif()
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+ecbuild_debug_var( CMAKE_C_FLAGS )
+ecbuild_debug_var( CMAKE_C_FLAGS_DEBUG )
diff --git a/ecbuild/examples/config-bundle/subproj2_config.cmake b/ecbuild/examples/config-bundle/subproj2_config.cmake
new file mode 100644
index 0000000..01db1c7
--- /dev/null
+++ b/ecbuild/examples/config-bundle/subproj2_config.cmake
@@ -0,0 +1,3 @@
+set( ECBUILD_TRUST_FLAGS TRUE )
+set( ECBUILD_C_FLAGS "-common2" )
+set( ECBUILD_C_FLAGS_DEBUG "-debugopts2")
diff --git a/ecbuild/examples/cpp-bundle/CMakeLists.txt b/ecbuild/examples/cpp-bundle/CMakeLists.txt
new file mode 100644
index 0000000..79d6ec9
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" )
+
+include( ecbuild_bundle )
+
+project( cpp_bundle C CXX )
+
+ecbuild_bundle_initialize()
+
+ecbuild_bundle( PROJECT foo )
+ecbuild_bundle( PROJECT bar )
+
+ecbuild_bundle_finalize()
diff --git a/ecbuild/examples/cpp-bundle/VERSION.cmake b/ecbuild/examples/cpp-bundle/VERSION.cmake
new file mode 100644
index 0000000..caf4027
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.10.0")
diff --git a/ecbuild/examples/cpp-bundle/bar/CMakeLists.txt b/ecbuild/examples/cpp-bundle/bar/CMakeLists.txt
new file mode 100644
index 0000000..a2a9277
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/CMakeLists.txt
@@ -0,0 +1,57 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( bar C CXX )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+### targets
+
+ecbuild_use_package( PROJECT foo REQUIRED )
+
+ecbuild_add_library(
+    TARGET       baz
+    TYPE         OBJECT
+    INCLUDES     ${FOO_INCLUDE_DIRS}
+    SOURCES      baz.h baz.c
+)
+
+ecbuild_add_library(
+    TARGET       bar
+    INCLUDES     ${FOO_INCLUDE_DIRS}
+    LIBS         foo
+    SOURCES      bar.h bar.c
+    OBJECTS      baz
+)
+
+ecbuild_add_library(
+    TARGET       zingo
+    TYPE         OBJECT
+    INCLUDES     ${FOO_INCLUDE_DIRS}
+    SOURCES      zingo.h zingo.c
+)
+
+ecbuild_add_executable(
+    TARGET     master_bar
+    SOURCES    main.cc
+    OBJECTS    zingo
+    LIBS       bar
+)
+
+ecbuild_add_test(
+    TARGET     test_bar
+    SOURCES    test.cc
+    OBJECTS    zingo
+    LIBS       bar
+)
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/cpp-bundle/bar/VERSION.cmake b/ecbuild/examples/cpp-bundle/bar/VERSION.cmake
new file mode 100644
index 0000000..e0959dc
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "1.0.0" )
diff --git a/ecbuild/examples/cpp-bundle/bar/bar.c b/ecbuild/examples/cpp-bundle/bar/bar.c
new file mode 100644
index 0000000..8e617da
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/bar.c
@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int bar()
+{
+  return foo() * foo();
+}
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/bar.h b/ecbuild/examples/cpp-bundle/bar/bar.h
new file mode 100644
index 0000000..bcffbba
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/bar.h
@@ -0,0 +1,6 @@
+#ifndef bar_h
+#define bar_h
+
+int bar();
+
+#endif
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/baz.c b/ecbuild/examples/cpp-bundle/bar/baz.c
new file mode 100644
index 0000000..be4fcbc
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/baz.c
@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int baz()
+{
+  return foo() * foo() * foo();
+}
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/baz.h b/ecbuild/examples/cpp-bundle/bar/baz.h
new file mode 100644
index 0000000..f56fd6c
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/baz.h
@@ -0,0 +1,6 @@
+#ifndef baz_h
+#define baz_h
+
+int baz();
+
+#endif
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/main.cc b/ecbuild/examples/cpp-bundle/bar/main.cc
new file mode 100644
index 0000000..e0e78eb
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/main.cc
@@ -0,0 +1,14 @@
+#include <iostream>
+
+extern "C" {
+#include "bar.h"
+#include "baz.h"
+#include "zingo.h"
+}
+
+int main()
+{
+  std::cout << "bar is " << bar() << std::endl;
+  std::cout << "baz is " << baz() << std::endl;
+  std::cout << "zingo is " << zingo() << std::endl;
+}
diff --git a/ecbuild/examples/cpp-bundle/bar/test.cc b/ecbuild/examples/cpp-bundle/bar/test.cc
new file mode 100644
index 0000000..ff5b9b4
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/test.cc
@@ -0,0 +1,19 @@
+#include <iostream>
+
+extern "C" {
+#include "bar.h"
+#include "zingo.h"
+}
+
+int main()
+{
+  if( bar() == 42*42 )
+    std::cout << "ok" << std::endl;
+  else
+    std::cout << "failed" << std::endl;
+
+  if( zingo() == 41*42 )
+    std::cout << "ok" << std::endl;
+  else
+    std::cout << "failed" << std::endl;
+}
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/zingo.c b/ecbuild/examples/cpp-bundle/bar/zingo.c
new file mode 100644
index 0000000..b12706b
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/zingo.c
@@ -0,0 +1,6 @@
+#include "foo.h"
+
+int zingo()
+{
+  return (foo() - 1) * foo();
+}
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/bar/zingo.h b/ecbuild/examples/cpp-bundle/bar/zingo.h
new file mode 100644
index 0000000..b3ef0ac
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/bar/zingo.h
@@ -0,0 +1,6 @@
+#ifndef zingo_h
+#define zingo_h
+
+int zingo();
+
+#endif
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/foo/CMakeLists.txt b/ecbuild/examples/cpp-bundle/foo/CMakeLists.txt
new file mode 100644
index 0000000..65bd592
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( foo )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+### targets
+
+set( FOO_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} )
+
+ecbuild_add_library(TARGET       foo
+                    SOURCES      foo.h foo.c
+                    INCLUDES     ${FOO_INCLUDE_DIRS} )
+
+ecbuild_add_executable(
+    TARGET      master_foo
+    SOURCES     main.cc
+    LIBS        foo
+    INCLUDES    ${FOO_INCLUDE_DIRS}  )
+
+ecbuild_add_test(
+    TARGET      test_foo
+    SOURCES     test.cc
+    LIBS        foo
+    INCLUDES    ${FOO_INCLUDE_DIRS}  )
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
+
diff --git a/ecbuild/examples/cpp-bundle/foo/VERSION.cmake b/ecbuild/examples/cpp-bundle/foo/VERSION.cmake
new file mode 100644
index 0000000..e0959dc
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "1.0.0" )
diff --git a/ecbuild/examples/cpp-bundle/foo/foo.c b/ecbuild/examples/cpp-bundle/foo/foo.c
new file mode 100644
index 0000000..88adcb5
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/foo.c
@@ -0,0 +1,4 @@
+int foo()
+{
+  return 42;
+}
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/foo/foo.h b/ecbuild/examples/cpp-bundle/foo/foo.h
new file mode 100644
index 0000000..6b68dbb
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/foo.h
@@ -0,0 +1,6 @@
+#ifndef foo_h
+#define foo_h
+
+int foo();
+
+#endif
\ No newline at end of file
diff --git a/ecbuild/examples/cpp-bundle/foo/main.cc b/ecbuild/examples/cpp-bundle/foo/main.cc
new file mode 100644
index 0000000..aec42da
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/main.cc
@@ -0,0 +1,10 @@
+#include <iostream>
+
+extern "C" {
+#include "foo.h"
+}
+
+int main()
+{
+  std::cout << "foo is " << foo() << std::endl;
+}
diff --git a/ecbuild/examples/cpp-bundle/foo/test.cc b/ecbuild/examples/cpp-bundle/foo/test.cc
new file mode 100644
index 0000000..946404d
--- /dev/null
+++ b/ecbuild/examples/cpp-bundle/foo/test.cc
@@ -0,0 +1,13 @@
+#include <iostream>
+
+extern "C" {
+#include "foo.h"
+}
+
+int main()
+{
+  if( foo() == 42)
+    std::cout << "ok" << std::endl;
+  else
+    std::cout << "failed" << std::endl;
+}
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/CMakeLists.txt b/ecbuild/examples/fortran/CMakeLists.txt
new file mode 100644
index 0000000..904293f
--- /dev/null
+++ b/ecbuild/examples/fortran/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( compute_circle Fortran )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+### options
+
+ecbuild_add_option( FEATURE AREA_COMPUTER
+                    DESCRIPTION "Wether we should build the area_computer executable" )
+
+### lib const
+
+ecbuild_add_library(
+    TARGET  const
+    TYPE    STATIC
+    SOURCES constants.f90
+    )
+
+### lib area_circle
+
+ecbuild_list_add_pattern( LIST area_srcs GLOB "*.f*" )
+
+ecbuild_list_exclude_pattern( LIST area_srcs REGEX old.*f90 ^const.* .*f95 main.[fF]90 api/* )
+
+ecbuild_generate_fortran_interfaces(
+
+    TARGET      intf_circ
+
+    SOURCE_DIR  ${CMAKE_CURRENT_SOURCE_DIR}
+
+    DIRECTORIES api/area api/volume
+
+    DESTINATION  api
+
+    INCLUDE_DIRS intf_circ_includes
+)
+
+ecbuild_add_library(
+
+    TARGET  api_objs
+
+    TYPE    OBJECT
+
+    SOURCES_GLOB  "api/area/*.F90" "api/volume/*.F90"
+)
+
+ecbuild_debug( "intf_circ_includes" )
+
+ecbuild_debug_var( intf_circ_includes )
+
+ecbuild_add_library(
+    TARGET  area_circle
+    SOURCES ${area_srcs}
+    OBJECTS api_objs
+    LIBS    const
+    DEPENDS intf_circ
+)
+
+# application conditionally compiled
+
+ecbuild_add_executable(
+    TARGET  area_computer
+    SOURCES main.F90
+    LIBS area_circle
+    CONDITION HAVE_AREA_COMPUTER
+    )
+
+### finalize project
+
+ecbuild_pkgconfig(
+    NAME area_circle
+    DESCRIPTION "A library to compute the area of a circle"
+    LIBRARIES area_circle
+    )
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/fortran/VERSION.cmake b/ecbuild/examples/fortran/VERSION.cmake
new file mode 100644
index 0000000..caf4027
--- /dev/null
+++ b/ecbuild/examples/fortran/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.10.0")
diff --git a/ecbuild/examples/fortran/api/area/area.F90 b/ecbuild/examples/fortran/api/area/area.F90
new file mode 100644
index 0000000..7bd57ac
--- /dev/null
+++ b/ecbuild/examples/fortran/api/area/area.F90
@@ -0,0 +1,4 @@
+SUBROUTINE Compute_Area(r, Area)
+     REAL, INTENT(IN) :: r
+     REAL, INTENT(OUT) :: Area
+END SUBROUTINE Compute_Area
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/api/volume/volume.F90 b/ecbuild/examples/fortran/api/volume/volume.F90
new file mode 100644
index 0000000..a73f36b
--- /dev/null
+++ b/ecbuild/examples/fortran/api/volume/volume.F90
@@ -0,0 +1,4 @@
+SUBROUTINE Compute_Volume(r, Volume)
+     REAL, INTENT(IN) :: r
+     REAL, INTENT(OUT) :: Volume
+END SUBROUTINE Compute_Volume
diff --git a/ecbuild/examples/fortran/area_circle.f90 b/ecbuild/examples/fortran/area_circle.f90
new file mode 100644
index 0000000..744d6dc
--- /dev/null
+++ b/ecbuild/examples/fortran/area_circle.f90
@@ -0,0 +1,15 @@
+!-----Area_Circle----------------------------------------------------
+!
+!  Function to compute the area of a circle of given radius
+!
+!---------------------------------------------------------------------
+FUNCTION Area_Circle(r)
+USE Circle, ONLY : Pi
+
+IMPLICIT NONE
+REAL :: Area_Circle
+REAL, INTENT(IN) :: r
+
+Area_Circle = Pi * r * r
+
+END FUNCTION Area_Circle
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/area_circle.h b/ecbuild/examples/fortran/area_circle.h
new file mode 100644
index 0000000..058f399
--- /dev/null
+++ b/ecbuild/examples/fortran/area_circle.h
@@ -0,0 +1,6 @@
+INTERFACE
+   FUNCTION Area_Circle (r)
+     	REAL :: Area_Circle
+	REAL, INTENT(IN) :: r
+   END FUNCTION Area_Circle
+END INTERFACE
diff --git a/ecbuild/examples/fortran/circle.f90 b/ecbuild/examples/fortran/circle.f90
new file mode 100644
index 0000000..6069beb
--- /dev/null
+++ b/ecbuild/examples/fortran/circle.f90
@@ -0,0 +1,11 @@
+MODULE Circle
+USE Constants, ONLY : ZOOM
+!---------------------------------------------------------------------
+!
+!  Module containing definitions of variables needed to
+!  compute the area of a circle of radius r
+!
+!---------------------------------------------------------------------
+   REAL, PARAMETER :: Pi = 3.1415927 * ZOOM
+   REAL :: radius
+END MODULE Circle
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/cmake/fcm-make-interfaces.cfg b/ecbuild/examples/fortran/cmake/fcm-make-interfaces.cfg
new file mode 100644
index 0000000..1a89308
--- /dev/null
+++ b/ecbuild/examples/fortran/cmake/fcm-make-interfaces.cfg
@@ -0,0 +1,30 @@
+# FCM configuration file used to auto-generate interface files
+# for F77 and F90 files.
+# Interface files will have the extention ".intfb.h"
+# Results will be in a directory "interfaces/include" relative to cwd
+
+# Usage: fcm make --config-file=<path -to-this-file> \
+#                 interfaces.ns-incl="<space-sep-list-of-dirs>"
+
+$SRC{?}  = $HERE
+
+step.class[interfaces] = build
+steps  = interfaces
+
+interfaces.target{task}     = ext-iface
+interfaces.target{category} = include
+
+interfaces.source = $SRC
+
+# Exclude all
+interfaces.ns-excl = /
+
+# Include some
+# interfaces.ns-incl = <list of dirs passed at command-line>
+
+# Extention of interface files
+interfaces.prop{file-ext.f90-interface} = .intfb.h
+
+# Do not follow includes
+interfaces.prop{no-dep.f.module} = *
+interfaces.prop{no-dep.include} = *
diff --git a/ecbuild/examples/fortran/constants.f90 b/ecbuild/examples/fortran/constants.f90
new file mode 100644
index 0000000..857f752
--- /dev/null
+++ b/ecbuild/examples/fortran/constants.f90
@@ -0,0 +1,12 @@
+MODULE Constants
+!---------------------------------------------------------------------
+!
+!  Module containing definitions of variables needed to
+!  compute the area of a circle of radius r
+!
+!---------------------------------------------------------------------
+   REAL, PARAMETER :: ZOOM = 1
+END MODULE Constants
+
+
+
diff --git a/ecbuild/examples/fortran/lolo.f95 b/ecbuild/examples/fortran/lolo.f95
new file mode 100644
index 0000000..744d6dc
--- /dev/null
+++ b/ecbuild/examples/fortran/lolo.f95
@@ -0,0 +1,15 @@
+!-----Area_Circle----------------------------------------------------
+!
+!  Function to compute the area of a circle of given radius
+!
+!---------------------------------------------------------------------
+FUNCTION Area_Circle(r)
+USE Circle, ONLY : Pi
+
+IMPLICIT NONE
+REAL :: Area_Circle
+REAL, INTENT(IN) :: r
+
+Area_Circle = Pi * r * r
+
+END FUNCTION Area_Circle
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/main.F90 b/ecbuild/examples/fortran/main.F90
new file mode 100644
index 0000000..b84ead6
--- /dev/null
+++ b/ecbuild/examples/fortran/main.F90
@@ -0,0 +1,25 @@
+PROGRAM Area
+!---------------------------------------------------------------------
+!
+!  This program computes the area of a circle given the input radius
+!
+!  Uses:  MODULE Circle
+!         FUNCTION Area_Circle (r)
+!
+!---------------------------------------------------------------------
+
+USE Circle, ONLY : radius
+IMPLICIT NONE
+
+#include "area_circle.h"
+
+!  Prompt user for radius of circle
+write(*, '(A)', ADVANCE = "NO") "Enter the radius of the circle:  "
+read(*,*) radius
+
+! Write out area of circle using function call
+write(*,100) "Area of circle with radius", radius, " is", &
+            Area_Circle(radius)
+100 format (A, 2x, F6.2, A, 2x, F11.2)
+
+END PROGRAM Area
\ No newline at end of file
diff --git a/ecbuild/examples/fortran/old_circle.f90 b/ecbuild/examples/fortran/old_circle.f90
new file mode 100644
index 0000000..744d6dc
--- /dev/null
+++ b/ecbuild/examples/fortran/old_circle.f90
@@ -0,0 +1,15 @@
+!-----Area_Circle----------------------------------------------------
+!
+!  Function to compute the area of a circle of given radius
+!
+!---------------------------------------------------------------------
+FUNCTION Area_Circle(r)
+USE Circle, ONLY : Pi
+
+IMPLICIT NONE
+REAL :: Area_Circle
+REAL, INTENT(IN) :: r
+
+Area_Circle = Pi * r * r
+
+END FUNCTION Area_Circle
\ No newline at end of file
diff --git a/ecbuild/examples/mix/CMakeLists.txt b/ecbuild/examples/mix/CMakeLists.txt
new file mode 100644
index 0000000..c5eea8f
--- /dev/null
+++ b/ecbuild/examples/mix/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( mix CXX Fortran )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" )
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 1.9 )
+
+### open project
+
+ecbuild_declare_project()
+
+### targets
+
+add_subdirectory( fort )
+
+add_subdirectory( cpp )
+
+### pkgconfig exports
+
+ecbuild_pkgconfig( NAME fortbar DESCRIPTION "A fortran library bar" LIBRARIES fortbar )
+ecbuild_pkgconfig( NAME fortbaz DESCRIPTION "A fortran library baz" LIBRARIES fortbaz )
+
+### finalize project
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/mix/README.md b/ecbuild/examples/mix/README.md
new file mode 100644
index 0000000..61a6096
--- /dev/null
+++ b/ecbuild/examples/mix/README.md
@@ -0,0 +1,5 @@
+Usage:
+=====
+
+ecbuild -DECBUILD_SOURCE_FLAGS=flags.json examples/mix
+
diff --git a/ecbuild/examples/mix/VERSION.cmake b/ecbuild/examples/mix/VERSION.cmake
new file mode 100644
index 0000000..caf4027
--- /dev/null
+++ b/ecbuild/examples/mix/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.10.0")
diff --git a/ecbuild/examples/mix/cpp/CMakeLists.txt b/ecbuild/examples/mix/cpp/CMakeLists.txt
new file mode 100644
index 0000000..65579da
--- /dev/null
+++ b/ecbuild/examples/mix/cpp/CMakeLists.txt
@@ -0,0 +1 @@
+ecbuild_add_executable( TARGET capp SOURCES foo.cc LIBS fortbaz fortbar )
diff --git a/ecbuild/examples/mix/cpp/foo.cc b/ecbuild/examples/mix/cpp/foo.cc
new file mode 100644
index 0000000..28b6cde
--- /dev/null
+++ b/ecbuild/examples/mix/cpp/foo.cc
@@ -0,0 +1,48 @@
+#include <iostream>
+
+using namespace std;
+
+  extern "C"
+  {
+      void ir2_(int*,int *);
+      int  if3_(int *);
+
+      void    dr2_(double*,double *);
+      double  df3_(double *);
+
+      void lir2_(int*,int *);
+      int  lif3_(int *);
+
+      void    ldr2_(double*,double *);
+      double  ldf3_(double *);
+  }
+
+  int main()
+  {
+      cout << "INT" << endl;
+
+      int n = 10;
+      int n2 = 0;
+      int n3 = 0;
+
+      ir2_(&n,&n2);
+      cout << n << "^2 = " << n2 << endl;
+
+      n3 = if3_(&n);
+      cout << n << "^3 = " << n3 << endl;
+
+      cout << "DOUBLE" << endl;
+
+      double d  = 4.4;
+      double d2 = 0;
+      double d3 = 0;
+
+      dr2_(&d,&d2);
+      cout << d << "^2 = " << d2 << endl;
+
+      d3 = df3_(&d);
+      cout << d << "^3 = " << d3 << endl;
+
+      return 0;
+  }
+
diff --git a/ecbuild/examples/mix/flags.json b/ecbuild/examples/mix/flags.json
new file mode 100644
index 0000000..ad142a8
--- /dev/null
+++ b/ecbuild/examples/mix/flags.json
@@ -0,0 +1,19 @@
+{
+    "*"       : [ "+", "-g0"  ],
+
+    "*.cxx"   : [ "+", "-cxx11" ],
+    "*.f90"   : [ "+", "-pipe" ],
+
+    "foo.c"   : [ "+", "-O0" ],
+    "foo.cc"  : [ "+", "-O2", "-pipe"  ],
+
+    "bar/*": {
+      "*.f90" : [ "=", "-O1" ]
+    },
+
+    "baz/*": {
+      "*.f90" : [ "/", "-pipe" ],
+      "*.f90" : [ "/", "-O2"  ],
+      "*.f90" : [ "+", "-O3"  ]
+    }
+}
diff --git a/ecbuild/examples/mix/fort/CMakeLists.txt b/ecbuild/examples/mix/fort/CMakeLists.txt
new file mode 100644
index 0000000..5c8c5a1
--- /dev/null
+++ b/ecbuild/examples/mix/fort/CMakeLists.txt
@@ -0,0 +1,3 @@
+ecbuild_add_library( TARGET fortbar TYPE STATIC SOURCES bar/bar.f90 )
+
+ecbuild_add_library( TARGET fortbaz TYPE SHARED SOURCES baz/baz.f90 )
diff --git a/ecbuild/examples/mix/fort/bar/bar.f90 b/ecbuild/examples/mix/fort/bar/bar.f90
new file mode 100644
index 0000000..049faac
--- /dev/null
+++ b/ecbuild/examples/mix/fort/bar/bar.f90
@@ -0,0 +1,22 @@
+        SUBROUTINE ir2(N,M)
+        M=N*N
+        RETURN
+        END
+
+        INTEGER FUNCTION if3(N)
+        if3=N*N*N
+        RETURN
+        END
+
+        SUBROUTINE dr2(N,M)
+        REAL*8, INTENT(IN)  :: N
+        REAL*8, INTENT(OUT) :: M
+        M=N*N
+        RETURN
+        END
+
+        REAL*8 FUNCTION df3(N)
+        REAL*8, INTENT(IN)  :: N
+        df3=N*N*N
+        RETURN
+        END
diff --git a/ecbuild/examples/mix/fort/baz/baz.f90 b/ecbuild/examples/mix/fort/baz/baz.f90
new file mode 100644
index 0000000..352176b
--- /dev/null
+++ b/ecbuild/examples/mix/fort/baz/baz.f90
@@ -0,0 +1,22 @@
+        SUBROUTINE lir2(N,M)
+        M=N*N
+        RETURN
+        END
+
+        INTEGER FUNCTION lif3(N)
+        lif3=N*N*N
+        RETURN
+        END
+
+        SUBROUTINE ldr2(N,M)
+        REAL*8, INTENT(IN)  :: N
+        REAL*8, INTENT(OUT) :: M
+        M=N*N
+        RETURN
+        END
+
+        REAL*8 FUNCTION ldf3(N)
+        REAL*8, INTENT(IN)  :: N
+        ldf3=N*N*N
+        RETURN
+        END
diff --git a/ecbuild/examples/override-compile-flags/CMakeLists.txt b/ecbuild/examples/override-compile-flags/CMakeLists.txt
new file mode 100644
index 0000000..5d5c157
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" )
+
+include( ecbuild_bundle )
+
+project( override_compile_flags C Fortran )
+
+ecbuild_bundle_initialize()
+
+ecbuild_bundle( PROJECT bar )
+ecbuild_bundle( PROJECT foo )
+
+ecbuild_bundle_finalize()
diff --git a/ecbuild/examples/override-compile-flags/README.md b/ecbuild/examples/override-compile-flags/README.md
new file mode 100644
index 0000000..e8767f4
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/README.md
@@ -0,0 +1,27 @@
+An example of a bundle containing two sub-projects, bar and foo, where foo
+needs to override compile flags of several source files.
+
+# Setup
+
+    SRC_DIR=$(pwd)
+    BUILD_DIR=$(pwd)/build
+    mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
+
+# Standard build using preset compile flags:
+
+    ecbuild --build=Debug ${SRC_DIR}
+    make VERBOSE=1
+
+# Use custom compile flags defined in JSON data file:
+
+    ecbuild --build=Debug -DFOO_ECBUILD_SOURCE_FLAGS=${SRC_DIR}/foo/flags-cray-debug.json ${SRC_DIR}
+    make VERBOSE=1
+
+    module switch cdt cdt/15.11
+    ecbuild --build=Debug -DFOO_ECBUILD_SOURCE_FLAGS=${SRC_DIR}/foo/flags-cray-8.4.1-debug.json ${SRC_DIR}
+    make VERBOSE=1
+
+# Use custom compile flags defined in CMake script:
+
+    ecbuild --build=Debug -DFOO_ECBUILD_COMPILE_FLAGS=${SRC_DIR}/foo/flags.cmake ${SRC_DIR}
+    make VERBOSE=1
diff --git a/ecbuild/examples/override-compile-flags/bar/CMakeLists.txt b/ecbuild/examples/override-compile-flags/bar/CMakeLists.txt
new file mode 100644
index 0000000..b4e3340
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/bar/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( bar C )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake" )
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+### targets
+
+ecbuild_add_library( TARGET bar SOURCES bar.c )
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/override-compile-flags/bar/bar.c b/ecbuild/examples/override-compile-flags/bar/bar.c
new file mode 100644
index 0000000..e425999
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/bar/bar.c
@@ -0,0 +1 @@
+void bar() {}
diff --git a/ecbuild/examples/override-compile-flags/foo/CMakeLists.txt b/ecbuild/examples/override-compile-flags/foo/CMakeLists.txt
new file mode 100644
index 0000000..1373a9a
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required( VERSION 3.3.2 FATAL_ERROR )
+
+project( foo Fortran C )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake" )
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### open project
+
+ecbuild_declare_project()
+
+ecbuild_enable_fortran ( REQUIRED MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/module )
+
+ecbuild_use_package( PROJECT bar REQUIRED )
+
+### targets
+
+ecbuild_add_library( TARGET  foo
+                     SOURCES foo.c
+                             foo.f90
+                             foo_contiguous.f90
+                             foo_intolerant.f90
+                             foo_ivybridge.f90
+                             foo_no_debug_symbols.f90
+                     LIBS    bar )
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-debug.json b/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-debug.json
new file mode 100644
index 0000000..396d0b3
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-debug.json
@@ -0,0 +1,8 @@
+{
+    "*.f90" : [ "=", "-G0 -ram -emf -hadd_paren -hflex_mp=conservative -hfp0" ],
+    "*.c" : [ "=", "-g -O0 -fPIC" ],
+    "*foo_contiguous.f90" : [ "+", "-hcontiguous" ],
+    "*foo_intolerant.f90" : [ "=", "-G0 -ram -emf -hadd_paren -hflex_mp=intolerant -hfp0" ],
+    "*foo_ivybridge.f90" : [ "+", "-hcpu=ivybridge" ],
+    "*foo_no_debug_symbols.f90" : [ "=", "-ram -emf -hadd_paren -hflex_mp=conservative -hfp0" ]
+}
diff --git a/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-release.json b/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-release.json
new file mode 100644
index 0000000..e501d3a
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/flags-cray-8.4.1-release.json
@@ -0,0 +1,7 @@
+{
+    "*.f90" : [ "=", "-ram -emf -hadd_paren -Othread1 -hflex_mp=conservative -hfp1" ],
+    "*.c" : [ "=", "-O0 -fPIC" ],
+    "*foo_contiguous.f90" : [ "+", "-hcontiguous" ],
+    "*foo_intolerant.f90" : [ "=", "-ram -emf -hadd_paren -hflex_mp=intolerant -hfp1" ],
+    "*foo_ivybridge.f90" : [ "+", "-hcpu=ivybridge" ]
+}
diff --git a/ecbuild/examples/override-compile-flags/foo/flags-cray-debug.json b/ecbuild/examples/override-compile-flags/foo/flags-cray-debug.json
new file mode 100644
index 0000000..333cd16
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/flags-cray-debug.json
@@ -0,0 +1,6 @@
+{
+    "*.f90" : [ "=", "-G0 -ram -emf -hadd_paren -hflex_mp=conservative -hfp0" ],
+    "*.c" : [ "=", "-g -O0 -fPIC" ],
+    "*foo_contiguous.f90" : [ "+", "-hcontiguous" ],
+    "*foo_intolerant.f90" : [ "=", "-G0 -ram -emf -hadd_paren -hflex_mp=intolerant -hfp0" ]
+}
diff --git a/ecbuild/examples/override-compile-flags/foo/flags-cray-release.json b/ecbuild/examples/override-compile-flags/foo/flags-cray-release.json
new file mode 100644
index 0000000..200542e
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/flags-cray-release.json
@@ -0,0 +1,6 @@
+{
+    "*.f90" : [ "=", "-ram -emf -hadd_paren -Othread1 -hflex_mp=conservative -hfp1" ],
+    "*.c" : [ "=", "-O0 -fPIC" ],
+    "*foo_contiguous.f90" : [ "+", "-hcontiguous" ],
+    "*foo_intolerant.f90" : [ "=", "-ram -emf -hadd_paren -hflex_mp=intolerant -hfp1" ]
+}
diff --git a/ecbuild/examples/override-compile-flags/foo/flags.cmake b/ecbuild/examples/override-compile-flags/foo/flags.cmake
new file mode 100644
index 0000000..6ec23e9
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/flags.cmake
@@ -0,0 +1,37 @@
+if(CMAKE_Fortran_COMPILER_ID MATCHES "Cray")
+
+  set(FOO_Fortran_FLAGS "-ram -emf -hadd_paren") # common flags for all build types
+  set(FOO_Fortran_FLAGS_RELEASE "-hflex_mp=conservative -Othread1 -hfp1")
+  set(FOO_Fortran_FLAGS_RELWITHDEBINFO "-G2 ${FOO_Fortran_FLAGS_RELEASE}")
+  set(FOO_Fortran_FLAGS_DEBUG "-G0 -hflex_mp=conservative -hfp0")
+
+  set(FOO_C_FLAGS_RELEASE "-O0 -fPIC")
+  set(FOO_C_FLAGS_RELWITHDEBINFO "-g -O0 -fPIC")
+  set(FOO_C_FLAGS_DEBUG "-g -O0 -fPIC")
+
+  set_source_files_properties(foo_contiguous.f90 PROPERTIES COMPILE_FLAGS "-hcontiguous")
+
+  set_source_files_properties(foo_intolerant.f90
+    PROPERTIES OVERRIDE_COMPILE_FLAGS_RELEASE "${FOO_Fortran_FLAGS} -hflex_mp=intolerant -hfp1"
+               OVERRIDE_COMPILE_FLAGS_RELWITHDEBINFO "-G2 ${FOO_Fortran_FLAGS} -hflex_mp=intolerant -hfp1"
+               OVERRIDE_COMPILE_FLAGS_DEBUG "-G0 ${FOO_Fortran_FLAGS} -hflex_mp=intolerant -hfp0")
+
+  if($ENV{CRAY_FTN_VERSION} VERSION_EQUAL 8.4.1) # cdt/15.11
+
+    set_source_files_properties(foo_ivybridge.f90 PROPERTIES COMPILE_FLAGS "-hcpu=ivybridge")
+
+    string(TOUPPER ${CMAKE_BUILD_TYPE} btype)
+    string(REGEX REPLACE "-g|-G[0-2]|-Gfast" "" flags "${FOO_Fortran_FLAGS_${btype}}")
+    set_source_files_properties(foo_no_debug_symbols.f90 PROPERTIES OVERRIDE_COMPILE_FLAGS "${flags}")
+    
+  endif()
+
+elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
+
+  # ...
+
+elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
+
+  # ...
+
+endif()
diff --git a/ecbuild/examples/override-compile-flags/foo/foo.c b/ecbuild/examples/override-compile-flags/foo/foo.c
new file mode 100644
index 0000000..85e6cd8
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo.c
@@ -0,0 +1 @@
+void foo() {}
diff --git a/ecbuild/examples/override-compile-flags/foo/foo.f90 b/ecbuild/examples/override-compile-flags/foo/foo.f90
new file mode 100644
index 0000000..b5112d2
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo.f90
@@ -0,0 +1,2 @@
+subroutine foo
+end subroutine
diff --git a/ecbuild/examples/override-compile-flags/foo/foo_contiguous.f90 b/ecbuild/examples/override-compile-flags/foo/foo_contiguous.f90
new file mode 100644
index 0000000..eb7fc91
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo_contiguous.f90
@@ -0,0 +1,2 @@
+subroutine foo_contiguous
+end subroutine
diff --git a/ecbuild/examples/override-compile-flags/foo/foo_intolerant.f90 b/ecbuild/examples/override-compile-flags/foo/foo_intolerant.f90
new file mode 100644
index 0000000..6e96ccb
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo_intolerant.f90
@@ -0,0 +1,2 @@
+subroutine foo_intolerant
+end subroutine
diff --git a/ecbuild/examples/override-compile-flags/foo/foo_ivybridge.f90 b/ecbuild/examples/override-compile-flags/foo/foo_ivybridge.f90
new file mode 100644
index 0000000..086f1b7
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo_ivybridge.f90
@@ -0,0 +1,2 @@
+subroutine foo_ivybridge
+end subroutine
diff --git a/ecbuild/examples/override-compile-flags/foo/foo_no_debug_symbols.f90 b/ecbuild/examples/override-compile-flags/foo/foo_no_debug_symbols.f90
new file mode 100644
index 0000000..d109ba9
--- /dev/null
+++ b/ecbuild/examples/override-compile-flags/foo/foo_no_debug_symbols.f90
@@ -0,0 +1,2 @@
+subroutine foo_no_debug_symbols
+end subroutine
diff --git a/ecbuild/examples/simple/CMakeLists.txt b/ecbuild/examples/simple/CMakeLists.txt
new file mode 100644
index 0000000..decee66
--- /dev/null
+++ b/ecbuild/examples/simple/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required( VERSION 2.8.12 FATAL_ERROR )
+
+project( simple CXX Fortran )
+
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" )
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 1.9 )
+
+### open project
+
+ecbuild_declare_project()
+
+### declare options
+
+ecbuild_add_option( FEATURE MATRIX
+                    DESCRIPTION "Use matrix operations"
+                    REQUIRED_PACKAGES LAPACK )
+
+ecbuild_add_option( FEATURE BESSEL
+                    CONDITION HAVE_MATRIX
+                    DESCRIPTION "Compute Bessel function"
+                    REQUIRED_PACKAGES GSL )
+
+### targets
+
+add_subdirectory( circle )
+
+add_subdirectory( compute )
+
+### pkgconfig exports
+
+ecbuild_pkgconfig(
+    NAME circle
+    DESCRIPTION "Functions on circles"
+    LIBRARIES circle )
+
+### finalize project
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/ecbuild/examples/simple/README.md b/ecbuild/examples/simple/README.md
new file mode 100644
index 0000000..ab40cac
--- /dev/null
+++ b/ecbuild/examples/simple/README.md
@@ -0,0 +1,5 @@
+Usage:
+=====
+
+ecbuild examples/simple
+
diff --git a/ecbuild/examples/simple/VERSION.cmake b/ecbuild/examples/simple/VERSION.cmake
new file mode 100644
index 0000000..caf4027
--- /dev/null
+++ b/ecbuild/examples/simple/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.10.0")
diff --git a/ecbuild/examples/simple/circle/CMakeLists.txt b/ecbuild/examples/simple/circle/CMakeLists.txt
new file mode 100644
index 0000000..3760d02
--- /dev/null
+++ b/ecbuild/examples/simple/circle/CMakeLists.txt
@@ -0,0 +1,12 @@
+ecbuild_add_library(
+    TARGET circle
+    TYPE SHARED
+    SOURCES area.f90
+)
+
+
+ecbuild_add_test(
+    TARGET  test_area
+    SOURCES test_area.c
+    LIBS    circle
+)
\ No newline at end of file
diff --git a/ecbuild/examples/simple/circle/area.f90 b/ecbuild/examples/simple/circle/area.f90
new file mode 100644
index 0000000..4c1c010
--- /dev/null
+++ b/ecbuild/examples/simple/circle/area.f90
@@ -0,0 +1,11 @@
+FUNCTION area_circle(r)
+IMPLICIT NONE
+
+REAL*8, PARAMETER :: Pi = 3.1415927
+REAL*8, INTENT(IN) :: r
+REAL*8 :: area_circle
+
+area_circle = Pi * r * r
+RETURN
+
+END FUNCTION area_circle
\ No newline at end of file
diff --git a/ecbuild/examples/simple/circle/test_area.c b/ecbuild/examples/simple/circle/test_area.c
new file mode 100644
index 0000000..3592d81
--- /dev/null
+++ b/ecbuild/examples/simple/circle/test_area.c
@@ -0,0 +1,18 @@
+#include "stdio.h"
+#include "math.h"
+
+double area_circle_(double *);
+
+int main() {
+
+  double r = 10.;
+  double a = area_circle_(&r);
+
+  printf("%le %le %le\n", r, a, fabs(a - 100*M_PI));
+
+  if( fabs(a - 100*M_PI) > 1E-5 )
+    return -1;
+  else
+    return 0;
+}
+
diff --git a/ecbuild/examples/simple/compute/CMakeLists.txt b/ecbuild/examples/simple/compute/CMakeLists.txt
new file mode 100644
index 0000000..0bf1b75
--- /dev/null
+++ b/ecbuild/examples/simple/compute/CMakeLists.txt
@@ -0,0 +1,23 @@
+if( HAVE_MATRIX )
+    set( compute_defs HAVE_MATRIX_LAPACK )
+endif()
+
+ecbuild_add_executable(
+
+    TARGET
+        compute
+
+    SOURCES
+        compute.cc
+
+    DEFINITIONS
+        "${compute_defs}"
+
+    INCLUDES
+        ${GSL_INCLUDE_DIRS}
+
+    LIBS
+        circle
+        ${LAPACK_LIBRARIES}
+        ${GSL_LIBRARIES}
+)
diff --git a/ecbuild/examples/simple/compute/compute.cc b/ecbuild/examples/simple/compute/compute.cc
new file mode 100644
index 0000000..ca648c8
--- /dev/null
+++ b/ecbuild/examples/simple/compute/compute.cc
@@ -0,0 +1,67 @@
+#include <iostream>
+#include <vector>
+
+#ifdef HAVE_GSL
+#include <gsl/gsl_sf_bessel.h>
+#endif
+
+using namespace std;
+
+extern "C"
+{
+  double area_circle_(double *);
+
+  void dgetrf_( int* m, int* n, double* a, int* lda, int* ipiv, int *info );
+  void dgetrs_( char* trans, int* n, int* nrhs, const double* a, int* lda, const int* ipiv,double* b, int* ldb, int *info );
+}
+
+int main() {
+
+  double x = 12.;
+  std::cout << "x = "  << x << std::endl;
+
+  double ca = area_circle_(&x);
+
+  std::cout << "area_circle = " << ca << std::endl;
+
+#ifdef HAVE_GSL
+  double cb = gsl_sf_bessel_J0(x);
+  std::cout << "Bessel J0 = " << cb << std::endl;
+#endif
+
+
+#ifdef HAVE_MATRIX_LAPACK
+
+    char    TRANS = 'N';
+    int     INFO=3;
+    int     LDA = 3;
+    int     LDB = 3;
+    int     N = 3;
+    int     NRHS = 1;
+    int     IPIV[3] ;
+
+    double  A[9] =
+    {
+    1, 2, 3,
+    2, 3, 4,
+    3, 4, 1
+    };
+
+    double B[3] =
+    {
+    -4,
+    -1,
+    -2
+    };
+
+    dgetrf_(&N,&N,A,&LDA,IPIV,&INFO);
+
+    dgetrs_(&TRANS,&N,&NRHS,A,&LDA,IPIV,B,&LDB,&INFO);
+
+    std::cout << "[" << B[0] << ", " << B[1] <<", " << B[2] << "]" << std::endl;
+
+#endif
+
+  return 0;
+}
+
diff --git a/ecbuild/examples/simple/project_summary.cmake b/ecbuild/examples/simple/project_summary.cmake
new file mode 100644
index 0000000..f4ee34c
--- /dev/null
+++ b/ecbuild/examples/simple/project_summary.cmake
@@ -0,0 +1,11 @@
+message( STATUS "---------------------------------------------------------" )
+
+if( LAPACK_FOUND )
+    ecbuild_info( " LAPACK : [${LAPACK_LIBRARIES}]" )
+endif()
+
+if( GSL_FOUND )
+    ecbuild_info( " GSL include : [${GSL_INCLUDE_DIRS}]" )
+    ecbuild_info( "     libs    : [${GSL_LIBRARIES}]" )
+endif()
+
diff --git a/ecbuild/project_summary.cmake b/ecbuild/project_summary.cmake
new file mode 100644
index 0000000..f14d9a2
--- /dev/null
+++ b/ecbuild/project_summary.cmake
@@ -0,0 +1,21 @@
+message( STATUS "---------------------------------------------------------" )
+
+ecbuild_info( "[Generic]" )
+
+if( PERL_EXECUTABLE )
+  ecbuild_info( " Perl             : [${PERL_EXECUTABLE}] (${PERL_VERSION})" )
+endif()
+
+if(PYTHONINTERP_FOUND)
+  ecbuild_info( " Python           : [${PYTHON_EXECUTABLE}] (${PYTHON_VERSION})" )
+endif()
+
+if(PYTHONLIBS_FOUND)
+  ecbuild_info( " Python   include : [${PYTHON_INCLUDE_DIRS}]" )
+  ecbuild_info( "          libs    : [${PYTHON_LIBRARIES}]" )
+endif()
+
+if( DEFINED FORTRAN_LIBRARIES )
+  ecbuild_info( "Fortan libs       : [${FORTRAN_LIBRARIES}]" )
+endif()
+
diff --git a/ecbuild/share/CMakeLists.txt b/ecbuild/share/CMakeLists.txt
new file mode 100644
index 0000000..bcf42de
--- /dev/null
+++ b/ecbuild/share/CMakeLists.txt
@@ -0,0 +1,5 @@
+# ecbuild_add_resources( TARGET ecbuild_share 
+#	SOURCE_PACK ecmwf_license_header.txt
+#	DONT_PACK_DIRS ecbuild )
+
+install( DIRECTORY ecbuild/toolchains DESTINATION ${INSTALL_DATA_DIR} )
diff --git a/ecbuild/share/ecbuild/cmake/2.8/CMakePushCheckState.cmake b/ecbuild/share/ecbuild/cmake/2.8/CMakePushCheckState.cmake
new file mode 100644
index 0000000..0a42128
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/2.8/CMakePushCheckState.cmake
@@ -0,0 +1,61 @@
+# This module defines two macros:
+# CMAKE_PUSH_CHECK_STATE()
+# and
+# CMAKE_POP_CHECK_STATE()
+# These two macros can be used to save and restore the state of the variables
+# CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_LIBRARIES
+# and CMAKE_REQUIRED_INCLUDES used by the various Check-files coming with CMake,
+# like e.g. check_function_exists() etc.
+# The variable contents are pushed on a stack, pushing multiple times is supported.
+# This is useful e.g. when executing such tests in a Find-module, where they have to be set,
+# but after the Find-module has been executed they should have the same value
+# as they had before.
+#
+# Usage:
+#   cmake_push_check_state()
+#   set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -DSOME_MORE_DEF)
+#   check_function_exists(...)
+#   cmake_pop_check_state()
+
+#=============================================================================
+# Copyright 2006-2011 Alexander Neundorf, <neundorf at kde.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+
+MACRO(CMAKE_PUSH_CHECK_STATE)
+
+   IF(NOT DEFINED _CMAKE_PUSH_CHECK_STATE_COUNTER)
+      SET(_CMAKE_PUSH_CHECK_STATE_COUNTER 0)
+   ENDIF()
+
+   MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}+1")
+
+   SET(_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}    ${CMAKE_REQUIRED_INCLUDES})
+   SET(_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER} ${CMAKE_REQUIRED_DEFINITIONS})
+   SET(_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}   ${CMAKE_REQUIRED_LIBRARIES})
+   SET(_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}       ${CMAKE_REQUIRED_FLAGS})
+ENDMACRO(CMAKE_PUSH_CHECK_STATE)
+
+MACRO(CMAKE_POP_CHECK_STATE)
+
+# don't pop more than we pushed
+   IF("${_CMAKE_PUSH_CHECK_STATE_COUNTER}" GREATER "0")
+
+      SET(CMAKE_REQUIRED_INCLUDES    ${_CMAKE_REQUIRED_INCLUDES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_DEFINITIONS ${_CMAKE_REQUIRED_DEFINITIONS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_LIBRARIES   ${_CMAKE_REQUIRED_LIBRARIES_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+      SET(CMAKE_REQUIRED_FLAGS       ${_CMAKE_REQUIRED_FLAGS_SAVE_${_CMAKE_PUSH_CHECK_STATE_COUNTER}})
+
+      MATH(EXPR _CMAKE_PUSH_CHECK_STATE_COUNTER "${_CMAKE_PUSH_CHECK_STATE_COUNTER}-1")
+   ENDIF()
+
+ENDMACRO(CMAKE_POP_CHECK_STATE)
diff --git a/ecbuild/share/ecbuild/cmake/CMakeLists.txt b/ecbuild/share/ecbuild/cmake/CMakeLists.txt
new file mode 100644
index 0000000..d42a153
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/CMakeLists.txt
@@ -0,0 +1,5 @@
+file( GLOB_RECURSE ecbuild_support_files  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" )
+
+ecbuild_add_resources(  TARGET ${PROJECT_NAME}_ecbuild_support_files
+						SOURCES_PACK
+							${ecbuild_support_files} )
diff --git a/ecbuild/share/ecbuild/cmake/FindADSM.cmake b/ecbuild/share/ecbuild/cmake/FindADSM.cmake
new file mode 100644
index 0000000..daadf0e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindADSM.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ADSM
+# Once done this will define
+#  ADSM_FOUND - System has ADSM
+#  ADSM_INCLUDE_DIRS - The ADSM include directories
+#  ADSM_LIBRARIES - The libraries needed to use ADSM
+
+if( EC_OS_BITS EQUAL 32 )
+	set( ADSM_LIBNAME ApiDS )
+endif()
+if( EC_OS_BITS EQUAL 64 )
+	set( ADSM_LIBNAME ApiTSM64 )
+endif()
+if( NOT DEFINED ADSM_LIBNAME )
+	message( STATUS "MARS only supports ADSM with 32 or 64 bits" )
+endif()
+
+if( DEFINED ADSM_PATH )
+	find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATHS ${ADSM_PATH} ${ADSM_PATH}/include ${ADSM_PATH}/sample NO_DEFAULT_PATH )
+	find_library(ADSM_LIBRARY  ${ADSM_LIBNAME} PATHS ${ADSM_PATH} ${ADSM_PATH}/lib     ${ADSM_PATH}/lib64  NO_DEFAULT_PATH )
+endif()
+
+find_path(ADSM_INCLUDE_DIR dsmapitd.h      PATH_SUFFIXES bin64 )
+find_library( ADSM_LIBRARY ${ADSM_LIBNAME} PATH_SUFFIXES bin64 )
+
+set( ADSM_LIBRARIES    ${ADSM_LIBRARY} )
+set( ADSM_INCLUDE_DIRS ${ADSM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set ADSM_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ADSM  DEFAULT_MSG
+								  ADSM_LIBRARY ADSM_INCLUDE_DIR)
+
+mark_as_advanced(ADSM_INCLUDE_DIR ADSM_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindAEC.cmake b/ecbuild/share/ecbuild/cmake/FindAEC.cmake
new file mode 100644
index 0000000..0b0f69b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindAEC.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find AEC (Adaptive Entropy Coding library)
+# See https://www.dkrz.de/redmine/projects/aec/wiki
+
+# Once done this will define
+#  AEC_FOUND        - System has AEC
+#  AEC_INCLUDE_DIRS - The AEC include directories
+#  AEC_LIBRARIES    - The libraries needed to use AEC
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  AEC_DIR          - prefix path of the AEC installation
+#  AEC_PATH         - prefix path of the AEC installation
+
+find_path( AEC_INCLUDE_DIR szlib.h
+           PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+           PATH_SUFFIXES include include/aec NO_DEFAULT_PATH )
+find_path( AEC_INCLUDE_DIR szlib.h PATH_SUFFIXES include include/aec )
+
+find_library( AEC_LIBRARY  NAMES aec
+              PATHS ${AEC_DIR} ${AEC_PATH} ENV AEC_DIR ENV AEC_PATH
+              PATH_SUFFIXES lib lib64 lib/aec lib64/aec NO_DEFAULT_PATH )
+find_library( AEC_LIBRARY NAMES aec PATH_SUFFIXES lib lib64 lib/aec lib64/aec )
+
+set( AEC_LIBRARIES    ${AEC_LIBRARY} )
+set( AEC_INCLUDE_DIRS ${AEC_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(AEC  DEFAULT_MSG AEC_LIBRARY AEC_INCLUDE_DIR)
+
+mark_as_advanced(AEC_INCLUDE_DIR AEC_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindAIO.cmake b/ecbuild/share/ecbuild/cmake/FindAIO.cmake
new file mode 100644
index 0000000..5dd9244
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindAIO.cmake
@@ -0,0 +1,66 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# OUTPUT:
+# RT_LIB  = the library to link against
+
+if( CMAKE_SYSTEM_NAME MATCHES "Linux" )
+
+	find_package( Realtime )
+
+	if( REALTIME_FOUND ) # check that aio needs realtime
+		set( AIO_LIBRARIES ${RT_LIB} )
+	endif()
+
+endif()
+
+find_path( AIO_INCLUDE_DIRS NAMES aio.h HINTS ENV AIO_PATH ${AIO_PATH} )
+
+mark_as_advanced( AIO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( AIO  DEFAULT_MSG  AIO_INCLUDE_DIRS  )
+
+# checks for AIO64 vs AIO
+if( AIO_FOUND )
+
+	include( CheckCSourceCompiles )
+	include( CMakePushCheckState )
+    
+    cmake_push_check_state()
+    
+		set( CMAKE_REQUIRED_INCLUDES ${AIO_INCLUDE_DIRS} )
+
+		if( AIO_LIBRARIES )
+			set( CMAKE_REQUIRED_LIBRARIES ${AIO_LIBRARIES} )
+		endif()
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb* aiocbp;
+									  int n = aio_write(aiocbp);
+									  n = aio_read(aiocbp);
+									  n = aio_fsync(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB )
+
+		check_c_source_compiles( "#include <aio.h>
+								  #include <fcntl.h>
+								  int main(){
+									  struct aiocb64* aiocbp;
+									  int n = aio_write64(aiocbp);
+									  n = aio_read64(aiocbp);
+									  n = aio_fsync64(O_SYNC,aiocbp);
+									  return 0; }"
+								EC_HAVE_AIOCB64 )
+
+    cmake_pop_check_state()
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindArmadillo.cmake b/ecbuild/share/ecbuild/cmake/FindArmadillo.cmake
new file mode 100644
index 0000000..dfe77a2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindArmadillo.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Armadillo
+# Once done this will define
+#
+#  ARMADILLO_FOUND         - system has Armadillo
+#  ARMADILLO_INCLUDE_DIRS  - the Armadillo include directory
+#  ARMADILLO_LIBRARIES     - the Armadillo library
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  ARMADILLO_PATH          - prefix path of the Armadillo installation
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_path(ARMADILLO_INCLUDE_DIR armadillo
+          PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(ARMADILLO_INCLUDE_DIR  armadillo PATH_SUFFIXES include )
+
+# Search with priority for ARMADILLO_PATH if given as CMake or env var
+find_library(ARMADILLO_LIBRARY armadillo
+             PATHS ${ARMADILLO_PATH} ENV ARMADILLO_PATH
+             PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+find_library( ARMADILLO_LIBRARY  armadillo   PATH_SUFFIXES lib64 lib )
+
+set( ARMADILLO_LIBRARIES    ${ARMADILLO_LIBRARY} )
+set( ARMADILLO_INCLUDE_DIRS ${ARMADILLO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set ARMADILLO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Armadillo  DEFAULT_MSG
+                                  ARMADILLO_LIBRARY ARMADILLO_INCLUDE_DIR)
+
+mark_as_advanced(ARMADILLO_INCLUDE_DIR ARMADILLO_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindCMath.cmake b/ecbuild/share/ecbuild/cmake/FindCMath.cmake
new file mode 100644
index 0000000..741728a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindCMath.cmake
@@ -0,0 +1,26 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# CMATH_LIBRARIES      = the library to link against (RT etc)
+
+IF(UNIX)
+  if( DEFINED CMATH_PATH )
+    find_library(CMATH_LIBRARIES m PATHS ${CMATH_PATH}/lib NO_DEFAULT_PATH )
+  endif()
+
+  find_library(CMATH_LIBRARIES m )
+
+  include(FindPackageHandleStandardArgs)
+
+  # handle the QUIET and REQUIRED arguments and set CMATH_FOUND to TRUE
+  # if all listed variables are TRUE
+  # Note: capitalisation of the package name must be the same as in the file name
+  find_package_handle_standard_args(CMath DEFAULT_MSG CMATH_LIBRARIES )
+
+ENDIF(UNIX)
diff --git a/ecbuild/share/ecbuild/cmake/FindCairo.cmake b/ecbuild/share/ecbuild/cmake/FindCairo.cmake
new file mode 100644
index 0000000..4298a1d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindCairo.cmake
@@ -0,0 +1,59 @@
+# - Try to find the cairo library
+# Once done this will define
+#
+# CAIRO_FOUND - system has cairo
+# CAIRO_INCLUDE_DIRS - the cairo include directory
+# CAIRO_LIBRARIES - Link these to use cairo
+#
+# Define CAIRO_MIN_VERSION for which version desired.
+
+
+if( NOT DEFINED CAIRO_PATH AND NOT "$ENV{CAIRO_PATH}" STREQUAL "" )
+    set( APPEND CAIRO_PATH "$ENV{CAIRO_PATH}" )
+endif()
+
+if( NOT DEFINED CAIRO_PATH )
+
+    include(FindPkgConfig)
+
+    if(Cairo_FIND_REQUIRED)
+        set(_pkgconfig_REQUIRED "REQUIRED")
+    else()
+        set(_pkgconfig_REQUIRED "")
+    endif()
+
+    if(CAIRO_MIN_VERSION)
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo>=${CAIRO_MIN_VERSION})
+    else()
+        pkg_check_modules(PKCAIRO ${_pkgconfig_REQUIRED} cairo)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKCAIRO_FOUND )
+
+        find_path(CAIRO_INCLUDE_DIR cairo.h HINTS ${PKCAIRO_INCLUDEDIR} ${PKCAIRO_INCLUDE_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+        find_library(CAIRO_LIBRARY  cairo   HINTS ${PKCAIRO_LIBDIR}     ${PKCAIRO_LIBRARY_DIRS} PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+    endif()
+
+else()
+
+    find_path(CAIRO_INCLUDE_DIR cairo.h PATHS ${CAIRO_PATH}/include PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+    find_library(CAIRO_LIBRARY  cairo   PATHS ${CAIRO_PATH}/lib     PATH_SUFFIXES cairo NO_DEFAULT_PATH )
+
+endif()
+
+find_path(CAIRO_INCLUDE_DIR cairo.h PATH_SUFFIXES cairo )
+find_library( CAIRO_LIBRARY cairo   PATH_SUFFIXES cairo )
+
+set( CAIRO_LIBRARIES    ${CAIRO_LIBRARY} )
+set( CAIRO_INCLUDE_DIRS ${CAIRO_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set CAIRO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Cairo  DEFAULT_MSG
+                                  CAIRO_LIBRARY CAIRO_INCLUDE_DIR)
+
+mark_as_advanced( CAIRO_INCLUDE_DIR CAIRO_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindDl.cmake b/ecbuild/share/ecbuild/cmake/FindDl.cmake
new file mode 100644
index 0000000..31e426f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindDl.cmake
@@ -0,0 +1,23 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# DL_LIBRARIES      = the library to link against (RT etc)
+
+if( DEFINED DL_PATH )
+    find_library(DL_LIBRARIES dl PATHS ${DL_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library(DL_LIBRARIES dl )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set DL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Dl DEFAULT_MSG DL_LIBRARIES )
diff --git a/ecbuild/share/ecbuild/cmake/FindEMOS.cmake b/ecbuild/share/ecbuild/cmake/FindEMOS.cmake
new file mode 100644
index 0000000..43f896e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindEMOS.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find EMOS
+# Once done this will define
+#  EMOS_FOUND - System has EMOS
+#  EMOS_INCLUDE_DIRS - The EMOS include directories
+#  EMOS_LIBRARIES - The libraries needed to use EMOS
+
+if( NOT DEFINED EMOS_PATH AND DEFINED $ENV{EMOS_PATH} )
+	set( EMOS_PATH $ENV{EMOS_PATH} )
+endif()
+
+if( DEFINED EMOS_PATH )
+    find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos PATHS ${EMOS_PATH} PATH_SUFFIXES lib lib/emos NO_DEFAULT_PATH)
+endif()
+
+find_library( EMOS_LIBRARY NAMES emos.R64.D64.I32 emos.R64 emosR64 emos)
+
+ecbuild_find_fortranlibs()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( EMOS  DEFAULT_MSG  EMOS_LIBRARY FORTRANLIBS_FOUND )
+
+mark_as_advanced(EMOS_LIBRARY)
+
+if( EMOS_FOUND )
+    set( EMOS_LIBRARIES  ${EMOS_LIBRARY} ${FORTRAN_LIBRARIES} )
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindFDB.cmake b/ecbuild/share/ecbuild/cmake/FindFDB.cmake
new file mode 100644
index 0000000..66879c1
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindFDB.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find FDB
+# Once done this will define
+#  FDB_FOUND - System has FDB
+#  FDB_INCLUDE_DIRS - The FDB include directories
+#  FDB_LIBRARIES - The libraries needed to use FDB
+
+
+if( NOT FDB_FOUND )
+
+	if( DEFINED FDB_PATH )
+		find_library( FDB_LIBRARY NAMES fdb PATHS ${FDB_PATH} ${FDB_PATH}/lib NO_DEFAULT_PATH)
+	endif()
+	
+	find_library( FDB_LIBRARY NAMES fdb )
+	
+	set( FDB_LIBRARIES  ${FDB_LIBRARY} )
+	
+	include(FindPackageHandleStandardArgs)
+	
+	# handle the QUIETLY and REQUIRED arguments and set FDB_FOUND to TRUE
+	# if all listed variables are TRUE
+	find_package_handle_standard_args(FDB  DEFAULT_MSG
+									  FDB_LIBRARY )
+	
+	mark_as_advanced(FDB_LIBRARY)
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindFFTW.cmake b/ecbuild/share/ecbuild/cmake/FindFFTW.cmake
new file mode 100644
index 0000000..d57c09a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindFFTW.cmake
@@ -0,0 +1,211 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# FindFFTW
+# ========
+#
+# Find the FFTW library. ::
+#
+#   find_package(FFTW [REQUIRED] [QUIET]
+#                [COMPONENTS [single] [double] [long_double] [quad]])
+#
+# By default, search for the double precision library ``fftw3``
+#
+# Components
+# ----------
+#
+# If a different version or multiple versions of the library are required,
+# these need to be specified as ``COMPONENTS``. Note that double must be given
+# explicitly if any ``COMPONENTS`` are specified.
+#
+# The libraries corresponding to each of the ``COMPONENTS`` are:
+#
+# :single:      ``fftw3f``
+# :double:      ``fftw3``
+# :long_double: ``fftw3l``
+# :quad:        ``fftw3q``
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set on completion:
+#
+# :FFTW_FOUND:      true if FFTW is found on the system
+# :FFTW_LIBRARIES:  full paths to requested FFTW libraries
+# :FFTW_INCLUDES:   FFTW include directory
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are checked by the function:
+#
+# :FFTW_USE_STATIC_LIBS:  if true, only static libraries are found
+# :FFTW_ROOT:             if set, this path is exclusively searched
+# :FFTW_DIR:              equivalent to FFTW_ROOT
+# :FFTW_PATH:             equivalent to FFTW_ROOT
+# :FFTW_LIBRARY:          FFTW library to use
+# :FFTW_INCLUDE_DIR:      FFTW include directory
+#
+##############################################################################
+
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_ROOT} )
+  set( FFTW_ROOT ${FFTW_ROOT} )
+endif()
+if( NOT FFTW_ROOT AND $FFTW_DIR )
+  set( FFTW_ROOT ${FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_DIR} )
+  set( FFTW_ROOT $ENV{FFTW_DIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTWDIR )
+  set( FFTW_ROOT ${FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTWDIR} )
+  set( FFTW_ROOT $ENV{FFTWDIR} )
+endif()
+if( (NOT FFTW_ROOT) AND FFTW_PATH )
+  set( FFTW_ROOT ${FFTW_PATH} )
+endif()
+if( (NOT FFTW_ROOT) AND EXISTS $ENV{FFTW_PATH})
+  set( FFTW_ROOT $ENV{FFTW_PATH} )
+endif()
+
+if( FFTW_ROOT ) # On cc[a|b|t] FFTW_DIR is set to the lib directory :(
+  get_filename_component(_dirname ${FFTW_ROOT} NAME)
+  if( _dirname MATCHES "lib" )
+    set( FFTW_ROOT "${FFTW_ROOT}/.." )
+  endif()
+endif()
+
+if( NOT FFTW_ROOT )
+  # Check if we can use PkgConfig
+  find_package(PkgConfig)
+
+  #Determine from PKG
+  if( PKG_CONFIG_FOUND AND NOT FFTW_ROOT )
+    pkg_check_modules( PKG_FFTW QUIET "fftw3" )
+  endif()
+endif()
+
+#Check whether to search static or dynamic libs
+set( CMAKE_FIND_LIBRARY_SUFFIXES_SAV ${CMAKE_FIND_LIBRARY_SUFFIXES} )
+
+if( ${FFTW_USE_STATIC_LIBS} )
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX} )
+else()
+  set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} )
+endif()
+
+if( FFTW_FIND_COMPONENTS )
+  ecbuild_debug( "FindFFTW: looking for components: ${FFTW_FIND_COMPONENTS}" )
+  foreach( _component ${FFTW_FIND_COMPONENTS} )
+    if( _component MATCHES "single" )
+      ecbuild_debug( "FindFFTW: looking for single precision (fftw3f)" )
+      set( _require_sp TRUE )
+    elseif( _component MATCHES "double" )
+      ecbuild_debug( "FindFFTW: looking for double precision (fftw3)" )
+      set( _require_dp TRUE )
+    elseif( _component MATCHES "long_double" )
+      ecbuild_debug( "FindFFTW: looking for long double precision (fftw3l)" )
+      set( _require_lp TRUE )
+    elseif( _component MATCHES "quad" )
+      ecbuild_debug( "FindFFTW: looking for quad precision (fftw3q)" )
+      set( _require_qp TRUE )
+    else()
+    endif()
+  endforeach()
+else()
+  ecbuild_debug( "FindFFTW: no components specified, looking for double precision (fftw3)" )
+  set( _require_dp TRUE )
+endif()
+
+if( FFTW_ROOT )
+  set( _default_paths NO_DEFAULT_PATH )
+  set( _lib_paths ${FFTW_ROOT} )
+  set( _include_paths ${FFTW_ROOT} )
+else()
+  set( _lib_paths ${PKG_FFTW_LIBRARY_DIRS} ${LIB_INSTALL_DIR} )
+  set( _include_paths ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} )
+endif()
+
+#find libs
+
+if( _require_dp )
+  find_library(
+    FFTW_LIB
+    NAMES "fftw3"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTW_LIB )
+    ecbuild_warn("FindFFTW: double precision required, but fftw3 was not found")
+  endif()
+endif()
+
+if( _require_sp )
+  find_library(
+    FFTWF_LIB
+    NAMES "fftw3f"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWF_LIB )
+    ecbuild_warn("FindFFTW: single precision required, but fftw3f was not found")
+  endif()
+endif()
+
+if( _require_lp )
+  find_library(
+    FFTWL_LIB
+    NAMES "fftw3l"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWL_LIB )
+    ecbuild_warn("FindFFTW: long double precision required, but fftw3l was not found")
+  endif()
+endif()
+
+if( _require_qp )
+  find_library(
+    FFTWQ_LIB
+    NAMES "fftw3q"
+    PATHS ${_lib_paths}
+    PATH_SUFFIXES "lib" "lib64"
+    ${_default_paths}
+  )
+  if( NOT FFTWQ_LIB )
+    ecbuild_warn("FindFFTW: quad precision required, but fftw3q was not found")
+  endif()
+endif()
+
+#find includes
+
+find_path(
+  FFTW_INCLUDES
+  NAMES "fftw3.h"
+  PATHS ${_include_paths}
+  PATH_SUFFIXES "include"
+  ${_default_paths}
+)
+
+set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB} ${FFTWL_LIB} ${FFTWQ_LIB})
+
+set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} )
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(FFTW DEFAULT_MSG
+                                  FFTW_INCLUDES FFTW_LIBRARIES)
+
+mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB)
diff --git a/ecbuild/share/ecbuild/cmake/FindGeoTIFF.cmake b/ecbuild/share/ecbuild/cmake/FindGeoTIFF.cmake
new file mode 100644
index 0000000..7226f61
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindGeoTIFF.cmake
@@ -0,0 +1,46 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the GeoTIFF includes and library
+# This module defines
+#
+#  GEOTIFF_FOUND         - System has GeoTIFF
+#  GEOTIFF_INCLUDE_DIRS  - the GeoTIFF include directories
+#  GEOTIFF_LIBRARIES     - the libraries needed to use GeoTIFF
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  GEOTIFF_DIR   - root folder of the GeoTIFF installation
+#  GEOTIFF_PATH  - root folder of the GeoTIFF installation
+
+find_path( GEOTIFF_INCLUDE_DIR geotiff.h
+           PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                 ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+           PATH_SUFFIXES include include/libgeotiff
+           NO_DEFAULT_PATH )
+find_path( GEOTIFF_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES include include/libgeotiff )
+
+find_library( GEOTIFF_LIBRARY NAMES geotiff
+              PATHS ${GEOTIFF_PATH} ENV GEOTIFF_PATH
+                    ${GEOTIFF_DIR}  ENV GEOTIFF_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+find_library( GEOTIFF_LIBRARY NAMES geotiff )
+
+set( GEOTIFF_LIBRARIES    ${GEOTIFF_LIBRARY} )
+set( GEOTIFF_INCLUDE_DIRS ${GEOTIFF_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GEOTIFF_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( GeoTIFF DEFAULT_MSG
+                                   GEOTIFF_LIBRARY GEOTIFF_INCLUDE_DIR )
+
+mark_as_advanced( GEOTIFF_INCLUDE_DIR GEOTIFF_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindHPSS.cmake b/ecbuild/share/ecbuild/cmake/FindHPSS.cmake
new file mode 100644
index 0000000..1cc829e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindHPSS.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find HPSS
+# Once done this will define
+#  HPSS_FOUND - System has HPSS
+#  HPSS_INCLUDE_DIRS - The HPSS include directories
+#  HPSS_LIBRARIES - The libraries needed to use HPSS
+#  HPSS_DEFINITIONS - Compiler switches required for using HPSS
+
+if( DEFINED HPSS_PATH )
+	find_path(HPSS_INCLUDE_DIR hpss_api.h PATHS ${HPSS_PATH}/include PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+	find_library(HPSS_LIBRARY  hpss       PATHS ${HPSS_PATH}/lib     PATH_SUFFIXES hpss NO_DEFAULT_PATH)
+endif()
+
+find_path(HPSS_INCLUDE_DIR hpss_api.h PATH_SUFFIXES hpss )
+find_library( HPSS_LIBRARY hpss       PATH_SUFFIXES hpss )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set HPSS_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(HPSS  DEFAULT_MSG
+                                  HPSS_LIBRARY HPSS_INCLUDE_DIR)
+
+mark_as_advanced(HPSS_INCLUDE_DIR HPSS_LIBRARY )
+
+if( HPSS_FOUND )
+    set( HPSS_LIBRARIES    ${HPSS_LIBRARY} )
+    set( HPSS_INCLUDE_DIRS ${HPSS_INCLUDE_DIR} )
+else()
+    set( HPSS_LIBRARIES    "" )
+    set( HPSS_INCLUDE_DIRS "" )
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindLEX.cmake b/ecbuild/share/ecbuild/cmake/FindLEX.cmake
new file mode 100644
index 0000000..b6ab838
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindLEX.cmake
@@ -0,0 +1,129 @@
+# - Find lex executable and provides a macro to generate custom build rules
+#
+# The module defines the following variables:
+#  LEX_FOUND - true is lex executable is found
+#  LEX_EXECUTABLE - the path to the lex executable
+#  LEX_LIBRARIES - The lex libraries
+#  LEX_INCLUDE_DIRS - The path to the lex headers
+#
+#
+# If lex is found on the system, the module provides the macro:
+#  LEX_TARGET(Name LexInput LexOutput [COMPILE_FLAGS <string>])
+# which creates a custom command  to generate the <LexOutput> file from
+# the <LexInput> file.  If  COMPILE_FLAGS option is specified, the next
+# parameter is added to the lex  command line. Name is an alias used to
+# get  details of  this custom  command.  Indeed the  macro defines  the
+# following variables:
+#  LEX_${Name}_DEFINED - true is the macro ran successfully
+#  LEX_${Name}_OUTPUTS - the source file generated by the custom rule, an
+#  alias for LexOutput
+#  LEX_${Name}_INPUT - the lex source file, an alias for ${LexInput}
+#
+# Lex scanners oftenly use tokens  defined by Yacc: the code generated
+# by Lex  depends of the header  generated by Yacc.   This module also
+# defines a macro:
+#  ADD_LEX_YACC_DEPENDENCY(LexTarget YaccTarget)
+# which  adds the  required dependency  between a  scanner and  a parser
+# where  <LexTarget>  and <YaccTarget>  are  the  first parameters  of
+# respectively LEX_TARGET and YACC_TARGET macros.
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(LEX_EXECUTABLE lex DOC "path to the lex executable")
+MARK_AS_ADVANCED(LEX_EXECUTABLE)
+
+FIND_LIBRARY(FL_LIBRARY NAMES fl
+  DOC "Path to the fl library")
+
+FIND_PATH(LEX_INCLUDE_DIR LexLexer.h
+  DOC "Path to the lex headers")
+
+MARK_AS_ADVANCED(FL_LIBRARY LEX_INCLUDE_DIR)
+
+SET(LEX_INCLUDE_DIRS ${LEX_INCLUDE_DIR})
+SET(LEX_LIBRARIES ${FL_LIBRARY})
+
+IF(LEX_EXECUTABLE)
+
+  #============================================================
+  # LEX_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(LEX_TARGET Name Input Output)
+    SET(LEX_TARGET_usage "LEX_TARGET(<Name> <Input> <Output> [COMPILE_FLAGS <string>]")
+    IF(${ARGC} GREATER 3)
+      IF(${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          SET(LEX_EXECUTABLE_opts  "${ARGV4}")
+          SEPARATE_ARGUMENTS(LEX_EXECUTABLE_opts)
+        ELSE()
+          MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+        ENDIF()
+      ELSE()
+        MESSAGE(SEND_ERROR ${LEX_TARGET_usage})
+      ENDIF()
+    ENDIF()
+
+    message( STATUS "${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}" )
+
+    ADD_CUSTOM_COMMAND(OUTPUT ${Output}
+      COMMAND ${LEX_EXECUTABLE} ${LEX_EXECUTABLE_opts} -t ${Input} > ${Output}
+      DEPENDS ${Input}
+      COMMENT "[LEX][${Name}] Building scanner with lex ${LEX_VERSION}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+    SET(LEX_${Name}_DEFINED TRUE)
+    SET(LEX_${Name}_OUTPUTS ${Output})
+    SET(LEX_${Name}_INPUT ${Input})
+    SET(LEX_${Name}_COMPILE_FLAGS ${LEX_EXECUTABLE_opts})
+  ENDMACRO(LEX_TARGET)
+  #============================================================
+
+
+  #============================================================
+  # ADD_LEX_YACC_DEPENDENCY (public macro)
+  #============================================================
+  #
+  MACRO(ADD_LEX_YACC_DEPENDENCY LexTarget YaccTarget)
+
+    IF(NOT LEX_${LexTarget}_OUTPUTS)
+      MESSAGE(SEND_ERROR "Lex target `${LexTarget}' does not exists.")
+    ENDIF()
+
+    IF(NOT YACC_${YaccTarget}_OUTPUT_HEADER)
+      MESSAGE(SEND_ERROR "Yacc target `${YaccTarget}' does not exists.")
+    ENDIF()
+
+    SET_SOURCE_FILES_PROPERTIES(${LEX_${LexTarget}_OUTPUTS}
+      PROPERTIES OBJECT_DEPENDS ${YACC_${YaccTarget}_OUTPUT_HEADER})
+  ENDMACRO(ADD_LEX_YACC_DEPENDENCY)
+  #============================================================
+
+ENDIF(LEX_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LEX REQUIRED_VARS LEX_EXECUTABLE)
+
+# FindLEX.cmake ends here
diff --git a/ecbuild/share/ecbuild/cmake/FindLibGFortran.cmake b/ecbuild/share/ecbuild/cmake/FindLibGFortran.cmake
new file mode 100644
index 0000000..7f9cc64
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindLibGFortran.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# gfortran libs
+
+set( __libgfortran_names gfortran libgfortran.so.1 libgfortran.so.3 )
+
+# use gfortran to find the library
+
+find_program( GFORTRAN_EXECUTABLE gfortran )
+
+if( GFORTRAN_EXECUTABLE )
+
+	execute_process(COMMAND ${GFORTRAN_EXECUTABLE} "-print-search-dirs"
+		RESULT_VARIABLE _GFORTRAN_SEARCH_SUCCESS
+		OUTPUT_VARIABLE _GFORTRAN_VALUES_OUTPUT
+		ERROR_VARIABLE _GFORTRAN_ERROR_VALUE
+		OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+#	ecbuild_debug_var(_GFORTRAN_SEARCH_SUCCESS)
+#	ecbuild_debug_var(_GFORTRAN_VALUES_OUTPUT)
+#	ecbuild_debug_var(_GFORTRAN_ERROR_VALUE)
+
+	if(_GFORTRAN_SEARCH_SUCCESS MATCHES 0)
+		string(REGEX REPLACE ".*libraries: =(.*)" "\\1" _result  ${_GFORTRAN_VALUES_OUTPUT})
+		string(REGEX REPLACE ":" ";" _gfortran_hints ${_result} )
+	endif()
+
+	ecbuild_debug_var( _gfortran_hints )
+
+endif()
+
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${LIBGFORTRAN_PATH} ENV LIBGFORTRAN_PATH PATHS PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH )
+find_library( GFORTRAN_LIB NAMES ${__libgfortran_names}  HINTS ${_gfortran_hints} PATHS PATH_SUFFIXES lib64 lib )
+
+mark_as_advanced( GFORTRAN_LIB )
+
+if( GFORTRAN_LIB )
+	set( GFORTRAN_LIBRARIES ${GFORTRAN_LIB} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBGFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibGFortran DEFAULT_MSG GFORTRAN_LIBRARIES  )
diff --git a/ecbuild/share/ecbuild/cmake/FindLibIFort.cmake b/ecbuild/share/ecbuild/cmake/FindLibIFort.cmake
new file mode 100644
index 0000000..4c3e299
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindLibIFort.cmake
@@ -0,0 +1,50 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# date:   July 2015
+# author: Florian Rathgeber
+
+###############################################################################
+
+# - Try to find Intel Fortran (ifort) runtime libraries libifcore and libifport
+# Once done this will define
+#
+#  LIBIFORT_FOUND   - system has Intel Fortran (ifort) runtime libraries
+#  IFORT_LIBRARIES  - the Intel Fortran (ifort) runtime libraries
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  INTEL_PATH       - prefix path of the Intel installation
+#
+# Otherwise the libraries are assumed to be on LIBRARY_PATH or LD_LIBRARY_PATH
+
+# FIXME: might need to add further libraries in future, see
+# http://nf.nci.org.au/facilities/software/Compilers/Intel8/doc/f_ug1/files_32.htm
+
+# Search with priority for INTEL_PATH if given as CMake or env var
+find_library( IFORT_LIB_CORE ifcore PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ${INTEL_PATH} ENV INTEL_PATH
+              PATH_SUFFIXES lib/intel64 compiler/lib/intel64 NO_DEFAULT_PATH )
+
+# Otherwise, search LIBRARY_PATH and LD_LIBRARY_PATH
+find_library( IFORT_LIB_CORE ifcore PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+find_library( IFORT_LIB_PORT ifport PATHS ENV LIBRARY_PATH LD_LIBRARY_PATH )
+
+mark_as_advanced( IFORT_LIB_CORE IFORT_LIB_PORT )
+
+if( IFORT_LIB_CORE AND IFORT_LIB_PORT )
+  set( IFORT_LIBRARIES ${IFORT_LIB_CORE} ${IFORT_LIB_PORT} )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set LIBIFORT_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( LibIFort DEFAULT_MSG IFORT_LIBRARIES )
diff --git a/ecbuild/share/ecbuild/cmake/FindLustreAPI.cmake b/ecbuild/share/ecbuild/cmake/FindLustreAPI.cmake
new file mode 100644
index 0000000..6db0ba2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindLustreAPI.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find lib Lustre API
+
+# usually installed on Cray systems under /opt/cray/lustre-cray_ari_s/default / create_test.c -L
+# .../include/lustre/lustreapi.h
+# .../lib64/liblustreapi.so
+
+# Once done this will define
+#  LUSTREAPI_FOUND        - System has LustreAPI
+#  LUSTREAPI_INCLUDE_DIRS - The LustreAPI include directories
+#  LUSTREAPI_LIBRARIES    - The libraries needed to use LustreAPI
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  LUSTREAPI_DIR          - prefix path of the LustreAPI installation
+#  LUSTREAPI_PATH         - prefix path of the LustreAPI installation
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h
+           PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+           PATH_SUFFIXES include NO_DEFAULT_PATH )
+
+find_path( LUSTREAPI_INCLUDE_DIR lustre/lustreapi.h PATH_SUFFIXES include )
+
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi
+              PATHS ${LUSTREAPI_DIR} ${LUSTREAPI_PATH} ENV LUSTREAPI_DIR ENV LUSTREAPI_PATH
+              PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+find_library( LUSTREAPI_LIBRARY NAMES lustreapi PATH_SUFFIXES lib lib64 )
+
+set( LUSTREAPI_LIBRARIES    ${LUSTREAPI_LIBRARY} )
+set( LUSTREAPI_INCLUDE_DIRS ${LUSTREAPI_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(LUSTREAPI  DEFAULT_MSG LUSTREAPI_LIBRARY LUSTREAPI_INCLUDE_DIR)
+
+mark_as_advanced(LUSTREAPI_INCLUDE_DIR LUSTREAPI_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindMKL.cmake b/ecbuild/share/ecbuild/cmake/FindMKL.cmake
new file mode 100644
index 0000000..59eaa60
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindMKL.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find MKL
+# Once done this will define
+#
+#  MKL_FOUND         - system has Intel MKL
+#  MKL_INCLUDE_DIRS  - the MKL include directories
+#  MKL_LIBRARIES     - link these to use MKL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  MKLROOT           - root directory of the MKL installation
+#  MKL_PATH          - root directory of the MKL installation
+#  MKL_ROOT          - root directory of the MKL installation
+
+option( MKL_PARALLEL "if mkl shoudl be parallel" OFF )
+
+if( MKL_PARALLEL )
+
+  set( __mkl_lib_par  MKL_LIB_INTEL_THREAD )
+  set( __mkl_lib_name mkl_intel_thread )
+
+  find_package(Threads)
+
+else()
+
+  set( __mkl_lib_par MKL_LIB_SEQUENTIAL )
+  set( __mkl_lib_name mkl_sequential )
+
+endif()
+
+# Search with priority for MKLROOT, MKL_PATH and MKL_ROOT if set in CMake or env
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATHS ${MKLROOT} ${MKL_PATH} ${MKL_ROOT} ENV MKLROOT ENV MKL_PATH ENV MKL_ROOT
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+find_path(MKL_INCLUDE_DIR mkl.h
+          PATH_SUFFIXES include)
+
+if( MKL_INCLUDE_DIR ) # use include dir to find libs
+
+  set( MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} )
+
+  if( CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" )
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/intel64 ABSOLUTE )
+    set( __libsfx _lp64 )
+  else()
+    get_filename_component( MKL_LIB_PATH ${MKL_INCLUDE_DIR}/../lib/ia32 ABSOLUTE )
+    set( __libsfx "" )
+  endif()
+
+  find_library( MKL_LIB_INTEL         NAMES mkl_intel${__libsfx} PATHS ${MKL_LIB_PATH} )
+  find_library( ${__mkl_lib_par}      NAMES ${__mkl_lib_name} PATHS ${MKL_LIB_PATH} )
+  find_library( MKL_LIB_CORE          NAMES mkl_core PATHS ${MKL_LIB_PATH} )
+
+  if( MKL_PARALLEL )
+    find_library( MKL_LIB_IOMP5  NAMES iomp5 PATHS ${MKL_LIB_PATH} )
+  endif()
+
+  if( MKL_LIB_INTEL AND ${__mkl_lib_par} AND MKL_LIB_CORE )
+    set( MKL_LIBRARIES ${MKL_LIB_INTEL} ${${__mkl_lib_par}} ${MKL_LIB_CORE} ${MKL_LIB_IOMP5} ${CMAKE_THREAD_LIBS_INIT} )
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( MKL DEFAULT_MSG
+                                   MKL_LIBRARIES MKL_INCLUDE_DIRS )
+
+mark_as_advanced( MKL_INCLUDE_DIR MKL_LIB_LAPACK MKL_LIB_INTEL MKL_LIB_SEQUENTIAL MKL_LIB_CORE )
diff --git a/ecbuild/share/ecbuild/cmake/FindNAG.cmake b/ecbuild/share/ecbuild/cmake/FindNAG.cmake
new file mode 100644
index 0000000..883a375
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindNAG.cmake
@@ -0,0 +1,43 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find the NAG includes and library
+# This module defines
+#
+#  NAG_FOUND         - System has NAG
+#  NAG_INCLUDE_DIRS  - the NAG include directories
+#  NAG_LIBRARIES     - the libraries needed to use NAG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  NAG_DIR   - root folder of the NAG installation
+#  NAG_PATH  - root folder of the NAG installation
+
+find_path( NAG_INCLUDE_DIR nag_library.mod
+           PATHS ${NAG_PATH} ENV NAG_PATH
+                 ${NAG_DIR}  ENV NAG_DIR
+           PATH_SUFFIXES include
+           NO_DEFAULT_PATH )
+
+find_library( NAG_LIBRARY NAMES nag nag_nag
+              PATHS ${NAG_PATH} ENV NAG_PATH
+                    ${NAG_DIR}  ENV NAG_DIR
+              PATH_SUFFIXES lib lib64
+              NO_DEFAULT_PATH )
+
+set( NAG_LIBRARIES    ${NAG_LIBRARY} )
+set( NAG_INCLUDE_DIRS ${NAG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set NAG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args( NAG DEFAULT_MSG
+                                   NAG_LIBRARY NAG_INCLUDE_DIR )
+
+mark_as_advanced( NAG_INCLUDE_DIR NAG_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindNDBM.cmake b/ecbuild/share/ecbuild/cmake/FindNDBM.cmake
new file mode 100644
index 0000000..8cd350e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindNDBM.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find NetCDF
+# Once done this will define
+#  NDBM_FOUND - System has NetCDF
+#  NDBM_INCLUDE_DIRS - The NetCDF include directories
+#  NDBM_LIBRARIES - The libraries needed to use NetCDF
+#  NDBM_DEFINITIONS - Compiler switches required for using NetCDF
+
+if( DEFINED NDBM_PATH )
+	find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATHS ${NDBM_PATH} ${NDBM_PATH}/include PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+	find_library(NDBM_LIBRARY  NAMES ndbm dbm PATHS ${NDBM_PATH} ${NDBM_PATH}/lib     PATH_SUFFIXES ndbm NO_DEFAULT_PATH)
+endif()
+
+find_path(NDBM_INCLUDE_DIR NAMES ndbm.h   PATH_SUFFIXES ndbm )
+find_library( NDBM_LIBRARY NAMES ndbm dbm PATH_SUFFIXES ndbm )
+
+set( NDBM_LIBRARIES    ${NDBM_LIBRARY} )
+set( NDBM_INCLUDE_DIRS ${NDBM_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(NDBM  DEFAULT_MSG
+								  NDBM_LIBRARY NDBM_INCLUDE_DIR)
+
+mark_as_advanced(NDBM_INCLUDE_DIR NDBM_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindNetCDF.cmake b/ecbuild/share/ecbuild/cmake/FindNetCDF.cmake
new file mode 100644
index 0000000..69b88bd
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindNetCDF.cmake
@@ -0,0 +1,164 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF3 or NetCDF4 -- default is 4
+#
+# find_package( NetCDF <version> COMPONENTS C CXX Fortran )
+#
+# Input:
+#  * NETCDF_PATH    - user defined path where to search for the library first
+#  * NETCDF_DIR     - user defined path where to search for the library first
+#  * NETCDF_ROOT    - user defined path where to search for the library first
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_DEFINITIONS
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+# default is netcdf4
+if( NetCDF_FIND_VERSION STREQUAL "3" )
+  set( PREFER_NETCDF3 1 )
+endif()
+
+if( NOT PREFER_NETCDF3 )
+  set( PREFER_NETCDF4 1 )
+else()
+  set( PREFER_NETCDF4 0 )
+endif()
+mark_as_advanced( PREFER_NETCDF4 PREFER_NETCDF3 )
+
+set( NETCDF_FIND_REQUIRED   ${NetCDF_FIND_REQUIRED} )
+set( NETCDF_FIND_QUIETLY    ${NetCDF_FIND_QUIETLY} )
+set( NETCDF_FIND_COMPONENTS ${NetCDF_FIND_COMPONENTS} )
+
+list( APPEND NETCDF_FIND_COMPONENTS C )
+
+if( NETCDF_CXX )
+  ecbuild_debug( "FindNetCDF: also looking for C++ libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS CXX )
+endif()
+
+if( NETCDF_Fortran OR NETCDF_FORTRAN OR NETCDF_F90 )
+  ecbuild_debug( "FindNetCDF: also looking for Fortran libraries" )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "FORTRAN" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS F90 )
+endif()
+
+list (FIND NETCDF_FIND_COMPONENTS "F90" _index)
+if(${_index} GREATER -1)
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN )
+endif()
+
+list(FIND NETCDF_FIND_COMPONENTS "Fortran" _index)
+if(${_index} GREATER -1)
+  list( REMOVE_ITEM NETCDF_FIND_COMPONENTS Fortran )
+  list( APPEND NETCDF_FIND_COMPONENTS FORTRAN F90 )
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_FIND_COMPONENTS )
+ecbuild_debug( "FindNetCDF: looking for components ${NETCDF_FIND_COMPONENTS}" )
+
+### NetCDF4
+
+if( PREFER_NETCDF4 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF4" )
+
+  ## hdf5
+
+  # Note: Only the HDF5 C-library is required for NetCDF
+  #       ( even for Fortan and CXX bindings)
+  find_package( HDF5 COMPONENTS C QUIET )
+
+  ## netcdf4
+
+  # CONFIGURE the NETCDF_FIND_COMPONENTS variable
+
+  # Find NetCDF4
+
+  # message( "NETCDF CMAKE_PREFIX_PATH = [${CMAKE_PREFIX_PATH}]")
+  # ecbuild_debug_var( NETCDF_ROOT )
+  # ecbuild_debug_var( NETCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NETCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NETCDF_FIND_REQUIRED )
+  find_package( NetCDF4 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+  # ecbuild_debug_var( NETCDF4_FOUND )
+  # ecbuild_debug_var( NETCDF_FOUND )
+  # ecbuild_debug_var( NETCDF_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+
+  list( APPEND NETCDF_Fortran_LIBRARIES ${NETCDF_FORTRAN_LIBRARIES} ${NETCDF_F90_LIBRARIES} )
+  if( NETCDF_Fortran_LIBRARIES )
+    list( REMOVE_DUPLICATES NETCDF_Fortran_LIBRARIES )
+  endif()
+
+  # ecbuild_debug_var( NETCDF_Fortran_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_C_LIBRARIES )
+  # ecbuild_debug_var( NETCDF_CXX_LIBRARIES )
+
+
+  set_package_properties( NetCDF4 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF4 file format" )
+
+  if( NETCDF_FOUND AND HDF5_FOUND )
+    # list( APPEND NETCDF_DEFINITIONS  ${HDF5_DEFINITIONS} )
+    list( APPEND NETCDF_LIBRARIES    ${HDF5_HL_LIBRARIES} ${HDF5_LIBRARIES}  )
+    list( APPEND NETCDF_INCLUDE_DIRS ${HDF5_INCLUDE_DIRS} )
+  endif()
+
+  #ecbuild_debug_var( NETCDF_FOUND )
+  #ecbuild_debug_var( NETCDF_LIBRARIES )
+  #ecbuild_debug_var( NETCDF_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_FOUND )
+  #ecbuild_debug_var( HDF5_INCLUDE_DIRS )
+  #ecbuild_debug_var( HDF5_HL_LIBRARIES )
+  #ecbuild_debug_var( HDF5_LIBRARIES )
+
+endif()
+
+### NetCDF3
+
+if( PREFER_NETCDF3 )
+
+  ecbuild_debug( "FindNetCDF: looking for NetCDF3" )
+
+  # ecbuild_debug_var( NetCDF_FIND_COMPONENTS )
+  # ecbuild_debug_var( NetCDF_FIND_QUIETLY )
+  # ecbuild_debug_var( NetCDF_FIND_REQUIRED )
+
+  list(FIND NetCDF_FIND_COMPONENTS "CXX" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_CXX 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "Fortran" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "FORTRAN" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  list(FIND NetCDF_FIND_COMPONENTS "F90" _index)
+  if(${_index} GREATER -1)
+    set( NETCDF_Fortran 1 )
+  endif()
+
+  #message( "NETCDF CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}" )
+
+  find_package( NetCDF3 COMPONENTS ${NETCDF_FIND_COMPONENTS} )
+
+  set_package_properties( NetCDF3 PROPERTIES TYPE RECOMMENDED PURPOSE "support for NetCDF3 file format" )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindNetCDF3.cmake b/ecbuild/share/ecbuild/cmake/FindNetCDF3.cmake
new file mode 100644
index 0000000..1783a72
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindNetCDF3.cmake
@@ -0,0 +1,113 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find NetCDF
+#
+# Input:
+#  * NETCDF_PATH     - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_DIR      - user defined path where to search for the library first
+#                      (CMake or environment variable)
+#  * NETCDF_CXX      - search also for netcdf_c++ wrapper library
+#  * NETCDF_Fortran  - search also for netcdff wrapper library
+#
+# Output:
+#  NETCDF_FOUND - System has NetCDF
+#  NETCDF_INCLUDE_DIRS - The NetCDF include directories
+#  NETCDF_LIBRARIES - The libraries needed to use NetCDF
+
+### TODO: generalize this into a macro for all ecbuild
+
+if( DEFINED NETCDF_PATH )
+    list( APPEND _netcdf_incs ${NETCDF_PATH} ${NETCDF_PATH}/include )
+    list( APPEND _netcdf_libs ${NETCDF_PATH} ${NETCDF_PATH}/lib )
+endif()
+	
+if( DEFINED NETCDF_DIR )
+    list( APPEND _netcdf_incs ${NETCDF_DIR} ${NETCDF_DIR}/include )
+    list( APPEND _netcdf_libs ${NETCDF_DIR} ${NETCDF_DIR}/lib )
+endif()
+
+# Honour environment variables NETCDF_DIR, NETCDF_PATH
+list( APPEND _netcdf_incs ENV NETCDF_DIR ENV NETCDF_PATH )
+list( APPEND _netcdf_libs ENV NETCDF_DIR ENV NETCDF_PATH )
+
+###
+
+set( _inc_sfx netcdf include )
+set( _lib_sfx netcdf lib64 lib )
+
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH )
+find_path( NETCDF_INCLUDE_DIR  netcdf.h  PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  NO_DEFAULT_PATH )
+find_library( NETCDF_LIBRARY  netcdf  PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx}  )
+
+set( NETCDF_LIBRARIES    ${NETCDF_LIBRARY} )
+set( NETCDF_INCLUDE_DIRS ${NETCDF_INCLUDE_DIR} )
+
+mark_as_advanced(NETCDF_INCLUDE_DIR NETCDF_LIBRARY )
+
+list( APPEND NETCDF_REQUIRED_VARS NETCDF_LIBRARY NETCDF_INCLUDE_DIR )
+
+if( NETCDF_CXX )
+
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_CXX_INCLUDE_DIR netcdfcpp.h PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_cxx netcdf_c++ netcdf_c++ netcdf_c++4 )
+
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_CXX_LIBRARY NAMES ${_ncdf_cxx} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_CXX_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_CXX_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+    mark_as_advanced(NETCDF_CXX_INCLUDE_DIR NETCDF_CXX_LIBRARY )
+
+endif()
+
+if( NETCDF_Fortran )
+
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} NO_DEFAULT_PATH)
+    find_path( NETCDF_Fortran_INCLUDE_DIR netcdf.mod PATHS ${_netcdf_incs} PATH_SUFFIXES ${_inc_sfx} )
+
+    set( _ncdf_fortran netcdff )
+
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} NO_DEFAULT_PATH )
+    find_library( NETCDF_Fortran_LIBRARY NAMES ${_ncdf_fortran} PATHS ${_netcdf_libs} PATH_SUFFIXES ${_lib_sfx} )
+
+    list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_Fortran_INCLUDE_DIR} )
+    list( APPEND NETCDF_LIBRARIES    ${NETCDF_Fortran_LIBRARY} )
+
+    list( APPEND NETCDF_REQUIRED_VARS NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+    mark_as_advanced(NETCDF_Fortran_INCLUDE_DIR NETCDF_Fortran_LIBRARY )
+
+endif()
+
+list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+
+if( NETCDF_FIND_QUIETLY )
+  set( NETCDF3_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+endif()
+if( NETCDF_FIND_REQUIRED )
+  set( NETCDF3_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+endif()
+
+# Handle the QUIET and REQUIRED arguments and set NETCDF3_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF3  DEFAULT_MSG ${NETCDF_REQUIRED_VARS} )
+
+set( NETCDF_FOUND ${NETCDF3_FOUND} )
+
diff --git a/ecbuild/share/ecbuild/cmake/FindODB.cmake b/ecbuild/share/ecbuild/cmake/FindODB.cmake
new file mode 100644
index 0000000..7c35d6e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindODB.cmake
@@ -0,0 +1,53 @@
+# © Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB
+# Once done this will define
+#  ODB_FOUND - System has ODB
+#  ODB_INCLUDE_DIRS - The ODB include directories
+#  ODB_LIBRARIES - The libraries needed to use ODB
+
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/include/odbdump.h
+# /usr/local/apps/odb/CY37R3.001/pgf90/LP64/module/odb.mod
+
+# -lodb -lodbec -lifsaux -lmpi_serial -lodbdummy
+
+find_package( Dl ) # find the dynamic linker
+
+list( APPEND _odb_search_libs odb odbec ifsaux mpi_serial odbdummy  )
+
+if( DEFINED ODB_PATH )
+    find_path(ODB_INCLUDE_DIR odbdump.h PATHS  ${ODB_ROOT} ${ODB_ROOT}/include ${ODB_PATH} ${ODB_PATH}/include PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    find_path(ODB_MODULE_DIR odb.mod PATHS ${ODB_ROOT} ${ODB_ROOT}/module ${ODB_PATH} ${ODB_PATH}/module PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    foreach( _lib ${_odb_search_libs} )
+      find_library(ODB_LIBRARY_${_lib}  ${_lib} PATHS ${ODB_ROOT} ${ODB_ROOT}/lib ${ODB_PATH} ${ODB_PATH}/lib     PATH_SUFFIXES odb NO_DEFAULT_PATH)
+    endforeach()
+endif()
+
+find_path(ODB_INCLUDE_DIR odbdump.h PATH_SUFFIXES odb )
+find_path(ODB_MODULE_DIR odb.mod PATH_SUFFIXES odb )
+foreach( _lib ${_odb_search_libs} )
+  find_library( ODB_LIBRARY_${_lib} ${_lib} PATH_SUFFIXES odb )
+endforeach()
+
+foreach( _lib ${_odb_search_libs} )
+  list( APPEND ODB_LIB_LIST   ODB_LIBRARY_${_lib} )
+  list( APPEND ODB_LIBRARIES  ${ODB_LIBRARY_${_lib}} )
+  mark_as_advanced(${ODB_LIBRARY_${_lib}})
+endforeach()
+
+set( ODB_INCLUDE_DIRS ${ODB_INCLUDE_DIR} ${ODB_MODULE_DIR})
+mark_as_advanced(ODB_INCLUDE_DIR )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ODB  DEFAULT_MSG
+                                  ODB_INCLUDE_DIR ${ODB_LIB_LIST} )
+
diff --git a/ecbuild/share/ecbuild/cmake/FindOpenCL.cmake b/ecbuild/share/ecbuild/cmake/FindOpenCL.cmake
new file mode 100644
index 0000000..510a3a9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindOpenCL.cmake
@@ -0,0 +1,70 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find OpenCL
+# Once done this will define
+#
+#  OPENCL_FOUND           - system has OpenCL
+#  OPENCL_INCLUDE_DIRS    - the OpenCL include directory
+#  OPENCL_LIBRARIES       - link these to use OpenCL
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENCL_ROOT            - root folder of the OpenCL installation
+#  CUDA_TOOLKIT_ROOT_DIR  - root folder of the CUDA installation (ships OpenCL)
+#  CUDA_ROOT              - root folder of the CUDA installation (ships OpenCL)
+
+if(UNIX)
+
+  if(APPLE)
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS OpenCL/cl.h
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATH_SUFFIXES lib )
+
+  else()
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+              PATH_SUFFIXES include NO_DEFAULT_PATH)
+    find_path(OPENCL_INCLUDE_DIRS NAMES CL/cl.h CL/opencl.h
+              PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+              PATH_SUFFIXES include )
+
+    # Search with priority for OPENCL_ROOT if given as CMake or env var
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${OPENCL_ROOT} ENV OPENCL_ROOT
+                 PATH_SUFFIXES lib64 lib NO_DEFAULT_PATH)
+    find_library(OPENCL_LIBRARIES OpenCL
+                 PATHS ${CUDA_TOOLKIT_ROOT_DIR} ${CUDA_ROOT} /usr/local/cuda
+                 PATH_SUFFIXES lib64 lib )
+
+  endif()
+
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set OPENCL_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( OpenCL DEFAULT_MSG
+                                   OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS )
+
+mark_as_advanced( OPENCL_INCLUDE_DIRS OPENCL_LIBRARIES )
diff --git a/ecbuild/share/ecbuild/cmake/FindOpenJPEG.cmake b/ecbuild/share/ecbuild/cmake/FindOpenJPEG.cmake
new file mode 100644
index 0000000..66d976e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindOpenJPEG.cmake
@@ -0,0 +1,54 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find the OpenJPEG includes and library (version 1.5.x or 2.1.x)
+# This module defines
+#
+#  OPENJPEG_FOUND         - System has OpenJPEG
+#  OPENJPEG_INCLUDE_DIRS  - the OpenJPEG include directories
+#  OPENJPEG_LIBRARIES     - the libraries needed to use OpenJPEG
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  OPENJPEG_DIR   - root folder of the OpenJPEG installation
+#  OPENJPEG_PATH  - root folder of the OpenJPEG installation
+
+# Note: OpenJPEG has a version-specific subdirectory in the include
+# e.g. include/openjpeg-2.0 or include/openjpeg-2.1.
+# Only version 1.5.x and 2.1.x are supported.
+# The library name is different for 1.x (libopenjpeg) and 2.x (libopenjp2).
+
+set( _suff include include/openjpeg include/openjpeg-1.5 include/openjpeg-2.1 )
+find_path( OPENJPEG_INCLUDE_DIR openjpeg.h
+           PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                 ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+           PATH_SUFFIXES ${_suff}
+           NO_DEFAULT_PATH )
+find_path( OPENJPEG_INCLUDE_DIR  openjpeg.h
+           PATH_SUFFIXES ${_suff} )
+unset( _suff )
+
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATHS ${OPENJPEG_PATH} ENV OPENJPEG_PATH
+                    ${OPENJPEG_DIR}  ENV OPENJPEG_DIR
+              PATH_SUFFIXES lib lib/openjpeg
+              NO_DEFAULT_PATH )
+find_library( OPENJPEG_LIBRARY NAMES openjpeg openjp2
+              PATH_SUFFIXES lib lib/openjpeg )
+
+set( OPENJPEG_LIBRARIES    ${OPENJPEG_LIBRARY} )
+set( OPENJPEG_INCLUDE_DIRS ${OPENJPEG_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set OPENJPEG_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(OpenJPEG  DEFAULT_MSG
+                                  OPENJPEG_LIBRARY OPENJPEG_INCLUDE_DIR)
+
+mark_as_advanced( OPENJPEG_INCLUDE_DIR OPENJPEG_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindPGIFortran.cmake b/ecbuild/share/ecbuild/cmake/FindPGIFortran.cmake
new file mode 100644
index 0000000..5d33239
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindPGIFortran.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+# FORTRAN support
+
+# set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl  pgftnrtl nspgc pgc rt pgsse1 pgsse2 ) # init
+# set( PGIFORTRAN_SEARCH_LIBS pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf pgc pgf90 rt pgsse1 pgsse2 )             # mars client linux.2
+# set( PGIFORTRAN_SEARCH_LIBS pgftnrtl nspgc pgc rt pgsse1 pgsse2 )                                                    # mars client linux.3
+
+if( NOT DEFINED PGIFORTRAN_SEARCH_LIBS )
+	set( PGIFORTRAN_SEARCH_LIBS pgmp pgbind numa pgf90 pgf90_rpm1 pgf902 pgf90rtl pgftnrtl pghpf nspgc pgc pgf90 pgf902 pghpf_rpm1 pghpf2 pgsse1 pgsse2 ) # better ?                                                    #
+endif()
+
+set( pgi_fortran_all_libs_found 1 )
+
+foreach( pglib ${PGIFORTRAN_SEARCH_LIBS} )
+
+	find_library( ${pglib}_lib  ${pglib} PATHS ${PGI_PATH} PATH_SUFFIXES lib libso NO_DEFAULT_PATH )
+
+	find_library( ${pglib}_lib  ${pglib} HINTS /usr/local/apps/pgi/pgi-10.8/linux86-64/10.8 PATH PATH_SUFFIXES lib libso )
+
+    if( ${pglib}_lib )
+        list( APPEND PGIFORTRAN_LIBRARIES ${${pglib}_lib} )
+#	else()
+#		set( pgi_fortran_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+
+# Handle the QUIET and REQUIRED arguments and set PGIFORTRAN_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( PGIFortran DEFAULT_MSG pgi_fortran_all_libs_found PGIFORTRAN_LIBRARIES  )
+
+if( PGIFORTRAN_FOUND )
+	find_package( Realtime )
+endif()
+
+if( REALTIME_FOUND )
+	set( PGIFORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} ${RT_LIB} )
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindPango.cmake b/ecbuild/share/ecbuild/cmake/FindPango.cmake
new file mode 100644
index 0000000..f135864
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindPango.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find Pango
+
+# Output:
+#   PANGO_FOUND
+#   PANGO_LIBRARIES
+#   PANGO_INCLUDE_DIRS
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGO QUIET pango)
+
+ecbuild_debug_var( PC_LIBPANGO_FOUND )
+ecbuild_debug_var( PC_LIBPANGO_VERSION )
+ecbuild_debug_var( PC_LIBPANGO_LIBRARIES )
+ecbuild_debug_var( PC_LIBPANGO_INCLUDE_DIRS )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set PANGO_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( Pango DEFAULT_MSG PC_LIBPANGO_LIBRARIES PC_LIBPANGO_INCLUDE_DIRS )
+
+set( PANGO_VERSION ${PC_LIBPANGO_VERSION} )
+set( PANGO_LIBRARIES ${PC_LIBPANGO_LIBRARIES} )
+set( PANGO_INCLUDE_DIRS ${PC_LIBPANGO_INCLUDE_DIRS} )
diff --git a/ecbuild/share/ecbuild/cmake/FindPangoCairo.cmake b/ecbuild/share/ecbuild/cmake/FindPangoCairo.cmake
new file mode 100644
index 0000000..cb70737
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindPangoCairo.cmake
@@ -0,0 +1,96 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find PangoCairo
+
+# Output:
+#   PANGOCAIRO_FOUND
+#   PANGOCAIRO_LIBRARIES
+#   PANGOCAIRO_INCLUDE_DIRS
+
+
+find_package(PkgConfig)
+
+pkg_check_modules(PC_LIBPANGOCAIRO QUIET pangocairo)
+
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_FOUND )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_VERSION )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LIBRARIES )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_LDFLAGS_OTHER )
+#ecbuild_debug_var( PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+
+if(PC_LIBPANGOCAIRO_FOUND)
+
+    include(FindPackageHandleStandardArgs)
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo DEFAULT_MSG PC_LIBPANGOCAIRO_LIBRARIES PC_LIBPANGOCAIRO_INCLUDE_DIRS )
+    set( PANGOCAIRO_VERSION ${PC_LIBPANGOCAIRO_VERSION} )
+    set( PANGOCAIRO_LIBRARIES "${PC_LIBPANGOCAIRO_LDFLAGS} ${PC_LIBPANGOCAIRO_LDFLAGS_OTHER}" )
+    set( PANGOCAIRO_INCLUDE_DIRS ${PC_LIBPANGOCAIRO_INCLUDE_DIRS} )
+
+else()
+
+    # this is to get magics compiling on mac with macbrew
+
+    include(FindPackageHandleStandardArgs)
+
+    set(PANGO_VERSION 1.0)
+    set(GLIB_VERSION 2.0)
+
+    find_path( _PANGOCAIRO_INCLUDE_DIRS
+        NAMES pango/pangocairo.h
+        HINTS /usr/local/include PATH_SUFFIXES pango-${PANGO_VERSION})
+
+    find_path( _CAIRO_INCLUDE_DIRS
+        NAMES cairo.h
+        HINTS /usr/local/include PATH_SUFFIXES cairo)
+
+    find_path( _GLIB_INCLUDE_DIRS_1
+        NAMES glib.h
+        HINTS /usr/local/include PATH_SUFFIXES glib-${GLIB_VERSION})
+
+    find_path( _GLIB_INCLUDE_DIRS_2
+        NAMES glibconfig.h
+        HINTS /usr/local/lib/glib-${GLIB_VERSION} PATH_SUFFIXES include)
+
+
+    find_package(X11)
+
+    set(PANGOCAIRO_INCLUDE_DIRS
+        ${_PANGOCAIRO_INCLUDE_DIRS}
+        ${_CAIRO_INCLUDE_DIRS}
+        ${_GLIB_INCLUDE_DIRS_1}
+        ${_GLIB_INCLUDE_DIRS_2}
+	${X11_INCLUDE_DIR}
+    )
+
+    find_library( _PANGOCAIRO_LIBRARIES NAMES pangocairo pangocairo-${PANGO_VERSION})
+    find_library( _PANGO_LIBRARIES NAMES pango pango-${PANGO_VERSION})
+    find_library( _CAIRO_LIBRARIES NAMES cairo)
+    find_library( _GLIB_LIBRARIES NAMES glib-${GLIB_VERSION})
+
+    set(PANGOCAIRO_LIBRARIES
+        ${_PANGOCAIRO_LIBRARIES}
+        ${_PANGO_LIBRARIES}
+        ${_CAIRO_LIBRARIES}
+        ${_GLIB_LIBRARIES}
+	${X11_LIBRARIES}
+    )
+
+    # Handle the QUIET and REQUIRED arguments and set PANGOCAIRO_FOUND to TRUE
+    # if all listed variables are TRUE
+    # Note: capitalisation of the package name must be the same as in file name
+    find_package_handle_standard_args( PangoCairo  DEFAULT_MSG
+                                       PANGOCAIRO_LIBRARIES
+                                       PANGOCAIRO_INCLUDE_DIRS  )
+
+endif()
+
diff --git a/ecbuild/share/ecbuild/cmake/FindProj4.cmake b/ecbuild/share/ecbuild/cmake/FindProj4.cmake
new file mode 100644
index 0000000..a3560b2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindProj4.cmake
@@ -0,0 +1,69 @@
+# - Try to find the proj4 library
+# Once done this will define
+#
+# PROJ4_FOUND - system has proj4
+# PROJ4_INCLUDE_DIRS - the proj4 include directory
+# PROJ4_LIBRARIES - Link these to use proj4
+#
+# Define PROJ4_MIN_VERSION for which version desired.
+
+if( NOT PROJ4_PATH )
+    if ( NOT "$ENV{PROJ4_PATH}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_PATH}" )
+    elseif ( NOT "$ENV{PROJ4_DIR}" STREQUAL "" )
+        set( PROJ4_PATH "$ENV{PROJ4_DIR}" )
+    endif()
+endif()
+
+if( NOT PROJ4_PATH )
+
+    include(FindPkgConfig)
+
+#    if(Proj4_FIND_REQUIRED)
+#        set(_pkgconfig_REQUIRED "REQUIRED")
+#    else()
+#        set(_pkgconfig_REQUIRED "")
+#    endif()
+
+    if(PROJ4_MIN_VERSION)
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4>=${PROJ4_MIN_VERSION})
+    else()
+        pkg_check_modules(PKPROJ4 ${_pkgconfig_REQUIRED} QUIET proj4)
+    endif()
+
+    if( PKG_CONFIG_FOUND AND PKPROJ4_FOUND )
+
+        find_path(PROJ4_INCLUDE_DIR proj_api.h HINTS ${PKPROJ4_INCLUDEDIR} ${PKPROJ4_INCLUDE_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+        find_library(PROJ4_LIBRARY  proj       HINTS ${PKPROJ4_LIBDIR}     ${PKPROJ4_LIBRARY_DIRS} PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+    endif()
+
+#    ecbuild_debug_var( PKG_CONFIG_FOUND )
+#    ecbuild_debug_var( PKPROJ4_FOUND )
+#    ecbuild_debug_var( PROJ4_MIN_VERSION )
+
+endif()
+
+if( PROJ4_PATH )
+
+    find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS ${PROJ4_PATH} ${PROJ4_PATH}/include PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+    find_library(PROJ4_LIBRARY  NAMES proj       PATHS ${PROJ4_PATH} ${PROJ4_PATH}/lib     PATH_SUFFIXES proj4 NO_DEFAULT_PATH )
+
+endif()
+
+find_path(PROJ4_INCLUDE_DIR NAMES proj_api.h PATHS PATH_SUFFIXES proj4 )
+find_library( PROJ4_LIBRARY NAMES proj       PATHS PATH_SUFFIXES proj4 )
+
+
+# ecbuild_debug_var( PROJ4_INCLUDE_DIR )
+# ecbuild_debug_var( PROJ4_LIBRARY )
+
+# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Proj4  DEFAULT_MSG
+                                  PROJ4_LIBRARY PROJ4_INCLUDE_DIR)
+
+set( PROJ4_LIBRARIES    ${PROJ4_LIBRARY} )
+set( PROJ4_INCLUDE_DIRS ${PROJ4_INCLUDE_DIR} )
+
+mark_as_advanced( PROJ4_INCLUDE_DIR PROJ4_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindREADLINE.cmake b/ecbuild/share/ecbuild/cmake/FindREADLINE.cmake
new file mode 100644
index 0000000..a146c62
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindREADLINE.cmake
@@ -0,0 +1,87 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find READLINE
+# Once done this will define
+#  READLINE_FOUND - System has READLINE
+#  READLINE_INCLUDE_DIRS - The READLINE include directories
+#  READLINE_LIBRARIES - The libraries needed to use READLINE
+#  READLINE_DEFINITIONS - Compiler switches required for using READLINE
+
+if( DEFINED READLINE_PATH )
+    find_path(READLINE_INCLUDE_DIR readline/readline.h PATHS ${READLINE_PATH}/include NO_DEFAULT_PATH)
+    find_library(READLINE_LIBRARY  readline            PATHS ${READLINE_PATH}/lib     PATH_SUFFIXES readline NO_DEFAULT_PATH)
+endif()
+
+find_path(READLINE_INCLUDE_DIR readline/readline.h )
+find_library( READLINE_LIBRARY readline            PATH_SUFFIXES readline )
+
+# check what version we got
+cmake_push_check_state()
+
+  set( CMAKE_REQUIRED_LIBRARIES ${READLINE_LIBRARY} )
+  set( CMAKE_REQUIRED_INCLUDES  ${READLINE_INCLUDE_DIR} )
+
+  # sometimes the link might fail missing -ltermcap or -l(n)curses
+  # if we searched before for Curses, then lets try to use it
+  if(CURSES_FOUND)
+      list( APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARIES} )
+      list( APPEND CMAKE_REQUIRED_INCLUDES  ${CURSES_INCLUDE_DIR} )
+  endif()
+
+  ecbuild_check_cxx_source_return(
+     "#include <stdio.h>
+      #include <readline/readline.h>
+      #include <iostream>
+      int main() {
+          std::cout << rl_library_version << std::flush;
+          return 0;
+     }"
+     VAR readline_version
+     OUTPUT __readline_version_out )
+
+cmake_pop_check_state()
+
+# ecbuild_debug_var( readline_version )
+# ecbuild_debug_var( __readline_version_out )
+
+set( __readline_fail 0 )
+if( __readline_version_out )
+
+    if( "${__readline_version_out}" MATCHES "^EditLine" )
+      message( STATUS "Found EditLine instead of Readline at '${READLINE_INCLUDE_DIR}'" )
+      if( READLINE_WRAPPER_OK )
+        set( READLINE_WRAPPER      "EditLine" )
+        set( __readline_fail 0 )
+      else()
+        message( STATUS "Readline wrapper not accepted -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+        set( __readline_fail 1 )
+      endif()
+    endif()
+
+else()
+    message( STATUS "Readline test run failed -- rejecting Readline at '${READLINE_INCLUDE_DIR}'" )
+    set( __readline_fail 1 )
+endif()
+
+if( __readline_fail )
+    set( READLINE_LIBRARY      READLINE_LIBRARY-NOTFOUND )
+    set( READLINE_INCLUDE_DIR  READLINE_INCLUDE_DIR-NOTFOUND )
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(READLINE  DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR)
+
+if( READLINE_FOUND )
+    set( READLINE_VERSION      ${__readline_version_out} )
+    set( READLINE_LIBRARIES    ${READLINE_LIBRARY} )
+    set( READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR} )
+endif()
+
+mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindRPCGEN.cmake b/ecbuild/share/ecbuild/cmake/FindRPCGEN.cmake
new file mode 100644
index 0000000..42ed90c
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindRPCGEN.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RPCGEN_FOUND           = prcgen was found
+# RPCGEN_EXECUTABLE      = the executable rpcgen
+
+if( DEFINED RPCGEN_PATH )
+    find_program( RPCGEN_EXECUTABLE NAMES rpcgen PATHS ${RPCGEN_PATH} PATH_SUFFIXES bin NO_DEFAULT_PATH )
+endif()
+find_program( RPCGEN_EXECUTABLE NAMES rpcgen )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args( RPCGEN  DEFAULT_MSG RPCGEN_EXECUTABLE )
diff --git a/ecbuild/share/ecbuild/cmake/FindRealtime.cmake b/ecbuild/share/ecbuild/cmake/FindRealtime.cmake
new file mode 100644
index 0000000..78fa4c5
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindRealtime.cmake
@@ -0,0 +1,24 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#Sets:
+# RT_LIB      = the library to link against
+
+if( DEFINED REALTIME_PATH )
+    find_library(RT_LIB rt PATHS ${REALTIME_PATH}/lib NO_DEFAULT_PATH )
+endif()
+
+find_library( RT_LIB rt )
+
+mark_as_advanced( RT_LIB )
+
+include(FindPackageHandleStandardArgs)
+# Handle the QUIET and REQUIRED arguments and set REALTIME_FOUND to TRUE
+# if all listed variables are TRUE
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(Realtime  DEFAULT_MSG RT_LIB )
diff --git a/ecbuild/share/ecbuild/cmake/FindSZip.cmake b/ecbuild/share/ecbuild/cmake/FindSZip.cmake
new file mode 100644
index 0000000..f005546
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindSZip.cmake
@@ -0,0 +1,30 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find SZip
+# Once done this will define
+#  SZIP_FOUND - System has SZip
+#  SZIP_INCLUDE_DIRS - The SZip include directories
+#  SZIP_LIBRARIES - The libraries needed to use SZip
+
+if( DEFINED SZIP_PATH )
+    find_path( SZIP_INCLUDE_DIR szlib.h       PATHS ${SZIP_PATH}/include PATH_SUFFIXES szip NO_DEFAULT_PATH )
+    find_library( SZIP_LIBRARY  NAMES szip sz PATHS ${SZIP_PATH}/lib     PATH_SUFFIXES szip NO_DEFAULT_PATH )
+endif()
+
+find_path( SZIP_INCLUDE_DIR szlib.h PATH_SUFFIXES szip )
+find_library( SZIP_LIBRARY NAMES szip sz  PATH_SUFFIXES szip )
+
+set( SZIP_LIBRARIES    ${SZIP_LIBRARY} )
+set( SZIP_INCLUDE_DIRS ${SZIP_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(SZip  DEFAULT_MSG SZIP_LIBRARY SZIP_INCLUDE_DIR)
+
+mark_as_advanced(SZIP_INCLUDE_DIR SZIP_LIBRARY )
diff --git a/ecbuild/share/ecbuild/cmake/FindTrilinos.cmake b/ecbuild/share/ecbuild/cmake/FindTrilinos.cmake
new file mode 100644
index 0000000..b1584f8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindTrilinos.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# Try to find the Trilinos library
+#
+# Needs environmental variables
+#   TRILINOS_PATH
+# Sets
+#   TRILINOS_INCLUDE_DIRS
+#   TRILINOS_LIBRARIES
+#   TRILINOS_FOUND
+
+# Try to find Trilinos using Trilinos recommendations
+
+if( DEFINED $ENV{TRILINOS_PATH} )
+    find_package(Trilinos PATHS $ENV{TRILINOS_PATH}/lib/cmake/Trilinos $ENV{TRILINOS_PATH}/include )
+endif()
+
+if( TRILINOS_PATH )
+    find_package(Trilinos PATHS ${TRILINOS_PATH}/lib/cmake/Trilinos ${TRILINOS_PATH}/include )
+endif()
+
+if( Trilinos_FOUND )
+
+        set( TRILINOS_INCLUDE_DIRS "" )
+
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_INCLUDE_DIRS} )
+        list( APPEND TRILINOS_INCLUDE_DIRS ${Trilinos_TPL_INCLUDE_DIRS} )
+
+        foreach( test_lib ${Trilinos_LIBRARIES} )
+          if(NOT ${test_lib} STREQUAL "pytrilinos")
+            find_library( ${test_lib}_lib ${test_lib} PATHS  ${Trilinos_LIBRARY_DIRS}  NO_DEFAULT_PATH)
+            find_library( ${test_lib}_lib ${test_lib})
+            mark_as_advanced( ${test_lib}_lib )
+            list( APPEND TRILINOS_LIBRARIES ${${test_lib}_lib} )
+          endif()
+        endforeach()
+
+        list( APPEND TRILINOS_LIBRARIES ${Trilinos_TPL_LIBRARIES} )
+
+    set( TRILINOS_FOUND TRUE )
+	set( TRILINOS_VERSION ${Trilinos_VERSION} )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/FindViennaCL.cmake b/ecbuild/share/ecbuild/cmake/FindViennaCL.cmake
new file mode 100644
index 0000000..8f442c9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindViennaCL.cmake
@@ -0,0 +1,39 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# - Try to find ViennaCL
+# Once done this will define
+#
+#  VIENNACL_FOUND         - system has ViennaCL
+#  VIENNACL_INCLUDE_DIRS  - the ViennaCL include directories
+#
+# The following paths will be searched with priority if set in CMake or env
+#
+#  VIENNACL_PATH          - prefix path of the ViennaCL installation
+#
+# ViennaCL is header only, so there are no libraries to be found
+
+# Search with priority for VIENNACL_PATH if given as CMake or env var
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATHS ${VIENNACL_PATH} ENV VIENNACL_PATH
+          PATH_SUFFIXES include NO_DEFAULT_PATH)
+
+find_path(VIENNACL_INCLUDE_DIR viennacl/version.hpp
+          PATH_SUFFIXES include )
+
+set( VIENNACL_INCLUDE_DIRS ${VIENNACL_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+
+# handle the QUIET and REQUIRED arguments and set VIENNACL_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args(ViennaCL  DEFAULT_MSG
+                                  VIENNACL_INCLUDE_DIR)
+
+mark_as_advanced(VIENNACL_INCLUDE_DIRS)
diff --git a/ecbuild/share/ecbuild/cmake/FindXLFortranLibs.cmake b/ecbuild/share/ecbuild/cmake/FindXLFortranLibs.cmake
new file mode 100644
index 0000000..80f2923
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindXLFortranLibs.cmake
@@ -0,0 +1,51 @@
+# © Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+
+list( APPEND xl_libs xlf90 xlopt xlf xlsmp )
+
+set( xlf_all_libs_found 1 )
+
+foreach( lib ${xl_libs} )
+
+	find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+	find_library( ${lib}_lib  ${lib} )
+
+	if( ${lib}_lib )
+        list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+	else()
+		set( xlf_all_libs_found 0 )
+    endif()
+
+endforeach()
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIET and REQUIRED arguments and set XLFORTRANLIBS_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( XLFortranLibs DEFAULT_MSG xlf_all_libs_found XLFORTRAN_LIBRARIES  )
+
+# HACK for support libraries
+
+if(  LIBXLFORTRAN_FOUND )
+	list( APPEND xl_extra_libs pthreads m essl )
+	foreach( lib ${xl_extra_libs} )
+
+		find_library( ${lib}_lib  ${lib} PATHS ${XLF_PATH} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH )
+
+		find_library( ${lib}_lib  ${lib} )
+
+		if( ${lib}_lib )
+			list( APPEND XLFORTRAN_LIBRARIES ${${lib}_lib} )
+		endif()
+
+	endforeach()
+endif()
+
diff --git a/ecbuild/share/ecbuild/cmake/FindYACC.cmake b/ecbuild/share/ecbuild/cmake/FindYACC.cmake
new file mode 100644
index 0000000..3eb3b5c
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/FindYACC.cmake
@@ -0,0 +1,157 @@
+# - Find yacc executable and provides macros to generate custom build rules
+# The module defines the following variables:
+#
+#  YACC_EXECUTABLE - path to the yacc program
+#  YACC_FOUND - true if the program was found
+#
+# The minimum required version of yacc can be specified using the
+# standard CMake syntax, e.g. find_package(YACC 2.1.3)
+#
+# If yacc is found, the module defines the macros:
+#  YACC_TARGET(<Name> <YaccInput> <CodeOutput> [VERBOSE <file>]
+#              [COMPILE_FLAGS <string>])
+# which will create  a custom rule to generate  a parser. <YaccInput> is
+# the path to  a yacc file. <CodeOutput> is the name  of the source file
+# generated by yacc.  A header file is also  be generated, and contains
+# the  token  list.  If  COMPILE_FLAGS  option is  specified,  the  next
+# parameter is  added in the yacc  command line.  if  VERBOSE option is
+# specified, <file> is created  and contains verbose descriptions of the
+# grammar and parser. The macro defines a set of variables:
+#  YACC_${Name}_DEFINED - true is the macro ran successfully
+#  YACC_${Name}_INPUT - The input source file, an alias for <YaccInput>
+#  YACC_${Name}_OUTPUT_SOURCE - The source file generated by yacc
+#  YACC_${Name}_OUTPUT_HEADER - The header file generated by yacc
+#  YACC_${Name}_OUTPUTS - The sources files generated by yacc
+#  YACC_${Name}_COMPILE_FLAGS - Options used in the yacc command line
+#
+#  ====================================================================
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+# Copyright 2006 Tristan Carel
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# This file is based on the FindFLEX CMake macro, and adapted by ECMWF
+
+#=============================================================================
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+FIND_PROGRAM(YACC_EXECUTABLE yacc DOC "path to the yacc/yacc executable")
+MARK_AS_ADVANCED(YACC_EXECUTABLE)
+
+IF(YACC_EXECUTABLE)
+  # the yacc commands should be executed with the C locale, otherwise
+  # the message (which are parsed) may be translated
+  SET(_Yacc_SAVED_LC_ALL "$ENV{LC_ALL}")
+  SET(ENV{LC_ALL} C)
+
+  SET(ENV{LC_ALL} ${_Yacc_SAVED_LC_ALL})
+
+  # internal macro
+  MACRO(YACC_TARGET_option_verbose Name YaccOutput filename)
+    LIST(APPEND YACC_TARGET_cmdopt "--verbose")
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_path "${YaccOutput}" PATH)
+    GET_FILENAME_COMPONENT(YACC_TARGET_output_name "${YaccOutput}" NAME_WE)
+    ADD_CUSTOM_COMMAND(OUTPUT ${filename}
+      COMMAND ${CMAKE_COMMAND}
+      ARGS -E copy
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      "${filename}"
+      DEPENDS
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output"
+      COMMENT "[YACC][${Name}] Copying yacc verbose table to ${filename}"
+      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+    SET(YACC_${Name}_VERBOSE_FILE ${filename})
+    LIST(APPEND YACC_TARGET_extraoutputs
+      "${YACC_TARGET_output_path}/${YACC_TARGET_output_name}.output")
+  ENDMACRO(YACC_TARGET_option_verbose)
+
+  # internal macro
+  MACRO(YACC_TARGET_option_extraopts Options)
+    SET(YACC_TARGET_extraopts "${Options}")
+    SEPARATE_ARGUMENTS(YACC_TARGET_extraopts)
+    LIST(APPEND YACC_TARGET_cmdopt ${YACC_TARGET_extraopts})
+  ENDMACRO(YACC_TARGET_option_extraopts)
+
+  #============================================================
+  # YACC_TARGET (public macro)
+  #============================================================
+  #
+  MACRO(YACC_TARGET Name YaccInput YaccOutput)
+    SET(YACC_TARGET_output_header "")
+    SET(YACC_TARGET_cmdopt "")
+    SET(YACC_TARGET_outputs "${YaccOutput}")
+    IF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+      MESSAGE(SEND_ERROR "Usage")
+    ELSE()
+      # Parsing parameters
+      IF(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5)
+        IF("${ARGV3}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV4}")
+        ENDIF()
+        IF("${ARGV3}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV4}")
+        ENDIF()
+      ENDIF()
+
+      IF(${ARGC} EQUAL 7)
+        IF("${ARGV5}" STREQUAL "VERBOSE")
+          YACC_TARGET_option_verbose(${Name} ${YaccOutput} "${ARGV6}")
+        ENDIF()
+
+        IF("${ARGV5}" STREQUAL "COMPILE_FLAGS")
+          YACC_TARGET_option_extraopts("${ARGV6}")
+        ENDIF()
+      ENDIF()
+
+      # Header's name generated by yacc (see option -d)
+      LIST(APPEND YACC_TARGET_cmdopt "-d")
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${ARGV2}")
+      STRING(REPLACE "c" "h" _fileext ${_fileext})
+      STRING(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}"
+          YACC_${Name}_OUTPUT_HEADER "${ARGV2}")
+      LIST(APPEND YACC_TARGET_outputs "${YACC_${Name}_OUTPUT_HEADER}")
+
+#       message ( STATUS "${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}" )
+#       message ( STATUS "${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}" )
+
+      ADD_CUSTOM_COMMAND(OUTPUT ${YACC_TARGET_outputs} ${YACC_TARGET_extraoutputs}
+        COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${ARGV1} ${CMAKE_CURRENT_BINARY_DIR}
+        COMMAND ${YACC_EXECUTABLE} ${YACC_TARGET_cmdopt} ${CMAKE_CURRENT_BINARY_DIR}/${ARGV1}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.h ${YACC_${Name}_OUTPUT_HEADER}
+        COMMAND ${CMAKE_COMMAND} -E rename ${CMAKE_CURRENT_BINARY_DIR}/y.tab.c ${ARGV2}
+        DEPENDS ${ARGV1}
+        COMMENT "[YACC][${Name}] Building parser with yacc"
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+      # define target variables
+      SET(YACC_${Name}_DEFINED TRUE)
+      SET(YACC_${Name}_INPUT ${ARGV1})
+      SET(YACC_${Name}_OUTPUTS ${YACC_TARGET_outputs})
+      SET(YACC_${Name}_COMPILE_FLAGS ${YACC_TARGET_cmdopt})
+      SET(YACC_${Name}_OUTPUT_SOURCE "${YaccOutput}")
+
+    ENDIF(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7)
+  ENDMACRO(YACC_TARGET)
+  #
+  #============================================================
+
+ENDIF(YACC_EXECUTABLE)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(YACC REQUIRED_VARS  YACC_EXECUTABLE )
+
+# FindYACC.cmake ends here
diff --git a/ecbuild/share/ecbuild/cmake/Findgrib_api.cmake b/ecbuild/share/ecbuild/cmake/Findgrib_api.cmake
new file mode 100644
index 0000000..c6f6421
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/Findgrib_api.cmake
@@ -0,0 +1,133 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find GRIB_API
+# Once done this will define
+#  GRIB_API_FOUND - System has GRIB_API
+#  GRIB_API_INCLUDE_DIRS - The GRIB_API include directories
+#  GRIB_API_LIBRARIES - The libraries needed to use GRIB_API
+#  GRIB_API_DEFINITIONS - Compiler switches required for using GRIB_API
+
+option( NO_GRIB_API_BINARIES "skip trying to find grib_api installed binaries" OFF )
+option( GRIB_API_PNG "use png with grib_api" ON )
+option( GRIB_API_JPG "use jpg with grib_api" ON )
+
+if( NOT grib_api_FOUND AND NOT NO_GRIB_API_BINARIES )
+
+    if( GRIB_API_JPG ) # jpeg support
+
+        find_package( JPEG     QUIET ) # grib_api might be a static .a library in which
+
+        if( NOT "$ENV{JASPER_PATH}" STREQUAL "" )
+            list( APPEND CMAKE_PREFIX_PATH "$ENV{JASPER_PATH}" )
+        endif()
+        find_package( Jasper   QUIET ) # case we don't know if which jpeg library was used
+
+        find_package( OpenJPEG QUIET ) # so we try to find all jpeg libs and link to them
+
+        if(JPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JPEG_LIBRARIES} )
+        endif()
+        if(JASPER_FOUND)
+            list( APPEND _grib_api_jpg_incs ${JASPER_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${JASPER_LIBRARIES} )
+        endif()
+        if(OPENJPEG_FOUND)
+            list( APPEND _grib_api_jpg_incs ${OPENJPEG_INCLUDE_DIR} )
+            list( APPEND _grib_api_jpg_libs ${OPENJPEG_LIBRARIES} )
+        endif()
+
+    endif()
+
+    if( GRIB_API_PNG ) # png support
+
+        find_package(PNG)
+
+        if( DEFINED PNG_PNG_INCLUDE_DIR AND NOT DEFINED PNG_INCLUDE_DIRS )
+          set( PNG_INCLUDE_DIRS ${PNG_PNG_INCLUDE_DIR}  CACHE INTERNAL "PNG include dirs" )
+        endif()
+        if( DEFINED PNG_LIBRARY AND NOT DEFINED PNG_LIBRARIES )
+          set( PNG_LIBRARIES ${PNG_LIBRARY} CACHE INTERNAL "PNG libraries" )
+        endif()
+
+        if(PNG_FOUND)
+            list( APPEND _grib_api_png_defs ${PNG_DEFINITIONS} )
+            list( APPEND _grib_api_png_incs ${PNG_INCLUDE_DIRS} )
+            list( APPEND _grib_api_png_libs ${PNG_LIBRARIES} )
+        endif()
+
+    endif()
+
+	# The grib_api on macos that comes with 'port' is linked against ghostscript
+	if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+		find_library(GS_LIBRARIES NAMES gs)
+		if( GS_LIBRARIES )
+			list( APPEND GRIB_API_LIBRARIES ${GS_LIBRARIES} )
+		endif()
+	endif()
+
+    # find external grib_api
+
+    if( NOT DEFINED GRIB_API_PATH AND NOT "$ENV{GRIB_API_PATH}" STREQUAL "" )
+        list( APPEND GRIB_API_PATH "$ENV{GRIB_API_PATH}" )
+    endif()
+
+    if( DEFINED GRIB_API_PATH )
+        find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/include PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIBRARY  NAMES grib_api   PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F90  NAMES grib_api_f90 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_library(GRIB_API_LIB_F77  NAMES grib_api_f77 PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/lib     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+        find_program(GRIB_API_INFO     NAMES grib_info  PATHS ${GRIB_API_PATH} ${GRIB_API_PATH}/bin     PATH_SUFFIXES grib_api  NO_DEFAULT_PATH)
+    endif()
+
+    find_path(GRIB_API_INCLUDE_DIR NAMES grib_api.h PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIBRARY NAMES grib_api   PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F90 NAMES grib_api_f90 PATHS PATH_SUFFIXES grib_api )
+    find_library( GRIB_API_LIB_F77 NAMES grib_api_f77 PATHS PATH_SUFFIXES grib_api )
+    find_program(GRIB_API_INFO     NAMES grib_info  PATHS PATH_SUFFIXES grib_api )
+
+    list( APPEND GRIB_API_LIBRARIES    ${GRIB_API_LIBRARY} ${GRIB_API_LIB_F90} ${GRIB_API_LIB_F77} )
+    set( GRIB_API_INCLUDE_DIRS ${GRIB_API_INCLUDE_DIR} )
+
+    if( GRIB_API_INFO )
+
+        execute_process( COMMAND ${GRIB_API_INFO} -v  OUTPUT_VARIABLE _grib_info_out ERROR_VARIABLE _grib_info_err OUTPUT_STRIP_TRAILING_WHITESPACE )
+
+        # ecbuild_debug_var( _grib_info_out )
+
+        string( REPLACE "." " " _version_list ${_grib_info_out} ) # dots to spaces
+        separate_arguments( _version_list )
+
+        list( GET _version_list 0 GRIB_API_MAJOR_VERSION )
+        list( GET _version_list 1 GRIB_API_MINOR_VERSION )
+        list( GET _version_list 2 GRIB_API_PATCH_VERSION )
+
+        set( GRIB_API_VERSION     "${GRIB_API_MAJOR_VERSION}.${GRIB_API_MINOR_VERSION}.${GRIB_API_PATCH_VERSION}" )
+        set( GRIB_API_VERSION_STR "${_grib_info_out}" )
+
+        set( grib_api_VERSION     "${GRIB_API_VERSION}" )
+        set( grib_api_VERSION_STR "${GRIB_API_VERSION_STR}" )
+
+    endif()
+
+    include(FindPackageHandleStandardArgs)
+
+    # handle the QUIETLY and REQUIRED arguments and set GRIB_API_FOUND to TRUE
+    find_package_handle_standard_args( grib_api DEFAULT_MSG
+                                       GRIB_API_LIBRARY GRIB_API_INCLUDE_DIR GRIB_API_INFO )
+
+    mark_as_advanced( GRIB_API_INCLUDE_DIR GRIB_API_LIBRARY GRIB_API_INFO )
+
+    list( APPEND GRIB_API_DEFINITIONS  ${_grib_api_jpg_defs} ${_grib_api_png_defs} )
+    list( APPEND GRIB_API_INCLUDE_DIRS ${_grib_api_jpg_incs} ${_grib_api_png_incs} )
+	list( APPEND GRIB_API_LIBRARIES    ${_grib_api_jpg_libs} ${_grib_api_png_libs} )
+
+    set( grib_api_FOUND ${GRIB_API_FOUND} )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/Findodb_api.cmake b/ecbuild/share/ecbuild/cmake/Findodb_api.cmake
new file mode 100644
index 0000000..00f4152
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/Findodb_api.cmake
@@ -0,0 +1,97 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT odb_api_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED ODB_API_PATH AND NOT "$ENV{ODB_API_PATH}" STREQUAL "" )
+        list( APPEND ODB_API_PATH "$ENV{ODB_API_PATH}" )
+    endif()
+
+    if( DEFINED ODB_API_PATH )
+
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS ${ODB_API_PATH} ${ODB_API_PATH}/include
+                   PATH_SUFFIXES odb_api
+                   NO_DEFAULT_PATH )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS ${ODB_API_PATH} ${ODB_API_PATH}/lib
+                      PATH_SUFFIXES odb_api
+                      NO_DEFAULT_PATH )
+    endif()
+    
+        find_path( ODB_API_INCLUDE_DIR
+                   NAMES odb_api_config.h
+                   PATHS
+                   PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_LIBRARY NAMES Odb
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+
+    # get the version
+
+    if( ODB_API_INCLUDE_DIR )
+
+        set(_odb_api_VERSION_REGEX "([0-9]+)")
+
+        foreach( v MAJOR MINOR PATCH )
+
+          file(STRINGS "${ODB_API_INCLUDE_DIR}/odb_api_config.h" _odb_api_${v}_VERSION_CONTENTS REGEX "#define ODB_API_${v}_VERSION ")
+
+          if( "${_odb_api_${v}_VERSION_CONTENTS}" MATCHES ".*#define ODB_API_${v}_VERSION ${_odb_api_VERSION_REGEX}.*")
+
+              set( ODB_API_${v}_VERSION "${CMAKE_MATCH_1}" )
+
+          endif()
+
+        endforeach()
+
+    endif()
+
+    set( ODB_API_VERSION     "${ODB_API_MAJOR_VERSION}.${ODB_API_MINOR_VERSION}.${ODB_API_PATCH_VERSION}" )
+    set( ODB_API_VERSION_STR "${_odb_info_out}" )
+
+    set( odb_api_VERSION     "${ODB_API_VERSION}" )
+    set( odb_api_VERSION_STR "${ODB_API_VERSION_STR}" )
+
+    # handle the QUIETLY and REQUIRED arguments and set ODB_API_FOUND to TRUE
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( odb_api DEFAULT_MSG
+                                       ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY ODB_API_INCLUDE_DIR )
+    
+    set( ODB_API_LIBRARIES    ${ODB_API_LIBRARY} ${ODB_API_ECLIB_LIBRARY} )
+    set( ODB_API_INCLUDE_DIRS ${ODB_API_INCLUDE_DIR} )
+
+    mark_as_advanced( ODB_API_INCLUDE_DIR ODB_API_LIBRARY ODB_API_ECLIB_LIBRARY )
+    
+    set( odb_api_FOUND ${ODB_API_FOUND} )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/Findspot.cmake b/ecbuild/share/ecbuild/cmake/Findspot.cmake
new file mode 100644
index 0000000..d932bfe
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/Findspot.cmake
@@ -0,0 +1,65 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# - Try to find ODB_API
+# Once done this will define
+#  ODB_API_FOUND - System has ODB_API
+#  ODB_API_INCLUDE_DIRS - The ODB_API include directories
+#  ODB_API_LIBRARIES - The libraries needed to use ODB_API
+#  ODB_API_DEFINITIONS - Compiler switches required for using ODB_API
+
+if( NOT spot_FOUND )
+
+    # find external odb_api
+
+    if( NOT DEFINED SPOT_PATH AND NOT "$ENV{SPOT_PATH}" STREQUAL "" )
+        list( APPEND SPOT_PATH "$ENV{SPOT_PATH}" )
+    endif()
+
+    if( DEFINED SPOT_PATH )
+
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS ${SPOT_PATH} ${SPOT_PATH}/include
+                   NO_DEFAULT_PATH )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS ${SPOT_PATH} ${SPOT_PATH}/lib
+                      NO_DEFAULT_PATH )
+
+    endif()
+    
+        find_path( SPOT_INCLUDE_DIR
+                   NAMES spot_database.h
+                   PATHS
+                   PATH_SUFFIXES spot )
+
+        find_library( SPOT_LIBRARY NAMES spot_database
+                      PATHS
+                      PATH_SUFFIXES odb_api )
+
+        find_library( ODB_API_ECLIB_LIBRARY NAMES Ec
+                      PATHS
+                      PATH_SUFFIXES spot )
+
+
+    # get the version
+
+    include(FindPackageHandleStandardArgs)
+
+    find_package_handle_standard_args( spot DEFAULT_MSG
+                                       SPOT_LIBRARY SPOT_INCLUDE_DIR )
+    
+    set( SPOT_LIBRARIES    ${SPOT_LIBRARY} )
+    set( SPOT_INCLUDE_DIRS ${SPOT_INCLUDE_DIR} )
+
+    mark_as_advanced( SPOT_INCLUDE_DIR SPOT_LIBRARY )
+    
+    set( spot_FOUND ${SPOT_FOUND} )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/VERSION.cmake b/ecbuild/share/ecbuild/cmake/VERSION.cmake
new file mode 100644
index 0000000..a95e4a9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/VERSION.cmake
@@ -0,0 +1,7 @@
+set( ECBUILD_MAJOR_VERSION "2" )
+set( ECBUILD_MINOR_VERSION "7" )
+set( ECBUILD_PATCH_VERSION "0" )
+
+set( ECBUILD_VERSION_STR  "2.7.0" )
+
+set( ECBUILD_MACRO_VERSION "${ECBUILD_VERSION_STR}" )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_C.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_C.cmake
new file mode 100644
index 0000000..51cf72b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_CXX.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_CXX.cmake
new file mode 100644
index 0000000..7f6524e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Clang_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"   CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"   CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"         CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"         CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"         CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_C.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_C.cmake
new file mode 100644
index 0000000..f9b6e4b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C flags"  FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C flags"               FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug Cflags"                     FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_CXX.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_CXX.cmake
new file mode 100644
index 0000000..fdc4749
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"        CACHE STRING "Release C++ flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -hfp1 -Gfast -DNDEBUG"                     CACHE STRING "Release-with-debug-info C++ flags" FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -hfp1 -G2"                                 CACHE STRING "Production C++ flags"              FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -hfp1 -G2 -hflex_mp=conservative -DNDEBUG" CACHE STRING "Bit-reproducible C++ flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -G0"                                       CACHE STRING "Debug CXX flags"                   FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake
new file mode 100644
index 0000000..8575bba
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Cray_Fortran.cmake
@@ -0,0 +1,15 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# -emf activates .mods and uses lower case
+# -rmoid produces a listing file
+set( CMAKE_Fortran_FLAGS_RELEASE        "-emf -rmoid -O3 -hfp3 -hscalar3 -hvector3 -DNDEBUG"                    CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-emf -rmoid -O2 -hfp1 -Gfast -DNDEBUG"                                 CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-emf -rmoid -O2 -hfp1 -G2"                                             CACHE STRING "Production Fortran flags"              FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-emf -rmoid -O2 -hfp1 -G2 -hflex_mp=conservative -hadd_paren -DNDEBUG" CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-emf -rmoid -O0 -G0"                                                   CACHE STRING "Debug Fortran flags"                   FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_C.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_C.cmake
new file mode 100644
index 0000000..288fc70
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_C.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C compiler flags for Release builds"          FORCE )
+set( CMAKE_C_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C compiler flags for Debug builds"            FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C compiler flags for Production builds."      FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_CXX.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_CXX.cmake
new file mode 100644
index 0000000..1a01e2d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_CXX.cmake
@@ -0,0 +1,18 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"                       CACHE STRING "C++ compiler flags for Release builds"          FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG" CACHE STRING "C++ compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g"                             CACHE STRING "C++ compiler flags for Debug builds"            FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O2 -g"                             CACHE STRING "C++ compiler flags for Production builds."      FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"                             CACHE STRING "C++ compiler flags for RelWithDebInfo builds."  FORCE )
+
+# NOTE: gcc does not guarrante that -O3 performs better than -O2
+#       -- it can perform worse due to assembly code bloating.
+#   Moreover for gcc 4.1.2 we found that -O3 remove the parser code generated from Lex/Yacc
+#   and therefore in production mode we downgrade to -O2 if the compiler is GCC (for all versions).
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake
new file mode 100644
index 0000000..cd4dbfa
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/GNU_Fortran.cmake
@@ -0,0 +1,23 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -funroll-all-loops -finline-functions"                                CACHE STRING "Fortran compiler flags for Release builds"          FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-g -O2 -m64 -march=native -DNDEBUG -fno-range-check -fconvert=big-endian" CACHE STRING "Fortran compiler flags for Bit-reproducible builds" FORCE )
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -fcheck=bounds -fbacktrace -finit-real=snan"                       CACHE STRING "Fortran compiler flags for Debug builds"            FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for Production builds."      FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g"                                                                   CACHE STRING "Fortran compiler flags for RelWithDebInfo builds."  FORCE )
+
+set( Fortran_FLAG_STACK_ARRAYS "-fstack-arrays" )
+
+####################################################################
+
+# Meaning of flags
+# ----------------
+# -fstack-arrays     : Allocate automatic arrays on the stack (needs large stacksize!!!)
+# -funroll-all-loops : Unroll all loops
+# -fcheck=bounds     : Bounds checking
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_C.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_C.cmake
new file mode 100644
index 0000000..5736004
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_C.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C compiler flags"                  FORCE )
+set( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C compiler flags"  FORCE )
+set( CMAKE_C_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C compiler flags"         FORCE )
+set( CMAKE_C_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C compiler flags"                    FORCE )
+set( CMAKE_C_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C compiler flags"               FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_CXX.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_CXX.cmake
new file mode 100644
index 0000000..8e466e5
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_CXX.cmake
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE        "-O3 -DNDEBUG"      CACHE STRING "Release C++ compiler flags"                 FORCE )
+set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g"            CACHE STRING "Release-with-debug-info C++ compiler flags" FORCE )
+set( CMAKE_CXX_FLAGS_BIT            "-O2 -DNDEBUG"      CACHE STRING "Bit-reproducible C++ compiler flags"        FORCE )
+set( CMAKE_CXX_FLAGS_DEBUG          "-O0 -g -traceback" CACHE STRING "Debug C++ compiler flags"                   FORCE )
+set( CMAKE_CXX_FLAGS_PRODUCTION     "-O3 -g"            CACHE STRING "Production C++ compiler flags"              FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake
new file mode 100644
index 0000000..d6e9719
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/Intel_Fortran.cmake
@@ -0,0 +1,20 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( Fortran_AUTOMATIC_ARRAYS_LIMIT 32768 )  # (32 kb)
+math( EXPR Fortran_AUTOMATIC_ARRAYS_LIMIT_KB "${Fortran_AUTOMATIC_ARRAYS_LIMIT}/1024" )
+
+set( Fortran_FLAG_STACK_ARRAYS     "-no-heap-arrays" )
+set( Fortran_FLAG_AUTOMATIC_ARRAYS "-heap-arrays ${Fortran_AUTOMATIC_ARRAYS_LIMIT_KB}" )
+
+set( CMAKE_Fortran_FLAGS_RELEASE        "-O3 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Release Fortran flags"                 FORCE )
+set( CMAKE_Fortran_FLAGS_RELWITHDEBINFO "-O2 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Release-with-debug-info Fortran flags" FORCE )
+set( CMAKE_Fortran_FLAGS_BIT            "-O2 -unroll -inline ${Fortran_FLAG_AUTOMATIC_ARRAYS}"          CACHE STRING "Bit-reproducible Fortran flags"        FORCE )
+# -check all implies -check bounds
+set( CMAKE_Fortran_FLAGS_DEBUG          "-O0 -g -traceback ${Fortran_FLAG_AUTOMATIC_ARRAYS} -check all" CACHE STRING "Debug Fortran flags"                   FORCE )
+set( CMAKE_Fortran_FLAGS_PRODUCTION     "-O3 -g ${Fortran_FLAG_AUTOMATIC_ARRAYS}"                       CACHE STRING "Production Fortran compiler flags"     FORCE )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_C.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_C.cmake
new file mode 100644
index 0000000..1cc6485
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_C.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_C_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C compiler flags" FORCE )
+
+set( CMAKE_C_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_CXX.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_CXX.cmake
new file mode 100644
index 0000000..6d31cf4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_CXX.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_CXX_FLAGS_RELEASE "-fast -O3 -DNDEBUG" CACHE STRING "Release C++ compiler flags" FORCE )
+
+set( CMAKE_CXX_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake
new file mode 100644
index 0000000..86c2a35
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/compiler_flags/PGI_Fortran.cmake
@@ -0,0 +1,11 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( CMAKE_Fortran_FLAGS_RELEASE "-fast -O3" CACHE STRING "Release Fortran compiler flags" FORCE )
+
+set( CMAKE_Fortran_LINK_FLAGS "" CACHE STRING "" )
diff --git a/ecbuild/share/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake b/ecbuild/share/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
new file mode 100644
index 0000000..1b5178d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/CMakeCheckCompilerFlagCommonPatterns.cmake
@@ -0,0 +1,33 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+
+# Do NOT include this module directly into any of your code. It is meant as
+# a library for Check*CompilerFlag.cmake modules. It's content may change in
+# any way between releases.
+
+macro (CHECK_COMPILER_FLAG_COMMON_PATTERNS _VAR)
+   set(${_VAR}
+     FAIL_REGEX "[Uu]nrecogni[sz]ed .*option"               # GNU, NAG
+     FAIL_REGEX "unknown .*option"                          # Clang
+     FAIL_REGEX "optimization flag .* not supported"        # Clang
+     FAIL_REGEX "unknown argument ignored"                  # Clang (cl)
+     FAIL_REGEX "ignoring unknown option"                   # MSVC, Intel
+     FAIL_REGEX "warning D9002"                             # MSVC, any lang
+     FAIL_REGEX "option.*not supported"                     # Intel
+     FAIL_REGEX "invalid argument .*option"                 # Intel
+     FAIL_REGEX "ignoring option .*argument required"       # Intel
+     FAIL_REGEX "ignoring option .*argument is of wrong type" # Intel
+     FAIL_REGEX "[Uu]nknown option"                         # HP
+     FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+     FAIL_REGEX "command option .* is not recognized"       # XL
+     FAIL_REGEX "command option .* contains an incorrect subargument" # XL
+     FAIL_REGEX "not supported in this configuration. ignored"       # AIX
+     FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+     FAIL_REGEX "[Uu]nknown switch"                         # PGI
+     FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+     FAIL_REGEX "Incorrect command line option:"            # Borland
+     FAIL_REGEX "Warning: illegal option"                   # SunStudio 12
+     FAIL_REGEX "[Ww]arning: Invalid suboption"             # Fujitsu
+   )
+endmacro ()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake b/ecbuild/share/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake
new file mode 100644
index 0000000..8519fcc
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/CheckFortranCompilerFlag.cmake
@@ -0,0 +1,53 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranCompilerFlag
+# ------------------------
+#
+# Check whether the Fortran compiler supports a given flag.
+#
+# CHECK_Fortran_COMPILER_FLAG(<flag> <var>)
+#
+# ::
+#
+#   <flag> - the compiler flag
+#   <var>  - variable to store the result
+#            Will be created as an internal cache variable.
+#
+# This internally calls the check_fortran_source_compiles macro and
+# sets CMAKE_REQUIRED_DEFINITIONS to <flag>.  See help for
+# CheckFortranSourceCompiles for a listing of variables that can
+# otherwise modify the build.  The result only tells that the compiler
+# does not give an error message when it encounters the flag.  If the
+# flag has any effect or even a specific one is beyond the scope of
+# this module.
+
+include(CheckFortranSourceCompiles)
+include(CMakeCheckCompilerFlagCommonPatterns)
+
+macro (CHECK_Fortran_COMPILER_FLAG _FLAG _RESULT)
+  set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+  set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+
+  # Normalize locale during test compilation.
+  set(_CheckFortranCompilerFlag_LOCALE_VARS LC_ALL LC_MESSAGES LANG)
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(_CheckFortranCompilerFlag_SAVED_${v} "$ENV{${v}}")
+    set(ENV{${v}} C)
+  endforeach()
+  CHECK_COMPILER_FLAG_COMMON_PATTERNS(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+  CHECK_Fortran_SOURCE_COMPILES("       program test\n       stop\n       end program" ${_RESULT}
+    # Some compilers do not fail with a bad flag
+    FAIL_REGEX "command line option .* is valid for .* but not for Fortran" # GNU
+    ${_CheckFortranCompilerFlag_COMMON_PATTERNS}
+    )
+  foreach(v ${_CheckFortranCompilerFlag_LOCALE_VARS})
+    set(ENV{${v}} ${_CheckFortranCompilerFlag_SAVED_${v}})
+    unset(_CheckFortranCompilerFlag_SAVED_${v})
+  endforeach()
+  unset(_CheckFortranCompilerFlag_LOCALE_VARS)
+  unset(_CheckFortranCompilerFlag_COMMON_PATTERNS)
+
+  set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+endmacro ()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake b/ecbuild/share/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake
new file mode 100644
index 0000000..c42254c
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/CheckFortranSourceCompiles.cmake
@@ -0,0 +1,106 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# CheckFortranSourceCompiles
+# --------------------------
+#
+# Check if given Fortran source compiles and links into an executable::
+#
+#   CHECK_Fortran_SOURCE_COMPILES(<code> <var> [FAIL_REGEX <fail-regex>]
+#                                 [SRC_EXT <ext>])
+#
+# The arguments are:
+#
+# ``<code>``
+#   Source code to try to compile.  It must define a PROGRAM entry point.
+# ``<var>``
+#   Variable to store whether the source code compiled.
+#   Will be created as an internal cache variable.
+# ``FAIL_REGEX <fail-regex>``
+#   Fail if test output matches this regex.
+# ``SRC_EXT <ext>``
+#   Use source extension ``.<ext>`` instead of the default ``.F``.
+#
+# The following variables may be set before calling this macro to modify
+# the way the check is run::
+#
+#   CMAKE_REQUIRED_FLAGS = string of compile command line flags
+#   CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)
+#   CMAKE_REQUIRED_INCLUDES = list of include directories
+#   CMAKE_REQUIRED_LIBRARIES = list of libraries to link
+#   CMAKE_REQUIRED_QUIET = execute quietly without messages
+
+macro(CHECK_Fortran_SOURCE_COMPILES SOURCE VAR)
+  if(NOT DEFINED "${VAR}")
+    set(_FAIL_REGEX)
+    set(_SRC_EXT)
+    set(_key)
+    foreach(arg ${ARGN})
+      if("${arg}" MATCHES "^(FAIL_REGEX|SRC_EXT)$")
+        set(_key "${arg}")
+      elseif(_key)
+        list(APPEND _${_key} "${arg}")
+      else()
+        message(FATAL_ERROR "Unknown argument:\n  ${arg}\n")
+      endif()
+    endforeach()
+    if(NOT _SRC_EXT)
+      set(_SRC_EXT F)
+    endif()
+    set(MACRO_CHECK_FUNCTION_DEFINITIONS
+      "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+    if(CMAKE_REQUIRED_LIBRARIES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES
+        LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+    endif()
+    if(CMAKE_REQUIRED_INCLUDES)
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES
+        "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+    else()
+      set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+    endif()
+    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}"
+      "${SOURCE}\n")
+
+    if(NOT CMAKE_REQUIRED_QUIET)
+      message(STATUS "Performing Test ${VAR}")
+    endif()
+    try_compile(${VAR}
+      ${CMAKE_BINARY_DIR}
+      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}
+      COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+      ${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}
+      CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+      "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+      OUTPUT_VARIABLE OUTPUT)
+
+    foreach(_regex ${_FAIL_REGEX})
+      if("${OUTPUT}" MATCHES "${_regex}")
+        set(${VAR} 0)
+      endif()
+    endforeach()
+
+    if(${VAR})
+      set(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Success")
+      endif()
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Performing Fortran SOURCE FILE Test ${VAR} succeeded with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    else()
+      if(NOT CMAKE_REQUIRED_QUIET)
+        message(STATUS "Performing Test ${VAR} - Failed")
+      endif()
+      set(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Performing Fortran SOURCE FILE Test ${VAR} failed with the following output:\n"
+        "${OUTPUT}\n"
+        "Source file was:\n${SOURCE}\n")
+    endif()
+  endif()
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/FindEigen3.cmake b/ecbuild/share/ecbuild/cmake/contrib/FindEigen3.cmake
new file mode 100644
index 0000000..9315294
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/FindEigen3.cmake
@@ -0,0 +1,97 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  else()
+	set( EIGEN3_VERSION ${EIGEN3_VERSION} CACHE INTERNAL "Eigen3 version" )
+  endif()
+
+endmacro(_eigen3_check_version)
+
+if(EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else(EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      ${EIGEN3_PATH}/include
+      ${EIGEN3_DIR}/include
+      ${EIGEN3_ROOT}/include
+      ${EIGEN_PATH}/include
+      ${EIGEN_DIR}/include
+      ${EIGEN_ROOT}/include
+      ENV EIGEN3_PATH
+      ENV EIGEN3_DIR
+      ENV EIGEN3_ROOT
+      ENV EIGEN_PATH
+      ENV EIGEN_DIR
+      ENV EIGEN_ROOT
+      PATH_SUFFIXES eigen3 eigen include/eigen3 include/eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+set( EIGEN3_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIR} )
diff --git a/ecbuild/share/ecbuild/cmake/contrib/FindNetCDF4.cmake b/ecbuild/share/ecbuild/cmake/contrib/FindNetCDF4.cmake
new file mode 100644
index 0000000..70e54af
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/FindNetCDF4.cmake
@@ -0,0 +1,337 @@
+# Project uclales
+# http://gitorious.org/uclales
+# License: Academic Free License v3.0
+#
+# - Find NETCDF, a library for reading and writing self describing array data.
+#
+# This module invokes the NETCDF wrapper compiler that should be installed
+# alongside NETCDF.  Depending upon the NETCDF Configuration, the wrapper compiler
+# is called either h5cc or h5pcc.  If this succeeds, the module will then call
+# the compiler with the -show argument to see what flags are used when compiling
+# an NETCDF client application.
+#
+# The module will optionally accept the COMPONENTS argument.  If no COMPONENTS
+# are specified, then the find module will default to finding only the NETCDF C
+# library.  If one or more COMPONENTS are specified, the module will attempt to
+# find the language bindings for the specified components.  Currently, the only
+# valid components are C, CXX, FORTRAN and F90.
+#
+# On UNIX systems, this module will read the variable NETCDF_USE_STATIC_LIBRARIES
+# to determine whether or not to prefer a static link to a dynamic link for NETCDF
+# and all of it's dependencies.  To use this feature, make sure that the
+# NETCDF_USE_STATIC_LIBRARIES variable is set before the call to find_package.
+#
+# To provide the module with a hint about where to find your NETCDF installation,
+# set the CMake or environment variable NETCDF_ROOT, NETCDF_DIR, NETCDF_PATH or
+# NETCDF4_DIR. The Find module will then look in this path when searching for
+# NETCDF executables, paths, and libraries.
+#
+# In addition to finding the includes and libraries required to compile an NETCDF
+# client application, this module also makes an effort to find tools that come
+# with the NETCDF distribution that may be useful for regression testing.
+#
+# This module will define the following variables:
+#  NETCDF_INCLUDE_DIRS - Location of the NETCDF includes
+#  NETCDF_INCLUDE_DIR - Location of the NETCDF includes (deprecated)
+#  NETCDF_DEFINITIONS - Required compiler definitions for NETCDF
+#  NETCDF_C_LIBRARIES - Required libraries for the NETCDF C bindings.
+#  NETCDF_CXX_LIBRARIES - Required libraries for the NETCDF C++ bindings
+#  NETCDF_FORTRAN_LIBRARIES - Required libraries for the NETCDF FORTRAN bindings
+#  NETCDF_F90_LIBRARIES - Required libraries for the NETCDF FORTRAN 90 bindings
+#  NETCDF_LIBRARIES - Required libraries for all requested bindings
+#  NETCDF_FOUND - true if NETCDF was found on the system
+#  NETCDF_LIBRARY_DIRS - the full set of library directories
+#  NETCDF_IS_PARALLEL - Whether or not NETCDF was found with parallel IO support
+#  NETCDF_CONFIG_EXECUTABLE - the path to the NC-CONFIG tool
+
+#=============================================================================
+# Copyright 2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+# This module is maintained by Thijs Heus <thijs.heus at zmaw.de>.
+
+include(SelectLibraryConfigurations)
+include(FindPackageHandleStandardArgs)
+
+# List of the valid NETCDF components
+set( NETCDF_VALID_COMPONENTS
+    FORTRAN
+    F90
+    CXX
+    C
+)
+
+# Invoke the NETCDF wrapper compiler.  The compiler return value is stored to the
+# return_value argument, the text output is stored to the output variable.
+macro( _NETCDF_CONFIG flag output return_value )
+    if( NETCDF_CONFIG_EXECUTABLE )
+        exec_program( ${NETCDF_CONFIG_EXECUTABLE}
+            ARGS ${flag}
+            OUTPUT_VARIABLE ${output}
+            RETURN_VALUE ${return_value}
+        )
+        if( ${${return_value}} EQUAL 0 )
+            # do nothing
+        else()
+            message( STATUS
+              "Unable to determine ${flag} from NC-CONFIG." )
+        endif()
+    endif()
+endmacro()
+#
+# try to find the NETCDF wrapper compilers
+find_program( NETCDF_CONFIG_EXECUTABLE
+    NAMES nc-config
+    HINTS ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+          ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+    PATH_SUFFIXES bin Bin
+    DOC "NETCDF CONFIG PROGRAM.  Used only to detect NETCDF compile flags." )
+mark_as_advanced( NETCDF_CONFIG_EXECUTABLE )
+ecbuild_debug("FindNetCDF4: nc-config executable = ${NETCDF_CONFIG_EXECUTABLE}")
+
+set(output "no")
+_NETCDF_CONFIG (--has-hdf5 output return)
+set(HAS_HDF5 FALSE)
+
+if(${output} STREQUAL yes)
+  set(HAS_HDF5 TRUE)
+  set(HDF5_FIND_QUIETLY ${NETCDF_FIND_QUIETLY})
+  set(HDF5_FIND_REQUIRED ${NETCDF_FIND_REQUIRED})
+  find_package(HDF5)
+#        list( APPEND NETCDF_LIBRARIES_DEBUG
+#            ${HDF5_LIBRARIES_DEBUG} )
+#        list( APPEND NETCDF_LIBRARIES_RELEASE
+#            ${HDF5_LIBRARIES_RELEASE} )
+  set (NETCDF_IS_PARALLEL ${HDF5_IS_PARALLEL})
+endif()
+_NETCDF_CONFIG (--has-pnetcdf output return)
+if(${output} STREQUAL yes)
+  set (NETCDF_IS_PARALLEL TRUE)
+else()
+#   set(NETCDF_IS_PARALLEL FALSE)
+endif()
+set( NETCDF_IS_PARALLEL TRUE CACHE BOOL
+    "NETCDF library compiled with parallel IO support" )
+
+
+if( NETCDF_INCLUDE_DIRS AND NETCDF_LIBRARIES )
+    # Do nothing: we already have NETCDF_INCLUDE_PATH and NETCDF_LIBRARIES in the
+    # cache, it would be a shame to override them
+else()
+    if( NOT NETCDF_FIND_COMPONENTS )
+        set( NETCDF_LANGUAGE_BINDINGS "C" )
+    else()
+        # add the extra specified components, ensuring that they are valid.
+        foreach( component ${NETCDF_FIND_COMPONENTS} )
+            list( FIND NETCDF_VALID_COMPONENTS ${component} component_location )
+            if( ${component_location} EQUAL -1 )
+                message( FATAL_ERROR
+                    "\"${component}\" is not a valid NETCDF component." )
+            else()
+                list( APPEND NETCDF_LANGUAGE_BINDINGS ${component} )
+            endif()
+        endforeach()
+    endif()
+
+    # seed the initial lists of libraries to find with items we know we need
+    set( NETCDF_C_INCLUDE_NAMES netcdf.h )
+    set( NETCDF_CXX_INCLUDE_NAMES netcdfcpp.h ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_FORTRAN_INCLUDE_NAMES ${NETCDF_C_INCLUDE_NAMES} )
+    set( NETCDF_F90_INCLUDE_NAMES netcdf.mod typesizes.mod ${NETCDF_C_INCLUDE_NAMES} )
+
+    set( NETCDF_C_LIBRARY_NAMES netcdf)
+    set( NETCDF_CXX_LIBRARY_NAMES netcdf_c++ netcdf_c++4 ${NETCDF_C_LIBRARY_NAMES} )
+    set( NETCDF_FORTRAN_LIBRARY_NAMES netcdff ${NETCDF_C_LIBRARY_NAMES})
+    set( NETCDF_F90_LIBRARY_NAMES ${NETCDF_FORTRAN_LIBRARY_NAMES} )
+
+    set( NETCDF_REQUIRED netcdf.h netcdfcpp.h netcdf.mod typesizes.mod netcdf netcdff netcdf_c++ netcdf_c++4)
+
+    foreach( LANGUAGE ${NETCDF_LANGUAGE_BINDINGS} )
+        ecbuild_debug("FindNetCDF4: looking for ${LANGUAGE} language bindings")
+
+        set( NETCDF_${LANGUAGE}_FOUND 1 ) # disable this in following if necessary
+
+        # find the NETCDF includes
+        foreach( INC ${NETCDF_${LANGUAGE}_INCLUDE_NAMES} )
+          #ecbuild_debug( "FindNetCDF4: looking for include file ${INC}")
+
+          find_path( NETCDF_${INC}_INCLUDE_DIR ${INC}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+          )
+          if( NOT NETCDF_${INC}_INCLUDE_DIR )
+            #ecbuild_debug( "FindNetCDF4: ${INC} not found" )
+            GET_FILENAME_COMPONENT( _basename ${INC} NAME_WE )
+            GET_FILENAME_COMPONENT( _ext ${INC} EXT )
+            string( TOUPPER ${_basename} _BASENAME )
+            set( INC_MOD "${_BASENAME}${_ext}")
+            #ecbuild_debug( "FindNetCDF4:     try ${INC_MOD}" )
+            find_path( NETCDF_${INC}_INCLUDE_DIR ${INC_MOD}
+              HINTS ${NETCDF_${LANGUAGE}_INCLUDE_FLAGS}
+                    ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                    ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+              PATH_SUFFIXES
+                  include
+                  Include
+            )
+          endif()
+
+          mark_as_advanced( NETCDF_${INC}_INCLUDE_DIR )
+          #ecbuild_debug_var( NETCDF_${INC}_INCLUDE_DIR)
+          if (NETCDF_${INC}_INCLUDE_DIR)
+            list( APPEND NETCDF_INCLUDE_DIRS ${NETCDF_${INC}_INCLUDE_DIR} )
+          else()
+            list( FIND NETCDF_REQUIRED ${INC} location )
+            if( ${location} EQUAL -1 )
+              else()
+              if(NETCDF_FIND_REQUIRED)
+                ecbuild_error( "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${INC}\" is not found for NetCDF component ${LANGUAGE}" )
+              endif()
+              set( NETCDF_${LANGUAGE}_FOUND 0 )
+            else()
+            endif()
+          endif()
+        endforeach()
+        # find the NETCDF libraries
+        foreach( LIB ${NETCDF_${LANGUAGE}_LIBRARY_NAMES} )
+            if( UNIX AND NETCDF_USE_STATIC_LIBRARIES )
+                # According to bug 1643 on the CMake bug tracker, this is the
+                # preferred method for searching for a static library.
+                # See http://www.cmake.org/Bug/view.php?id=1643.  We search
+                # first for the full static library name, but fall back to a
+                # generic search on the name if the static search fails.
+                set( THIS_LIBRARY_SEARCH_DEBUG lib${LIB}d.a ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} )
+            else()
+                set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d )
+                set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} )
+            endif()
+            find_library( NETCDF_${LIB}_LIBRARY_DEBUG
+                NAMES ${THIS_LIBRARY_SEARCH_DEBUG}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib)
+            find_library( NETCDF_${LIB}_LIBRARY_RELEASE
+                NAMES ${THIS_LIBRARY_SEARCH_RELEASE}
+                HINTS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS}
+                      ${NETCDF_ROOT} ${NETCDF_DIR} ${NETCDF_PATH} ${NETCDF4_DIR}
+                      ENV NETCDF_ROOT ENV NETCDF_DIR ENV NETCDF_PATH ENV NETCDF4_DIR
+                PATH_SUFFIXES lib64 Lib64 lib Lib )
+            select_library_configurations( NETCDF_${LIB} )
+            # even though we adjusted the individual library names in
+            # select_library_configurations, we still need to distinguish
+            # between debug and release variants because NETCDF_LIBRARIES will
+            # need to specify different lists for debug and optimized builds.
+            # We can't just use the NETCDF_${LIB}_LIBRARY variable (which was set
+            # up by the selection macro above) because it may specify debug and
+            # optimized variants for a particular library, but a list of
+            # libraries is allowed to specify debug and optimized only once.
+          if (NETCDF_${LIB}_LIBRARY_RELEASE)
+            list( APPEND NETCDF_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_RELEASE ${NETCDF_${LIB}_LIBRARY_RELEASE} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_DEBUG)
+            list( APPEND NETCDF_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+            list( APPEND NETCDF_${LANGUAGE}_LIBRARIES_DEBUG ${NETCDF_${LIB}_LIBRARY_DEBUG} )
+          endif()
+          if (NETCDF_${LIB}_LIBRARY_RELEASE OR NETCDF_${LIB}_LIBRARY_DEBUG )
+          else()
+            list( FIND NETCDF_REQUIRED ${LIB} location )
+            if( ${location} EQUAL -1 )
+            else()
+              if(NETCDF_FIND_REQUIRED)
+                message( SEND_ERROR "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              elseif( NOT NETCDF_FIND_QUIETLY )
+                message( STATUS "\"${LIB}\" is not found for NetCDF component ${LANGUAGE}." )
+              else()
+                set( NETCDF_${LANGUAGE}_FOUND 0 )
+              endif()
+           endif()
+          endif()
+        endforeach()
+        list( APPEND NETCDF_LIBRARY_DIRS ${NETCDF_${LANGUAGE}_LIBRARY_DIRS} )
+
+        # Append the libraries for this language binding to the list of all
+        # required libraries.
+
+        if( NETCDF_${LANGUAGE}_FOUND )
+            ecbuild_debug( "FindNetCDF4: ${LANGUAGE} language bindings found" )
+            if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    debug ${NETCDF_${LANGUAGE}_LIBRARIES_DEBUG}
+                    optimized ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            else()
+                list( APPEND NETCDF_${LANGUAGE}_LIBRARIES
+                    ${NETCDF_${LANGUAGE}_LIBRARIES_RELEASE} )
+            endif()
+        endif()
+        # ecbuild_debug_var( NETCDF_${LANGUAGE}_LIBRARIES )
+        list( APPEND NETCDF_FOUND_REQUIRED_VARS NETCDF_${LANGUAGE}_FOUND )
+    endforeach()
+
+    # We may have picked up some duplicates in various lists during the above
+    # process for the language bindings (both the C and C++ bindings depend on
+    # libz for example).  Remove the duplicates.
+    if( NETCDF_INCLUDE_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_INCLUDE_DIRS )
+    endif()
+    if( NETCDF_LIBRARIES_DEBUG )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_DEBUG )
+    endif()
+    if( NETCDF_LIBRARIES_RELEASE )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARIES_RELEASE )
+    endif()
+    if( NETCDF_LIBRARY_DIRS )
+        list( REMOVE_DUPLICATES NETCDF_LIBRARY_DIRS )
+    endif()
+
+    # Construct the complete list of NETCDF libraries with debug and optimized
+    # variants when the generator supports them.
+    if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
+        set( NETCDF_LIBRARIES
+            debug ${NETCDF_LIBRARIES_DEBUG}
+            optimized ${NETCDF_LIBRARIES_RELEASE} )
+    else()
+        set( NETCDF_LIBRARIES ${NETCDF_LIBRARIES_RELEASE} )
+    endif()
+endif()
+
+set( NETCDF4_FIND_QUIETLY ${NETCDF_FIND_QUIETLY} )
+set( NETCDF4_FIND_REQUIRED ${NETCDF_FIND_REQUIRED} )
+# handle the QUIET and REQUIRED arguments and set NETCDF4_FOUND to TRUE
+# if all listed variables are valid
+# Note: capitalisation of the package name must be the same as in the file name
+find_package_handle_standard_args( NetCDF4 DEFAULT_MSG
+    ${NETCDF_FOUND_REQUIRED_VARS}
+    NETCDF_LIBRARIES
+    NETCDF_INCLUDE_DIRS
+)
+
+mark_as_advanced(
+    NETCDF_INCLUDE_DIRS
+    NETCDF_LIBRARIES
+    NETCDF_LIBRARY_DIRS
+)
+
+set( NETCDF_FOUND ${NETCDF4_FOUND} )
+
+# For backwards compatibility we set NETCDF_INCLUDE_DIR to the value of
+# NETCDF_INCLUDE_DIRS
+set( NETCDF_INCLUDE_DIR "${NETCDF_INCLUDE_DIRS}" )
+
diff --git a/ecbuild/share/ecbuild/cmake/contrib/FindNumPy.cmake b/ecbuild/share/ecbuild/cmake/contrib/FindNumPy.cmake
new file mode 100644
index 0000000..ba02cec
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/FindNumPy.cmake
@@ -0,0 +1,101 @@
+# - Find the NumPy libraries
+# This module finds if NumPy is installed, and sets the following variables
+# indicating where it is.
+#
+# TODO: Update to provide the libraries and paths for linking npymath lib.
+#
+#  NUMPY_FOUND               - was NumPy found
+#  NUMPY_VERSION             - the version of NumPy found as a string
+#  NUMPY_VERSION_MAJOR       - the major version number of NumPy
+#  NUMPY_VERSION_MINOR       - the minor version number of NumPy
+#  NUMPY_VERSION_PATCH       - the patch version number of NumPy
+#  NUMPY_VERSION_DECIMAL     - e.g. version 1.6.1 is 10601
+#  NUMPY_INCLUDE_DIRS        - path to the NumPy include files
+
+#============================================================================
+# Copyright 2012 Continuum Analytics, Inc.
+#
+# MIT License
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to permit
+# persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+#============================================================================
+
+# Finding NumPy involves calling the Python interpreter
+if(NumPy_FIND_REQUIRED)
+    find_package(PythonInterp REQUIRED)
+else()
+    find_package(PythonInterp)
+endif()
+
+if(NOT PYTHONINTERP_FOUND)
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
+    "import numpy as n; print(n.__version__); print(n.get_include());"
+    RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS
+    OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT
+    ERROR_VARIABLE _NUMPY_ERROR_VALUE
+    OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0)
+    if(NumPy_FIND_REQUIRED)
+        message(FATAL_ERROR
+            "NumPy import failure:\n${_NUMPY_ERROR_VALUE}")
+    endif()
+    set(NUMPY_FOUND FALSE)
+    return()
+endif()
+
+# Convert the process output into a list
+string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT})
+string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES})
+# Just in case there is unexpected output from the Python command.
+list(GET _NUMPY_VALUES -2 NUMPY_VERSION)
+list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS)
+
+string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}")
+if("${_VER_CHECK}" STREQUAL "")
+    # The output from Python was unexpected. Raise an error always
+    # here, because we found NumPy, but it appears to be corrupted somehow.
+    message(FATAL_ERROR
+        "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n")
+    return()
+endif()
+
+# Make sure all directory separators are '/'
+string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS})
+
+# Get the major and minor version numbers
+string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION})
+list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR)
+list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR)
+list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH)
+string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH})
+math(EXPR NUMPY_VERSION_DECIMAL
+    "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}")
+
+find_package_message(NUMPY
+    "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}"
+    "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}")
+
+set(NUMPY_FOUND TRUE)
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake b/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake
new file mode 100644
index 0000000..ea1da07
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake
@@ -0,0 +1,123 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+#  get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+#  git_describe(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+#  git_get_exact_tag(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+    return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+    set(GIT_PARENT_DIR "${PROJECT_SOURCE_DIR}")
+    set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    while(NOT EXISTS "${GIT_DIR}")  # .git dir not found, search parent directories
+        set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+        get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+        if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+            # We have reached the root directory, we are not in git
+            set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+            return()
+        endif()
+        set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+    endwhile()
+    set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+    if(NOT EXISTS "${GIT_DATA}")
+        file(MAKE_DIRECTORY "${GIT_DATA}")
+    endif()
+
+    if(NOT EXISTS "${GIT_DIR}/HEAD")
+        return()
+    endif()
+    set(HEAD_FILE "${GIT_DATA}/HEAD")
+    configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+    configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+        "${GIT_DATA}/grabRef.cmake"
+        @ONLY)
+    include("${GIT_DATA}/grabRef.cmake")
+
+    set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+    set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+    if(NOT GIT_FOUND)
+        find_package(Git QUIET)
+    endif()
+    get_git_head_revision(refspec hash)
+    if(NOT GIT_FOUND)
+        set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+    if(NOT hash)
+        set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+        return()
+    endif()
+
+    # TODO sanitize
+    #if((${ARGN}" MATCHES "&&") OR
+    #   (ARGN MATCHES "||") OR
+    #   (ARGN MATCHES "\\;"))
+    #   message("Please report the following error to the project!")
+    #   message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+    #endif()
+
+    #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+    execute_process(COMMAND
+        "${GIT_EXECUTABLE}"
+        describe
+        ${hash}
+        ${ARGN}
+        WORKING_DIRECTORY
+        "${CMAKE_SOURCE_DIR}"
+        RESULT_VARIABLE
+        res
+        OUTPUT_VARIABLE
+        out
+        ERROR_QUIET
+        OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if(NOT res EQUAL 0)
+        set(out "${out}-${res}-NOTFOUND")
+    endif()
+
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+    git_describe(out --exact-match ${ARGN})
+    set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in b/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in
new file mode 100644
index 0000000..9fd3e64
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,42 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+    # named branch
+    string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+    if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+        configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+    elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
+        configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+        set(HEAD_HASH "${HEAD_REF}")
+    endif()
+else()
+    # detached HEAD
+    configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+  if(EXISTS "@GIT_DATA@/head-ref")
+    file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+    string(STRIP "${HEAD_HASH}" HEAD_HASH)
+  else()
+    set(HEAD_HASH "unknown")
+  endif()
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes
new file mode 100644
index 0000000..0b6acf1
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitattributes
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc  diff=astextplain
+*.DOC  diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF  diff=astextplain
+*.rtf  diff=astextplain
+*.RTF  diff=astextplain
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore
new file mode 100644
index 0000000..8a94597
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/.gitignore
@@ -0,0 +1,6 @@
+build/
+.*.swp
+.*.swo
+*.pyc
+wiki
+*.*~
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
new file mode 100644
index 0000000..ac7f456
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake
@@ -0,0 +1,59 @@
+include(CheckCXXCompilerFlag)
+
+#�On older cmake versions + newer compilers, 
+#�the given version of CheckCXXCompilerFlags does not quite work.
+if(CMAKE_VERSION VERSION_LESS 2.8.9)
+  macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+     set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+     set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+     CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+       # Some compilers do not fail with a bad flag
+       FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+       FAIL_REGEX "unrecognized .*option"                     # GNU
+       FAIL_REGEX "unknown .*option"                          # Clang
+       FAIL_REGEX "ignoring unknown option"                   # MSVC
+       FAIL_REGEX "warning D9002"                             # MSVC, any lang
+       FAIL_REGEX "option.*not supported"                     # Intel
+       FAIL_REGEX "invalid argument .*option"                 # Intel
+       FAIL_REGEX "ignoring option .*argument required"       # Intel
+       FAIL_REGEX "[Uu]nknown option"                         # HP
+       FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+       FAIL_REGEX "command option .* is not recognized"       # XL
+       FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+       FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+       FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+       )
+     set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+  endmacro ()
+endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+if(MINGW) 
+  check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+  check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+endif(MINGW)
+if(has_std_gnupp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
+elseif(has_std_gnupp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
+elseif(has_std_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+elseif(has_std_cpp0x)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+elseif(has_hstd_cpp11)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -hstd=c++11")
+endif(has_std_gnupp11)
+
+if(MSVC) 
+  set(MSWINDOBE TRUE)
+  add_definitions(/EHsc)
+  # Wd4251 stops MSCrapWare from issuing meaningless warnings. Seems Microsoft engineers don't grok
+  # dynamic libraries yet. Or templates. Or both acting alone or together. In any case, issuing
+  # warning sure is easier on them than fixing  their OS.
+  # Unfortunately, it does disable warnings that may be of interest. Possibly. 
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_VARIADIC_MAX=10 /wd4251")
+endif(MSVC)
+
+set(PROJECT_USES_CPP11 True CACHE INTERNAL "Uses c++11.")
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
new file mode 100644
index 0000000..7db9357
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/AddGTest.cmake
@@ -0,0 +1,72 @@
+# CMake arguments for gtest.
+set(GTEST_CMAKE_ARGS 
+      -DBUILD_SHARED_LIBS=OFF
+      -Dgtest_force_shared_crt=ON
+      -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
+      -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
+      -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+      -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+      -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+      -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+      -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+      -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL})
+if(MINGW)
+  list(APPEND GTEST_CMAKE_ARGS -Dgtest_disable_pthreads=ON)
+else(MINGW)
+  find_package(Threads)
+endif(MINGW)
+
+
+# Add gtest
+if(NOT EXTERNAL_ROOT)
+  set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+endif(NOT EXTERNAL_ROOT)
+include(ExternalProject)
+ExternalProject_Add(
+    googletest
+    PREFIX ${EXTERNAL_ROOT}
+    SVN_REPOSITORY http://googletest.googlecode.com/svn/trunk/
+    TIMEOUT 10
+    # Force separate output paths for debug and release builds to allow easy
+    # identification of correct lib in subsequent TARGET_LINK_LIBRARIES commands
+    CMAKE_ARGS ${GTEST_CMAKE_ARGS}
+    # Disable install step
+    INSTALL_COMMAND ""
+    # Wrap download, configure and build steps in a script to log output
+    LOG_DOWNLOAD ON
+    LOG_CONFIGURE ON
+    LOG_BUILD ON)
+
+if(PROJECT_USES_CPP11)
+  add_definitions(-DGTEST_LANG_CXX11)
+endif(PROJECT_USES_CPP11)
+
+macro(add_gtest name source)
+
+  ExternalProject_Get_Property(googletest source_dir)
+  include_directories(${source_dir}/include)
+  # Better, but only works on CMake 2.8.6?
+  # get_target_property(THISTEST_INCLUDE test_${name} INCLUDE_DIRECTORIES)
+  # set_target_properties(test_${name} PROPERTIES INCLUDE_DIRECTORIES
+  #                       "${source_dir}/include;${THISTEST_INCLUDE}") 
+
+  add_executable(test_${name} ${source})
+  ExternalProject_Get_Property(googletest binary_dir)
+  if(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/${CMAKE_CFG_INTDIR}/gtest.lib)
+  else(MSVC)
+    target_link_libraries(test_${name} ${binary_dir}/libgtest.a)
+  endif(MSVC)
+  if(CMAKE_THREAD_LIBS_INIT)
+    target_link_libraries(test_${name} ${CMAKE_THREAD_LIBS_INIT})
+  endif(CMAKE_THREAD_LIBS_INIT)
+
+  add_dependencies(test_${name} googletest)
+  if(NOT "${ARGN}" STREQUAL "")
+    target_link_libraries(test_${name} ${ARGN})
+  endif(NOT "${ARGN}" STREQUAL "")
+
+  add_test(cxx_${name} ${EXECUTABLE_OUTPUT_PATH}/test_${name}
+              --gtest_output=xml:${CMAKE_BINARY_DIR}/test-results/test_${name}.xml)
+  set_tests_properties(cxx_${name} PROPERTIES LABELS "gtest")
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
new file mode 100644
index 0000000..593b62f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake
@@ -0,0 +1,198 @@
+# Checks for C++11 features
+#
+# USAGE: There are two functions
+#
+# cxx11_find_all_features(OUTPUT_VARIABLE)
+# This function returns a variable with all possible features.
+#
+# cxx11_feature_check([feature feature] [REQUIRED [feature feature]])
+# If no arguments are provided, then checks all available features
+# Features appeacing before REQUIRED are optional.
+# If arguments are provided and those features are available, sets
+# the variable HAS_CXX11_FEATURENAME, where FEATURENAME is the input in capital letters.
+# Fails if required feature are not available
+#
+# For possible features, please print out the result from the first function.
+#
+# Original script by Rolf Eike Beer
+# Modifications by Andreas Weis
+# Further Modifications by RSDT at UCL
+# Adapted to ecBuild by Florian Rathgeber <florian.rathgeber at ecmwf.int>
+
+set(CPP11_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR}/cpp11 CACHE INTERNAL "c++11 file directory")
+
+MACRO(cxx11_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/cxx11_feature_tests/cxx11_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${CPP11_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking C++11 support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.cpp")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.cpp")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.cpp")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking C++11 support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "C++11 support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(cxx11_check_single_feature)
+
+# Find list of all features
+function(cxx11_find_all_features outvar)
+  FILE(GLOB ALL_CPP11_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/*.cpp")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_CPP11_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_cxx11_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${CPP11_FEATURE_CHECK_DIR}/${current_feature}*.cpp")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[c++11] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+  cxx11_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_cxx11_feature)
+
+function(cxx11_feature_check)
+
+  # find all features
+  cxx11_find_all_features(ALL_CPP11_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_CPP11_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[c++11] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # MinGW has not implemented std::random_device fully yet. Unfortunately, this can only be detected
+  # by running a program which tries to call std::random_device. However that generates an error that
+  # is *not* caught by CMake's try_run.
+  if(MSYS)
+    list(REMOVE_ITEM OPTIONALS "random_device")
+    list(FIND REQUIRED "random_device" feature_was_found)
+    if(NOT feature_was_found EQUAL "-1")
+      ecbuild_critical("[c++1] MSYS does not implement Random devices fully.\n"
+                       "       It cannot be required on this system.")
+    endif()
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_cxx11_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_cxx11_feature(${current_feature})
+    set(VARNAME HAS_CXX11_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[c++11] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(cxx11_feature_check)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
new file mode 100644
index 0000000..bc74e08
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/CheckIsNaN.cmake
@@ -0,0 +1,53 @@
+# Tries and find which isnan to use
+# defines ISNAN_VARIATION, which should be included in a configuration files somewher as
+#   @ISNAN_VARIATION@
+if(ISNAN_VARIATION)
+  return()
+endif(ISNAN_VARIATION)
+
+include(CheckCXXSourceCompiles)
+CHECK_CXX_SOURCE_COMPILES(
+  "#include <cmath>\nint main() { bool a = std::isnan(0e0); return 0; }\n" 
+  CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = isnan(0e0); return 0; }\n" 
+    CXX_HAS_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "#include <math.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS___ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+  CHECK_CXX_SOURCE_COMPILES(
+    "# include <float.h>\nint main() { bool a = _isnan(0e0); return 0; }\n" 
+    CXX_HAS_FLOAT_H_ISNAN)
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN)
+
+if(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+  message(FATAL_ERROR "[isnan] could not find standard function on this OS.")
+endif(NOT CXX_HAS_STD_ISNAN AND NOT CXX_HAS_ISNAN AND NOT CXX_HAS___ISNAN AND NOT CXX_HAS_FLOAT_H_ISNAN)
+
+if(CXX_HAS_STD_ISNAN)
+  set(ISNAN_HEADERS "#include <cmath>")
+  set(ISNAN_VARIATION "std::isnan")
+elseif(CXX_HAS_ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "::isnan")
+elseif(CXX_HAS___ISNAN)
+  set(ISNAN_HEADERS "#include <math.h>")
+  set(ISNAN_VARIATION "__isnan")
+elseif(CXX_HAS_FLOAT_H_ISNAN)
+  set(ISNAN_HEADERS "#include <float.h>")
+  set(ISNAN_VARIATION "_isnan")
+else()
+  message(FATAL_ERROR "AM HERE")
+endif()
+if(ISNAN_VARIATION)
+  set(ISNAN_VARIATION ${ISNAN_VARIATION} CACHE INTERNAL "Definition for isnan\n")
+  set(ISNAN_HEADERS ${ISNAN_HEADERS} CACHE INTERNAL "Headers containing isnan definition\n")
+endif(ISNAN_VARIATION)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
new file mode 100644
index 0000000..646d5dd
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/FindEigen.cmake
@@ -0,0 +1,131 @@
+# - Try to find Eigen3 lib
+#
+# This module supports requiring a minimum version, e.g. you can do
+#   find_package(Eigen3 3.1.2)
+# to require version 3.1.2 or newer of Eigen3.
+#
+# Once done this will define
+#
+#  EIGEN3_FOUND - system has eigen lib with correct version
+#  EIGEN3_INCLUDE_DIR - the eigen include directory
+#  EIGEN3_VERSION - eigen version
+
+# Copyright (c) 2006, 2007 Montel Laurent, <montel at kde.org>
+# Copyright (c) 2008, 2009 Gael Guennebaud, <g.gael at free.fr>
+# Copyright (c) 2009 Benoit Jacob <jacob.benoit.1 at gmail.com>
+# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
+# Modified by RSDT at UCL 
+
+if(NOT Eigen3_FIND_VERSION)
+  if(NOT Eigen3_FIND_VERSION_MAJOR)
+    set(Eigen3_FIND_VERSION_MAJOR 2)
+  endif(NOT Eigen3_FIND_VERSION_MAJOR)
+  if(NOT Eigen3_FIND_VERSION_MINOR)
+    set(Eigen3_FIND_VERSION_MINOR 91)
+  endif(NOT Eigen3_FIND_VERSION_MINOR)
+  if(NOT Eigen3_FIND_VERSION_PATCH)
+    set(Eigen3_FIND_VERSION_PATCH 0)
+  endif(NOT Eigen3_FIND_VERSION_PATCH)
+
+  set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
+endif(NOT Eigen3_FIND_VERSION)
+
+macro(_eigen3_check_version)
+  file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
+
+  string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
+  set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
+  string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
+  set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
+
+  set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
+  if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK FALSE)
+  else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+    set(EIGEN3_VERSION_OK TRUE)
+  endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
+
+  if(NOT EIGEN3_VERSION_OK)
+
+    message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
+                   "but at least version ${Eigen3_FIND_VERSION} is required")
+  endif(NOT EIGEN3_VERSION_OK)
+endmacro(_eigen3_check_version)
+
+if(NOT EIGEN3_INCLUDE_DIR)
+  if(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+    set(EIGEN3_INCLUDE_DIR $ENV{EIGEN3_INCLUDE_DIR})
+  endif(NOT "$ENV{EIGEN3_INCLUDE_DIR}" STREQUAL "")
+endif(NOT EIGEN3_INCLUDE_DIR)
+if (EIGEN3_INCLUDE_DIR)
+
+  # in cache already
+  _eigen3_check_version()
+  set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
+
+else (EIGEN3_INCLUDE_DIR)
+
+  find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
+      PATHS
+      $ENV{HOME}/usr/include
+      ${CMAKE_INSTALL_PREFIX}/include
+      ${KDE4_INCLUDE_DIR}
+      /usr/include
+      /usr/local/include
+      ${EXTERNAL_ROOT}/include
+      PATH_SUFFIXES eigen3 eigen
+    )
+
+  if(EIGEN3_INCLUDE_DIR)
+    _eigen3_check_version()
+  endif(EIGEN3_INCLUDE_DIR)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
+
+  mark_as_advanced(EIGEN3_INCLUDE_DIR)
+
+endif(EIGEN3_INCLUDE_DIR)
+
+if(NOT EIGEN3_FOUND)
+  if(CMAKE_VERSION VERSION_LESS 2.8.10)
+    # Doesn't have Hg download prior to 2.8.10
+    message(FATAL_ERROR "Please install eigen.")
+  else(CMAKE_VERSION VERSION_LESS 2.8.10)
+    if(NOT EXTERNAL_ROOT)
+      set(EXTERNAL_ROOT ${CMAKE_BINARY_DIR}/external)
+    endif(NOT EXTERNAL_ROOT)
+    find_package(Hg)
+    if(HG_FOUND)
+    
+      message(STATUS "Eigen3 not found. Will attempt to download it.")
+      include(ExternalProject)
+      ExternalProject_Add(
+          eigen
+          PREFIX ${EXTERNAL_ROOT}
+          HG_REPOSITORY https://bitbucket.org/eigen/eigen/
+          HG_TAG 3.2.0
+          TIMEOUT 10
+          CMAKE_ARGS 
+            -DCMAKE_INSTALL_PREFIX=${EXTERNAL_ROOT}
+            -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
+            -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
+            -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+            -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWIDTHDEBINFO}
+            -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+            -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL}
+          # Wrap download, configure and build steps in a script to log output
+          LOG_DOWNLOAD ON
+          LOG_CONFIGURE ON
+          LOG_BUILD ON)
+      set(EIGEN3_INCLUDE_DIR ${EXTERNAL_ROOT}/include/eigen3)
+  
+    else(HG_FOUND)
+   
+      message(FATAL_ERROR "Hg not found, and eigen not found.\nNeed one or the other.")
+   
+    endif(HG_FOUND)
+  endif(CMAKE_VERSION VERSION_LESS 2.8.10)
+endif(NOT EIGEN3_FOUND)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE
new file mode 100644
index 0000000..8dbb759
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 University College London
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md
new file mode 100644
index 0000000..a0a6947
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/README.md
@@ -0,0 +1,176 @@
+The Great CMake CookOff
+=======================
+
+
+This is a repository of usefull and less than usefull cmake recipes.  It is distributed under the
+[MIT License](http://opensource.org/licenses/MIT)
+
+
+Adding [Eigen](http://eigen.tuxfamily.org/) to a project
+========================================================
+
+Looks for the Eigen installed on system. If not found, then uses external project to download it.
+Usage is as follows:
+
+```cmake
+# Tell cmake to look into GreatCMakeCookOff for recipes
+set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/GreatCMakeCookOff) 
+
+# Optionally, tell cmake where to download eigen, if needed.
+# Defaults to value below.
+set(EXTERNAL_ROOT ${PROJECT_BINARY_DIR}/external)
+
+# Now look for cmake.
+find_package(Eigen)
+```
+
+**NOTE:** After building the first time, run cmake again. It will find the eigen it downloaded
+previously, and it will stop checking for updates. 
+
+Adding [GTest](https://code.google.com/p/googletest/) to a project
+==================================================================
+
+For googly reasons, whether valid or 404, GTest prefers to be compiled for each an every project. 
+This script does two things:
+
+- it adds GTest as an external project
+- it provides a function to add gtests to ctest
+
+This implies that GTest is downloaded the first time that make runs. Furthermore, it will be
+checked each and every time that makes runs. So, make now requires a working internet connection.
+Unlike Eigen above, there is currently no option avoid checking for updates.
+
+The CMakeLists.txt file could look like this:
+
+```cmake
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  find_package(GTest)
+  enable_testing()
+endif(tests)
+```
+
+And adding a test comes down to
+
+```cmake
+if(tests)
+
+  add_gtest(testme testme.cc mylib)
+
+endif(tests)
+```
+
+- first argument: name of the test
+- second argument: list of source files
+- other arguments: additional libraries to add during linking
+
+The test do expect an explicit main function. See the test generated in ``tests/addgtest.cmake``.
+
+**NOTE:** When using c++11, it is recommended to first include the c++11 flag script
+``AddCPP11Flags.cmake`` (see below) so that the gtest can be compiled with ``GTEST_LANG_CXX11``. 
+
+C++11
+=====
+
+Checking for specific features
+------------------------------
+
+Look for some c++11 features. Uses a script modified from [here](http://pageant.ghulbus.eu/?p=664).
+Usage is given below.
+
+```cmake
+# First need to enable c++
+enable_language(CXX)
+
+# The following will print out all available features.
+cxx11_find_all_features(ALL_FEATURES)
+message(STATUS "[c++11] features we can check for: ${ALL_FEATURES}")
+
+# The following checks for all features
+cxx11_feature_check()
+
+# An internal value is set if a particular feature exists.
+if(HAS_CXX11_AUTO)
+  message(STATUS "[c++11] has auto.")
+endif()
+if(HAS_CXX11_LAMBDA)
+  message(STATUS "[c++11] has lambda.")
+endif()
+```
+
+Alternatively, only a subset of features can be checked for, and some can be required:
+```cmake
+cxx11_feature_check(auto lambda REQUIRED long_long share_ptr variadic_templates)
+```
+The previous statement will fail if ``long long``, ``std::shared_ptr<...>``, and variadic templates
+are not available. It will also check for the availability of ``auto`` and ``lambda``, but without
+failing.
+
+Figuring out flags for some compilers
+-------------------------------------
+
+The script checks the existence of a few flags to enable c++11 features on different compilers.
+The output is somewhat verbose, but it seems to do the job for gcc, darwin-gcc, and microsoft visual
+studio. In addition, the intel compilers have to be told to use an external c++11 standard library.
+This script cannot figure where this library would be (hint: g++ provides it), so that is left up to
+the user. The script can be activated with a one liner.
+
+```cmake
+include("path/to/cookoff/AddCPP11Flags.cmake")
+```
+
+**NOTE:** On windows + visual studio, disables warnings 4251 and ups fake variadic templates to 10.
+
+
+Figure out ``isnan``
+====================
+
+Each and every vendor provides a different ``isnan``. There is a script to help define a portable
+c++ macro. It is meant to be used within a configuration file as follows:
+
+```cmake
+include("path/to/cookoff/CheckIsNaN.cmake")
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+configure_file(/path/to/config.h.in /path/to/config.h)
+```
+
+Two cmake variables are defined:
+
+- ISNAN_HEADERS will the header(s) relevant to the local ``isnan`` definition
+- ISNAN_VARIATIOPN is the fully qualified name to the local ``isnan`` definition
+
+They can be used as follows in a the configuration file ``config.h.in``:
+
+``cpp
+ at ISNAN_HEADERS@
+
+#define not_a_number(X) @ISNAN_VARIATION@
+``
+
+One should then use the macro ``not_a_number`` in-place of any ``isnan`` flavour.
+
+In c++11, it is also possible to define a function that takes only arithmetic type, thus obviating
+the need for a macro:
+
+```cpp
+ at ISNAN_HEADERS@
+#include <type_traits>
+
+template<class T>
+  typename std::enable_if<std::is_arithmetic<T>::value, bool> :: type 
+    not_a_number(T const &_in) { return @ISNAN_VARIATION@(_in); }
+```
+
+Testing CMake scripts
+=====================
+
+The file ``TestCMake.cmake`` contains a function to test cmake scripts. It converts an input cmake
+file into a project which is then configured, built, and run using ``ctest``. Unless an optional
+"SOURCE" is provided as argument, the test program is an empty ``main`` function returning 0. If the
+keyword is provided, then a ``main.cc`` or ``main.c`` file should provided the cmake script.
+
+For examples, look at the tests in this package.
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
new file mode 100644
index 0000000..4fc877a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/TestCMake.cmake
@@ -0,0 +1,58 @@
+function(_args_have_option outvar optionname arglist)
+  list(FIND ${arglist} "${optionname}" HASOPTION)
+  if(HASOPTION EQUAL -1)
+    set(${outvar} False PARENT_SCOPE)
+  else()
+    set(${outvar} True PARENT_SCOPE)
+    list(REMOVE_ITEM ${arglist} ${optionname})
+    set(${arglist} ${${arglist}} PARENT_SCOPE)
+  endif()
+endfunction(_args_have_option)
+
+function(cmake_test testname)
+
+  # Parse further arguments
+  # Let caller create a source file
+  set(ALL_OPTIONS ${ARGN})
+  _args_have_option(SOURCE "SOURCE" ALL_OPTIONS)
+  # Let caller create executable to run
+  # Should be used with test-command
+  _args_have_option(NOEXEC "NOEXEC" ALL_OPTIONS)
+
+  # set source and build dir.
+  set(FAKE_PROJECT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${testname})
+  set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/fake_project_builds/${testname})
+
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${testname}.cmake 
+                 ${FAKE_PROJECT_DIR}/CMakeData.cmake @ONLY)
+  message(STATUS "[${testname}] project in ${FAKE_PROJECT_DIR}")
+
+  if(NOT SOURCE)
+    file(WRITE ${FAKE_PROJECT_DIR}/main.c "int main() { return 0; }" )
+  endif(NOT SOURCE)
+  file(WRITE ${FAKE_PROJECT_DIR}/CMakeLists.txt
+       "cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)\n"
+       "project(allfeatures)\n"
+       "include(\"${FAKE_PROJECT_DIR}/CMakeData.cmake\")\n"
+       "enable_language(C)\n"
+       "if(NOT ${NOEXEC})\n"
+       "  file(GLOB ALLFILES \${PROJECT_SOURCE_DIR}/*.c \${PROJECT_SOURCE_DIR}/*.cc)\n"
+       "  add_executable(${testname} \${ALLFILES})\n"
+       "endif(NOT ${NOEXEC})\n")
+  
+  
+  if(EXISTS ${BUILD_DIR})
+    file(REMOVE_RECURSE ${BUILD_DIR})
+  endif(EXISTS ${BUILD_DIR})
+  
+  file(MAKE_DIRECTORY ${BUILD_DIR})
+  
+  add_test(cmake_test_${testname}
+             ${CMAKE_CTEST_COMMAND} --build-and-test ${FAKE_PROJECT_DIR} ${BUILD_DIR}
+                                    --build-generator ${CMAKE_GENERATOR}
+                                    --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+                                    --build-project ${testname}
+                                    --build-options -Dcookoff_path=${CMAKE_SOURCE_DIR}/..
+                                    ${ALL_OPTIONS})
+
+endfunction(cmake_test)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
new file mode 100644
index 0000000..d961df8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/__func__-N2340.cpp
@@ -0,0 +1,8 @@
+#include <cstring>
+
+int main()
+{
+	if (!__func__) { return 1; }
+	if(std::strlen(__func__) <= 0) { return 1; }
+	return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
new file mode 100644
index 0000000..948648e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/auto-N2546.cpp
@@ -0,0 +1,12 @@
+
+int main()
+{
+	auto i = 5;
+	auto f = 3.14159f;
+	auto d = 3.14159;
+	bool ret = (
+		(sizeof(f) < sizeof(d)) &&
+		(sizeof(i) == sizeof(int))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
new file mode 100644
index 0000000..870ba92
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/begin_function.cc
@@ -0,0 +1,12 @@
+#include <iterator>
+#include <vector>
+#include <exception>
+
+int main() {
+  std::vector<int> vector(2, 1);
+  auto i_first = std::begin(vector);
+  auto i_end = std::end(vector);
+  if(i_first + 2 != i_end) return 1;
+  if(*i_first != 1) return 2;
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
new file mode 100644
index 0000000..ed62451
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constexpr-N2235.cpp
@@ -0,0 +1,19 @@
+constexpr int square(int x)
+{
+	return x*x;
+}
+
+constexpr int the_answer()
+{
+	return 42;
+}
+
+int main()
+{
+	int test_arr[square(3)];
+	bool ret = (
+		(square(the_answer()) == 1764) &&
+		(sizeof(test_arr)/sizeof(test_arr[0]) == 9)
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
new file mode 100644
index 0000000..4934d83
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/constructor_delegate.cpp
@@ -0,0 +1,32 @@
+#include <cmath>
+#include <assert.h>
+
+class A {
+  public:
+    int a;
+    double b;
+
+    A() : A(0, 0) {}
+    explicit A(int _a) : A(_a, 0) {}
+    explicit A(double _b) : A(0, _b) {}
+    A(int _a, double _b) : a(_a), b(_b) {}
+};
+
+int main() {
+  A instance(1, 1.5e0);
+  assert(instance.a == 1);
+  assert(std::abs(instance.b - 1.5) < 1e-8);
+
+  A instance1(1);
+  assert(instance1.a == 1);
+  assert(std::abs(instance1.b) < 1e-8);
+
+  A instance2(1.5);
+  assert(instance2.a == 0);
+  assert(std::abs(instance2.b - 1.5) < 1e-8);
+
+  A instance3;
+  assert(instance3.a == 0);
+  assert(std::abs(instance3.b) < 1e-8);
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
new file mode 100644
index 0000000..be2878f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/cstdint.cpp
@@ -0,0 +1,10 @@
+#include <cstdint>
+int main()
+{
+	bool test = 
+		(sizeof(int8_t) == 1) &&
+		(sizeof(int16_t) == 2) &&
+		(sizeof(int32_t) == 4) &&
+		(sizeof(int64_t) == 8);
+	return test ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
new file mode 100644
index 0000000..843f83a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/decltype-N2343.cpp
@@ -0,0 +1,11 @@
+
+bool check_size(int i)
+{
+	return sizeof(int) == sizeof(decltype(i));
+}
+
+int main()
+{
+	bool ret = check_size(42);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
new file mode 100644
index 0000000..565da27
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor.cpp
@@ -0,0 +1,10 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
new file mode 100644
index 0000000..a71d565
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/deleted_constructor_fail_compile.cpp
@@ -0,0 +1,11 @@
+struct A {
+  int b;
+  A(int _b) : b(_b) {};
+  A(A const &) = delete;
+};
+
+int main() {
+  A first(1);
+  A second(first);
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
new file mode 100644
index 0000000..4eed94b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/enable_if.cpp
@@ -0,0 +1,8 @@
+#include <type_traits>
+
+template<int N>
+  typename std::enable_if<N==2, int>::type testme() { return 0; }
+
+int main() {
+  return testme<2>();
+};
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
new file mode 100644
index 0000000..37c97f9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/explicit_cast.cpp
@@ -0,0 +1,13 @@
+class A {
+  public:
+   A(int a = 5) : a_(a) {};
+   explicit operator bool() const { return a_ == 2; }
+  protected:
+   int a_;
+};
+
+int main () {
+  A a(6);
+  A const b(2);
+  return (static_cast<bool>(a) == false and static_cast<bool>(b) == true) ? 0: 1;  
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
new file mode 100644
index 0000000..462575f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/initialization.cpp
@@ -0,0 +1,7 @@
+#include <vector>
+
+int main() {
+  std::vector<double> a{0, 1, 2, 3};
+  std::vector< std::vector<double> > b{ {5, 6, 7}, {8, 9, 10, 11} };
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
new file mode 100644
index 0000000..4c33ed5
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/lambda-N2927.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int ret = 0;
+	return ([&ret]() -> int { return ret; })();
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
new file mode 100644
index 0000000..8f005ee
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_double.cpp
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return sizeof(long double) > sizeof(double) ? 0: 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
new file mode 100644
index 0000000..0911127
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/long_long-N1811.cpp
@@ -0,0 +1,7 @@
+int main(void)
+{
+	long long l;
+	unsigned long long ul;
+
+	return ((sizeof(l) >= 8) && (sizeof(ul) >= 8)) ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
new file mode 100644
index 0000000..0ea69d4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/noexcept.cpp
@@ -0,0 +1,9 @@
+class A {
+    public:
+    A() noexcept {};
+};
+int main()
+{
+   A a = A();
+   return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
new file mode 100644
index 0000000..c78fac4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int* test = nullptr;
+	return test ? 1 : 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
new file mode 100644
index 0000000..7ab77a2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/nullptr-N2431_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	int i = nullptr;
+	return 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
new file mode 100644
index 0000000..d8d4e0d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    virtual void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
new file mode 100644
index 0000000..19f43c9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/override_fail_compile.cpp
@@ -0,0 +1,22 @@
+class A {
+  public:
+    virtual void foo(int &_a) = 0;
+    void bar(int &_a)  { _a += 1; }
+};
+
+class B : public A {
+  public:
+    void foo(int &_a) override { _a += 1; }
+    void bar(int &_a) override { _a += 2; }
+};
+
+int main() {
+  A * b = static_cast<A*>(new B);
+  int a = 0;
+  b->foo(a); 
+  if(a != 1) return 1;
+  b->bar(a); 
+  if(a != 3) return 1;
+  delete b;
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
new file mode 100644
index 0000000..a777421
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/random_device.cpp
@@ -0,0 +1,13 @@
+#include <random>
+#include <sstream>
+
+int main()
+{
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<> dis(1, 6);
+    std::ostringstream sstr;
+    for(int n=0; n<10; ++n) sstr << dis(gen) << ' ';
+    return 0;
+}
+
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
new file mode 100644
index 0000000..75fb555
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/rvalue_references-N2118.cpp
@@ -0,0 +1,15 @@
+int foo(int& lvalue)
+{
+	return 123;
+}
+
+int foo(int&& rvalue)
+{
+	return 321;
+}
+
+int main()
+{
+	int i = 42;
+	return ((foo(i) == 123) && (foo(42) == 321)) ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
new file mode 100644
index 0000000..4a4ff82
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/shared_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::shared_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
new file mode 100644
index 0000000..a55fc09
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/sizeof_member-N2253.cpp
@@ -0,0 +1,14 @@
+struct foo {
+	char bar;
+	int baz;
+};
+
+int main(void)
+{
+	bool ret = (
+		(sizeof(foo::bar) == 1) &&
+		(sizeof(foo::baz) >= sizeof(foo::bar)) &&
+		(sizeof(foo) >= sizeof(foo::bar)+sizeof(foo::baz))
+	);
+	return ret ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
new file mode 100644
index 0000000..c3d74ca
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(0 < 1, "your ordering of integers is screwed");
+	return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
new file mode 100644
index 0000000..4cb1183
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/static_assert-N1720_fail_compile.cpp
@@ -0,0 +1,5 @@
+int main()
+{
+	static_assert(1 < 0, "this should fail");
+	return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
new file mode 100644
index 0000000..52881a9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/template_alias.cpp
@@ -0,0 +1,8 @@
+template<class A, class B> struct AClass {
+  typedef A t_first;
+  typedef B t_second;
+};
+
+template<class B> using Specialized = AClass<int, B>;
+
+int main() { return 0; }
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
new file mode 100644
index 0000000..8cf8319
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/trivial_type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_trivially_move_constructible<int>::value;
+  bool const b = std::is_trivially_move_assignable<int>::value;
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
new file mode 100644
index 0000000..4344f76
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/type_traits.cpp
@@ -0,0 +1,7 @@
+#include<type_traits>
+int main()
+{
+  bool const a = std::is_move_constructible<int>::value;
+  bool const b = std::is_move_assignable<int>::value;
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
new file mode 100644
index 0000000..159591d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/unique_ptr.cpp
@@ -0,0 +1,6 @@
+#include<memory>
+
+int main() {
+  std::unique_ptr<int> a(new int(1));
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
new file mode 100644
index 0000000..4518e88
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/cpp11/variadic_templates-N2555.cpp
@@ -0,0 +1,23 @@
+int Accumulate()
+{
+	return 0;
+}
+
+template<typename T, typename... Ts>
+int Accumulate(T v, Ts... vs)
+{
+	return v + Accumulate(vs...);
+}
+
+template<int... Is>
+int CountElements()
+{
+	return sizeof...(Is);
+}
+
+int main()
+{
+	int acc = Accumulate(1, 2, 3, 4, -5);
+	int count = CountElements<1,2,3,4,5>();
+	return ((acc == 5) && (count == 5)) ? 0 : 1;
+}
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
new file mode 100644
index 0000000..f2eb4d0
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR)
+project(COOKOFF_TEST)
+
+enable_testing()
+
+include(${PROJECT_SOURCE_DIR}/../TestCMake.cmake)
+
+cmake_test(checkisnan SOURCE)
+cmake_test(checkcpp11flags)
+cmake_test(addgtest NOEXEC SOURCE --test-command ${CMAKE_MAKE_PROGRAM} test)
+
+add_subdirectory(cpp11)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
new file mode 100644
index 0000000..b1139d9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/addgtest.cmake
@@ -0,0 +1,20 @@
+option(tests          "Enable testing."                         on)
+
+if(tests) 
+  include(${cookoff_path}/AddGTest.cmake)
+  enable_testing()
+endif(tests)
+
+file(WRITE ${CMAKE_SOURCE_DIR}/mytest.cc 
+     "#include <gtest/gtest.h>\n\n"
+     "class TestMe : public ::testing::Test {};\n\n"
+     "TEST_F(TestMe, TestThis) {\n"
+     "  EXPECT_TRUE(true); \n"
+     "}\n\n"
+     "int main(int argc, char **argv) {\n"
+     "  ::testing::InitGoogleTest(&argc, argv);\n"
+     "  return RUN_ALL_TESTS();\n"
+     "}\n")
+
+
+add_gtest(mytest mytest.cc)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
new file mode 100644
index 0000000..1f7e8a4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkcpp11flags.cmake
@@ -0,0 +1 @@
+include(${cookoff_path}/AddCPP11Flags.cmake)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
new file mode 100644
index 0000000..e9cbdaa
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/checkisnan.cmake
@@ -0,0 +1,19 @@
+include(${cookoff_path}/CheckIsNaN.cmake)
+if(NOT ISNAN_VARIATION)
+  message(STATUS "Could not find working isnan.")
+endif(NOT ISNAN_VARIATION)
+
+enable_language(CXX)
+file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in
+     "\@ISNAN_HEADERS\@\n"
+     "#include <limits>\n"
+     "#include <exception>\n"
+     "#define not_a_number(X) \@ISNAN_VARIATION\@(X)\n"
+     "int main() {\n"
+     "  if(not_a_number(0.5e0)) throw std::exception(); \n"
+     "  if(not_a_number(2l)) throw std::exception(); \n"
+     "  if(not not_a_number(std::numeric_limits<double>::quiet_NaN())) throw std::exception(); \n"
+     "  return 0;\n"
+     "}\n")
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/isnan.cc.in ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
new file mode 100644
index 0000000..45d20a5
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_test(allfeatures)
+cmake_test(parse_input_features)
+cmake_test(check_features)
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
new file mode 100644
index 0000000..f4dec87
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/allfeatures.cmake
@@ -0,0 +1,7 @@
+enable_language(CXX)
+include(${cookoff_path}/CheckCXX11Features.cmake)
+cxx11_find_all_features(ALL_CPP11_FEATURES)
+LIST(LENGTH ALL_CPP11_FEATURES LIST_LENGTH)
+if(${LIST_LENGTH} EQUAL 0)
+  message(FATAL_ERROR "No c++11 features found")
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
new file mode 100644
index 0000000..1ea2fdb
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/check_features.cmake
@@ -0,0 +1,9 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function to test.
+cxx11_feature_check("auto")
+cxx11_feature_check()
diff --git a/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
new file mode 100644
index 0000000..ca88de0
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/contrib/GreatCMakeCookOff/tests/cpp11/parse_input_features.cmake
@@ -0,0 +1,68 @@
+# This is a C++ feature
+enable_language(CXX)
+
+# Include check feature.
+include(${cookoff_path}/CheckCXX11Features.cmake)
+
+# Now call function with fake input
+set(ALL_FEATURES "first;second;third")
+
+# No input OPTIONALS == ALL_FEATURES
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "")
+if(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+  message(FATAL_ERROR "OPTIONALS results should contain everything.") 
+endif(NOT "${OPTIONALS}" STREQUAL "${ALL_FEATURES}")
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif(NOT "${REQUIRED}" STREQUAL "")
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif(NOT "${ERRORS}" STREQUAL "")
+
+# Single input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "second")
+if(NOT "${OPTIONALS}" STREQUAL "second")
+  message(FATAL_ERROR "OPTIONALS results should second.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# Single error input without REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS "none")
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "")
+  message(FATAL_ERROR "REQUIRED should be empty.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
+
+# Single valid input with REQUIRED
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS REQUIRED first)
+if(NOT "${OPTIONALS}" STREQUAL "")
+  message(FATAL_ERROR "OPTIONALS results should be empty.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "")
+  message(FATAL_ERROR "ERRORS should be empty.") 
+endif()
+
+# one of each
+parse_input_features("${ALL_FEATURES}" OPTIONALS REQUIRED ERRORS second third REQUIRED first none)
+if(NOT "${OPTIONALS}" STREQUAL "second;third")
+  message(FATAL_ERROR "OPTIONALS results should be second;optional.") 
+endif()
+if(NOT "${REQUIRED}" STREQUAL "first")
+  message(FATAL_ERROR "REQUIRED should be first.") 
+endif()
+if(NOT "${ERRORS}" STREQUAL "none")
+  message(FATAL_ERROR "ERRORS should be none.") 
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_c_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_c_flags.cmake
new file mode 100644
index 0000000..138e4d8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_c_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_c_flags
+# ===================
+#
+# Add C compiler flags to CMAKE_C_FLAGS only if supported by the compiler. ::
+#
+#   ecbuild_add_c_flags( <flag1> [ <flag2> ... ]
+#                        [ BUILD <build> ]
+#                        [ NAME <name> ]
+#                        [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_C_FLAGS_<build>`` instead of ``CMAKE_C_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_c_flags m_c_flags )
+
+  set( _flags ${m_c_flags} )
+
+  if( _flags AND CMAKE_C_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+      if( NOT DEFINED N_CFLAG )
+        set( N_CFLAG 0 )
+      endif()
+
+      math( EXPR N_CFLAG '${N_CFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_c_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_c_compiler_flag( ${_flags} C_FLAG_TEST_${N_CFLAG} )
+          set( _flag_ok ${C_FLAG_TEST_${N_CFLAG}} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_C_FLAGS_${_PAR_BUILD} "${CMAKE_C_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_flags}" )
+          ecbuild_debug( "C FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised C flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised C flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_c_flags m_c_flags )
+  ecbuild_deprecate( " cmake_add_c_flags is deprecated, use ecbuild_add_c_flags instead." )
+  ecbuild_add_c_flags( ${m_c_flags} )
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake
new file mode 100644
index 0000000..6c5c861
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx11_flags.cmake
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx11_flags
+# =======================
+#
+# Add cxx11 flags to CXX compilation flags. ::
+#
+#   ecbuild_add_cxx11_flags()
+#
+# This macro uses macros from http://github.com/UCL/GreatCMakeCookOff.
+#
+##############################################################################
+
+macro( ecbuild_add_cxx11_flags )
+
+	# if( CMAKE_COMPILER_IS_GNUCXX )
+	# 	if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7 )
+	# 		ecbuild_add_cxx_flags("-std=c++0x")
+	# 	else()
+	# 		ecbuild_add_cxx_flags("-std=c++11")
+	# 	endif()
+	# endif()
+
+	include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/AddCPP11Flags.cmake )
+
+endmacro( ecbuild_add_cxx11_flags )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx_flags.cmake
new file mode 100644
index 0000000..699c50a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_cxx_flags.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_cxx_flags
+# =====================
+#
+# Add C++ compiler flags to CMAKE_CXX_FLAGS only if supported by compiler. ::
+#
+#   ecbuild_add_cxx_flags( <flag1> [ <flag2> ... ]
+#                          [ BUILD <build> ]
+#                          [ NAME <name> ]
+#                          [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_CXX_FLAGS_<build>`` instead of ``CMAKE_CXX_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+macro( ecbuild_add_cxx_flags m_cxx_flags )
+
+  set( _flags ${m_cxx_flags} )
+  if( _flags AND CMAKE_CXX_COMPILER_LOADED )
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+    if( _try_add_flag )
+
+      if( NOT DEFINED N_CXXFLAG )
+        set( N_CXXFLAG 0 )
+      endif()
+
+      math( EXPR N_CXXFLAG '${N_CXXFLAG}+1' )
+
+      if( NOT ECBUILD_TRUST_FLAGS )
+        if( DEFINED _PAR_NAME )
+          check_cxx_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_cxx_compiler_flag( ${_flags} CXX_FLAG_TEST_${N_CXXFLAG} )
+          set( _flag_ok CXX_FLAG_TEST_${N_CXXFLAG} )
+        endif()
+      else()
+        set( _flag_ok 1 )
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_CXX_FLAGS_${_PAR_BUILD} "${CMAKE_CXX_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_flags}" )
+          ecbuild_debug( "C++ FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised CXX flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised CXX flag [${_flags}]" )
+      endif()
+    endif()
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_cxx_flags m_cxx_flags )
+  ecbuild_deprecate( " cmake_add_cxx_flags is deprecated, use ecbuild_add_cxx_flags instead." )
+  ecbuild_add_cxx_flags( ${m_cxx_flags} )
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_executable.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_executable.cmake
new file mode 100644
index 0000000..f6cb128
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_executable.cmake
@@ -0,0 +1,346 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_executable
+# ======================
+#
+# Add an executable with a given list of source files. ::
+#
+#   ecbuild_add_executable( TARGET <name>
+#                           SOURCES <source1> [<source2> ...]
+#                           [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                           [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                           [ OBJECTS <obj1> [<obj2> ...] ]
+#                           [ TEMPLATES <template1> [<template2> ...] ]
+#                           [ LIBS <library1> [<library2> ...] ]
+#                           [ INCLUDES <path1> [<path2> ...] ]
+#                           [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                           [ PERSISTENT <file1> [<file2> ...] ]
+#                           [ GENERATED <file1> [<file2> ...] ]
+#                           [ DEPENDS <target1> [<target2> ...] ]
+#                           [ CONDITION <condition> ]
+#                           [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                           [ NOINSTALL ]
+#                           [ VERSION <version> | AUTO_VERSION ]
+#                           [ CFLAGS <flag1> [<flag2> ...] ]
+#                           [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                           [ FFLAGS <flag1> [<flag2> ...] ]
+#                           [ LINKER_LANGUAGE <lang> ]
+#                           [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the executable
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   version to use as executable version
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   automatically version the executable with the package version
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_executable )
+
+  set( options NOINSTALL AUTO_VERSION )
+  set( single_value_args TARGET COMPONENT LINKER_LANGUAGE VERSION OUTPUT_NAME )
+  set( multi_value_args SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                        TEMPLATES LIBS INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                        CFLAGS CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_executable() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_executable() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+        ecbuild_error("ecbuild_add_executable(${_PAR_TARGET}): CUDA source files detected"
+                      "but CUDA was not found.")
+      endif()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): CUDA sources detected."
+                    "Building executable with ecbuild_add_executable() rather than intrinsic"
+                    "add_executable().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+    else()
+      cuda_add_executable( ${_PAR_TARGET} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list(REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION )
+        ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): set version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      endif()
+    endif()
+
+    # installation
+
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): installing to ${INSTALL_BIN_DIR}")
+
+      # add installation paths and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+               EXPORT  ${PROJECT_NAME}-targets
+               RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+               LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+               ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #        COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous command)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    else()
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): not installing")
+      # NOINSTALL targets are always built the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_executable(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_exe( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_EXES ${${PROJECT_NAME}_ALL_EXES} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endmacro( ecbuild_add_executable  )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake
new file mode 100644
index 0000000..690cde6
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_extra_search_paths.cmake
@@ -0,0 +1,33 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###############################################################################
+#
+# macro for adding search paths to CMAKE_PREFIX_PATH
+# for example the ECMWF /usr/local/apps paths
+#
+# usage: ecbuild_add_extra_search_paths( netcdf4 )
+
+function( ecbuild_add_extra_search_paths pkg )
+
+  ecbuild_deprecate( " ecbuild_add_extra_search_paths modifies CMAKE_PREFIX_PATH,"
+                     " which can affect future package discovery if not undone by the caller."
+                     " The current CMAKE_PREFIX_PATH is being backed up as _CMAKE_PREFIX_PATH"
+                     " so it can later be restored." )
+
+  # Back up current CMAKE_PREFIX_PATH so the caller can reset it
+  set( _CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+
+  string( TOUPPER ${pkg} _PKG )
+
+  ecbuild_list_extra_search_paths( ${pkg} CMAKE_PREFIX_PATH )
+
+  set( CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE )
+  # ecbuild_debug_var( CMAKE_PREFIX_PATH )
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_fortran_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_fortran_flags.cmake
new file mode 100644
index 0000000..2ac89e8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_fortran_flags.cmake
@@ -0,0 +1,109 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_fortran_flags
+# =========================
+#
+# Add Fortran compiler flags to CMAKE_Fortran_FLAGS only if supported by the
+# compiler. ::
+#
+#   ecbuild_add_fortran_flags( <flag1> [ <flag2> ... ]
+#                              [ BUILD <build> ]
+#                              [ NAME <name> ]
+#                              [ NO_FAIL ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   add flags to ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+# NAME : optional
+#   name of the check (if omitted, checks are enumerated)
+#
+# NO_FAIL : optional
+#   do not fail if the flag cannot be added
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_add_fortran_flags m_fortran_flags )
+
+  set( _flags ${m_fortran_flags} )
+
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( options NO_FAIL )
+    set( single_value_args BUILD NAME )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    set( _try_add_flag TRUE )
+    if( _PAR_BUILD )
+      string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+      string( TOUPPER ${_PAR_BUILD}  _PAR_BUILD_CAPS )
+      if( NOT CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}" )
+        set( _try_add_flag FALSE )
+      endif()
+    endif()
+
+    if( _try_add_flag )
+      if( NOT DEFINED N_FortranFLAG )
+        set( N_FortranFLAG 0 )
+      endif()
+
+      math( EXPR N_FortranFLAG '${N_FortranFLAG}+1' )
+
+      if( ECBUILD_TRUST_FLAGS )
+        set( _flag_ok 1 )
+      # Due to a bug in CMake < 3.0, check_fortran_compiler_flag ALWAYS fails with ifort
+      # see https://cmake.org/Bug/view.php?id=14507
+      elseif( CMAKE_MAJOR_VERSION LESS 3 AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+        set( _flag_ok 1 )
+        ecbuild_warn( "Not testing Fortran flags due to a bug in CMake < 3.0 with ifort" )
+      else()
+        if( DEFINED _PAR_NAME )
+          check_fortran_compiler_flag( ${_flags} ${_PAR_NAME} )
+          set( _flag_ok ${${_PAR_NAME}} )
+        else()
+          check_fortran_compiler_flag( ${_flags} Fortran_FLAG_TEST_${N_FortranFLAG} )
+          set( _flag_ok ${Fortran_FLAG_TEST_${N_FortranFLAG}} )
+        endif()
+      endif()
+
+      if( _flag_ok )
+        if( _PAR_BUILD )
+          set( CMAKE_Fortran_FLAGS_${_PAR_BUILD} "${CMAKE_Fortran_FLAGS_${_PAR_BUILD}} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added for build type ${_PAR_BUILD}" )
+        else()
+          set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${_flags}" )
+          ecbuild_debug( "Fortran FLAG [${_flags}] added" )
+        endif()
+      elseif( _PAR_NO_FAIL )
+        ecbuild_info( "Unrecognised Fortran flag [${_flags}] -- skipping" )
+      else()
+        ecbuild_error( "Unrecognised Fortran flag [${_flags}]" )
+      endif()
+    endif()
+
+    unset( _flags )
+    unset( _flag_ok )
+    unset( _try_add_flag )
+  endif()
+
+endmacro()
+
+macro( cmake_add_fortran_flags m_fortran_flags )
+  ecbuild_deprecate( " cmake_add_fortran_flags is deprecated, use ecbuild_add_fortran_flags instead." )
+  ecbuild_add_fortran_flags( ${m_fortran_flags} )
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_library.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_library.cmake
new file mode 100644
index 0000000..f9f567f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_library.cmake
@@ -0,0 +1,578 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_library
+# ===================
+#
+# Add a library with a given list of source files. ::
+#
+#   ecbuild_add_library( TARGET <name>
+#                        SOURCES <source1> [<source2> ...]
+#                        [ SOURCES_GLOB <glob1> [<glob2> ...] ]
+#                        [ SOURCES_EXCLUDE_REGEX <regex1> [<regex2> ...] ]
+#                        [ TYPE SHARED|STATIC|MODULE|OBJECT ]
+#                        [ OBJECTS <obj1> [<obj2> ...] ]
+#                        [ TEMPLATES <template1> [<template2> ...] ]
+#                        [ LIBS <library1> [<library2> ...] ]
+#                        [ INCLUDES <path1> [<path2> ...] ]
+#                        [ PRIVATE_INCLUDES <path1> [<path2> ...] ]
+#                        [ PUBLIC_INCLUDES <path1> [<path2> ...] ]
+#                        [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                        [ PERSISTENT <file1> [<file2> ...] ]
+#                        [ GENERATED <file1> [<file2> ...] ]
+#                        [ DEPENDS <target1> [<target2> ...] ]
+#                        [ CONDITION <condition> ]
+#                        [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                        [ NOINSTALL ]
+#                        [ HEADER_DESTINATION <path> ]
+#                        [ INSTALL_HEADERS LISTED|ALL ]
+#                        [ INSTALL_HEADERS_LIST <header1> [<header2> ...] ]
+#                        [ INSTALL_HEADERS_REGEX <pattern> ]
+#                        [ VERSION <version> | AUTO_VERSION ]
+#                        [ SOVERSION <soversion> | AUTO_SOVERSION ]
+#                        [ CFLAGS <flag1> [<flag2> ...] ]
+#                        [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                        [ FFLAGS <flag1> [<flag2> ...] ]
+#                        [ LINKER_LANGUAGE <lang> ]
+#                        [ OUTPUT_NAME <name> ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+# SOURCES : required
+#   list of source files
+#
+# TYPE : optional
+#   library type, one of:
+#
+#   :SHARED: libraries are linked dynamically and loaded at runtime
+#   :STATIC: archives of object files for use when linking other targets.
+#   :MODULE: plugins that are not linked into other targets but may be loaded
+#            dynamically at runtime using dlopen-like functionality
+#   :OBJECT: files are just compiled into objects
+#
+# SOURCES_GLOB : optional
+#   search pattern to find source files to compile (note: not recommend according to CMake guidelines)
+#   it is usually better to explicitly list the source files in the CMakeList.txt
+#
+# SOURCES_EXCLUDE_REGEX : optional
+#   search pattern to exclude source files from compilation, applies o the results of SOURCES_GLOB
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# TEMPLATES : optional
+#   list of files specified as SOURCES which are not to be compiled separately
+#   (these are commonly template implementation files included in a header)
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : (DEPRECATED) optional
+#   list of paths to add to include directories, behaves as PUBLIC_INCLUDES if CMake >= 2.8.11
+#   and reverts to include_directories() for CMake < 2.8.11
+#
+# PUBLIC_INCLUDES : optional
+#   list of paths to add to include directories which will be publicly exported to other projects
+#
+# PRIVATE_INCLUDES : optional
+#   list of paths to add to include directories which won't be exported to other projects,
+#   equivalent to using a include_directories() before calling this macro
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# NOINSTALL : optional
+#   do not install the library
+#
+# HEADER_DESTINATION
+#   directory to install headers (if not specified, INSTALL_INCLUDE_DIR is used)
+#
+# INSTALL_HEADERS : optional
+#   specify which header files to install:
+#
+#   :LISTED: install header files listed as SOURCES
+#   :ALL:    install all header files ending in .h, .hh, .hpp, .H
+#
+# INSTALL_HEADERS_LIST : optional
+#   list of extra headers to install
+#
+# INSTALL_HEADERS_REGEX : optional
+#   regular expression to match extra headers to install
+#
+# VERSION : optional, AUTO_VERSION or LIBS_VERSION is used if not specified
+#   build version of the library
+#
+# AUTO_VERSION : optional, ignored if VERSION is specified
+#   use MAJOR.MINOR package version as build version of the library
+#
+# SOVERSION : optional, AUTO_SOVERSION or LIBS_SOVERSION is used if not specified
+#   ABI version of the library
+#
+# AUTO_SOVERSION : optional, ignored if SOVERSION is specified
+#   use MAJOR package version as ABI version of the library
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+# OUTPUT_NAME : optional
+#   sets the OUTPUT_NAME property on the target
+#
+##############################################################################
+
+function( ecbuild_add_library_impl )
+
+  set( options NOINSTALL AUTO_VERSION AUTO_SOVERSION )
+  set( single_value_args TARGET TYPE COMPONENT INSTALL_HEADERS
+                         INSTALL_HEADERS_REGEX LINKER_LANGUAGE
+                         HEADER_DESTINATION VERSION SOVERSION OUTPUT_NAME )
+  set( multi_value_args  SOURCES SOURCES_GLOB SOURCES_EXCLUDE_REGEX OBJECTS
+                         TEMPLATES LIBS INCLUDES PRIVATE_INCLUDES
+                         PUBLIC_INCLUDES DEPENDS PERSISTENT DEFINITIONS
+                         INSTALL_HEADERS_LIST CFLAGS CXXFLAGS FFLAGS GENERATED
+                         CONDITION PROPERTIES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_library(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_TARGET  )
+    ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+  endif()
+
+  if( NOT _PAR_SOURCES AND NOT _PAR_OBJECTS AND NOT _PAR_SOURCES_GLOB )
+    ecbuild_critical("The call to ecbuild_add_library() specifies neither SOURCES nor OBJECTS nor SOURCES_GLOB")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  if( _${_PAR_TARGET}_condition )
+
+    # defines the type of library
+    if( DEFINED _PAR_TYPE )
+      # checks that is either SHARED or STATIC or MODULE
+      if( NOT _PAR_TYPE MATCHES "STATIC" AND
+          NOT _PAR_TYPE MATCHES "SHARED" AND
+          NOT _PAR_TYPE MATCHES "OBJECT" AND
+          NOT _PAR_TYPE MATCHES "MODULE" )
+        ecbuild_critical( "library type must be one of [ STATIC | SHARED | MODULE | OBJECT ]" )
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): library type is ${_PAR_TYPE}")
+    endif()
+
+    # insert already compiled objects (from OBJECT libraries)
+    unset( _all_objects )
+    foreach( _obj ${_PAR_OBJECTS} )
+      list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+    endforeach()
+
+    # glob sources
+    unset( _glob_srcs )
+    foreach( pattern ${_PAR_SOURCES_GLOB} )
+      ecbuild_list_add_pattern( LIST _glob_srcs GLOB "${pattern}" )
+    endforeach()
+
+    foreach( pattern ${_PAR_SOURCES_EXCLUDE_REGEX} )
+      ecbuild_list_exclude_pattern( LIST _glob_srcs REGEX "${pattern}" )
+    endforeach()
+    list( APPEND _PAR_SOURCES ${_glob_srcs} )
+
+    if( ECBUILD_LIST_SOURCES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): sources ${_PAR_SOURCES}")
+    endif()
+
+    # add persistent layer files
+    if( DEFINED _PAR_PERSISTENT )
+      if( DEFINED PERSISTENT_NAMESPACE )
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+      else()
+        ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+      endif()
+    endif()
+
+    # remove templates from compilation sources
+    if( DEFINED _PAR_TEMPLATES )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): removing ${_PAR_TEMPLATES} from sources")
+      list( REMOVE_ITEM _PAR_SOURCES ${_PAR_TEMPLATES} )
+      add_custom_target( ${_PAR_TARGET}_templates SOURCES ${_PAR_TEMPLATES} )
+    endif()
+
+    # Separate sources
+    if( _PAR_SOURCES )
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+    endif()
+
+    if( ${_PAR_TARGET}_cuda_srcs )
+      if( NOT CUDA_FOUND )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CUDA was not found.")
+      endif()
+      if( _PAR_TYPE MATCHES "OBJECT" )
+          ecbuild_error("ecbuild_add_library(${_PAR_TARGET}): CUDA source files detected"
+                        "but CMake OBJECT libraries with CUDA are not supported.")
+      endif()
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): CUDA sources detected."
+                    "Building library with cuda_add_library() rather than intrinsic"
+                    "add_library().")
+    endif()
+
+    if( NOT ${_PAR_TARGET}_cuda_srcs )
+      add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    else()
+      cuda_add_library( ${_PAR_TARGET} ${_PAR_TYPE} ${_PAR_SOURCES}  ${_all_objects} )
+    endif()
+    # ecbuild_echo_target( ${_PAR_TARGET} )
+
+    # Set custom properties
+    if( ${_PAR_PROPERTIES} )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+    endif()
+
+    # set OUTPUT_NAME
+
+    if( DEFINED _PAR_OUTPUT_NAME )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set OUTPUT_NAME to ${_PAR_OUTPUT_NAME}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES OUTPUT_NAME ${_PAR_OUTPUT_NAME} )
+    endif()
+
+    # add extra dependencies
+    if( DEFINED _PAR_DEPENDS)
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+      add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+    endif()
+
+    # add the link libraries
+    if( DEFINED _PAR_LIBS )
+      list(REMOVE_DUPLICATES _PAR_LIBS )
+      list(REMOVE_ITEM _PAR_LIBS debug)
+      list(REMOVE_ITEM _PAR_LIBS optimized)
+      foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+        if( lib )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): linking with ${lib}")
+          target_link_libraries( ${_PAR_TARGET} ${lib} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${lib} not found - not linking")
+        endif()
+      endforeach()
+    endif()
+
+    # add include dirs if defined
+    if( DEFINED _PAR_INCLUDES )
+      list( REMOVE_DUPLICATES _PAR_INCLUDES )
+      foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" OR ECBUILD_USE_INCLUDE_DIRECTORIES )
+            include_directories( ${path} )
+          else()
+            target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+          endif()
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add private include dirs if defined
+    if( DEFINED _PAR_PRIVATE_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PRIVATE_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PRIVATE_INCLUDES )
+      foreach( path ${_PAR_PRIVATE_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PRIVATE ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # add public include dirs if defined
+    if( DEFINED _PAR_PUBLIC_INCLUDES )
+      if( "${CMAKE_VERSION}" VERSION_LESS "2.8.11" )
+        ecbuild_critical("ecbuild_add_library(${_PAR_TARGET}): cannot use PUBLIC_INCLUDES with CMake < 2.8.11" )
+      endif()
+      list( REMOVE_DUPLICATES _PAR_PUBLIC_INCLUDES )
+      foreach( path ${_PAR_PUBLIC_INCLUDES} ) # skip NOTFOUND
+        if( path )
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): add ${path} to include_directories")
+          target_include_directories( ${_PAR_TARGET} PUBLIC ${path} )
+        else()
+          ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+        endif()
+      endforeach()
+    endif()
+
+    # FIX: Cray compiler PIC option is not detected by CMake
+
+    get_property( _target_pic TARGET ${_PAR_TARGET} PROPERTY POSITION_INDEPENDENT_CODE )
+    if( _target_pic )
+      if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CFLAGS "-fPIC -h PIC ${_PAR_CFLAGS}" )
+      endif()
+      if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_CXXFLAGS "-fPIC -h PIC ${_PAR_CXXFLAGS}" )
+      endif()
+      if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+        set( _PAR_FFLAGS "-fPIC -h PIC ${_PAR_FFLAGS}" )
+      endif()
+    endif()
+
+    # define VERSION if requested
+    if( DEFINED _PAR_VERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${_PAR_VERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${_PAR_VERSION}" )
+    else()
+      if( _PAR_AUTO_VERSION OR LIBS_VERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}" )
+      elseif( DEFINED LIBS_VERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set build version to ${LIBS_VERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES VERSION "${LIBS_VERSION}" )
+      endif()
+    endif()
+
+    # define SOVERSION if requested
+    if( DEFINED _PAR_SOVERSION )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${_PAR_SOVERSION}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${_PAR_SOVERSION}" )
+    else()
+      if( _PAR_AUTO_SOVERSION OR LIBS_SOVERSION MATCHES "[Aa][Uu][Tt][Oo]")
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${${PNAME}_MAJOR_VERSION} (auto)")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${${PNAME}_MAJOR_VERSION}" )
+      elseif( DEFINED LIBS_SOVERSION )
+        ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): set ABI version to ${LIBS_SOVERSION}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES SOVERSION "${LIBS_SOVERSION}" )
+      endif()
+    endif()
+
+    # Override compilation flags on a per source file basis
+    ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+    if( DEFINED _PAR_GENERATED )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+      set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+    endif()
+
+    # set linker language
+    if( DEFINED _PAR_LINKER_LANGUAGE )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      if( ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES )
+        target_link_libraries( ${_PAR_TARGET} ${ECBUILD_${_PAR_LINKER_LANGUAGE}_IMPLICIT_LINK_LIBRARIES} )
+      endif()
+    endif()
+
+    if( ECBUILD_IMPLICIT_LINK_LIBRARIES )
+      target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} )
+    endif()
+
+    # installation (except for OBJECT libraries)
+
+    if( NOT _PAR_NOINSTALL AND NOT _PAR_TYPE MATCHES "OBJECT" )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): installing to ${INSTALL_LIB_DIR}")
+
+      # and associate with defined component
+      #            if( DEFINED _PAR_COMPONENT )
+      #                set( COMPONENT_DIRECTIVE "${_PAR_COMPONENT}" )
+      #            else()
+      #                set( COMPONENT_DIRECTIVE "${PROJECT_NAME}" )
+      #            endif()
+
+      install( TARGETS ${_PAR_TARGET}
+        EXPORT  ${PROJECT_NAME}-targets
+        RUNTIME DESTINATION ${INSTALL_BIN_DIR}
+        LIBRARY DESTINATION ${INSTALL_LIB_DIR}
+        ARCHIVE DESTINATION ${INSTALL_LIB_DIR} )
+      #              COMPONENT ${COMPONENT_DIRECTIVE} )
+
+      # install headers
+      if( _PAR_HEADER_DESTINATION )
+        set( _h_destination "${_PAR_HEADER_DESTINATION}" )
+      else()
+        set( _h_destination "${INSTALL_INCLUDE_DIR}" )
+      endif()
+
+      if( _PAR_INSTALL_HEADERS )
+        if( _PAR_INSTALL_HEADERS MATCHES "LISTED" )
+          foreach( file ${${_PAR_TARGET}_h_srcs} )
+            get_filename_component( _file_dir ${file} PATH )
+            install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+          endforeach()
+          if( DEFINED _PAR_TEMPLATES )
+            foreach( file ${_PAR_TEMPLATES} )
+              get_filename_component( _file_dir ${file} PATH )
+              install( FILES ${file} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+          if( DEFINED _PAR_PERSISTENT )
+            foreach( file ${_PAR_PERSISTENT} )
+              get_filename_component( _file_dir ${file} PATH )
+              get_filename_component( _file_we  ${file} NAME_WE )
+              set( pfile "${CMAKE_CURRENT_BINARY_DIR}/${_file_dir}/${_file_we}.b" )
+              install( FILES ${pfile} DESTINATION "${_h_destination}/${_file_dir}" )
+            endforeach()
+          endif()
+        endif()
+        if( _PAR_INSTALL_HEADERS MATCHES "ALL" ) # "(\\.h|\\.b|\\.hxx|\\.hh|\\.hpp|\\.H)" ????
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.h" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hh" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.hpp" )
+          install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "*.H" )
+        endif()
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_LIST )
+        install( FILES ${_PAR_INSTALL_HEADERS_LIST} DESTINATION ${_h_destination} )
+      endif()
+
+      if( DEFINED _PAR_INSTALL_HEADERS_REGEX )
+        install( DIRECTORY ./  DESTINATION ${_h_destination} FILES_MATCHING PATTERN "${_PAR_INSTALL_HEADERS_REGEX}")
+      endif()
+
+      # set build location
+
+      set_target_properties( ${_PAR_TARGET} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+      # export location of target to other projects -- must be exactly after setting the build location (see previous 2 commands)
+
+      export( TARGETS ${_PAR_TARGET} APPEND FILE "${TOP_PROJECT_TARGETS_FILE}" )
+
+    endif()
+
+    # add definitions to compilation
+    if( DEFINED _PAR_DEFINITIONS )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+      list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      ecbuild_debug("ecbuild_add_library(${_PAR_TARGET}): using definitions ${_target_defs}")
+      set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+    endif()
+
+    # make sure target is removed before - some problems with AIX
+    if( NOT _PAR_TYPE MATCHES "OBJECT" )
+      add_custom_command( TARGET ${_PAR_TARGET} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E remove $<TARGET_FILE:${_PAR_TARGET}> )
+    endif()
+
+    # for the links target
+    if( NOT _PAR_NOINSTALL )
+      ecbuild_link_lib( ${_PAR_TARGET} $<TARGET_FILE_NAME:${_PAR_TARGET}> $<TARGET_FILE:${_PAR_TARGET}>  )
+    endif()
+
+    # append to the list of this project targets
+    set( ${PROJECT_NAME}_ALL_LIBS ${${PROJECT_NAME}_ALL_LIBS} ${_PAR_TARGET} CACHE INTERNAL "" )
+
+  endif()
+
+  # mark source files as used
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+  if( DEFINED _PAR_TEMPLATES )
+    ecbuild_declare_project_files( ${_PAR_TEMPLATES} )
+  endif()
+
+endfunction( ecbuild_add_library_impl  )
+
+##############################################################################
+# auxiliary macro for adding a library
+##############################################################################
+
+macro( ecbuild_add_library )
+
+  set( options  )
+  set( single_value_args TARGET TYPE )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _p_TYPE ) # don't do anything if TYPE was specified
+
+    if( _p_TYPE MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+      ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} OUTPUT_NAME ${_p_TARGET} DEPENDS ${_p_TARGET} )
+
+    else()
+
+      ecbuild_add_library_impl( ${ARGV} )
+
+    endif()
+
+  else()
+
+    if( NOT DEFINED _p_TARGET )
+      ecbuild_critical("The call to ecbuild_add_library() doesn't specify the TARGET.")
+    else()
+
+      if( BUILD_SHARED_LIBS MATCHES "[Bb][Oo][Tt][Hh]" ) # build both types
+
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}        TYPE SHARED ${_p_UNPARSED_ARGUMENTS} )
+        ecbuild_add_library_impl( TARGET ${_p_TARGET}-static TYPE STATIC ${_p_UNPARSED_ARGUMENTS} DEPENDS ${_p_TARGET} )
+
+        # If the library is built conditionally the target might not exist
+        if ( TARGET ${_p_TARGET}-static )
+          set_target_properties( ${_p_TARGET}-static PROPERTIES OUTPUT_NAME ${_p_TARGET} )
+        endif()
+
+      else()
+
+        ecbuild_add_library_impl( ${ARGV} )
+
+      endif()
+
+    endif()
+
+  endif()
+
+endmacro( ecbuild_add_library )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_option.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_option.cmake
new file mode 100644
index 0000000..6d5363f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_option.cmake
@@ -0,0 +1,348 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_option
+# ==================
+#
+# Add a CMake configuration option, which may depend on a list of packages. ::
+#
+#   ecbuild_add_option( FEATURE <name>
+#                       [ DEFAULT ON|OFF ]
+#                       [ DESCRIPTION <description> ]
+#                       [ REQUIRED_PACKAGES <package1> [<package2> ...] ]
+#                       [ CONDITION <condition> ]
+#                       [ ADVANCED ] [ NO_TPL ] )
+#
+# Options
+# -------
+#
+# FEATURE : required
+#   name of the feature / option
+#
+# DEFAULT : optional, defaults to ON
+#   if set to ON, the feature is enabled even if not explicitly requested
+#
+# DESCRIPTION : optional
+#   string describing the feature (shown in summary and stored in the cache)
+#
+# REQUIRED_PACKAGES : optional
+#   list of packages required to be found for this feature to be enabled
+#
+#   The package specification can have one of two forms. Either ::
+#
+#     "<package> [ <version> ... ]"
+#
+#   to search for a given package using the CMake ``find_package`` mechanism.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``find_package`` are supported.
+#
+#   The other specification must start with ``PROJECT`` like this ::
+#
+#     "PROJECT <name> [ VERSION <version> ... ]"
+#
+#   and is used to search for an ecBuild project via ``ecbuild_use_package``.
+#   The entire specification must be enclosed in quotes and is passed on
+#   verbatim. Any options of ``ecbuild_use_package`` are supported.
+#
+#   .. note::
+#
+#     Arguments inside the package string that require quoting need to use the
+#     `bracket argument syntax`_ introduced in CMake 3.0 since
+#     regular quotes even when escaped are swallowed by the CMake parser.
+#
+#     Alternatively, the name of a CMake variable containing the string can be
+#     passed, which will be expanded by ``ecbuild_find_package``: ::
+#
+#       set( ECCODES_FAIL_MSG
+#            "grib_api can be used instead (select with -DENABLE_ECCODES=OFF)" )
+#       ecbuild_add_option( FEATURE ECCODES
+#                           DESCRIPTION "Use eccodes instead of grib_api"
+#                           REQUIRED_PACKAGES "PROJECT eccodes REQUIRED FAILURE_MSG ECCODES_FAIL_MSG"
+#                           DEFAULT ON )
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this option to be
+#   enabled (must be valid in a CMake ``if`` statement)
+#
+# ADVANCED : optional
+#   mark the feature as advanced
+#
+# NO_TPL : optional
+#   do not add any ``REQUIRED_PACKAGES`` to the list of third party libraries
+#
+# Usage
+# -----
+#
+# Features with ``DEFAULT OFF`` need to be explcitly enabled by the user with
+# ``-DENABLE_<FEATURE>=ON``. If a feature is enabled, all ``REQUIRED_PACKAGES``
+# are found and ``CONDITION`` is met, ecBuild sets the variable
+# ``HAVE_<FEATURE>`` to ``ON``. This is the variable to use to check for the
+# availability of the feature.
+#
+# If a feature is explicitly enabled but the required packages are not found,
+# configuration fails. This only applies when configuring from *clean cache*.
+# With an already populated cache, use ``-DENABLE_<FEATURE>=REQUIRE`` to make
+# the feature a required feature (this cannot be done via the CMake GUI).
+#
+# .. _bracket argument syntax: https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument
+#
+##############################################################################
+
+macro( ecbuild_add_option )
+
+  set( options ADVANCED NO_TPL )
+  set( single_value_args FEATURE DEFAULT DESCRIPTION TYPE PURPOSE )
+  set( multi_value_args  REQUIRED_PACKAGES CONDITION )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( _p_UNPARSED_ARGUMENTS )
+    ecbuild_critical("Unknown keywords given to ecbuild_add_option(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # check FEATURE parameter
+
+  if( NOT _p_FEATURE  )
+    ecbuild_critical("The call to ecbuild_add_option() doesn't specify the FEATURE.")
+  endif()
+
+  # check DEFAULT parameter
+
+  if( NOT DEFINED _p_DEFAULT )
+    set( _p_DEFAULT ON )
+  else()
+    if( NOT _p_DEFAULT MATCHES "[Oo][Nn]" AND NOT _p_DEFAULT MATCHES "[Oo][Ff][Ff]" )
+      ecbuild_critical("In macro ecbuild_add_option(), DEFAULT is either ON or OFF: \"${_p_DEFAULT}\"")
+    endif()
+  endif()
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defaults to ${_p_DEFAULT}")
+
+  if( _p_PURPOSE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument PURPOSE is ignored and will be removed in a future release." )
+  endif()
+  if( _p_TYPE  )
+    ecbuild_deprecate( "ecbuild_add_option: argument TYPE is ignored and will be removed in a future release." )
+  endif()
+
+  # check CONDITION parameter
+  if( DEFINED _p_CONDITION )
+    set(_feature_condition_file "${CMAKE_CURRENT_BINARY_DIR}/set_${_p_FEATURE}_condition.cmake")
+    file( WRITE  ${_feature_condition_file} "  if( ")
+    foreach( term ${_p_CONDITION} )
+      file( APPEND ${_feature_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_feature_condition_file} " )\n    set(_${_p_FEATURE}_condition TRUE)\n  else()\n    set(_${_p_FEATURE}_condition FALSE)\n  endif()\n")
+    include( ${_feature_condition_file} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): checking condition ${_p_CONDITION} -> ${_${_p_FEATURE}_condition}")
+  else()
+    set( _${_p_FEATURE}_condition TRUE )
+  endif()
+
+  # Check if user explicitly enabled/disabled the feature in cache
+  get_property( _in_cache CACHE ENABLE_${_p_FEATURE} PROPERTY VALUE SET )
+
+  # A feature set to REQUIRE is always treated as explicitly enabled
+  if( ENABLE_${_p_FEATURE} MATCHES "REQUIRE" )
+    set( ENABLE_${_p_FEATURE} ON CACHE BOOL "" FORCE )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} was required")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" FORCE )
+  elseif( NOT ENABLE_${_p_FEATURE} STREQUAL "" AND _in_cache )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}} was found in cache")
+    set( ${_p_FEATURE}_user_provided_input 1 CACHE BOOL "" )
+  else()
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE} not found in cache")
+    set( ${_p_FEATURE}_user_provided_input 0 CACHE BOOL "" )
+  endif()
+
+  mark_as_advanced( ${_p_FEATURE}_user_provided_input )
+
+
+  # define the option -- for cmake GUI
+
+  option( ENABLE_${_p_FEATURE} "${_p_DESCRIPTION}" ${_p_DEFAULT} )
+  get_property( _feature_desc GLOBAL PROPERTY _CMAKE_${_p_FEATURE}_DESCRIPTION )
+  if( _feature_desc )
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${_feature_desc}, ${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  else()
+    add_feature_info( ${_p_FEATURE} ENABLE_${_p_FEATURE} "${PROJECT_NAME}: ${_p_DESCRIPTION}" )
+  endif()
+
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): defining option ENABLE_${_p_FEATURE} '${_p_DESCRIPTION}' ${_p_DEFAULT}")
+  ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ENABLE_${_p_FEATURE}=${ENABLE_${_p_FEATURE}}")
+
+  if( ENABLE_${_p_FEATURE} )
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature enabled")
+
+    set( HAVE_${_p_FEATURE} 1 )
+
+    if( _${_p_FEATURE}_condition )
+
+      ### search for dependent packages
+
+      set( _failed_to_find_packages )  # clear variable
+      foreach( pkg ${_p_REQUIRED_PACKAGES} )
+        ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for dependent package ${pkg}")
+
+        string(REPLACE " " ";" pkglist ${pkg}) # string to list
+
+        list( GET pkglist 0 pkgname )
+
+        if( pkgname STREQUAL "PROJECT" )  # if 1st entry is PROJECT, then we are looking for a ecbuild project
+          set( pkgproject 1 )
+          list( GET pkglist 1 pkgname )
+          # Use feature description as package description if there is none
+          list( FIND pkglist DESCRIPTION __description )
+          if( __description LESS 0 )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): no description for ${pkgname}, using feature description '${_p_DESCRIPTION}'")
+            list( APPEND pkglist DESCRIPTION "${_p_DESCRIPTION}" )
+          endif()
+        else()                            # else 1st entry is package name
+          set( pkgproject 0 )
+        endif()
+
+        # ecbuild_debug_var( pkg )
+        # ecbuild_debug_var( pkglist )
+        # ecbuild_debug_var( pkgname )
+
+        string( TOUPPER ${pkgname} pkgUPPER )
+        string( TOLOWER ${pkgname} pkgLOWER )
+
+        set( __help_msg "Provide ${pkgname} location with -D${pkgUPPER}_PATH=/..." )
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+
+          ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): ${pkgname} has already been found")
+          set( ${pkgname}_already_found 1 )
+
+        else()
+
+          if( pkgproject )
+
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for ecbuild project ${pkgname} - ecbuild_use_package( ${pkglist} )")
+            ecbuild_use_package( ${pkglist} )
+
+          else()
+
+            if( pkgname STREQUAL "LAPACK" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for LAPACK - ecbuild_find_package( NAME ${pkglist} )")
+              ecbuild_find_package( NAME ${pkglist} )
+              if( HAVE_LAPACK AND TARGET lapack )
+                ecbuild_debug( "LAPACK found as CMake target lapack" )
+                set( LAPACK_LIBRARIES lapack )
+              endif()
+            elseif( pkgname STREQUAL "MPI" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "MPI" )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for MPI - ecbuild_find_mpi( ${_find_args} )")
+              ecbuild_find_mpi( ${_find_args} )
+            elseif( pkgname STREQUAL "OMP" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args "OMP" )
+              if( NOT ENABLE_${_p_FEATURE} )
+                list( APPEND _find_args STUBS )
+              endif()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for OpenMP - ecbuild_find_omp( ${_find_args} )")
+              ecbuild_find_omp( ${_find_args} )
+            elseif( pkgname STREQUAL "Python" OR pkgname STREQUAL "PYTHON" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for Python - ecbuild_find_python( ${_find_args} )")
+              ecbuild_find_python( ${_find_args} )
+              set( __help_msg "Specify the location of the Python interpreter with -DPYTHON_EXECUTABLE=/..." )
+            elseif( pkgname STREQUAL "LEXYACC" )
+              set( _find_args ${pkglist} )
+              list( REMOVE_ITEM _find_args ${pkgname} )
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for lex-yacc - ecbuild_find_lexyacc( ${_find_args} )")
+              ecbuild_find_lexyacc( ${_find_args} )
+            else()
+              ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): searching for package ${pkgname} - find_package( ${pkglist} )")
+              find_package( ${pkglist} )
+            endif()
+
+          endif()
+
+        endif()
+
+        # ecbuild_debug_var( ${pkgname}_FOUND  )
+        # ecbuild_debug_var( ${pkgLOWER}_FOUND )
+        # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+
+        # we have feature if all required packages were FOUND
+
+        if( ${pkgname}_FOUND OR ${pkgUPPER}_FOUND OR ${pkgLOWER}_FOUND )
+          ecbuild_info( "Found package ${pkgname} required for feature ${_p_FEATURE}" )
+
+          # append to list of third-party libraries (to be forward to other packages )
+          # unless the NO_TPL option was given
+          if( NOT _p_NO_TPL )
+            ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): appending ${pkgname} to ${PROJECT_NAME_CAPS}_TPLS")
+            list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${pkgname} )
+            list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+          endif()
+
+        else()
+          ecbuild_info( "Could NOT find package ${pkgname} required for feature ${_p_FEATURE} -- ${__help_msg}" )
+          set( HAVE_${_p_FEATURE} 0 )
+          list( APPEND _failed_to_find_packages ${pkgname} )
+        endif()
+
+      endforeach()
+    else( _${_p_FEATURE}_condition )
+      set( HAVE_${_p_FEATURE} 0 )
+    endif( _${_p_FEATURE}_condition )
+
+    # FINAL CHECK
+
+    if( HAVE_${_p_FEATURE} )
+
+      ecbuild_enable_feature( ${_p_FEATURE} )
+
+      ecbuild_info( "Feature ${_p_FEATURE} enabled" )
+
+    else() # if user provided input and we cannot satisfy FAIL otherwise WARN
+
+      ecbuild_disable_feature( ${_p_FEATURE} )
+
+      if( ${_p_FEATURE}_user_provided_input )
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_critical( "Feature ${_p_FEATURE} cannot be enabled -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+      else()
+        if( NOT _${_p_FEATURE}_condition )
+          string(REPLACE ";" " " _condition_msg "${_p_CONDITION}")
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following condition was not met: ${_condition_msg}" )
+        else()
+          ecbuild_info( "Feature ${_p_FEATURE} was not enabled (also not requested) -- following required packages weren't found: ${_failed_to_find_packages}" )
+        endif()
+        set( ENABLE_${_p_FEATURE} OFF )
+        ecbuild_disable_feature( ${_p_FEATURE} )
+      endif()
+
+    endif()
+
+  else()
+
+    ecbuild_debug("ecbuild_add_option(${_p_FEATURE}): feature disabled")
+    set( HAVE_${_p_FEATURE} 0 )
+    ecbuild_disable_feature( ${_p_FEATURE} )
+
+  endif()
+
+
+  if( ${_p_ADVANCED} )
+    mark_as_advanced( ENABLE_${_p_FEATURE} )
+  endif()
+
+  set( ${PROJECT_NAME_CAPS}_HAVE_${_p_FEATURE} ${HAVE_${_p_FEATURE}} )
+
+endmacro( ecbuild_add_option  )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_persistent.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_persistent.cmake
new file mode 100644
index 0000000..e5a875b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_persistent.cmake
@@ -0,0 +1,85 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_persistent
+# ======================
+#
+# Add persistent layer object classes. ::
+#
+#   ecbuild_add_persistent( SRC_LIST <variable>
+#                           FILES <file1> [<file2> ...] ]
+#                           [ NAMESPACE <namespace> ] )
+#
+# Options
+# -------
+#
+# SRC_LIST : required
+#   CMake variable to append the generated persistent layer objects to
+#
+# FILES : required
+#   list of base names of files to build persistent class information for
+#
+#   The source file is expected to have a .h extension, the generated file
+#   gets a .b extension.
+#
+# NAMESPACE : optional
+#   C++ namespace to place the persistent class information in
+#
+##############################################################################
+
+# define the script to build the persistent class information
+set( sg_perl "${CMAKE_CURRENT_LIST_DIR}/sg.pl" CACHE INTERNAL "perl script to generate persistent objects" )
+
+macro( ecbuild_add_persistent )
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args SRC_LIST NAMESPACE )
+  set( multi_value_args  FILES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_persistent(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SRC_LIST  )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the SRC_LIST.")
+  endif()
+
+  if( NOT _PAR_FILES )
+    ecbuild_critical("The call to ecbuild_add_persistent() doesn't specify the FILES.")
+  endif()
+
+  foreach( file ${_PAR_FILES} )
+
+    get_filename_component( _file_dir    ${file} PATH )
+    get_filename_component( _file_we     ${file} NAME_WE )
+
+    set( file ${_file_we} )
+    if( _file_dir )
+      file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} )
+      set( file ${_file_dir}/${_file_we} )
+    endif()
+
+    # ecbuild_debug_var(file)
+
+    add_custom_command( OUTPUT  ${file}.b
+                        COMMAND ${PERL_EXECUTABLE} ${sg_perl} ${CMAKE_CURRENT_SOURCE_DIR}/${file}.h
+                                ${CMAKE_CURRENT_BINARY_DIR}/${_file_dir} ${_PAR_NAMESPACE}
+                        DEPENDS ${sg_perl} ${file}.h )
+    set_source_files_properties( ${file}.h PROPERTIES OBJECT_DEPENDS "${file}.b" )
+    list( APPEND ${_PAR_SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/${file}.b )
+
+  endforeach()
+
+endmacro( ecbuild_add_persistent  )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_resources.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_resources.cmake
new file mode 100644
index 0000000..2dfaa5e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_resources.cmake
@@ -0,0 +1,151 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_resources
+# =====================
+#
+# Add resources as project files but optionally exclude them from packaging. ::
+#
+#   ecbuild_add_resources( TARGET <name>
+#                          [ SOURCES <source1> [<source2> ...] ]
+#                          [ SOURCES_PACK <source1> [<source2> ...] ]
+#                          [ SOURCES_DONT_PACK <source1> [<source2> ...] ]
+#                          [ PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK <file1> [<file2> ...] ]
+#                          [ DONT_PACK_DIRS <directory1> [<directory2> ...] ]
+#                          [ DONT_PACK_REGEX <regex1> [<regex2> ...] ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name (target will only be created if there are any sources)
+#
+# SOURCES : optional, alias for SOURCES_PACK
+#   list of source files included when packaging
+#
+# SOURCES_PACK : optional, alias for SOURCES
+#   list of source files included when packaging
+#
+# SOURCES_DONT_PACK : optional
+#   list of source files excluded when packaging
+#
+# PACK : optional, priority over DONT_PACK, DONT_PACK_DIRS, DONT_PACK_REGEX
+#   list of files to include when packaging
+#
+# DONT_PACK : optional
+#   list of files to exclude when packaging
+#
+# DONT_PACK_DIRS : optional
+#   list of directories to exclude when packaging
+#
+# DONT_PACK_REGEX : optional
+#   list of regular expressions to match files and directories to exclude when
+#   packaging
+#
+##############################################################################
+
+macro( ecbuild_add_resources )
+
+    set( options )
+    set( single_value_args TARGET )
+    set( multi_value_args  SOURCES SOURCES_PACK SOURCES_DONT_PACK PACK DONT_PACK DONT_PACK_DIRS DONT_PACK_REGEX )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_add_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_TARGET  )
+      ecbuild_critical("The call to ecbuild_add_resources() doesn't specify the TARGET.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_DONT_PACK_REGEX )
+        foreach( exp ${_PAR_DONT_PACK_REGEX} )
+            file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${exp} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+        endforeach()
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DONT_PACK_DIRS )
+        foreach( dir ${_PAR_DONT_PACK_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_DONT_PACK} )
+    endif()
+
+    # now lets remove files that we want to pack from the list
+    # note that these have priority over the files not to pack
+    # so we can GLOB_RECURSE * -> DONT_PACK and then select only the ones we pack
+
+    # files to pack but are not project files
+    if( DEFINED _PAR_PACK )
+        foreach( file ${_PAR_PACK} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # define as project files, but dont pack them
+    if( DEFINED _PAR_SOURCES_DONT_PACK )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_SOURCES_DONT_PACK} )
+		foreach( sfile ${_PAR_SOURCES_DONT_PACK} )
+			set( _full_sfile "${CMAKE_CURRENT_SOURCE_DIR}/${sfile}" )
+	        if( EXISTS ${_full_sfile} )
+				list( APPEND ${_PAR_TARGET}_files ${_full_sfile} )
+			endif()
+		endforeach()
+    endif()
+
+    # define as project files and pack them
+    # SOURCES_PACK is alias to SOURCES
+    if( DEFINED _PAR_SOURCES_PACK )
+        list( APPEND _PAR_SOURCES ${_PAR_SOURCES_PACK} )
+    endif()
+    if( DEFINED _PAR_SOURCES )
+        list( APPEND ${_PAR_TARGET}_files ${_PAR_SOURCES} )
+        foreach( file ${_PAR_SOURCES} )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endforeach()
+    endif()
+
+    # there are project files, so lets create the target
+    if( DEFINED ${_PAR_TARGET}_files )
+        add_custom_target( ${_PAR_TARGET} SOURCES ${${_PAR_TARGET}_files} )
+    endif()
+
+    # remove CMakeLists.txt
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        if( ${file} MATCHES "CMakeLists.txt" )
+            list( REMOVE_ITEM LOCAL_FILES_NOT_TO_PACK ${file} )
+        endif()
+    endforeach()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro( ecbuild_add_resources  )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_add_test.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_add_test.cmake
new file mode 100644
index 0000000..da0bd22
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_add_test.cmake
@@ -0,0 +1,506 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_add_test
+# ================
+#
+# Add a test as a script or an executable with a given list of source files. ::
+#
+#   ecbuild_add_test( [ TARGET <name> ]
+#                     [ SOURCES <source1> [<source2> ...] ]
+#                     [ OBJECTS <obj1> [<obj2> ...] ]
+#                     [ COMMAND <executable> ]
+#                     [ TYPE EXE|SCRIPT|PYTHON ]
+#                     [ LABELS <label1> [<label2> ...] ]
+#                     [ ARGS <argument1> [<argument2> ...] ]
+#                     [ RESOURCES <file1> [<file2> ...] ]
+#                     [ TEST_DATA <file1> [<file2> ...] ]
+#                     [ BOOST ]
+#                     [ MPI <number-of-mpi-tasks> ]
+#                     [ OMP <number-of-threads-per-mpi-task> ]
+#                     [ ENABLED ON|OFF ]
+#                     [ LIBS <library1> [<library2> ...] ]
+#                     [ INCLUDES <path1> [<path2> ...] ]
+#                     [ DEFINITIONS <definition1> [<definition2> ...] ]
+#                     [ PERSISTENT <file1> [<file2> ...] ]
+#                     [ GENERATED <file1> [<file2> ...] ]
+#                     [ DEPENDS <target1> [<target2> ...] ]
+#                     [ TEST_DEPENDS <target1> [<target2> ...] ]
+#                     [ CONDITION <condition> ]
+#                     [ PROPERTIES <prop1> <val1> [<prop2> <val2> ...] ]
+#                     [ ENVIRONMENT <variable1> [<variable2> ...] ]
+#                     [ WORKING_DIRECTORY <path> ]
+#                     [ CFLAGS <flag1> [<flag2> ...] ]
+#                     [ CXXFLAGS <flag1> [<flag2> ...] ]
+#                     [ FFLAGS <flag1> [<flag2> ...] ]
+#                     [ LINKER_LANGUAGE <lang> ] )
+#
+# Options
+# -------
+#
+# TARGET : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   target name to be built
+#
+# SOURCES : required if TARGET is provided
+#   list of source files to be compiled
+#
+# OBJECTS : optional
+#   list of object libraries to add to this target
+#
+# COMMAND : either TARGET or COMMAND must be provided, unless TYPE is PYTHON
+#   command or script to execute (no executable is built)
+#
+# TYPE : optional
+#   test type, one of:
+#
+#   :EXE:    run built executable, default if TARGET is provided
+#   :SCRIPT: run command or script, default if COMMAND is provided
+#   :PYTHON: run a Python script (requires the Python interpreter to be found)
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   The project name in lower case is always added as a label. Additional
+#   labels are assigned depending on the type of test:
+#
+#   :executable: for type ``EXE``
+#   :script:     for type ``SCRIPT``
+#   :python:     for type ``PYTHON``
+#   :boost:      uses Boost unit test
+#   :mpi:        if ``MPI`` is set
+#   :openmp:     if ``OMP`` is set
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# ARGS : optional
+#   list of arguments to pass to TARGET or COMMAND when running the test
+#
+# RESOURCES : optional
+#   list of files to copy from the test source directory to the test directory
+#
+# TEST_DATA : optional
+#   list of test data files to download
+#
+# BOOST : optional
+#   use the Boost Unit Test Framework
+#
+# MPI : optional
+#   Run with MPI using the given number of MPI tasks.
+#
+#   If greater than 1, and ``MPIEXEC`` is not available, the test is disabled.
+#
+# OMP : optional
+#   number of OpenMP threads per MPI task to use.
+#
+#   If set, the environment variable OMP_NUM_THREADS will set.
+#   Also, in case of launchers like aprun, the OMP_NUMTHREADS_FLAG will be used.
+#
+# ENABLED : optional
+#   if set to OFF, the test is built but not enabled as a test case
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# PERSISTENT : optional
+#   list of persistent layer object files
+#
+# GENERATED : optional
+#   list of files to mark as generated (sets GENERATED source file property)
+#
+# DEPENDS : optional
+#   list of targets to be built before this target
+#
+# TEST_DEPENDS : optional
+#   list of tests to be run before this one
+#
+# CONDITION : optional
+#   conditional expression which must evaluate to true for this target to be
+#   built (must be valid in a CMake ``if`` statement)
+#
+# PROPERTIES : optional
+#   custom properties to set on the target
+#
+# ENVIRONMENT : optional
+#   list of environment variables to set in the test environment
+#
+# WORKING_DIRECTORY : optional
+#   directory to switch to before running the test
+#
+# CFLAGS : optional
+#   list of C compiler flags to use for all C source files
+#
+# CXXFLAGS : optional
+#   list of C++ compiler flags to use for all C++ source files
+#
+# FFLAGS : optional
+#   list of Fortran compiler flags to use for all Fortran source files
+#
+# LINKER_LANGUAGE : optional
+#   sets the LINKER_LANGUAGE property on the target
+#
+##############################################################################
+
+macro( ecbuild_add_test )
+
+  set( options           BOOST )
+  set( single_value_args TARGET ENABLED COMMAND TYPE LINKER_LANGUAGE MPI OMP WORKING_DIRECTORY )
+  set( multi_value_args  SOURCES OBJECTS LIBS INCLUDES TEST_DEPENDS DEPENDS LABELS ARGS
+                         PERSISTENT DEFINITIONS RESOURCES TEST_DATA CFLAGS
+                         CXXFLAGS FFLAGS GENERATED CONDITION PROPERTIES ENVIRONMENT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_test(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  set( _TEST_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+
+  # Undocumented flag for disabling all MPI tests for test environment without suitable MPI(EXEC)
+  if( _PAR_MPI AND ECBUILD_DISABLE_MPI_TESTS )
+    ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ECBUILD_DISABLE_MPI_TESTS set - disabling test")
+    set( _PAR_ENABLED 0 )
+  elseif( _PAR_MPI )
+    # Check for MPIEXEC if it not set
+    find_program( MPIEXEC NAMES mpiexec mpirun lamexec srun
+                  DOC "Executable for running MPI programs." )
+    if( MPIEXEC )
+      set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC")
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): Running using ${MPIEXEC} on ${_PAR_MPI} MPI rank(s)")
+      set( _PAR_LABELS mpi ${_PAR_LABELS} )
+    elseif( _PAR_MPI GREATER 1 )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${_PAR_MPI} MPI ranks requested but MPIEXEC not available - disabling test")
+      set( _PAR_ENABLED 0 )
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): 1 MPI rank requested but MPIEXEC not available - running sequentially")
+      set( _PAR_MPI 0 )
+    endif()
+  endif()
+
+  # Check for OMP
+  if( DEFINED _PAR_OMP )
+    set( _PAR_LABELS openmp ${_PAR_LABELS} )
+  else()
+    set( _PAR_OMP 1 )
+  endif()
+  list( APPEND _PAR_ENVIRONMENT "OMP_NUM_THREADS=${_PAR_OMP}" )
+
+
+  # default is enabled
+  if( NOT DEFINED _PAR_ENABLED )
+    set( _PAR_ENABLED 1 )
+  endif()
+
+
+  ### check test type
+
+  # command implies script
+  if( DEFINED _PAR_COMMAND )
+    set( _PAR_TYPE "SCRIPT" )
+    set( _PAR_LABELS script ${_PAR_LABELS} )
+  endif()
+
+  # default of TYPE
+  if( NOT _PAR_TYPE AND DEFINED _PAR_TARGET )
+    set( _PAR_TYPE "EXE" )
+    set( _PAR_LABELS executable ${_PAR_LABELS} )
+    if( NOT _PAR_SOURCES )
+      ecbuild_critical("The call to ecbuild_add_test() defines a TARGET without SOURCES.")
+    endif()
+  endif()
+
+  if( _PAR_TYPE MATCHES "PYTHON" )
+    if( PYTHONINTERP_FOUND )
+      set( _PAR_COMMAND ${PYTHON_EXECUTABLE} )
+      set( _PAR_LABELS python ${_PAR_LABELS} )
+    else()
+      ecbuild_warn( "Requested a python test but python interpreter not found - disabling test\nPYTHON_EXECUTABLE: [${PYTHON_EXECUTABLE}]" )
+      set( _PAR_ENABLED 0 )
+    endif()
+  endif()
+
+  ### further checks
+
+  if( _PAR_ENABLED AND NOT _PAR_TARGET AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a TARGET nor a COMMAND.")
+  endif()
+
+  if( _PAR_ENABLED AND NOT _PAR_COMMAND AND NOT _PAR_SOURCES )
+    ecbuild_critical("The call to ecbuild_add_test() defines neither a COMMAND nor SOURCES, so no test can be defined or built.")
+  endif()
+
+  if( _PAR_TYPE MATCHES "SCRIPT" AND NOT _PAR_COMMAND )
+    ecbuild_critical("The call to ecbuild_add_test() defines a 'script' but doesn't specify the COMMAND.")
+  endif()
+
+  ### conditional build
+
+  if( DEFINED _PAR_CONDITION )
+    set(_target_condition_file "${_TEST_DIR}/set_${_PAR_TARGET}_condition.cmake")
+    file( WRITE  ${_target_condition_file} "  if( ")
+    foreach( term ${_PAR_CONDITION} )
+      file( APPEND ${_target_condition_file} " ${term}")
+    endforeach()
+    file( APPEND ${_target_condition_file} " )\n    set(_${_PAR_TARGET}_condition TRUE)\n  else()\n    set(_${_PAR_TARGET}_condition FALSE)\n  endif()\n")
+    include( ${_target_condition_file} )
+  else()
+    set( _${_PAR_TARGET}_condition TRUE )
+  endif()
+
+  # boost unit test linking to unit_test lib ?
+
+  if( _PAR_BOOST AND ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    if( HAVE_BOOST_UNIT_TEST )
+      set( _PAR_LABELS boost ${_PAR_LABELS} )
+      if( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} )
+        include_directories( ${Boost_INCLUDE_DIRS}  ) # temporary until we ship Boost Unit Test with ecBuild
+      else()
+        include_directories( ${ECBUILD_BOOST_HEADER_DIRS} ${Boost_INCLUDE_DIRS} )
+      endif()
+    else()
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): boost unit test framework not available - not building test")
+      set( _${_PAR_TARGET}_condition FALSE )
+    endif()
+
+  endif()
+
+  ### enable the tests
+
+  if( ENABLE_TESTS AND _${_PAR_TARGET}_condition )
+
+    # add resources
+
+    if( DEFINED _PAR_RESOURCES )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): copying resources ${_PAR_RESOURCES}")
+      foreach( rfile ${_PAR_RESOURCES} )
+        execute_process( COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${rfile} ${_TEST_DIR} )
+      endforeach()
+    endif()
+
+    # build executable
+
+    if( DEFINED _PAR_SOURCES )
+
+      # add include dirs if defined
+      if( DEFINED _PAR_INCLUDES )
+        list(REMOVE_DUPLICATES _PAR_INCLUDES )
+        foreach( path ${_PAR_INCLUDES} ) # skip NOTFOUND
+          if( path )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add ${path} to include_directories")
+            include_directories( ${path} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${path} not found - not adding to include_directories")
+          endif()
+        endforeach()
+      endif()
+
+      # add persistent layer files
+      if( DEFINED _PAR_PERSISTENT )
+        if( DEFINED PERSISTENT_NAMESPACE )
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} NAMESPACE ${PERSISTENT_NAMESPACE} )
+        else()
+          ecbuild_add_persistent( SRC_LIST _PAR_SOURCES FILES ${_PAR_PERSISTENT} )
+        endif()
+      endif()
+
+      # insert already compiled objects (from OBJECT libraries)
+      unset( _all_objects )
+      foreach( _obj ${_PAR_OBJECTS} )
+        list( APPEND _all_objects $<TARGET_OBJECTS:${_obj}> )
+      endforeach()
+
+      add_executable( ${_PAR_TARGET} ${_PAR_SOURCES} ${_all_objects} )
+
+      # add extra dependencies
+      if( DEFINED _PAR_DEPENDS)
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): add dependency on ${_PAR_DEPENDS}")
+        add_dependencies( ${_PAR_TARGET} ${_PAR_DEPENDS} )
+      endif()
+
+      # add the link libraries
+      if( DEFINED _PAR_LIBS )
+        list(REMOVE_DUPLICATES _PAR_LIBS )
+        list(REMOVE_ITEM _PAR_LIBS debug)
+        list(REMOVE_ITEM _PAR_LIBS optimized)
+        foreach( lib ${_PAR_LIBS} ) # skip NOTFOUND
+          if( lib )
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): linking with ${lib}")
+            target_link_libraries( ${_PAR_TARGET} ${lib} )
+          else()
+            ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): ${lib} not found - not linking")
+          endif()
+        endforeach()
+      endif()
+
+      # add test libraries
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_LINKED AND HAVE_BOOST_UNIT_TEST )
+        target_link_libraries( ${_PAR_TARGET} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${Boost_TEST_EXEC_MONITOR_LIBRARY} )
+      endif()
+
+      # filter sources
+      ecbuild_separate_sources( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} )
+
+      # Override compilation flags on a per source file basis
+      ecbuild_target_flags( ${_PAR_TARGET} "${_PAR_CFLAGS}" "${_PAR_CXXFLAGS}" "${_PAR_FFLAGS}" )
+
+      if( DEFINED _PAR_GENERATED )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): mark as generated ${_PAR_GENERATED}")
+        set_source_files_properties( ${_PAR_GENERATED} PROPERTIES GENERATED 1 )
+      endif()
+
+      # modify definitions to compilation ( -D... )
+      get_property( _target_defs TARGET ${_PAR_TARGET} PROPERTY COMPILE_DEFINITIONS )
+
+      if( DEFINED _PAR_DEFINITIONS )
+        list( APPEND _target_defs ${_PAR_DEFINITIONS} )
+      endif()
+
+      if( _PAR_BOOST AND BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+        list( APPEND _target_defs BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY )
+      endif()
+
+      if( _target_defs )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using definitions ${_target_defs}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES COMPILE_DEFINITIONS "${_target_defs}" )
+      endif()
+
+      # set build location to local build dir
+      # not the project base as defined for libs and execs
+      set_target_properties( ${_PAR_TARGET} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${_TEST_DIR} )
+
+      # whatever project settings are, we always build tests with the build_rpath, not the install_rpath
+      set_target_properties( ${_PAR_TARGET} PROPERTIES BUILD_WITH_INSTALL_RPATH FALSE )
+      set_target_properties( ${_PAR_TARGET} PROPERTIES SKIP_BUILD_RPATH         FALSE )
+
+      # set linker language
+      if( DEFINED _PAR_LINKER_LANGUAGE )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): using linker language ${_PAR_LINKER_LANGUAGE}")
+        set_target_properties( ${_PAR_TARGET} PROPERTIES LINKER_LANGUAGE ${_PAR_LINKER_LANGUAGE} )
+      endif()
+
+      # make sure target is removed before - some problems with AIX
+      get_target_property(EXE_FILENAME ${_PAR_TARGET} OUTPUT_NAME)
+      add_custom_command( TARGET ${_PAR_TARGET}
+                          PRE_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E remove ${EXE_FILENAME} )
+
+    endif() # _PAR_SOURCES
+
+    if( DEFINED _PAR_COMMAND AND NOT _PAR_TARGET ) # in the absence of target, we use the command as a name
+      set( _PAR_TARGET ${_PAR_COMMAND} )
+    endif()
+
+    # scripts dont have actual build targets
+    # we build a phony target to trigger the dependencies
+    if( DEFINED _PAR_COMMAND AND DEFINED _PAR_DEPENDS )
+
+      add_custom_target( ${_PAR_TARGET}.x ALL COMMAND ${CMAKE_COMMAND} -E touch ${_PAR_TARGET}.x )
+
+      add_dependencies( ${_PAR_TARGET}.x ${_PAR_DEPENDS} )
+
+    endif()
+
+
+    # define the arguments
+    set( TEST_ARGS "" )
+    # Boost Unit Test >= 1.60 requires arguments to be passed to the application to be separated by --
+    if( DEFINED _PAR_ARGS AND _PAR_BOOST )
+      list( APPEND TEST_ARGS "--" ${_PAR_ARGS} )
+    elseif( DEFINED _PAR_ARGS )
+      list( APPEND TEST_ARGS ${_PAR_ARGS} )
+    endif()
+
+    # Wrap with MPIEXEC
+    if( _PAR_MPI )
+
+      set( MPIEXEC_TASKS ${MPIEXEC_NUMPROC_FLAG} ${_PAR_MPI} )
+      if( DEFINED MPIEXEC_NUMTHREAD_FLAG )
+        set( MPIEXEC_THREADS ${MPIEXEC_NUMTHREAD_FLAG} ${_PAR_OMP} )
+      endif()
+
+      set( _LAUNCH ${MPIEXEC} ${MPIEXEC_TASKS} ${MPIEXEC_THREADS} )
+
+      if( DEFINED _PAR_COMMAND )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_PAR_COMMAND}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_PAR_COMMAND} )
+      else()
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): running as ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET}")
+        set( _PAR_COMMAND ${_LAUNCH} ${_TEST_DIR}/${_PAR_TARGET} )
+      endif()
+    endif()
+
+    ### define the test
+
+    if( _PAR_ENABLED ) # we can disable and still build it but not run it with 'make tests'
+
+      if( DEFINED _PAR_COMMAND )
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_COMMAND} ${TEST_ARGS} ${_working_dir} ) # run a command as test
+      else()
+        add_test( NAME ${_PAR_TARGET} COMMAND ${_PAR_TARGET}  ${TEST_ARGS} ${_working_dir} ) # run the test that was generated
+      endif()
+
+      # Set custom properties
+      if( ${_PAR_PROPERTIES} )
+        set_target_properties( ${_PAR_TARGET} PROPERTIES ${_PAR_PROPERTIES} )
+      endif()
+
+      # get test data
+
+      if( _PAR_TEST_DATA )
+
+        ecbuild_get_test_multidata( TARGET ${_PAR_TARGET}_data NAMES ${_PAR_TEST_DATA} )
+
+        list( APPEND _PAR_TEST_DEPENDS ${_PAR_TARGET}_data )
+
+      endif()
+
+      # Add lower case project name to custom test labels
+      set( _PAR_LABELS ${PROJECT_NAME_LOWCASE} ${_PAR_LABELS} )
+      list( REMOVE_DUPLICATES _PAR_LABELS )
+      ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): assign labels ${_PAR_LABELS}")
+      set_property( TEST ${_PAR_TARGET} APPEND PROPERTY LABELS "${_PAR_LABELS}" )
+
+      if( DEFINED _PAR_ENVIRONMENT )
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY ENVIRONMENT "${_PAR_ENVIRONMENT}" )
+      endif()
+
+      if( DEFINED _PAR_WORKING_DIRECTORY )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set working directory to ${_PAR_WORKING_DIRECTORY}")
+        set_tests_properties( ${_PAR_TARGET} PROPERTIES WORKING_DIRECTORY "${_PAR_WORKING_DIRECTORY}")
+      endif()
+
+      if( DEFINED _PAR_TEST_DEPENDS )
+        ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): set test dependencies to ${_PAR_TEST_DEPENDS}")
+        set_property( TEST ${_PAR_TARGET} APPEND PROPERTY DEPENDS "${_PAR_TEST_DEPENDS}" )
+      endif()
+
+    endif()
+
+    # add to the overall list of tests
+    list( APPEND ECBUILD_ALL_TESTS ${_PAR_TARGET} )
+    list( REMOVE_DUPLICATES ECBUILD_ALL_TESTS )
+    set( ECBUILD_ALL_TESTS ${ECBUILD_ALL_TESTS} CACHE INTERNAL "" )
+
+  endif() # _condition
+
+  # finally mark project files
+  ecbuild_declare_project_files( ${_PAR_SOURCES} )
+
+endmacro( ecbuild_add_test )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_append_to_rpath.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_append_to_rpath.cmake
new file mode 100644
index 0000000..38ecbb2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_append_to_rpath.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_append_to_rpath
+# =======================
+#
+# Append paths to the rpath. ::
+#
+#   ecbuild_append_to_rpath( RPATH_DIRS )
+#
+# ``RPATH_DIRS`` is a list of directories to append to ``CMAKE_INSTALL_RPATH``.
+#
+# * If a directory is absolute, simply append it.
+# * If a directory is relative, build a platform-dependent relative path
+#   (using ``@loader_path`` on Mac OSX, ``$ORIGIN`` on Linux and Solaris)
+#   or fall back to making it absolute by prepending the install prefix.
+#
+##############################################################################
+
+function( _path_append var path )
+	if( "${${var}}" STREQUAL "" )
+		set( ${var} "${path}" PARENT_SCOPE )
+	else()
+		list( FIND ${var} ${path} _found )
+		if( _found EQUAL "-1" )
+			set( ${var} "${${var}}:${path}" PARENT_SCOPE )
+		endif()
+	endif()
+endfunction()
+
+macro( ecbuild_append_to_rpath RPATH_DIRS )
+   
+   if( NOT ${ARGC} EQUAL 1 )
+     ecbuild_error( "ecbuild_append_to_rpath takes 1 argument")
+   endif()
+
+   foreach( RPATH_DIR ${RPATH_DIRS} )
+     
+		if( NOT ${RPATH_DIR} STREQUAL "" )
+
+			file( TO_CMAKE_PATH ${RPATH_DIR} RPATH_DIR ) # sanitize the path
+
+			if( IS_ABSOLUTE ${RPATH_DIR} )
+
+				_path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+
+			else()
+
+				set( _done 0 )
+
+				if( EC_OS_NAME STREQUAL "macosx" )
+
+					if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS 3.0) # cmake < 3.0
+						set( CMAKE_INSTALL_NAME_DIR "@loader_path/${RPATH_DIR}" )
+					endif()
+					_path_append( CMAKE_INSTALL_RPATH "@loader_path/${RPATH_DIR}" )
+
+					set( _done 1 )
+
+				endif()
+
+                if( EC_OS_NAME STREQUAL "freebsd" )
+                    _path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+                if( EC_OS_NAME STREQUAL "linux" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+				if( EC_OS_NAME STREQUAL "solaris" )
+					_path_append( CMAKE_INSTALL_RPATH "$ORIGIN/${RPATH_DIR}" )
+					set( _done 1 )
+				endif()
+
+                if( EC_OS_NAME STREQUAL "aix" ) # always relative to exectuable path
+                    _path_append( CMAKE_INSTALL_RPATH "${RPATH_DIR}" )
+                    set( _done 1 )
+                endif()
+
+				# fallback
+
+				if( NOT _done )
+					_path_append( CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${RPATH_DIR}" )
+				endif()
+
+			endif()
+
+     endif()
+
+   endforeach()
+
+endmacro( ecbuild_append_to_rpath )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_bundle.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_bundle.cmake
new file mode 100644
index 0000000..9778e27
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_bundle.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+include(CMakeParseArguments)
+
+include(ecbuild_git)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_initialize
+# =========================
+#
+# Initialise the ecBuild environment for a bundle. *Must* be called *before*
+# any call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_initialize()
+#
+##############################################################################
+
+macro( ecbuild_bundle_initialize )
+
+  include( local-config.cmake OPTIONAL )
+
+  # ecmwf_stash( PROJECT ecbuild DIR ${PROJECT_SOURCE_DIR}/ecbuild STASH "ecsdk/ecbuild" BRANCH develop )
+
+  # set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/ecbuild/cmake;${CMAKE_MODULE_PATH}" )
+
+  include( ecbuild_system )
+
+  ecbuild_requires_macro_version( 1.6 )
+
+  ecbuild_declare_project()
+
+  file( GLOB local_config_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *local-config.cmake )
+
+  ecbuild_add_resources( TARGET ecbuild_bundle_dont_pack DONT_PACK "${local_config_files}" )
+
+  if( EXISTS "${PROJECT_SOURCE_DIR}/README.md" )
+    add_custom_target( ${PROJECT_NAME}_readme SOURCES "${PROJECT_SOURCE_DIR}/README.md" )
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle
+# ==============
+#
+# Declare a subproject to be built as part of this bundle. ::
+#
+#   ecbuild_bundle( PROJECT <name>
+#                   STASH <repository> | GIT <giturl> | SOURCE <path>
+#                   [ BRANCH <gitbranch> | TAG <gittag> ]
+#                   [ UPDATE | NOREMOTE ] )
+#                   [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# STASH : cannot be combined with GIT or SOURCE
+#   Stash repository in the form <project>/<repository>
+#
+# GIT : cannot be combined with STASH or SOURCE
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# SOURCE : cannot be combined with STASH or GIT
+#   Path to an existing local repository, which will be symlinked
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+# Usage
+# -----
+#
+# A bundle is used to build a number of projects together. Each subproject
+# needs to be declared with a call to ecbuild_bundle, where the order of
+# projects is important and needs to respect dependencies: if project B
+# depends on project A, A should be listed before B in the bundle.
+#
+# The first time a bundle is built, the sources of all subprojects are cloned
+# into directories named according to project in the *source* tree of the
+# bundle (which means these directories should be added to ``.gitignore``).
+# If the ``SOURCE`` option is used it must point to an existing local
+# repository on disk and no new repository is cloned. Be aware that using the
+# ``BRANCH`` or ``TAG`` option leads to the corresponding version being checked
+# out in that repository!
+#
+# Subprojects are configured and built in order. Due to being added as a
+# subproject, the usual project discovery mechanism (i.e. locating and
+# importing a ``<project>-config.cmake`` file) is not used. Also there are no
+# ``<project>-config.cmake`` files being generated for individual subprojects.
+# However there *are* package-config files being generated for each library.
+#
+# To switch off a subproject when building a bundle, set the CMake variable
+# ``BUNDLE_SKIP_<PNAME>`` where ``PNAME`` is the capitalised project name.
+#
+##############################################################################
+
+macro( ecbuild_bundle )
+
+  set( options )
+  set( single_value_args PROJECT STASH GIT SOURCE )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  string(TOUPPER "${_PAR_PROJECT}" PNAME)
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( BUNDLE_SKIP_${PNAME} )
+    ecbuild_info( "Skipping bundle project ${_PAR_PROJECT}" )
+  else()
+    ecbuild_info( "Adding bundle project ${_PAR_PROJECT}" )
+
+    if( _PAR_STASH )
+      ecmwf_stash( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} STASH ${_PAR_STASH} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_GIT )
+      ecbuild_git( PROJECT ${_PAR_PROJECT} DIR ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} URL ${_PAR_GIT} ${_PAR_UNPARSED_ARGUMENTS} )
+    elseif( _PAR_SOURCE )
+      if( DEFINED ${PNAME}_SOURCE )
+        ecbuild_critical( "ecbuild_bundle called with SOURCE for project ${_PAR_PROJECT} but ${PNAME}_SOURCE is defined" )
+      endif()
+      execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${_PAR_SOURCE} ${PROJECT_SOURCE_DIR}/${_PAR_PROJECT} )
+    endif()
+
+    if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT} OR NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}/CMakeLists.txt )
+      ecbuild_critical("Source directory '${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_PROJECT}' for subproject '${_PAR_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    # Do not descend into ecbuild if included in a bundle (ECBUILD-333)
+    if( NOT _PAR_PROJECT STREQUAL "ecbuild" )
+      ecbuild_use_package( PROJECT ${_PAR_PROJECT} )
+    endif()
+  endif()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_bundle_finalize
+# =======================
+#
+# Finalise the ecBuild environment for a bundle. *Must* be called *after* the
+# last call to ``ecbuild_bundle``. ::
+#
+#   ecbuild_bundle_finalize()
+#
+# Options
+# -------
+#
+# See documentation for ecbuild_install_project() since all arguments are
+# forwarded to an internal call to that macro.
+#
+# If no arguments are passed, then the default installation NAME is set to
+# the default project name ${CMAKE_PROJECT_NAME}
+#
+##############################################################################
+
+macro( ecbuild_bundle_finalize )
+
+  add_custom_target( update DEPENDS ${git_update_targets} )
+
+  ecbuild_info("---------------------------------------------------------")
+  ecbuild_info("Bundle ${CMAKE_PROJECT_NAME}")
+  ecbuild_info("---------------------------------------------------------")
+
+  if("${ARGV1}")
+      ecbuild_install_project( ${ARGV} )
+  else()
+      ecbuild_install_project( NAME ${CMAKE_PROJECT_NAME} )
+  endif()
+
+  ecbuild_print_summary()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_cache.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_cache.cmake
new file mode 100644
index 0000000..d45b31a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_cache.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecBuild Cache
+# =============
+#
+# During initialisation, ecBuild introspects the compiler and operating system
+# and performs a number of checks. The result of these is written to a
+# dedicated ``ecbuild-cache.cmake`` file in the build tree. This cache may be
+# used to speed up subsequent *clean* builds i.e. those where no CMakeCache.txt
+# exists yet.
+#
+# To use the ecBuild cache, configure with ``-DECBUILD_CACHE=<cache-file>``,
+# where ``<cache-file>`` is the path to an existing ``ecbuild-cache.cmake``.
+#
+# .. note ::
+#
+#   The ecBuild cache is specific to compiler *and* operating system. Do *not*
+#   attempt to use a cache file created on a different machine or with a
+#   different compiler!
+#
+##############################################################################
+
+# Prepare the cache and clobber any existing ecbuild-cache.cmake
+macro( ecbuild_prepare_cache )
+    include( CheckSymbolExists )
+    include( CheckIncludeFiles )
+    include( CheckCSourceCompiles )
+    include( CheckCXXSourceCompiles )
+    include( CheckTypeSize )
+    set( ecbuild_cache_file ${CMAKE_BINARY_DIR}/ecbuild-cache.cmake )
+    file(WRITE ${ecbuild_cache_file} "# ecbuild cache file\n\n")    
+endmacro()
+
+# Buffer the CMake variable var to be written to the ecBuild cache
+function( ecbuild_cache_var var )
+  if( NOT ${var} )
+    set( ${var} 0 )
+  endif()
+  set( ECBUILD_CACHE_BUFFER "${ECBUILD_CACHE_BUFFER}set( ${var} ${${var}} )\n" CACHE INTERNAL "Cache buffer" )
+endfunction()
+
+# Call check_symbol_exists only if the output is not defined yet
+function( ecbuild_cache_check_symbol_exists symbol includes output )
+  if( NOT DEFINED ${output} )
+    check_symbol_exists( ${symbol} ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_include_files only if the output is not defined yet
+function( ecbuild_cache_check_include_files includes output )
+  if( NOT DEFINED ${output} )
+    check_include_files( ${includes} ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_c_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_c_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_c_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_cxx_source_compiles only if the output is not defined yet
+function( ecbuild_cache_check_cxx_source_compiles source output )
+  if( NOT DEFINED ${output} )
+    check_cxx_source_compiles( "${source}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Call check_type_size only if the output is not defined yet
+function( ecbuild_cache_check_type_size type output )
+  if( NOT DEFINED ${output} )
+    check_type_size( "${type}" ${output} )
+  endif()
+  ecbuild_cache_var( ${output} )
+endfunction()
+
+# Flush the ecBuild cache to disk and reset the buffer
+function( ecbuild_flush_cache )
+  file( APPEND ${ecbuild_cache_file} "${ECBUILD_CACHE_BUFFER}" )
+  set( ECBUILD_CACHE_BUFFER "" CACHE INTERNAL "Cache buffer" )
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_c_source_return.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_c_source_return.cmake
new file mode 100644
index 0000000..38d89f7
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_c_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_c_source_return
+# =============================
+#
+# Compile and run a given C source code and return its output. ::
+#
+#   ecbuild_check_c_source_return( <source>
+#                                  VAR <name>
+#                                  OUTPUT <name>
+#                                  [ INCLUDES <path1> [ <path2> ... ] ]
+#                                  [ LIBS <library1> [ <library2> ... ] ]
+#                                  [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .c file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_c_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_c_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_c_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_C_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+    
+        # write the source file
+    
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.c
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing C SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing C SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_compiler.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_compiler.cmake
new file mode 100644
index 0000000..197f0b5
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_compiler.cmake
@@ -0,0 +1,156 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+###################################################################################################
+# enable C to use in system introspection
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+endif()
+
+############################################################################################
+# try to get compiler version if cmake did not
+
+if( NOT CMAKE_C_COMPILER_VERSION )
+
+    set( EC_COMPILER_VERSION "?.?" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Intel" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -dumpversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE "([0-9])\\.([0-9])(\\.([0-9]))?" "\\1.\\2"  EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "Clang" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} --version
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*clang version ([0-9])\\.([0-9])(\\.([0-9]))?.*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "SunPro" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -V
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+        exec_program( ${CMAKE_C_COMPILER}
+                      ARGS ${CMAKE_C_COMPILER_ARG1} -qversion
+                      OUTPUT_VARIABLE EC_COMPILER_VERSION )
+
+        string(REGEX REPLACE ".*V([0-9]+)\\.([0-9]+).*" "\\1.\\2" EC_COMPILER_VERSION ${EC_COMPILER_VERSION} )
+
+    endif()
+
+    if( NOT EC_COMPILER_VERSION STREQUAL "?.?" )
+        set(CMAKE_C_COMPILER_VERSION "${EC_COMPILER_VERSION}" )
+    endif()
+
+endif()
+
+############################################################################################
+# c compiler tests
+
+if( CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+	ecbuild_cache_check_c_source_compiles(
+		  " typedef int foo_t;
+			static inline foo_t static_foo(){return 0;}
+			foo_t foo(){return 0;}
+			int main(int argc, char *argv[]){return 0;}
+		  " EC_HAVE_C_INLINE )
+
+endif()
+
+############################################################################################
+# c++ compiler tests
+
+if( CMAKE_CXX_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+    # check for __FUNCTION__
+    ecbuild_cache_check_cxx_source_compiles( "#include <iostream>\nint main(int argc, char* argv[]) { std::cout << __FUNCTION__ << std::endl; }"
+      EC_HAVE_FUNCTION_DEF )
+
+    # check for c++ abi, usually present in GNU compilers
+    ecbuild_cache_check_cxx_source_compiles( "#include <cxxabi.h>\n int main() { char * type; int status; char * r = abi::__cxa_demangle(type, 0, 0, &status); }"
+    EC_HAVE_CXXABI_H )
+
+    # check for bool
+    ecbuild_cache_check_cxx_source_compiles( "int main() { bool aflag = true; }"
+	  EC_HAVE_CXX_BOOL )
+
+    # check for sstream
+    ecbuild_cache_check_cxx_source_compiles( "#include <sstream>\nint main() { std::stringstream s; }"
+	  EC_HAVE_CXX_SSTREAM )
+
+endif()
+
+############################################################################################
+# enable warnings
+
+if( CMAKE_COMPILER_IS_GNUCC )
+
+    ecbuild_add_c_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_c_flags("-Wall")
+        # ecbuild_add_c_flags("-pedantic")
+        # ecbuild_add_c_flags("-Wextra")
+    endif()
+
+endif()
+
+if( CMAKE_COMPILER_IS_GNUCXX )
+
+   ecbuild_add_cxx_flags("-pipe") # use pipe for faster compilation
+
+    if( ENABLE_WARNINGS )
+        ecbuild_add_cxx_flags("-Wall")
+        #    ecbuild_add_cxx_flags("-Wextra")
+    endif()
+
+endif()
+
+if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+  ecbuild_add_fortran_flags("-warn all")
+endif()
+
+############################################################################################
+# compiler dependent fixes
+
+# For Cray compilers add "-Wl,-Bdynamic" at very end of linker commands, in order to produce dynamic executables by default
+
+if( "${CMAKE_C_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Cray" )
+  set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+if( "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Cray" )
+  set(CMAKE_Fortran_LINK_EXECUTABLE "<CMAKE_Fortran_COMPILER> <CMAKE_Fortran_LINK_FLAGS> <LINK_FLAGS> <FLAGS> <OBJECTS>  -o <TARGET> <LINK_LIBRARIES> -Wl,-Bdynamic" )
+endif()
+
+############################################################################################
+# Fortran compiler specific flags
+# if( NOT HAVE_SINGLE_PRECISION )
+#  if(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
+#      ecbuild_add_fortran_flags("-r8")
+#  elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+#      # NOTE that if we add -fdefault-real-8 then we NEED -fdefault-double-8 to avoid quadmath
+#      ecbuild_add_fortran_flags("-fdefault-real-8 -fdefault-double-8")
+#  endif()
+# endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx11.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx11.cmake
new file mode 100644
index 0000000..d2f9629
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx11.cmake
@@ -0,0 +1,138 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx11
+# ===================
+#
+# Check for C++11 features. ::
+#
+#   ecbuild_check_cxx11( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                        [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                        [ PRINT ] )
+#
+# This function uses macros from http://github.com/UCL/GreatCMakeCookOff
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_cxx11 )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_cxx11(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/contrib/GreatCMakeCookOff/CheckCXX11Features.cmake )
+
+  cxx11_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  # Save CXX flags
+  set( CXX_FLAGS_SNASHOT ${CMAKE_CXX_FLAGS} )
+
+  # Add C++11 flags
+  include( ${ECBUILD_MACROS_DIR}/ecbuild_get_cxx11_flags.cmake )
+  ecbuild_get_cxx11_flags( CXX11_FLAGS )
+  ecbuild_debug( "ecbuild_check_cxx11: detected C++11 flag as ${CXX11_FLAGS}" )
+  set( CMAKE_CXX_FLAGS "${CXX11_FLAGS} ${CMAKE_CXX_FLAGS}" )
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    cxx11_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      cxx11_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      cxx11_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  # Restore CXX flags
+  set( CMAKE_CXX_FLAGS ${CXX_FLAGS_SNAPSHOT} )
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( CXX11_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( CXX11_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${CXX11_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_CXX11_${FEAT} )
+       list( APPEND CXX11_SUPPORTED_FEATURES ${f} )
+    else()
+       list( APPEND CXX11_NOT_SUPPORTED_FEATURES ${f} )
+    endif()
+  endforeach()
+
+  if( CXX11_CHECKED_FEATURES )
+    list( SORT CXX11_CHECKED_FEATURES )
+  endif()
+  if( CXX11_SUPPORTED_FEATURES )
+    list( SORT CXX11_SUPPORTED_FEATURES )
+  endif()
+  if( CXX11_NOT_SUPPORTED_FEATURES )
+    list( SORT CXX11_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( CXX11_CHECKED_FEATURES       ${CXX11_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( CXX11_SUPPORTED_FEATURES     ${CXX11_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( CXX11_NOT_SUPPORTED_FEATURES ${CXX11_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( CXX11_CHECKED_FEATURES )
+      join( CXX11_CHECKED_FEATURES " " CXX11_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked C++11 features: ${CXX11_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no C++11 features" )
+    endif()
+    if( CXX11_SUPPORTED_FEATURES )
+      join( CXX11_SUPPORTED_FEATURES " " CXX11_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found C++11 features: ${CXX11_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no C++11 features" )
+    endif()
+    if( CXX11_NOT_SUPPORTED_FEATURES )
+      join( CXX11_NOT_SUPPORTED_FEATURES " " CXX11_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found C++11 features: ${CXX11_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked C++11 features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_cxx11 )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake
new file mode 100644
index 0000000..7270540
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_cxx_source_return.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_cxx_source_return
+# ===============================
+#
+# Compile and run a given C++ code and return its output. ::
+#
+#   ecbuild_check_cxx_source_return( <source>
+#                                    VAR <name>
+#                                    OUTPUT <name>
+#                                    [ INCLUDES <path1> [ <path2> ... ] ]
+#                                    [ LIBS <library1> [ <library2> ... ] ]
+#                                    [ DEFINITIONS <definition1> [ <definition2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .cxx file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_cxx_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_cxx_source_return(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _p_VAR OR NOT _p_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_cxx_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+    set( _msg "Testing ${_p_VAR}:" )
+
+    if( NOT DEFINED ${_p_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_p_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES)
+        if(CMAKE_REQUIRED_LIBRARIES)
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _p_LIBS )
+            list( APPEND __add_libs ${_p_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES)
+        if(CMAKE_REQUIRED_INCLUDES)
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _p_INCLUDES )
+            list( APPEND __add_incs ${_p_INCLUDES} )
+        endif()
+        if( __add_incs )
+            set(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx" "${SOURCE}\n" )
+
+        ecbuild_debug( "${_msg}" )
+        try_run( ${_p_VAR}_EXITCODE ${_p_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_p_VAR}.cxx
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+
+        # ecbuild_debug_var( ${_p_VAR}_COMPILED )
+        # ecbuild_debug_var( ${_p_VAR}_EXITCODE )
+
+        # if it did not compile make the return value fail code of 1
+
+        if( NOT ${_p_VAR}_COMPILED )
+          ecbuild_debug( "${_msg} failed to compile" )
+        endif()
+
+        if( "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN" )
+          ecbuild_debug( "${_msg} failed to run" )
+        endif()
+
+        # if the return value was 0 then it worked
+        if( ${_p_VAR}_COMPILED AND "${${_p_VAR}_EXITCODE}" EQUAL 0 )
+
+          ecbuild_debug("${_msg} Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_p_VAR}     1              CACHE INTERNAL "Test ${_p_VAR}")
+          set( ${_p_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_p_VAR} output")
+
+        else()
+
+          if(CMAKE_CROSSCOMPILING AND "${${_p_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_p_VAR} "${${_p_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_p_VAR} "" CACHE INTERNAL "Test ${_p_VAR}")
+            set(${_p_OUTPUT} "" CACHE INTERNAL "Test ${_p_VAR} output")
+          endif()
+
+          ecbuild_debug("Test ${_p_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+            "Performing C++ SOURCE FILE Test ${_p_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n"
+            "Performing C++ SOURCE FILE Run ${_p_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n"
+            "Return value: ${${_p_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+
+    endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran.cmake
new file mode 100644
index 0000000..d7a63cf
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran.cmake
@@ -0,0 +1,126 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran
+# =====================
+#
+# Check for Fortran features. ::
+#
+#   ecbuild_check_fortran( [ FEATURES <feature1> [ <feature2> ... ] ]
+#                          [ REQUIRED <feature1> [ <feature2> ... ] ]
+#                          [ PRINT ] )
+#
+# Options
+# -------
+#
+# FEATURES : optional
+#   list of optional features to check for
+#
+# REQUIRED : optional
+#   list of required features to check for, fails if not detected
+#
+# PRINT : optional
+#   print a summary of features checked for, found and not found
+#
+# Note
+# ----
+#
+# If neither ``FEATURES`` nor ``REQUIRED`` are given, check for all features.
+#
+##############################################################################
+
+function( ecbuild_check_fortran )
+
+  # parse parameters
+
+  set( options PRINT )
+  set( single_value_args )
+  set( multi_value_args   FEATURES REQUIRED )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_check_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  include( ${ECBUILD_MACROS_DIR}/fortran_features/CheckFortranFeatures.cmake )
+
+  fortran_find_all_features( ALL_FEATURES ) # list all available features to check
+
+  if( NOT _p_FEATURES AND NOT _p_REQUIRED ) # no input, then search for all features
+
+    fortran_feature_check()
+
+  else()
+
+    foreach( _f ${_p_FEATURES} )
+      fortran_feature_check( ${_f} )
+    endforeach()
+
+    foreach( _f ${_p_REQUIRED} )
+      fortran_feature_check( REQUIRED ${_f} )
+    endforeach()
+
+  endif()
+
+  if( _p_FEATURES OR _p_REQUIRED )
+    set( Fortran_CHECKED_FEATURES ${_p_FEATURES} ${_p_REQUIRED} )
+  else()
+    set( Fortran_CHECKED_FEATURES ${ALL_FEATURES} )
+  endif()
+
+  foreach( f ${Fortran_CHECKED_FEATURES} )
+    string( TOUPPER ${f} FEAT )
+    if( HAS_Fortran_${FEAT} )
+       list( APPEND Fortran_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 1 PARENT_SCOPE )
+    else()
+       list( APPEND Fortran_NOT_SUPPORTED_FEATURES ${f} )
+       set( EC_HAVE_Fortran_${FEAT} 0 PARENT_SCOPE )
+    endif()
+  endforeach()
+
+  if( Fortran_CHECKED_FEATURES )
+    list( SORT Fortran_CHECKED_FEATURES )
+  endif()
+  if( Fortran_SUPPORTED_FEATURES )
+    list( SORT Fortran_SUPPORTED_FEATURES )
+  endif()
+  if( Fortran_NOT_SUPPORTED_FEATURES )
+    list( SORT Fortran_NOT_SUPPORTED_FEATURES )
+  endif()
+
+  set( Fortran_CHECKED_FEATURES       ${Fortran_CHECKED_FEATURES}       PARENT_SCOPE )
+  set( Fortran_SUPPORTED_FEATURES     ${Fortran_SUPPORTED_FEATURES}     PARENT_SCOPE )
+  set( Fortran_NOT_SUPPORTED_FEATURES ${Fortran_NOT_SUPPORTED_FEATURES} PARENT_SCOPE )
+
+  if( _p_PRINT )
+    if( Fortran_CHECKED_FEATURES )
+      join( Fortran_CHECKED_FEATURES " " Fortran_CHECKED_FEATURES_STR )
+      ecbuild_info( "Checked Fortran features: ${Fortran_CHECKED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Checked no Fortran features" )
+    endif()
+    if( Fortran_SUPPORTED_FEATURES )
+      join( Fortran_SUPPORTED_FEATURES " " Fortran_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Found Fortran features: ${Fortran_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found no Fortran features" )
+    endif()
+    if( Fortran_NOT_SUPPORTED_FEATURES )
+      join( Fortran_NOT_SUPPORTED_FEATURES " " Fortran_NOT_SUPPORTED_FEATURES_STR )
+      ecbuild_info( "Not found Fortran features: ${Fortran_NOT_SUPPORTED_FEATURES_STR}" )
+    else()
+      ecbuild_info( "Found all checked Fortran features" )
+    endif()
+  endif()
+
+endfunction( ecbuild_check_fortran )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake
new file mode 100644
index 0000000..92168b9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_fortran_source_return.cmake
@@ -0,0 +1,154 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_check_fortran_source_return
+# ===================================
+#
+# Compile and run a given Fortran code and return its output. ::
+#
+#   ecbuild_check_fortran_source_return( <source>
+#                                        VAR <name>
+#                                        OUTPUT <name>
+#                                        [ INCLUDES <path1> [ <path2> ... ] ]
+#                                        [ LIBS <library1> [ <library2> ... ] ]
+#                                        [ DEFINITIONS <def1> [ <def2> ... ] ] )
+#
+# Options
+# -------
+#
+# VAR : required
+#   name of the check and name of the CMake variable to write result to
+#
+# OUTPUT : required
+#   name of CMake variable to write the output to
+#
+# INCLUDES : optional
+#   list of paths to add to include directories
+#
+# LIBS : optional
+#   list of libraries to link against (CMake targets or external libraries)
+#
+# DEFINITIONS : optional
+#   list of definitions to add to preprocessor defines
+#
+# Usage
+# -----
+#
+# This will write the given source to a .f file and compile and run it with
+# try_run. If successful, ``${VAR}`` is set to 1 and ``${OUTPUT}`` is set to
+# the output of the successful run in the CMake cache.
+#
+# The check will not run if ``${VAR}`` is defined (e.g. from ecBuild cache).
+#
+##############################################################################
+
+macro( ecbuild_check_fortran_source_return SOURCE )
+
+    set( options )
+    set( single_value_args VAR  OUTPUT )
+    set( multi_value_args  INCLUDES LIBS DEFINITIONS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_check_fortran_source_return(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_VAR OR NOT _PAR_OUTPUT )
+      ecbuild_critical("The call to ecbuild_check_fortran_source_return() doesn't specify either SOURCE, VAR or OUTPUT")
+    endif()
+
+
+    if( NOT DEFINED ${_PAR_VAR} )
+
+        set(MACRO_CHECK_FUNCTION_DEFINITIONS "-D${_PAR_VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES)
+        if( CMAKE_REQUIRED_LIBRARIES )
+            list( APPEND __add_libs ${CMAKE_REQUIRED_LIBRARIES} )
+        endif()
+        if( _PAR_LIBS )
+            list( APPEND __add_libs ${_PAR_LIBS} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES "-DLINK_LIBRARIES:STRING=${__add_libs}")
+        endif()
+
+        set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES)
+        if( CMAKE_REQUIRED_INCLUDES )
+            list( APPEND __add_incs ${CMAKE_REQUIRED_INCLUDES} )
+        endif()
+        if( _PAR_INCLUDES )
+            list( APPEND __add_incs ${_PAR_INCLUDES} )
+        endif()
+        if( __add_libs )
+            set(CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${__add_incs}")
+        endif()
+
+        # write the source file
+
+        file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90" "${SOURCE}\n" )
+
+        ecbuild_debug( "Performing Test ${_PAR_VAR}" )
+        try_run( ${_PAR_VAR}_EXITCODE ${_PAR_VAR}_COMPILED
+          ${CMAKE_BINARY_DIR}
+          ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test_${_PAR_VAR}.F90
+          COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+          CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+          -DCMAKE_SKIP_RPATH:BOOL=${CMAKE_SKIP_RPATH}
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_LIBRARIES}"
+          "${CHECK_Fortran_SOURCE_COMPILES_ADD_INCLUDES}"
+          COMPILE_OUTPUT_VARIABLE compile_OUTPUT 
+          RUN_OUTPUT_VARIABLE     run_OUTPUT )
+    
+        # if it did not compile make the return value fail code of 1
+        if( NOT ${_PAR_VAR}_COMPILED )
+          set( ${_PAR_VAR}_EXITCODE 1 )
+        endif()
+    
+        # if the return value was 0 then it worked
+        if("${${_PAR_VAR}_EXITCODE}" EQUAL 0)
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Success")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} succeded with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} succeded with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}}\n"
+            "Source file was:\n${SOURCE}\n")
+
+          set( ${_PAR_VAR}     1              CACHE INTERNAL "Test ${_PAR_VAR}")
+          set( ${_PAR_OUTPUT} "${run_OUTPUT}" CACHE INTERNAL "Test ${_PAR_VAR} output")
+    
+        else()
+    
+          if(CMAKE_CROSSCOMPILING AND "${${_PAR_VAR}_EXITCODE}" MATCHES  "FAILED_TO_RUN")
+            set(${_PAR_VAR} "${${_PAR_VAR}_EXITCODE}")
+            set(${OUTPUT} "")
+          else()
+            set(${_PAR_VAR} "" CACHE INTERNAL "Test ${_PAR_VAR}")
+            set(${_PAR_OUTPUT} "" CACHE INTERNAL "Test ${_PAR_VAR} output")
+          endif()
+    
+          ecbuild_debug("Performing Test ${_PAR_VAR} - Failed")
+          file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log 
+            "Performing Fortran SOURCE FILE Test ${_PAR_VAR} failed with the following compile output:\n"
+            "${compile_OUTPUT}\n" 
+            "Performing Fortran SOURCE FILE Run ${_PAR_VAR} failed with the following run output:\n"
+            "${run_OUTPUT}\n" 
+            "Return value: ${${_PAR_VAR}_EXITCODE}\n"
+            "Source file was:\n${SOURCE}\n")
+        endif()
+    
+    endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_functions.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_functions.cmake
new file mode 100644
index 0000000..59fbd3a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_functions.cmake
@@ -0,0 +1,186 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# os capability checks
+
+if( ENABLE_OS_FUNCTIONS_TEST )
+
+    ### symbol checks ##################
+
+    ecbuild_cache_check_symbol_exists( fseek        "stdio.h"                         EC_HAVE_FSEEK  )
+    ecbuild_cache_check_symbol_exists( fseeko       "stdio.h"                         EC_HAVE_FSEEKO )
+    ecbuild_cache_check_symbol_exists( ftello       "stdio.h"                         EC_HAVE_FTELLO )
+    ecbuild_cache_check_symbol_exists( lseek        "sys/types.h;unistd.h"            EC_HAVE_LSEEK  )
+    ecbuild_cache_check_symbol_exists( ftruncate    "sys/types.h;unistd.h"            EC_HAVE_FTRUNCATE  )
+    ecbuild_cache_check_symbol_exists( open         "sys/types.h;sys/stat.h;fcntl.h"  EC_HAVE_OPEN   )
+    ecbuild_cache_check_symbol_exists( fopen        "stdio.h"                         EC_HAVE_FOPEN  )
+    ecbuild_cache_check_symbol_exists( fmemopen     "stdio.h"                         EC_HAVE_FMEMOPEN )
+    ecbuild_cache_check_symbol_exists( funopen      "stdio.h"                         EC_HAVE_FUNOPEN )
+    ecbuild_cache_check_symbol_exists( flock        "sys/file.h"                      EC_HAVE_FLOCK  )
+    ecbuild_cache_check_symbol_exists( mmap         "sys/mman.h"                      EC_HAVE_MMAP   )
+
+    ecbuild_cache_check_symbol_exists( posix_memalign "stdlib.h"                      EC_HAVE_POSIX_MEMALIGN )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK      "fcntl.h"                         EC_HAVE_F_GETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLK      "fcntl.h"                         EC_HAVE_F_SETLK  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW     "fcntl.h"                         EC_HAVE_F_SETLKW  )
+
+    ecbuild_cache_check_symbol_exists( F_GETLK64     "fcntl.h"                        EC_HAVE_F_GETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLK64     "fcntl.h"                        EC_HAVE_F_SETLK64  )
+    ecbuild_cache_check_symbol_exists( F_SETLKW64    "fcntl.h"                        EC_HAVE_F_SETLKW64  )
+
+    ecbuild_cache_check_symbol_exists( MAP_ANONYMOUS "sys/mman.h"                     EC_HAVE_MAP_ANONYMOUS )
+    ecbuild_cache_check_symbol_exists( MAP_ANON      "sys/mman.h"                     EC_HAVE_MAP_ANON )
+
+    ### include files checks ##################
+
+    ecbuild_cache_check_include_files( assert.h       EC_HAVE_ASSERT_H      )
+    ecbuild_cache_check_include_files( stdlib.h       EC_HAVE_STDLIB_H      )
+    ecbuild_cache_check_include_files( unistd.h       EC_HAVE_UNISTD_H      )
+    ecbuild_cache_check_include_files( string.h       EC_HAVE_STRING_H      )
+    ecbuild_cache_check_include_files( strings.h      EC_HAVE_STRINGS_H     )
+    ecbuild_cache_check_include_files( sys/stat.h     EC_HAVE_SYS_STAT_H    )
+    ecbuild_cache_check_include_files( sys/time.h     EC_HAVE_SYS_TIME_H    )
+    ecbuild_cache_check_include_files( sys/types.h    EC_HAVE_SYS_TYPES_H   )
+    ecbuild_cache_check_include_files( malloc.h       EC_HAVE_MALLOC_H      )
+    ecbuild_cache_check_include_files( sys/malloc.h   EC_HAVE_SYS_MALLOC_H  )
+
+    ecbuild_cache_check_include_files( sys/param.h    EC_HAVE_SYS_PARAM_H   )
+    ecbuild_cache_check_include_files( sys/mount.h    EC_HAVE_SYS_MOUNT_H   )
+    ecbuild_cache_check_include_files( sys/vfs.h      EC_HAVE_SYS_VFS_H     )
+
+    ### capability checks ##################
+
+    # test off_t
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\nint main(){ off_t l=0; return 0;}\n" EC_HAVE_OFFT )
+    # test off64_t
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){ off64_t l=0; return 0;}\n" EC_HAVE_OFF64T  )
+    # test struct stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; return 0; }"   EC_HAVE_STRUCT_STAT )
+    # test struct stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; return 0; }" EC_HAVE_STRUCT_STAT64 )
+    # test stat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s;	stat(\"\",&s); return 0; }"    EC_HAVE_STAT )
+    # test stat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; stat64(\"\",&s); return 0; }"  EC_HAVE_STAT64 )
+    # test fstat
+    ecbuild_cache_check_c_source_compiles( "#include <sys/stat.h>\nint main(){ struct stat s; fstat(1,&s); return 0; }" EC_HAVE_FSTAT )
+    # test fstat64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/stat.h>\nint main(){ struct stat64 s; fstat64(1,&s); return 0; }" EC_HAVE_FSTAT64 )
+    # test fseeko64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l=0;fseeko64(file,l,SEEK_CUR);return 0;}\n" EC_HAVE_FSEEKO64 )
+
+    # test for ftello64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\n#include <sys/types.h>\nint main(){FILE* file;off64_t l = ftello64(file);return 0;}\n"  EC_HAVE_FTELLO64 )
+
+    # test for lseek64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/types.h>\n#include <unistd.h>\nint main(){off64_t h = lseek64(0,0,SEEK_SET);return 0;}\n"  EC_HAVE_LSEEK64 )
+    # test for open64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){int fd = open64(\"name\",O_RDWR|O_CREAT,0777);return 0;}\n" EC_HAVE_OPEN64 )
+    # test for fopen64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <stdio.h>\nint main(){FILE* file = fopen64(\"name\",\"w\");return 0;}\n"  EC_HAVE_FOPEN64 )
+    # test for ftruncate64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <unistd.h>\n#include <sys/types.h>\nint main(){ftruncate64(0,(off64_t)0);return 0;}\n" EC_HAVE_FTRUNCATE64 )
+    # test for flock64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <fcntl.h>\nint main(){struct flock64 l;return 0;}\n" EC_HAVE_FLOCK64 )
+    # test for mmap64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/mman.h>\nint main(){void* addr = mmap64(0,10,PROT_READ|PROT_WRITE,MAP_PRIVATE,10,0); return 0;}\n" EC_HAVE_MMAP64 )
+    # test for struct statvfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/statvfs.h>\nint main(){ struct statvfs v; }" EC_HAVE_STRUCT_STATVFS )
+    # test for struct statvfs64
+    ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include <sys/statvfs.h>\nint main(){ struct statvfs64 v; }" EC_HAVE_STRUCT_STATVFS64 )
+
+    # test for fopencookie
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <stdio.h>\nint main(){ void* cookie; const char* mode; cookie_io_functions_t iof; FILE* fopencookie(void *cookie, const char *mode, cookie_io_functions_t iof); }" EC_HAVE_FOPENCOOKIE )
+
+    # test for fsync
+    ecbuild_cache_check_symbol_exists(fsync "unistd.h" EC_HAVE_FSYNC)
+    # test for fdatasync
+    ecbuild_cache_check_symbol_exists(fdatasync "unistd.h" EC_HAVE_FDATASYNC)
+    # test for dirfd
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <dirent.h>\nint main(){ DIR *dirp; int i = dirfd(dirp); }\n" EC_HAVE_DIRFD )
+    # test for sys/proc.h
+    ecbuild_cache_check_c_source_compiles( "#include <sys/proc.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROC )
+    # test for procfs
+    ecbuild_cache_check_c_source_compiles( "#include <sys/procfs.h>\nint main(){ return 0; }\n" EC_HAVE_SYSPROCFS )
+    # test for backtrace
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <execinfo.h>\n int main(){ void ** buffer; int i = backtrace(buffer, 256); }\n" EC_HAVE_EXECINFO_BACKTRACE )
+
+    #### reentrant funtions support  #############
+
+    # test for gmtime_r
+    ecbuild_cache_check_c_source_compiles( "#include <time.h>\nint main(){ time_t now; time(&now); struct tm t; gmtime_r(&now,&t); }\n" EC_HAVE_GMTIME_R )
+    # test for getpwuid_r
+    ecbuild_cache_check_c_source_compiles( "#include <unistd.h>\n#include <sys/types.h>\n#include <pwd.h>\nint main(){ char buf[4096]; struct passwd pwbuf; struct passwd *pwbufp = 0; getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pwbufp); }\n" EC_HAVE_GETPWUID_R )
+    # test for getpwnam_r
+    ecbuild_cache_check_c_source_compiles( "#include <sys/types.h>\n#include <pwd.h>\nint main(){ struct passwd p; char line[1024]; int n = getpwnam_r(\"user\",&p,line,sizeof(line),0); }\n" EC_HAVE_GETPWNAM_R )
+    # test for readdir_r
+    ecbuild_cache_check_c_source_compiles( "#include <dirent.h>\nint main(){ DIR *dirp; struct dirent *entry; struct dirent **result; int i = readdir_r(dirp, entry, result); }\n" EC_HAVE_READDIR_R )
+    # test for gethostbyname_r
+    ecbuild_cache_check_c_source_compiles( "#include <netdb.h>\nint main(){ const char *name; struct hostent *ret; char *buf; struct hostent **result; size_t buflen; int *h_errnop; int i = gethostbyname_r(name,ret,buf,buflen,result,h_errnop); }\n" EC_HAVE_GETHOSTBYNAME_R )
+
+    #### special compiler __atributes__  #############
+
+    # test for __attribute__ ((__constructor__)) -- usually present in GCC, Clang, Intel on Linux, Solaris, MacOSX; not present in AIX XLC
+    ecbuild_cache_check_c_source_compiles( "#include <stdio.h>\nstatic int argc_;static char** argv_;static char** envp_;\nint main(){printf(\"%d\", argc_);}\n__attribute__ ((__constructor__)) static void before_main(int argc, char* argv[], char* envp[]){argc_ = argc;argv_ = argv;envp_ = envp;}\n" EC_HAVE_ATTRIBUTE_CONSTRUCTOR )
+
+    if( NOT DEFINED EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+        ecbuild_check_c_source_return("
+            #include <stdio.h>
+            #include <string.h>
+            int main(){return 0;}
+            __attribute__ ((__constructor__))
+            static void before_main(int argc, char* argv[], char* envp[])
+            {
+                printf(\"%d:%d\",argc, strstr(argv[0],\"cmTryCompileExec\")?1:0);
+            }"
+            VAR    EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+            OUTPUT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT )
+
+        if( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV AND NOT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT STREQUAL "1:1" )
+          set(EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV 0 CACHE INTERNAL "ATTRIBUTE_CONSTRUCTOR doesnt init argv correctly")
+        endif()
+    endif()
+    ecbuild_cache_var( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV )
+
+
+    #### check for some Linux stuff #############
+
+    if( NOT DEFINED EC_HAVE_PROCFS )
+        ecbuild_check_c_source_return("
+            #include <sys/types.h>
+            #include <dirent.h>
+            int main()
+            {
+                DIR* d = opendir(\"/proc\");
+                if(d)
+                    return 0;
+                else
+                    return -1;
+            }"
+            VAR    EC_HAVE_PROCFS
+            OUTPUT EC_HAVE_PROCFS_OUTPUT )
+    endif()
+    ecbuild_cache_var( EC_HAVE_PROCFS )
+
+#    ecbuild_debug_var(EC_HAVE_PROCFS)
+#    ecbuild_debug_var(EC_HAVE_PROCFS_OUTPUT)
+
+    #### check support for DL library #############
+
+    ecbuild_cache_check_include_files( dlfcn.h  EC_HAVE_DLFCN_H )
+
+    cmake_push_check_state(RESET)
+    set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS} )
+    ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include <dlfcn.h>\nint main(){ void* addr; Dl_info info; dladdr(addr, &info); }\n" EC_HAVE_DLADDR )
+    cmake_pop_check_state()
+
+endif()
+
+
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_check_os.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_check_os.cmake
new file mode 100644
index 0000000..d2af403
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_check_os.cmake
@@ -0,0 +1,536 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# check size of pointer
+
+# Re-check size of void pointer since for some compiler combinations this is not properly set
+ecbuild_cache_check_type_size( "void*" CMAKE_SIZEOF_VOID_P  )
+
+if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS )
+
+  enable_language( C )
+  ecbuild_compiler_flags( C )
+
+endif()
+
+math( EXPR EC_OS_BITS "${CMAKE_SIZEOF_VOID_P} * 8" )
+
+# we only support 32 and 64 bit operating systems
+if( NOT EC_OS_BITS EQUAL "32" AND NOT EC_OS_BITS EQUAL "64" )
+  ecbuild_critical( "operating system ${CMAKE_SYSTEM} ${EC_OS_BITS} bits -- ecbuild only supports 32 or 64 bit OS's" )
+endif()
+
+############################################################################################
+# For 64 bit architectures enable PIC (position-independent code)
+
+# Allow overriding the position independent code setting (ECBUILD-220)
+if( DEFINED ECBUILD_POSITION_INDEPENDENT_CODE )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ${ECBUILD_POSITION_INDEPENDENT_CODE} )
+elseif( ${EC_OS_BITS} EQUAL 64 )
+  set( CMAKE_POSITION_INDEPENDENT_CODE ON )
+endif()
+
+
+############################################################################################
+# check architecture
+
+if( ENABLE_OS_TYPES_TEST )
+
+  set( EC_SIZEOF_PTR ${CMAKE_SIZEOF_VOID_P} )
+  ecbuild_cache_var( EC_SIZEOF_PTR )
+  ecbuild_cache_check_type_size( char           EC_SIZEOF_CHAR        )
+  ecbuild_cache_check_type_size( short          EC_SIZEOF_SHORT       )
+  ecbuild_cache_check_type_size( int            EC_SIZEOF_INT         )
+  ecbuild_cache_check_type_size( long           EC_SIZEOF_LONG        )
+  ecbuild_cache_check_type_size( "long long"    EC_SIZEOF_LONG_LONG   )
+  ecbuild_cache_check_type_size( float          EC_SIZEOF_FLOAT       )
+  ecbuild_cache_check_type_size( double         EC_SIZEOF_DOUBLE      )
+  ecbuild_cache_check_type_size( "long double"  EC_SIZEOF_LONG_DOUBLE )
+  ecbuild_cache_check_type_size( size_t         EC_SIZEOF_SIZE_T      )
+  ecbuild_cache_check_type_size( ssize_t        EC_SIZEOF_SSIZE_T     )
+  ecbuild_cache_check_type_size( off_t          EC_SIZEOF_OFF_T       )
+
+  ecbuild_debug( "sizeof void*       [${EC_SIZEOF_PTR}]" )
+  ecbuild_debug( "sizeof char        [${EC_SIZEOF_CHAR}]" )
+  ecbuild_debug( "sizeof short       [${EC_SIZEOF_SHORT}]" )
+  ecbuild_debug( "sizeof int         [${EC_SIZEOF_INT}]" )
+  ecbuild_debug( "sizeof long        [${EC_SIZEOF_LONG}]" )
+  ecbuild_debug( "sizeof long long   [${EC_SIZEOF_LONG_LONG}]" )
+  ecbuild_debug( "sizeof float       [${EC_SIZEOF_FLOAT}]" )
+  ecbuild_debug( "sizeof double      [${EC_SIZEOF_DOUBLE}]" )
+  ecbuild_debug( "sizeof long double [${EC_SIZEOF_LONG_DOUBLE}]" )
+  ecbuild_debug( "sizeof size_t      [${EC_SIZEOF_SIZE_T}]" )
+  ecbuild_debug( "sizeof ssize_t     [${EC_SIZEOF_SSIZE_T}]" )
+  ecbuild_debug( "sizeof off_t       [${EC_SIZEOF_OFF_T}]" )
+
+endif()
+
+############################################################################################
+# check for large file support
+
+# ensure we use 64bit access to files even on 32bit os -- aka Large File Support
+# by making off_t 64bit and stat behave as stat64
+
+if( ENABLE_LARGE_FILE_SUPPORT )
+
+  ecbuild_cache_check_type_size( off_t EC_SIZEOF_OFF_T )
+
+  if( EC_SIZEOF_OFF_T LESS "8" )
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+      add_definitions( -D_FILE_OFFSET_BITS=64 )
+    endif()
+
+    if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+      add_definitions( -D_LARGE_FILES=64 )
+    endif()
+
+    get_directory_property( __compile_defs COMPILE_DEFINITIONS )
+
+    if( __compile_defs )
+      foreach( def ${__compile_defs} )
+        list( APPEND CMAKE_REQUIRED_DEFINITIONS -D${def} )
+      endforeach()
+    endif()
+
+  endif()
+
+endif()
+
+############################################################################################
+# check endiness
+
+if( ENABLE_OS_ENDINESS_TEST )
+
+  if( NOT DEFINED EC_BIG_ENDIAN AND NOT DEFINED EC_LITTLE_ENDIAN )
+
+    test_big_endian( _BIG_ENDIAN )
+
+    if( _BIG_ENDIAN )
+        set( EC_BIG_ENDIAN    1 )
+        set( EC_LITTLE_ENDIAN 0 )
+    else()
+        set( EC_BIG_ENDIAN    0 )
+        set( EC_LITTLE_ENDIAN 1 )
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( EC_BIG_ENDIAN )
+  ecbuild_cache_var( EC_LITTLE_ENDIAN )
+
+  if( NOT DEFINED IEEE_BE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0x30,0x61,0xDE,0x80,0x93,0x67,0xCC,0xD9,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x05,0x83,0x48,0x22,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_BE )
+
+    if( "${IEEE_BE}" STREQUAL "" )
+      set( IEEE_BE 0 CACHE INTERNAL "Test IEEE_BE")
+    endif()
+
+  endif()
+
+  ecbuild_cache_var( IEEE_BE )
+
+  if( EC_BIG_ENDIAN AND NOT IEEE_BE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Big-Endian but compiled code runs differently -- to ignore this pass -DIEEE_BE=0 to CMake/ecBuild")
+  endif()
+
+  if( NOT DEFINED IEEE_LE )
+    check_c_source_runs(
+       "int compare(unsigned char* a,unsigned char* b) {
+         while(*a != 0) if (*(b++)!=*(a++)) return 1;
+         return 0;
+       }
+       int main(int argc,char** argv) {
+         unsigned char dc[]={0xD9,0xCC,0x67,0x93,0x80,0xDE,0x61,0x30,0};
+         double da=1.23456789e-75;
+         unsigned char* ca;
+
+         unsigned char fc[]={0x22,0x48,0x83,0x05,0};
+         float fa=1.23456789e-35;
+
+         if (sizeof(double)!=8) return 1;
+
+         ca=(unsigned char*)&da;
+         if (compare(dc,ca)) return 1;
+
+         if (sizeof(float)!=4) return 1;
+
+         ca=(unsigned char*)&fa;
+         if (compare(fc,ca)) return 1;
+
+         return 0;
+       }" IEEE_LE )
+
+    if( "${IEEE_LE}" STREQUAL "" )
+      set( IEEE_LE 0 CACHE INTERNAL "Test IEEE_LE")
+    endif()
+  endif()
+
+  ecbuild_cache_var( IEEE_LE )
+
+  if( EC_LITTLE_ENDIAN AND NOT IEEE_LE )
+    ecbuild_critical("Failed to sanity check on endiness: OS should be Little-Endian but compiled code runs differently -- to ignore this pass -DIEEE_LE=0 to CMake/ecBuild")
+  endif()
+
+endif()
+
+############################################################################################
+# enable profiling via gprof
+
+if( ENABLE_PROFILING )
+  ecbuild_deprecate( "ENABLE_PROFILING is deprecated and ignored, use ENABLE_GPROF instead" )
+endif()
+
+if( ENABLE_GPROF )
+
+  # User defined profiling flag takes precedence
+  if( ECBUILD_GPROF_FLAG )
+
+    ecbuild_debug( "Enabling profiling with user defined flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p  Generate extra code to write profile information suitable for the analysis program
+  #     prof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # -pg Generate extra code to write profile information suitable for the analysis program
+  #     gprof.  You must use this option when compiling the source files you want data about,
+  #     and you must also use it when linking.
+  #
+  # --coverage
+  #      This option is used to compile and link code instrumented for coverage analysis.  The
+  #      option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov
+  #      (when linking).  See the documentation for those options for more details.
+  #
+  #      *   Compile the source files with -fprofile-arcs plus optimization and code generation
+  #          options.  For test coverage analysis, use the additional -ftest-coverage option.
+  #          You do not need to profile every source file in a program.
+  #
+  #      *   Link your object files with -lgcov or -fprofile-arcs (the latter implies the
+  #          former).
+  #
+  #      *   Run the program on a representative workload to generate the arc profile
+  #          information.  This may be repeated any number of times.  You can run concurrent
+  #          instances of your program, and provided that the file system supports locking, the
+  #          data files will be correctly updated.  Also "fork" calls are detected and correctly
+  #          handled (double counting will not happen).
+  #
+  #      *   For profile-directed optimizations, compile the source files again with the same
+  #          optimization and code generation options plus -fbranch-probabilities.
+  #
+  #      *   For test coverage analysis, use gcov to produce human readable information from the
+  #          .gcno and .gcda files.  Refer to the gcov documentation for further information.
+  #
+  #      With -fprofile-arcs, for each function of your program GCC creates a program flow
+  #      graph, then finds a spanning tree for the graph.  Only arcs that are not on the
+  #      spanning tree have to be instrumented: the compiler adds code to count the number of
+  #      times that these arcs are executed.  When an arc is the only exit or only entrance to a
+  #      block, the instrumentation code can be added to the block; otherwise, a new basic block
+  #      must be created to hold the instrumentation code.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+
+    set( ECBUILD_GPROF_FLAG "-pg --coverage" )
+    ecbuild_debug( "Enabling profiling with GNU flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -p
+  #
+  #        Compiles and links for function profiling
+  #               with gprof(1).
+  #
+  #        Architecture  Restrictions:  Not  available  on  Intel(R)   64   architecture
+  #        targeting the
+  #               Intel(R)  Xeon  Phi(TM) coprocessor x100 product family (formerly code
+  #               name  Knights  Corner),  on  IA-32  architecture  targeting   Intel(R)
+  #               Graphics Technology, or on Intel(R) 64 architecture targeting Intel(R)
+  #               Graphics Technology
+  #
+  #        Arguments:
+  #
+  #        None
+  #
+  #        Default:
+  #
+  #        OFF               Files are compiled and linked without profiling.
+  #
+  #        Description:
+  #
+  #        This option compiles and links for function profiling with gprof(1).
+  #
+  #        When you specify this option, inlining is disabled. However, you can override
+  #        this  by  specifying  pragma forceinline, declspec forceinline (Windows* OS),
+  #        attribute always_inline (Linux* OS and OS X*), or a compiler option  such  as
+  #        [Q]inline-forceinline.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "Intel" )
+
+    set( ECBUILD_GPROF_FLAG "-p" )
+    ecbuild_debug( "Enabling profiling with Intel flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # -Mprof[=option[,option,...]]
+  #        Set performance profiling options.  Use of these options will cause the resulting
+  #        executable to create a performance profile that can be viewed and analyzed with the
+  #        PGPROF performance profiler.  In the descriptions below, PGI-style profiling implies
+  #        compiler-generated source instrumentation.  MPICH-style profiling implies the use of
+  #        instrumented wrappers for MPI library routines.  The -Mprof options are:
+  #
+  #        ccff
+  #
+  #        dwarf     Generate limited DWARF symbol information sufficient for most performance
+  #                  profilers.
+  #
+  #        func      Perform PGI-style function level profiling.
+  #
+  #        hwcts     Generate a profile using event-based sampling of hardware counters via the
+  #                  PAPI interface (linux86-64 only, PAPI must be installed).
+  #
+  #        lines     Perform PGI-style line level profiling.
+  #
+  #        hpmpi     (PGI CDK only) Perform MPICH-style profiling for the HP Implies
+  #                  -Mmpi=hpmpi.
+  #
+  #        mpich1    (PGI CDK only) Perform MPICH-style profiling for MPICH-1.  Implies
+  #                  -Mmpi=mpich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mpich2    (PGI CDK only) Perform MPICH-style profiling for MPICH-2.  Implies
+  #                  -Mmpi=mpich2.  Use MPIDIR to point to the MPICH-1 libraries.  This flag is
+  #                  no longer fully supported.
+  #
+  #        mvapich1  (PGI CDK only) Perform MPICH-style profiling for MVAPICH.  Implies
+  #                  -Mmpi=mvapich1.  Use MPIDIR to point to the MPICH-1 libraries.  This flag
+  #                  is no longer fully supported.
+  #
+  #        time      Generate a profile using time-based instruction-level statistical
+  #                  sampling. This is equivalent to -pg, except that the profile is saved in a
+  #                  file named pgprof.out instead of gmon.out.
+  #
+  #        On Linux systems that have OProfile installed, PGPROF supports collection of
+  #        performance data without recompilation. Use of -Mprof=dwarf is useful for this mode
+  #        of profiling.
+  elseif( CMAKE_C_COMPILER_ID MATCHES "PGI" )
+
+    set( ECBUILD_GPROF_FLAG "-Mprof=dwarf,time" )
+    ecbuild_debug( "Enabling profiling with PGI flag '${ECBUILD_GPROF_FLAG}'" )
+
+  # There is no equivalent to -pg for clang:
+  # http://lists.llvm.org/pipermail/cfe-dev/2010-September/011255.html
+  else()
+
+    ecbuild_warn( "Profiling enabled but ecbuild doesn't know how to enable for this particular compiler ${CMAKE_C_COMPILER_ID}")
+
+  endif()
+
+  set( CMAKE_EXE_LINKER_FLAGS    "${CMAKE_EXE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+  set( CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${ECBUILD_GPROF_FLAG}" )
+
+  set( _trust_flags ${ECBUILD_TRUST_FLAGS} )
+  set( ECBUILD_TRUST_FLAGS ON )
+  ecbuild_add_c_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_cxx_flags( "${ECBUILD_GPROF_FLAG}" )
+  ecbuild_add_fortran_flags( "${ECBUILD_GPROF_FLAG}" )
+  set( ECBUILD_TRUST_FLAGS ${_trust_flags} )
+  unset( _trust_flags )
+
+endif()
+
+############################################################################################
+# check operating system
+
+set( EC_OS_NAME "UNKNOWN" )
+
+### Unix's -- Proper operating systems
+
+if( UNIX )
+
+  ### APPLE ###
+
+  if( APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" )
+    set( EC_OS_NAME "macosx" )
+  endif()
+
+  ### Linux ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" )
+
+    set( EC_OS_NAME "linux" )
+
+    # The following option allows enabling the new dtags linker option
+    # (when set to OFF). ONLY SET TO OFF IF YOU KNOW WHAT YOU ARE DOING AND
+    # NEVER WHEN BUILDING PRODUCTION SOFTWARE. YOU HAVE BEEN WARNED!
+    option( ECBUILD_DISABLE_NEW_DTAGS "Set the linker flag --disable-new-dtags" ON )
+    mark_as_advanced( ECBUILD_DISABLE_NEW_DTAGS )
+
+    if( ECBUILD_DISABLE_NEW_DTAGS )
+      # recent linkers default to --enable-new-dtags
+      # which then adds both RPATH and RUNPATH to executables
+      # thus invalidating RPATH setting, and making LD_LIBRARY_PATH take precedence
+      # to be sure, use tool 'readelf -a <exe> | grep PATH' to see what paths are built-in
+      # see:
+      #  * http://blog.qt.digia.com/blog/2011/10/28/rpath-and-runpath
+      #  * http://www.cmake.org/Wiki/CMake_RPATH_handling
+      #  * man ld
+      #  * http://blog.tremily.us/posts/rpath
+      #  * http://fwarmerdam.blogspot.co.uk/2010/12/rpath-runpath-and-ldlibrarypath.html
+      set(CMAKE_EXE_LINKER_FLAGS     "${CMAKE_EXE_LINKER_FLAGS}    -Wl,--disable-new-dtags")
+      set(CMAKE_SHARED_LINKER_FLAGS  "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-new-dtags")
+      set(CMAKE_MODULE_LINKER_FLAGS  "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--disable-new-dtags")
+    endif()
+
+  endif()
+
+  ### FreeBSD ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
+    set( EC_OS_NAME "freebsd" )
+  endif()
+
+  ### Solaris ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" )
+    set( EC_OS_NAME "solaris" )
+  endif()
+
+  ### AIX ###
+
+  if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" )
+
+    set( EC_OS_NAME "aix" )
+
+    set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -bbigtoc" )
+
+    if( CMAKE_C_COMPILER_ID MATCHES "GNU" )
+      set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Xlinker" )
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCC )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_c_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_COMPILER_IS_GNUCXX )
+      if( EC_OS_BITS EQUAL "64" )
+        ecbuild_add_cxx_flags("-maix64")
+      endif()
+      if( EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-maix32")
+      endif()
+    endif()
+
+    if( CMAKE_C_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_c_flags("-qpic=large")
+#            ecbuild_add_c_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_c_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_c_flags("-qstrict")
+          ecbuild_add_c_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_c_flags("-qfullpath")
+          ecbuild_add_c_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_CXX_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_cxx_flags("-qpic=large")
+      ecbuild_add_cxx_flags("-bmaxdata:0x40000000")
+      ecbuild_add_cxx_flags("-qrtti")
+      ecbuild_add_cxx_flags("-qfuncsect")
+
+#           ecbuild_add_cxx_flags("-qweaksymbol")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_cxx_flags("-q32")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Release" OR ${CMAKE_BUILD_TYPE} MATCHES "Production" )
+          ecbuild_add_cxx_flags("-qstrict")
+          ecbuild_add_cxx_flags("-qinline")
+      endif()
+
+      if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
+          ecbuild_add_cxx_flags("-qfullpath")
+          ecbuild_add_cxx_flags("-qkeepparm")
+      endif()
+
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_ID MATCHES "XL" )
+
+      ecbuild_add_fortran_flags("-qxflag=dealloc_cfptr")
+      ecbuild_add_fortran_flags("-qextname")
+      ecbuild_add_fortran_flags("-qdpc=e")
+      ecbuild_add_fortran_flags("-bmaxdata:0x40000000")
+      ecbuild_add_fortran_flags("-bloadmap:loadmap -bmap:loadmap")
+
+      if(EC_OS_BITS EQUAL "32" )
+        ecbuild_add_fortran_flags("-q32")
+      endif()
+    endif()
+
+  endif()
+
+endif()
+
+### Cygwin
+
+if( ${CMAKE_SYSTEM_NAME} MATCHES "CYGWIN" )
+
+  set( EC_OS_NAME "cygwin" )
+  ecbuild_warn( "Building on Cygwin should work but is untested" )
+
+endif()
+
+### final warning / error
+
+if( ${EC_OS_NAME} MATCHES "UNKNOWN" )
+
+  if( DISABLE_OS_CHECK )
+    ecbuild_warn( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                  " -- DISABLE_OS_CHECK is ON so proceeding at your own risk ..." )
+  else()
+    ecbuild_critical( "ecBuild is untested for this operating system: [${CMAKE_SYSTEM_NAME}]"
+                      " -- refusing to continue. Disable this check with -DDISABLE_OS_CHECK=ON" )
+  endif()
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_compiler_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_compiler_flags.cmake
new file mode 100644
index 0000000..139038b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_compiler_flags.cmake
@@ -0,0 +1,217 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_compiler_flags
+# ======================
+#
+# Set compiler specific default compilation flags for a given language. ::
+#
+#   ecbuild_compiler_flags( <lang> )
+#
+# The procedure is as follows:
+#
+# 1.  ecBuild does **not** set ``CMAKE_<lang>_FLAGS`` i.e. the user can set
+#     these via ``-D`` or the CMake cache and these will be the "base" flags.
+#
+# 2.  ecBuild **overwrites** ``CMAKE_<lang>_FLAGS_<btype>`` in the CMake cache
+#     for all build types with compiler specific defaults for the currently
+#     loaded compiler i.e. any value set by the user via ``-D`` or the CMake
+#     cache **has no effect**.
+#
+# 3.  Any value the user provides via ``ECBUILD_<lang>_FLAGS`` or
+#     ``ECBUILD_<lang>_FLAGS_<btype>`` **overrides** the corresponding
+#     ``CMAKE_<lang>_FLAGS`` or ``CMAKE_<lang>_FLAGS_<btype>`` **without being
+#     written to the CMake cache**.
+#
+##############################################################################
+
+macro( ecbuild_compiler_flags _lang )
+
+  # Set compiler and language specific default flags - OVERWRITES variables in CMake cache
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): try include ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake ")
+    include( ${ECBUILD_MACROS_DIR}/compiler_flags/${CMAKE_${_lang}_COMPILER_ID}_${_lang}.cmake OPTIONAL )
+  endif()
+
+  # Apply user or toolchain specified compilation flag overrides (NOT written to cache)
+
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    if( DEFINED ECBUILD_${_lang}_FLAGS_${_btype} )
+      ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS_${_btype} with ${ECBUILD_${_lang}_FLAGS_${_btype}}")
+      set( CMAKE_${_lang}_FLAGS_${_btype} ${ECBUILD_${_lang}_FLAGS_${_btype}} )
+    endif()
+    mark_as_advanced( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+  if( DEFINED ECBUILD_${_lang}_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_FLAGS with ${ECBUILD_${_lang}_FLAGS}")
+    set( CMAKE_${_lang}_FLAGS "${ECBUILD_${_lang}_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_FLAGS )
+
+  if( DEFINED ECBUILD_${_lang}_LINK_FLAGS )
+    ecbuild_debug( "ecbuild_compiler_flags(${_lang}): overriding CMAKE_${_lang}_LINK_FLAGS with ${ECBUILD_${_lang}_LINK_FLAGS}")
+    set( CMAKE_${_lang}_LINK_FLAGS "${ECBUILD_${_lang}_LINK_FLAGS}" )
+  endif()
+
+  mark_as_advanced( CMAKE_${_lang}_LINK_FLAGS )
+
+  ecbuild_debug_var( CMAKE_${_lang}_FLAGS )
+  foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+    ecbuild_debug_var( CMAKE_${_lang}_FLAGS_${_btype} )
+  endforeach()
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# Using custom compilation flags
+# ==============================
+#
+# If compilation flags need to be controlled on a per source file basis,
+# ecBuild supports defining custom rules in a CMake or JSON file.
+#
+# When using this approach, *default compilation flags are NOT loaded*!
+#
+# Overriding compilation flags on a per source file basis using CMake rules
+# -------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_COMPILE_FLAGS`` to the *full path* of a CMake file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_COMPILE_FLAGS``
+# takes precendence and ``ECBUILD_COMPILE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# Flags can be overridden in 3 different ways:
+#
+# 1.  By defining project specific flags for a language and (optionally)
+#     build type e.g. ::
+#
+#       set(<PNAME>_Fortran_FLAGS "...") # common flags for all build types
+#       set(<PNAME>_Fortran_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 2.  By defining source file specific flags which are *combined* with the
+#     project and target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES COMPILE_FLAGS "..."  # common flags for all build types
+#                    COMPILE_FLAGS_DEBUG "...") # only for DEBUG build type
+#
+# 3.  By defining source file specific flags which *override* the project and
+#     target specific flags ::
+#
+#       set_source_files_properties(<source>
+#         PROPERTIES OVERRIDE_COMPILE_FLAGS "..."
+#                    OVERRIDE_COMPILE_FLAGS_DEBUG "...")
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+# Overriding compilation flags on a per source file basis using JSON rules
+# ------------------------------------------------------------------------
+#
+# Compiler flags can be overridden on a per source file basis by setting the
+# CMake variable ``ECBUILD_SOURCE_FLAGS`` to the *full path* of a JSON file
+# defining the override rules. If set, ``<PNAME>_ECBUILD_SOURCE_FLAGS``
+# takes precendence and ``ECBUILD_SOURCE_FLAGS`` is ignored, allowing for
+# rules that only apply to a subproject (e.g. in a bundle).
+#
+# The JSON file lists shell glob patterns and the rule to apply to each source
+# file matching the pattern, defined as an array ``[op, flag1, ...]``
+# containing an operator followed by one or more flags. Valid operators are:
+#
+# :+: Add the flags to the default compilation flags for matching files
+# :=: Set the flags for matching files, disregarding default compilation flags
+# :/: Remove the flags from the default compilation flags for matching files
+#
+# Rules can be nested to e.g. only apply to a subdirectory by setting the rule
+# to a dictionary, which will only apply to source files matching its pattern.
+#
+# An example JSON file demonstrating different rule types is given below: ::
+#
+#   {
+#     "*"       : [ "+", "-g0" ],
+#     "*.cxx"   : [ "+", "-cxx11" ],
+#     "*.f90"   : [ "+", "-pipe" ],
+#     "foo.c"   : [ "+", "-O0" ],
+#     "foo.cc"  : [ "+", "-O2", "-pipe" ],
+#     "bar/*": {
+#       "*.f90" : [ "=", "-O1" ]
+#     },
+#     "baz/*": {
+#       "*.f90" : [ "/", "-pipe" ],
+#       "*.f90" : [ "/", "-O2" ],
+#       "*.f90" : [ "+", "-O3" ]
+#     }
+#   }
+#
+# See ``examples/override-compile-flags`` in the ecBuild source tree for a
+# complete example using this technique.
+#
+##############################################################################
+
+# Custom (project specific) compilation flags enabled?
+foreach( _flags COMPILE SOURCE )
+  if( ${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS )
+    if ( ECBUILD_${_flags}_FLAGS )
+      ecbuild_debug( "Override ECBUILD_${_flags}_FLAGS (${ECBUILD_${_flags}_FLAGS}) with ${PROJECT_NAME} specific flags (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    else()
+      ecbuild_debug( "Use ${PROJECT_NAME} specific ECBUILD_${_flags}_FLAGS (${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS})" )
+    endif()
+    set( ECBUILD_${_flags}_FLAGS ${${PROJECT_NAME_CAPS}_ECBUILD_${_flags}_FLAGS} )
+  endif()
+  # Ensure ECBUILD_${_flags}_FLAGS is a valid file path
+  if( DEFINED ECBUILD_${_flags}_FLAGS AND NOT EXISTS ${ECBUILD_${_flags}_FLAGS} )
+    ecbuild_warn( "ECBUILD_${_flags}_FLAGS points to non-existent file ${ECBUILD_${_flags}_FLAGS} and will be ignored" )
+    unset( ECBUILD_${_flags}_FLAGS )
+    unset( ECBUILD_${_flags}_FLAGS CACHE )
+  endif()
+endforeach()
+if( ECBUILD_COMPILE_FLAGS )
+  include( "${ECBUILD_COMPILE_FLAGS}" )
+endif()
+
+foreach( _lang C CXX Fortran )
+  if( CMAKE_${_lang}_COMPILER_LOADED )
+
+    # Clear default compilation flags potentially inherited from parent scope
+    # when using custom compilation flags
+    if( ECBUILD_SOURCE_FLAGS OR ECBUILD_COMPILE_FLAGS )
+      set(CMAKE_${_lang}_FLAGS "")
+      foreach(_btype ALL RELEASE RELWITHDEBINFO PRODUCTION BIT DEBUG)
+        set(CMAKE_${_lang}_FLAGS_${_btype} "")
+      endforeach()
+    # Load default compilation flags only if custom compilation flags not enabled
+    else()
+      ecbuild_compiler_flags( ${_lang} )
+    endif()
+
+  endif()
+endforeach()
+
+# Apply user or toolchain specified linker flag overrides per object type (NOT written to cache)
+foreach( _obj EXE SHARED MODULE )
+  if( ECBUILD_${_obj}_LINKER_FLAGS )
+    set( CMAKE_${_obj}_LINKER_FLAGS ${ECBUILD_${_obj}_LINKER_FLAGS} )
+  endif()
+endforeach()
+
+foreach( _btype NONE DEBUG BIT PRODUCTION RELEASE RELWITHDEBINFO )
+
+  foreach( _obj EXE SHARED MODULE )
+    if( ECBUILD_${_obj}_LINKER_FLAGS_${_btype} )
+      set( CMAKE_${_obj}_LINKER_FLAGS_${_btype} ${ECBUILD_${_obj}_LINKER_FLAGS_${_btype}} )
+    endif()
+  endforeach()
+
+endforeach()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_config.h.in b/ecbuild/share/ecbuild/cmake/ecbuild_config.h.in
new file mode 100644
index 0000000..a7d7d75
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_config.h.in
@@ -0,0 +1,201 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef @PROJECT_NAME at _ecbuild_config_h
+#define @PROJECT_NAME at _ecbuild_config_h
+
+/* ecbuild info */
+
+#ifndef ECBUILD_VERSION_STR
+#define ECBUILD_VERSION_STR "@ECBUILD_VERSION_STR@"
+#endif
+#ifndef ECBUILD_MACROS_DIR
+#define ECBUILD_MACROS_DIR  "@ECBUILD_MACROS_DIR@"
+#endif
+
+/* cpu arch info */
+
+#cmakedefine EC_BIG_ENDIAN       @EC_BIG_ENDIAN@
+#cmakedefine EC_LITTLE_ENDIAN    @EC_LITTLE_ENDIAN@
+
+/* compiler support */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+/* os capability checks */
+
+/* --- symbols --- */
+
+#cmakedefine EC_HAVE_FSEEK
+#cmakedefine EC_HAVE_FSEEKO
+#cmakedefine EC_HAVE_FTELLO
+#cmakedefine EC_HAVE_LSEEK
+#cmakedefine EC_HAVE_FTRUNCATE
+#cmakedefine EC_HAVE_OPEN
+#cmakedefine EC_HAVE_FOPEN
+#cmakedefine EC_HAVE_FMEMOPEN
+#cmakedefine EC_HAVE_FUNOPEN
+#cmakedefine EC_HAVE_FLOCK
+#cmakedefine EC_HAVE_MMAP
+#cmakedefine EC_HAVE_FOPENCOOKIE
+
+#cmakedefine EC_HAVE_POSIX_MEMALIGN
+
+#cmakedefine EC_HAVE_F_GETLK
+#cmakedefine EC_HAVE_F_SETLKW
+#cmakedefine EC_HAVE_F_SETLK
+
+#cmakedefine EC_HAVE_F_GETLK64
+#cmakedefine EC_HAVE_F_SETLKW64
+#cmakedefine EC_HAVE_F_SETLK64
+
+#cmakedefine EC_HAVE_MAP_ANONYMOUS
+#cmakedefine EC_HAVE_MAP_ANON
+
+/* --- include files --- */
+
+#cmakedefine EC_HAVE_ASSERT_H
+#cmakedefine EC_HAVE_STDLIB_H
+#cmakedefine EC_HAVE_UNISTD_H
+#cmakedefine EC_HAVE_STRING_H
+#cmakedefine EC_HAVE_STRINGS_H
+#cmakedefine EC_HAVE_SYS_STAT_H
+#cmakedefine EC_HAVE_SYS_TIME_H
+#cmakedefine EC_HAVE_SYS_TYPES_H
+
+#cmakedefine EC_HAVE_MALLOC_H
+#cmakedefine EC_HAVE_SYS_MALLOC_H
+
+#cmakedefine EC_HAVE_SYS_PARAM_H
+#cmakedefine EC_HAVE_SYS_MOUNT_H
+#cmakedefine EC_HAVE_SYS_VFS_H
+
+/* --- capabilities --- */
+
+#cmakedefine EC_HAVE_OFFT
+#cmakedefine EC_HAVE_OFF64T
+
+#cmakedefine EC_HAVE_STRUCT_STAT
+#cmakedefine EC_HAVE_STRUCT_STAT64
+#cmakedefine EC_HAVE_STAT
+#cmakedefine EC_HAVE_STAT64
+#cmakedefine EC_HAVE_FSTAT
+#cmakedefine EC_HAVE_FSTAT64
+
+#cmakedefine EC_HAVE_FSEEKO64
+#cmakedefine EC_HAVE_FTELLO64
+#cmakedefine EC_HAVE_LSEEK64
+#cmakedefine EC_HAVE_OPEN64
+#cmakedefine EC_HAVE_FOPEN64
+#cmakedefine EC_HAVE_FTRUNCATE64
+#cmakedefine EC_HAVE_FLOCK64
+#cmakedefine EC_HAVE_MMAP64
+
+#cmakedefine EC_HAVE_STRUCT_STATVFS
+#cmakedefine EC_HAVE_STRUCT_STATVFS64
+#cmakedefine EC_HAVE_STATVFS
+#cmakedefine EC_HAVE_STATVFS64
+
+#cmakedefine EC_HAVE_FSYNC
+#cmakedefine EC_HAVE_FDATASYNC
+#cmakedefine EC_HAVE_DIRFD
+#cmakedefine EC_HAVE_SYSPROC
+#cmakedefine EC_HAVE_SYSPROCFS
+
+#cmakedefine EC_HAVE_EXECINFO_BACKTRACE
+
+/* --- asynchronous IO support --- */
+
+#cmakedefine EC_HAVE_AIOCB
+#cmakedefine EC_HAVE_AIO64CB
+
+/* --- reentrant funtions support --- */
+
+#cmakedefine EC_HAVE_GMTIME_R
+#cmakedefine EC_HAVE_GETPWUID_R
+#cmakedefine EC_HAVE_GETPWNAM_R
+#cmakedefine EC_HAVE_READDIR_R
+#cmakedefine EC_HAVE_GETHOSTBYNAME_R
+
+/* --- compiler __attribute__ support --- */
+
+#cmakedefine EC_HAVE_ATTRIBUTE_CONSTRUCTOR
+#cmakedefine EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV
+#cmakedefine EC_HAVE_PROCFS
+
+
+/* --- dl library support --- */
+
+#cmakedefine EC_HAVE_DLFCN_H
+#cmakedefine EC_HAVE_DLADDR
+
+/* --- c compiler support --- */
+
+#cmakedefine EC_HAVE_C_INLINE
+
+/* --- c++ compiler support --- */
+
+#cmakedefine EC_HAVE_FUNCTION_DEF
+
+#cmakedefine EC_HAVE_CXXABI_H
+#cmakedefine EC_HAVE_CXX_BOOL
+
+#cmakedefine EC_HAVE_CXX_SSTREAM
+
+/* config info */
+
+#define @PNAME at _OS_NAME          "@CMAKE_SYSTEM@"
+#define @PNAME at _OS_BITS          @EC_OS_BITS@
+#define @PNAME at _OS_BITS_STR      "@EC_OS_BITS@"
+#define @PNAME at _OS_STR           "@EC_OS_NAME at .@EC_OS_BITS@"
+#define @PNAME at _OS_VERSION       "@CMAKE_SYSTEM_VERSION@"
+#define @PNAME at _SYS_PROCESSOR    "@CMAKE_SYSTEM_PROCESSOR@"
+
+#define @PNAME at _BUILD_TIMESTAMP  "@EC_BUILD_TIMESTAMP@"
+#define @PNAME at _BUILD_TYPE       "@CMAKE_BUILD_TYPE@"
+
+#define @PNAME at _C_COMPILER_ID      "@CMAKE_C_COMPILER_ID@"
+#define @PNAME at _C_COMPILER_VERSION "@CMAKE_C_COMPILER_VERSION@"
+
+#define @PNAME at _CXX_COMPILER_ID      "@CMAKE_CXX_COMPILER_ID@"
+#define @PNAME at _CXX_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@"
+
+#define @PNAME at _C_COMPILER       "@CMAKE_C_COMPILER@"
+#define @PNAME at _C_FLAGS          "@EC_C_FLAGS@"
+
+#define @PNAME at _CXX_COMPILER     "@CMAKE_CXX_COMPILER@"
+#define @PNAME at _CXX_FLAGS        "@EC_CXX_FLAGS@"
+
+/* Needed for finding per package config files */
+
+#define @PNAME at _INSTALL_DIR       "@CMAKE_INSTALL_PREFIX@"
+#define @PNAME at _INSTALL_BIN_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_BIN_DIR@"
+#define @PNAME at _INSTALL_LIB_DIR   "@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB_DIR@"
+#define @PNAME at _INSTALL_DATA_DIR  "@CMAKE_INSTALL_PREFIX@/@INSTALL_DATA_DIR@"
+
+#define @PNAME at _DEVELOPER_SRC_DIR "@CMAKE_SOURCE_DIR@"
+#define @PNAME at _DEVELOPER_BIN_DIR "@CMAKE_BINARY_DIR@"
+
+#cmakedefine EC_HAVE_FORTRAN
+
+#ifdef EC_HAVE_FORTRAN
+
+#define @PNAME at _Fortran_COMPILER_ID      "@CMAKE_Fortran_COMPILER_ID@"
+#define @PNAME at _Fortran_COMPILER_VERSION "@CMAKE_Fortran_COMPILER_VERSION@"
+
+#define @PNAME at _Fortran_COMPILER "@CMAKE_Fortran_COMPILER@"
+#define @PNAME at _Fortran_FLAGS    "@EC_Fortran_FLAGS@"
+
+#endif
+
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#cmakedefine BOOST_UNIT_TEST_FRAMEWORK_LINKED
+
+#endif /* @PROJECT_NAME at _ecbuild_config_h */
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_declare_project.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_declare_project.cmake
new file mode 100644
index 0000000..b3f29e7
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_declare_project.cmake
@@ -0,0 +1,198 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_declare_project
+# =======================
+#
+# Initialise an ecBuild project. A CMake project must have previously been
+# declared with ``project( <name> ... )``. ::
+#
+#   ecbuild_declare_project()
+#
+# Sets the following CMake variables
+# (where ``PNAME`` is the capitalised project name):
+#
+# :<PNAME>_GIT_SHA1:       Git revision (if project is a Git repo)
+# :<PNAME>_GIT_SHA1_SHORT: short Git revision (if project is a Git repo)
+# :<PNAME>_VERSION:        version in format ``MAJOR.MINOR.PATCH``
+# :<PNAME>_VERSION_STR:    version as given in ``VERSION.cmake`` or 0.0.0
+# :<PNAME>_MAJOR_VERSION:  major version number
+# :<PNAME>_MINOR_VERSION:  minor version number
+# :<PNAME>_PATCH_VERSION:  patch version number
+# :INSTALL_BIN_DIR:        relative install directory for executables
+# :INSTALL_LIB_DIR:        relative install directory for libraries
+# :INSTALL_INCLUDE_DIR:    relative install directory for include files
+# :INSTALL_DATA_DIR:       relative install directory for data
+# :INSTALL_CMAKE_DIR:      relative install directory for CMake files
+#
+# Customising install locations
+# -----------------------------
+#
+# The relative installation directories of components can be customised by
+# setting the following CMake variables on the command line or in cache:
+#
+# :INSTALL_BIN_DIR:        directory for installing executables
+#                          (default: ``bin``)
+# :INSTALL_LIB_DIR:        directory for installing libraries
+#                          (default: ``lib``)
+# :INSTALL_INCLUDE_DIR:    directory for installing include files
+#                          (default: ``include``)
+# :INSTALL_DATA_DIR:       directory for installing data
+#                          (default: ``share/<project_name>``)
+# :INSTALL_CMAKE_DIR:      directory for installing CMake files
+#                          (default: ``share/<project_name>/cmake``)
+#
+# Using *relative* paths is recommended, which are interpreted relative to the
+# ``CMAKE_INSTALL_PREFIX``. Using absolute paths makes the build
+# non-relocatable and may break the generation of relocatable binary packages.
+#
+##############################################################################
+
+macro( ecbuild_declare_project )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  # reset the lists of targets (executables, libs, tests & resources)
+
+  set( ${PROJECT_NAME}_ALL_EXES "" CACHE INTERNAL "" )
+  set( ${PROJECT_NAME}_ALL_LIBS "" CACHE INTERNAL "" )
+
+  # if git project get its HEAD SHA1
+  # leave it here so we may use ${PNAME}_GIT_SHA1 on the version file
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/.git )
+    get_git_head_revision( GIT_REFSPEC ${PNAME}_GIT_SHA1 )
+    if( ${PNAME}_GIT_SHA1 )
+      string( SUBSTRING "${${PNAME}_GIT_SHA1}" 0 7 ${PNAME}_GIT_SHA1_SHORT )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1 )
+      #     ecbuild_debug_var( ${PNAME}_GIT_SHA1_SHORT )
+    else()
+      ecbuild_debug( "Could not get git-sha1 for project ${PNAME}")
+    endif()
+  endif()
+
+  # read and parse project version file
+  if( EXISTS ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+    include( ${PROJECT_SOURCE_DIR}/VERSION.cmake )
+  else()
+    set( ${PROJECT_NAME}_VERSION_STR "0.0.0" )
+  endif()
+
+  string( REPLACE "." " " _version_list ${${PROJECT_NAME}_VERSION_STR} ) # dots to spaces
+
+  separate_arguments( _version_list )
+
+  list( GET _version_list 0 ${PNAME}_MAJOR_VERSION )
+  list( GET _version_list 1 ${PNAME}_MINOR_VERSION )
+  list( GET _version_list 2 ${PNAME}_PATCH_VERSION )
+
+  # cleanup patch version of any extra qualifiers ( -dev -rc1 ... )
+
+  string( REGEX REPLACE "^([0-9]+).*" "\\1" ${PNAME}_PATCH_VERSION "${${PNAME}_PATCH_VERSION}" )
+
+  set( ${PNAME}_VERSION "${${PNAME}_MAJOR_VERSION}.${${PNAME}_MINOR_VERSION}.${${PNAME}_PATCH_VERSION}"
+       CACHE INTERNAL "package ${PNAME} version" )
+
+  set( ${PNAME}_VERSION_STR "${${PROJECT_NAME}_VERSION_STR}"
+       CACHE INTERNAL "package ${PNAME} version string" ) # ignore caps
+
+  #    ecbuild_debug_var( ${PNAME}_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_VERSION_STR )
+  #    ecbuild_debug_var( ${PNAME}_MAJOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_MINOR_VERSION )
+  #    ecbuild_debug_var( ${PNAME}_PATCH_VERSION )
+
+  # install dirs for this project
+
+  # Use defaults unless values are already present in cache
+  if( NOT INSTALL_BIN_DIR )
+    set( INSTALL_BIN_DIR bin )
+  endif()
+  if( NOT INSTALL_LIB_DIR )
+    set( INSTALL_LIB_DIR lib )
+  endif()
+  if( NOT INSTALL_INCLUDE_DIR )
+    set( INSTALL_INCLUDE_DIR include )
+  endif()
+  # INSTALL_DATA_DIR is package specific and needs to be reset for subpackages
+  # in a bundle. Users *cannot* override this directory (ECBUILD-315)
+  set( INSTALL_DATA_DIR share/${PROJECT_NAME} )
+  # share/${PROJECT_NAME}/cmake is a convention - it makes no sense to override it
+  set( INSTALL_CMAKE_DIR share/${PROJECT_NAME}/cmake )
+
+  mark_as_advanced( INSTALL_BIN_DIR )
+  mark_as_advanced( INSTALL_LIB_DIR )
+  mark_as_advanced( INSTALL_INCLUDE_DIR )
+  mark_as_advanced( INSTALL_DATA_DIR )
+  mark_as_advanced( INSTALL_CMAKE_DIR )
+
+  # warnings for non-relocatable projects
+
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+    if( IS_ABSOLUTE ${INSTALL_${p}_DIR} )
+      ecbuild_warn( "Defining INSTALL_${p}_DIR as absolute path '${INSTALL_${p}_DIR}' makes this build non-relocatable, possibly breaking the installation of RPMS and DEB packages" )
+    endif()
+  endforeach()
+
+  # make relative paths absolute ( needed later on ) and cache them ...
+  foreach( p LIB BIN INCLUDE DATA CMAKE )
+
+    set( var INSTALL_${p}_DIR )
+
+    if( NOT IS_ABSOLUTE "${${var}}" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${CMAKE_INSTALL_PREFIX}/${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    else()
+      ecbuild_warn( "Setting an absolute path for ${VAR} in project ${PNAME}, breakes generation of relocatable binary packages (rpm,deb,...)" )
+      set( ${PNAME}_FULL_INSTALL_${p}_DIR "${${var}}"
+           CACHE INTERNAL "${PNAME} ${p} full install path" )
+    endif()
+
+    #        ecbuild_debug_var( ${PNAME}_FULL_INSTALL_${p}_DIR )
+
+  endforeach()
+
+  # correctly set CMAKE_INSTALL_RPATH
+
+  if( ENABLE_RPATHS )
+
+    if( ENABLE_RELATIVE_RPATHS )
+
+      file( RELATIVE_PATH relative_rpath ${${PNAME}_FULL_INSTALL_BIN_DIR} ${${PNAME}_FULL_INSTALL_LIB_DIR} )
+      # ecbuild_debug_var( relative_rpath )
+
+      ecbuild_append_to_rpath( ${relative_rpath} )
+
+    else() # make rpaths absolute
+
+      if( IS_ABSOLUTE ${INSTALL_LIB_DIR} )
+        ecbuild_append_to_rpath( "${INSTALL_LIB_DIR}" )
+      else()
+        ecbuild_append_to_rpath( "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}" )
+      endif()
+
+    endif()
+
+  endif()
+
+  # ecbuild_debug_var( CMAKE_INSTALL_RPATH )
+
+  # print project header
+
+  ecbuild_info( "---------------------------------------------------------" )
+
+  if( ${PNAME}_GIT_SHA1_SHORT )
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR}) [${${PNAME}_GIT_SHA1_SHORT}]" )
+  else()
+    ecbuild_info( "[${PROJECT_NAME}] (${${PNAME}_VERSION_STR})" )
+  endif()
+
+endmacro( ecbuild_declare_project )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_build_types.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_build_types.cmake
new file mode 100644
index 0000000..7cfc591
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_build_types.cmake
@@ -0,0 +1,59 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# define default build type
+
+set( _BUILD_TYPE_MSG "Build type options are: [ None | Debug | Bit | Production | Release | RelWithDebInfo ]" )
+
+if( NOT ECBUILD_DEFAULT_BUILD_TYPE )
+	set( ECBUILD_DEFAULT_BUILD_TYPE "RelWithDebInfo" )
+endif()
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE ${ECBUILD_DEFAULT_BUILD_TYPE} CACHE STRING  ${_BUILD_TYPE_MSG}  FORCE )
+endif()
+
+# capitalize the build type for easy use with conditionals
+string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+# correct capitatlization of the build type
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "NONE" )
+  set(CMAKE_BUILD_TYPE None CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "DEBUG" )
+  set(CMAKE_BUILD_TYPE Debug CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "BIT" )
+  set(CMAKE_BUILD_TYPE Bit CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "PRODUCTION" )
+  set(CMAKE_BUILD_TYPE Production CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELEASE" )
+  set(CMAKE_BUILD_TYPE Release CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+if( CMAKE_BUILD_TYPE_CAPS STREQUAL "RELWITHDEBINFO" )
+  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING ${_BUILD_TYPE_MSG} FORCE )
+endif()
+
+# fail if build type is not one of the defined ones
+if( NOT CMAKE_BUILD_TYPE MATCHES "None"  AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Debug" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Bit" AND
+	  NOT CMAKE_BUILD_TYPE MATCHES "Production" AND
+    NOT CMAKE_BUILD_TYPE MATCHES "Release"  AND
+    NOT CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" )
+    ecbuild_critical( "CMAKE_BUILD_TYPE is not recognized. ${_BUILD_TYPE_MSG}" )
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake
new file mode 100644
index 0000000..799eb7d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_libs_and_execs_target.cmake
@@ -0,0 +1,29 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+############################################################################################
+# define libs and execs targets
+
+macro( ecbuild_define_libs_and_execs_targets )
+
+  add_custom_target( libs )
+
+  if( EC_ALL_LIBS )
+    add_dependencies( libs ${EC_ALL_LIBS} )
+  endif()
+
+  add_custom_target( execs )
+
+  if( EC_ALL_EXECS )
+    add_dependencies( execs ${EC_ALL_EXES} )
+  endif()
+
+endmacro(ecbuild_define_libs_and_execs_targets)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_links_target.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_links_target.cmake
new file mode 100644
index 0000000..745288c
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_links_target.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+# macro for adding a link to library on a development system
+
+set( EC_ALL_EXES "" CACHE INTERNAL "" )
+set( EC_ALL_LIBS "" CACHE INTERNAL "" )
+
+macro( ecbuild_link_exe TARGET  FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/bin
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/bin/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/bin/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/bin/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_EXES ${EC_ALL_EXES} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_exe  )
+
+###############################################################################
+# macro for adding a link to library on a development system
+
+macro( ecbuild_link_lib  TARGET FILENAME FILEPATH )
+
+    if( DEFINED EC_LINK_DIR )
+       add_custom_target(${TARGET}_link
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}
+          COMMAND ${CMAKE_COMMAND} -E make_directory ${EC_LINK_DIR}/lib
+          COMMAND ${CMAKE_COMMAND} -E remove ${EC_LINK_DIR}/lib/${FILENAME}
+          COMMAND ${CMAKE_COMMAND} -E create_symlink ${FILEPATH} ${EC_LINK_DIR}/lib/${FILENAME}
+          DEPENDS ${TARGET}
+          COMMENT "link ${EC_LINK_DIR}/lib/${FILENAME}" )
+    endif()
+
+    set( EC_ALL_LIBS ${EC_ALL_LIBS} ${TARGET} CACHE INTERNAL "" )
+
+endmacro( ecbuild_link_lib  )
+
+############################################################################################
+# define make links target
+
+macro( ecbuild_define_links_target )
+
+  if( DEFINED EC_LINK_DIR )
+
+    foreach( lib ${EC_ALL_LIBS} )
+      list( APPEND ec_link_libs ${lib}_link )
+    endforeach()
+    foreach( exe ${EC_ALL_EXES} )
+      list( APPEND ec_link_exes ${exe}_link )
+    endforeach()
+
+    add_custom_target( links DEPENDS ${ec_link_libs} ${ec_link_exes} )
+
+     #   ecbuild_debug_var( EC_ALL_EXES )
+     #   ecbuild_debug_var( ec_link_exes )
+
+     #  ecbuild_debug_var( EC_ALL_LIBS )
+     #   ecbuild_debug_var( ec_link_libs )
+
+  endif()
+
+endmacro(ecbuild_define_links_target)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_options.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_options.cmake
new file mode 100644
index 0000000..fc6376e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_options.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# general options
+
+option( BUILD_SHARED_LIBS       "build shared libraries when possible"            ON  )
+
+option( ENABLE_RPATHS           "when installing insert RPATHS into binaries"     ON  )
+option( ENABLE_RELATIVE_RPATHS  "try to use relative RPATHS, including build dir" ON  )
+option( ENABLE_WARNINGS         "enable compiler warnings"                        OFF )
+
+option( ENABLE_LARGE_FILE_SUPPORT "build with large file support"   ON  )
+
+option( ENABLE_PROFILING        "build with profiling support" OFF )
+
+mark_as_advanced( ENABLE_LARGE_FILE_SUPPORT )
+
+option( ENABLE_OS_TESTS          "Run all OS tests" ON )
+
+mark_as_advanced( ENABLE_OS_TESTS )
+
+option( ENABLE_FORTRAN_C_INTERFACE "Enable Fortran/C Interface" OFF )
+mark_as_advanced( ENABLE_FORTRAN_C_INTERFACE )
+
+option( DEVELOPER_MODE           "activates developer mode"               OFF )
+option( CHECK_UNUSED_FILES       "check for unused project files (slow)"  OFF )
+
+mark_as_advanced( DEVELOPER_MODE  )
+mark_as_advanced( CHECK_UNUSED_FILES  )
+
+include( CMakeDependentOption ) # make options depend on one another
+
+cmake_dependent_option( ENABLE_OS_TYPES_TEST     "Run sizeof tests on C types" ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_ENDINESS_TEST  "Run OS endiness tests"       ON "ENABLE_OS_TESTS" OFF)
+cmake_dependent_option( ENABLE_OS_FUNCTIONS_TEST "Run OS functions tests"      ON "ENABLE_OS_TESTS" OFF)
+
+mark_as_advanced( ENABLE_OS_TYPES_TEST ENABLE_OS_ENDINESS_TEST ENABLE_OS_FUNCTIONS_TEST  )
+
+option( ECBUILD_USE_INCLUDE_DIRECTORIES "Forces to use global include_directories() instead of target specific. Adverse effect on PkgConfig generation." OFF )
+
+mark_as_advanced( ECBUILD_USE_INCLUDE_DIRECTORIES )
+
+set( CMAKE_NO_SYSTEM_FROM_IMPORTED ON )
+
+# hide some CMake options from CMake UI
+
+mark_as_advanced( CMAKE_OSX_ARCHITECTURES CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OSX_SYSROOT )
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_paths.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_paths.cmake
new file mode 100644
index 0000000..16d7b94
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_paths.cmake
@@ -0,0 +1,49 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# define project paths
+
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
+file( MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
+
+#######################################################################################################
+
+# setup library building rpaths (both in build dir and then when installed)
+
+# add the automatic parts to RPATH which point to dirs outside build tree
+set( CMAKE_INSTALL_RPATH_USE_LINK_PATH   TRUE  )
+
+# use RPATHs for the build tree
+set( CMAKE_SKIP_BUILD_RPATH              FALSE  )
+
+# If INSTALL_LIB_DIR is set to anything other than lib, the relative install
+# RPATH is wrong in the build tree
+if( ENABLE_RELATIVE_RPATHS )
+  ecbuild_debug( "Relative RPATHS are enabled" )
+  if( INSTALL_LIB_DIR STREQUAL "lib" OR (NOT INSTALL_LIB_DIR) )
+    # when building, use the install RPATH immediately (we don't want to relink)
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      TRUE  )
+    ecbuild_debug( "Building with install RPATH" )
+  else()
+    # when building, don't use the install RPATH yet, but later on when installing
+    set( CMAKE_BUILD_WITH_INSTALL_RPATH      FALSE  )
+    ecbuild_debug( "Not building with install RPATH, need to relink when installing" )
+  endif()
+endif()
+
+# Always include srcdir and builddir in include path
+# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+# in about every subdir
+
+set( CMAKE_INCLUDE_CURRENT_DIR OFF )
+
+# put the include dirs which are in the source or build tree
+# before all other include dirs, so the headers in the sources
+# are prefered over the already installed ones (since cmake 2.4.1)
+
+set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_define_uninstall.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_define_uninstall.cmake
new file mode 100644
index 0000000..cc6efa9
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_define_uninstall.cmake
@@ -0,0 +1,7 @@
+### adds uninstall target ###############
+
+configure_file(
+  "${CMAKE_CURRENT_LIST_DIR}/ecbuild_uninstall.cmake.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake" IMMEDIATE @ONLY)
+
+add_custom_target( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/ecbuild_uninstall.cmake")
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_dont_pack.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_dont_pack.cmake
new file mode 100644
index 0000000..9f9f4a4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_dont_pack.cmake
@@ -0,0 +1,82 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_dont_pack
+# =================
+#
+# Specify files and directories to exclude from packaging. ::
+#
+#   ecbuild_dont_pack( [ FILES <file1> [ <file2> ... ] ]
+#                      [ DIRS <dir1> [ <dir2> ... ] ]
+#                      [ REGEX <regex> ] )
+#
+# Options
+# -------
+#
+# FILES : optional, one of FILES, DIRS, REGEX required
+#   list of files to exclude from packaging
+#
+# DIRS : optional, one of FILES, DIRS, REGEX required
+#   list of directories to exclude from packaging
+#
+# REGEX : optional, one of FILES, DIRS, REGEX required
+#   regular expression to match files / directories to exclude from packaging
+#
+##############################################################################
+
+macro( ecbuild_dont_pack )
+
+    set( options )
+    set( single_value_args REGEX )
+    set( multi_value_args  FILES DIRS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_dont_pack(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT DEFINED _PAR_REGEX AND NOT  DEFINED _PAR_FILES AND NOT  DEFINED _PAR_DIRS )
+      ecbuild_critical("Call to ecbuild_dont_pack does not speficify any list to avoid packing.")
+    endif()
+
+    set( LOCAL_FILES_NOT_TO_PACK "" )
+
+    # all recursive files are not to pack
+    if( DEFINED _PAR_REGEX )
+        file( GLOB_RECURSE all_files_in_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_PAR_REGEX} )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${all_files_in_subdirs} )
+    endif()
+
+    # selected dirs not to pack
+    if( DEFINED _PAR_DIRS )
+        foreach( dir ${_PAR_DIRS} )
+            list( APPEND LOCAL_FILES_NOT_TO_PACK ${dir}/ )
+        endforeach()
+    endif()
+
+    # selected files not to pack
+    if( DEFINED _PAR_FILES )
+        list( APPEND LOCAL_FILES_NOT_TO_PACK ${_PAR_FILES} )
+    endif()
+
+    # transform the local files  to full absolute paths
+    # and place them in the global list of files not to pack
+    foreach( file ${LOCAL_FILES_NOT_TO_PACK} )
+        list( APPEND ECBUILD_DONT_PACK_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file} )
+    endforeach()
+
+    # save cache if we added any files not to pack
+    if( LOCAL_FILES_NOT_TO_PACK )
+        set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" )
+    endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_download_resource.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_download_resource.cmake
new file mode 100644
index 0000000..e1e8eff
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_download_resource.cmake
@@ -0,0 +1,76 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_download_resource
+# =========================
+#
+# Download a file from a given URL and save to FILE at configure time. ::
+#
+#   ecbuild_download_resource( FILE URL )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+##############################################################################
+
+function( ecbuild_download_resource _p_OUT _p_URL )
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  if( NOT EXISTS ${_p_OUT} )
+
+    find_program( CURL_PROGRAM curl )
+    mark_as_advanced(CURL_PROGRAM)
+    if( CURL_PROGRAM )
+
+      execute_process( COMMAND ${CURL_PROGRAM} --silent --show-error --fail
+                               --retry ${ECBUILD_DOWNLOAD_RETRIES}
+                               --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+                               --output ${_p_OUT} ${_p_URL}
+                       WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+    else()
+
+      find_program( WGET_PROGRAM wget )
+
+      if( WGET_PROGRAM )
+
+        # wget takes the total number of tries, curl the number or retries
+        math( EXPR ECBUILD_DOWNLOAD_RETRIES ${ECBUILD_DOWNLOAD_RETRIES} + 1 )
+
+        execute_process( COMMAND ${WGET_PROGRAM} -nv -O ${_p_OUT}
+                                 -t ${ECBUILD_DOWNLOAD_RETRIES}
+                                 -T ${ECBUILD_DOWNLOAD_TIMEOUT} ${_p_URL}
+                         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE CMD_RESULT )
+
+      else()
+        ecbuild_critical("Could not find curl or wget. Error downloading ${_p_URL}")
+      endif()
+
+    endif()
+
+    if(CMD_RESULT)
+      ecbuild_critical("Error downloading ${_p_URL}")
+    endif()
+
+  endif()
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_echo_targets.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_echo_targets.cmake
new file mode 100644
index 0000000..61250ee
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_echo_targets.cmake
@@ -0,0 +1,233 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target_property
+# ============================
+#
+# Output a given property of a given target. ::
+#
+#   ecbuild_echo_target_property( <target> <property> )
+#
+##############################################################################
+
+function(ecbuild_echo_target_property tgt prop)
+
+  cmake_policy(PUSH)
+
+  if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD)
+  endif()
+
+  # v for value, d for defined, s for set
+  get_property(v TARGET ${tgt} PROPERTY ${prop})
+  get_property(d TARGET ${tgt} PROPERTY ${prop} DEFINED)
+  get_property(s TARGET ${tgt} PROPERTY ${prop} SET)
+
+  # only produce output for values that are set
+  if(s)
+    ecbuild_debug("tgt='${tgt}' prop='${prop}'")
+    ecbuild_debug("  value='${v}'")
+    ecbuild_debug("  defined='${d}'")
+    ecbuild_debug("  set='${s}'")
+    ecbuild_debug("")
+  endif()
+
+  cmake_policy(POP)
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_target
+# ===================
+#
+# Output all possible target properties of a given target. ::
+#
+#   ecbuild_echo_target( <target> )
+#
+##############################################################################
+
+function(ecbuild_echo_target tgt)
+  if(NOT TARGET ${tgt})
+    ecbuild_debug("There is no target named '${tgt}'")
+    return()
+  endif()
+
+  set(props
+DEBUG_OUTPUT_NAME
+DEBUG_POSTFIX
+RELEASE_OUTPUT_NAME
+RELEASE_POSTFIX
+ARCHIVE_OUTPUT_DIRECTORY
+ARCHIVE_OUTPUT_DIRECTORY_DEBUG
+ARCHIVE_OUTPUT_DIRECTORY_RELEASE
+ARCHIVE_OUTPUT_NAME
+ARCHIVE_OUTPUT_NAME_DEBUG
+ARCHIVE_OUTPUT_NAME_RELEASE
+AUTOMOC
+AUTOMOC_MOC_OPTIONS
+BUILD_WITH_INSTALL_RPATH
+BUNDLE
+BUNDLE_EXTENSION
+COMPILE_DEFINITIONS
+COMPILE_DEFINITIONS_DEBUG
+COMPILE_DEFINITIONS_RELEASE
+COMPILE_FLAGS
+DEBUG_POSTFIX
+RELEASE_POSTFIX
+DEFINE_SYMBOL
+ENABLE_EXPORTS
+EXCLUDE_FROM_ALL
+EchoString
+FOLDER
+FRAMEWORK
+Fortran_FORMAT
+Fortran_MODULE_DIRECTORY
+GENERATOR_FILE_NAME
+GNUtoMS
+HAS_CXX
+IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
+IMPORTED
+IMPORTED_CONFIGURATIONS
+IMPORTED_IMPLIB
+IMPORTED_IMPLIB_DEBUG
+IMPORTED_IMPLIB_RELEASE
+IMPORTED_LINK_DEPENDENT_LIBRARIES
+IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG
+IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_LANGUAGES
+IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG
+IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE
+IMPORTED_LINK_INTERFACE_LIBRARIES
+IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG
+IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE
+IMPORTED_LINK_INTERFACE_MULTIPLICITY
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_DEBUG
+IMPORTED_LINK_INTERFACE_MULTIPLICITY_RELEASE
+IMPORTED_LOCATION
+IMPORTED_LOCATION_DEBUG
+IMPORTED_LOCATION_RELEASE
+IMPORTED_NO_SONAME
+IMPORTED_NO_SONAME_DEBUG
+IMPORTED_NO_SONAME_RELEASE
+IMPORTED_SONAME
+IMPORTED_SONAME_DEBUG
+IMPORTED_SONAME_RELEASE
+IMPORT_PREFIX
+IMPORT_SUFFIX
+INCLUDE_DIRECTORIES
+INSTALL_NAME_DIR
+INSTALL_RPATH
+INSTALL_RPATH_USE_LINK_PATH
+INTERPROCEDURAL_OPTIMIZATION
+INTERPROCEDURAL_OPTIMIZATION_DEBUG
+INTERPROCEDURAL_OPTIMIZATION_RELEASE
+LABELS
+LIBRARY_OUTPUT_DIRECTORY
+LIBRARY_OUTPUT_DIRECTORY_DEBUG
+LIBRARY_OUTPUT_DIRECTORY_RELEASE
+LIBRARY_OUTPUT_NAME
+LIBRARY_OUTPUT_NAME_DEBUG
+LIBRARY_OUTPUT_NAME_RELEASE
+LINKER_LANGUAGE
+LINK_DEPENDS
+LINK_FLAGS
+LINK_FLAGS_DEBUG
+LINK_FLAGS_RELEASE
+LINK_INTERFACE_LIBRARIES
+LINK_INTERFACE_LIBRARIES_DEBUG
+LINK_INTERFACE_LIBRARIES_RELEASE
+LINK_INTERFACE_MULTIPLICITY
+LINK_INTERFACE_MULTIPLICITY_DEBUG
+LINK_INTERFACE_MULTIPLICITY_RELEASE
+LINK_SEARCH_END_STATIC
+LINK_SEARCH_START_STATIC
+LOCATION
+LOCATION_DEBUG
+LOCATION_RELEASE
+MACOSX_BUNDLE
+MACOSX_BUNDLE_INFO_PLIST
+MACOSX_FRAMEWORK_INFO_PLIST
+MAP_IMPORTED_CONFIG_DEBUG
+MAP_IMPORTED_CONFIG_RELEASE
+OSX_ARCHITECTURES
+OSX_ARCHITECTURES_DEBUG
+OSX_ARCHITECTURES_RELEASE
+OUTPUT_NAME
+OUTPUT_NAME_DEBUG
+OUTPUT_NAME_RELEASE
+POST_INSTALL_SCRIPT
+PREFIX
+PRE_INSTALL_SCRIPT
+PRIVATE_HEADER
+PROJECT_LABEL
+PUBLIC_HEADER
+RESOURCE
+RULE_LAUNCH_COMPILE
+RULE_LAUNCH_CUSTOM
+RULE_LAUNCH_LINK
+RUNTIME_OUTPUT_DIRECTORY
+RUNTIME_OUTPUT_DIRECTORY_DEBUG
+RUNTIME_OUTPUT_DIRECTORY_RELEASE
+RUNTIME_OUTPUT_NAME
+RUNTIME_OUTPUT_NAME_DEBUG
+RUNTIME_OUTPUT_NAME_RELEASE
+SKIP_BUILD_RPATH
+SOURCES
+SOVERSION
+STATIC_LIBRARY_FLAGS
+STATIC_LIBRARY_FLAGS_DEBUG
+STATIC_LIBRARY_FLAGS_RELEASE
+SUFFIX
+TYPE
+VERSION
+VS_DOTNET_REFERENCES
+VS_GLOBAL_WHATEVER
+VS_GLOBAL_KEYWORD
+VS_GLOBAL_PROJECT_TYPES
+VS_KEYWORD
+VS_SCC_AUXPATH
+VS_SCC_LOCALPATH
+VS_SCC_PROJECTNAME
+VS_SCC_PROVIDER
+VS_WINRT_EXTENSIONS
+VS_WINRT_REFERENCES
+WIN32_EXECUTABLE
+XCODE_ATTRIBUTE_WHATEVER
+)
+
+  ecbuild_debug("======================== ${tgt} ========================")
+  foreach(p ${props})
+    ecbuild_echo_target_property("${tgt}" "${p}")
+  endforeach()
+  ecbuild_debug("")
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_echo_targets
+# ====================
+#
+# Output all possible target properties of the specified list-of-targets.
+# This is very useful for debugging. ::
+#
+#   ecbuild_echo_targets( <list-of-targets> )
+#
+##############################################################################
+
+function(ecbuild_echo_targets)
+  set(tgts ${ARGV})
+  foreach(t ${tgts})
+    ecbuild_echo_target("${t}")
+  endforeach()
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_enable_fortran.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_enable_fortran.cmake
new file mode 100644
index 0000000..9d86aa7
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_enable_fortran.cmake
@@ -0,0 +1,89 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_fortran
+# ======================
+#
+# Enable the Fortran language. ::
+#
+#   ecbuild_enable_fortran( [ MODULE_DIRECTORY <directory> ] [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# MODULE_DIRECTORY : optional, defaults to ``${CMAKE_BINARY_DIR}/module``
+#   set the CMAKE_Fortran_MODULE_DIRECTORY
+#
+# REQUIRED : optional
+#   fail if no working Fortran compiler was detected
+#
+##############################################################################
+
+macro( ecbuild_enable_fortran )
+
+  set( options REQUIRED  )
+  set( single_value_args MODULE_DIRECTORY )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_enable_fortran(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT CMAKE_Fortran_COMPILER_LOADED )
+    enable_language( Fortran )
+    ecbuild_compiler_flags( Fortran )
+    if( ENABLE_WARNINGS AND CMAKE_Fortran_COMPILER_ID MATCHES "Intel" )
+      set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -warn all" )
+      ecbuild_debug( "Fortran FLAG [-warn all] added" )
+    endif()
+  endif()
+
+  if( DEFINED _PAR_REQUIRED )
+    if( CMAKE_Fortran_COMPILER_FORCED )
+      set( CMAKE_Fortran_COMPILER_WORKS 1 )
+    endif()
+    if( NOT CMAKE_Fortran_COMPILER OR NOT CMAKE_Fortran_COMPILER_WORKS )
+      ecbuild_critical( "Fortran compiler required by project ${PROJECT_NAME} but does not seem to work" )
+    endif()
+  endif()
+
+  if( CMAKE_Fortran_COMPILER_LOADED )
+
+    include(CheckFortranFunctionExists)
+    if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+      include(FortranCInterface)
+    endif()
+    set( EC_HAVE_FORTRAN 1 )
+
+    # see issue ECBUILD-298
+    if( CMAKE_Fortran_COMPILER_ID MATCHES PGI )
+      unset( CMAKE_Fortran_COMPILE_OPTIONS_PIE )
+      unset( CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS )
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_MODULE_DIRECTORY )
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${_PAR_MODULE_DIRECTORY} )
+  else()
+    set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module
+         CACHE PATH "directory for all fortran modules." )
+  endif()
+
+  file( MAKE_DIRECTORY ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  include_directories( ${CMAKE_Fortran_MODULE_DIRECTORY} )
+
+  install( CODE "EXECUTE_PROCESS (COMMAND \"${CMAKE_COMMAND}\" -E copy_directory \"${CMAKE_Fortran_MODULE_DIRECTORY}/\${BUILD_TYPE}\" \"${INSTALL_INCLUDE_DIR}\")" )
+
+endmacro( ecbuild_enable_fortran )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_features.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_features.cmake
new file mode 100644
index 0000000..9406fd8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_features.cmake
@@ -0,0 +1,57 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+# Internal macros to handle CMake features
+
+include( FeatureSummary )
+
+# Write list of enabled features to CMake variable ${OUT}
+macro( ecbuild_enabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY ENABLED_FEATURES )
+endmacro()
+
+# Write list of disabled features to CMake variable ${OUT}
+macro( ecbuild_disabled_features OUT )
+    get_property( ${OUT}  GLOBAL PROPERTY DISABLED_FEATURES )
+endmacro()
+
+# Enable the feature ${_name} (add to enabled features, remove from disabled)
+function( ecbuild_enable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _disabled_features )
+    list( REMOVE_ITEM _disabled_features ${_name} )
+  endif()
+
+  list( APPEND _enabled_features ${_name} )
+  list( REMOVE_DUPLICATES _enabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
+
+# Disable the feature ${_name} (add to disabled features, remove from enabled)
+function( ecbuild_disable_feature _name )
+
+  get_property( _enabled_features  GLOBAL PROPERTY ENABLED_FEATURES )
+  get_property( _disabled_features GLOBAL PROPERTY DISABLED_FEATURES )
+
+  if( _enabled_features )
+    list( REMOVE_ITEM _enabled_features ${_name} )
+  endif()
+
+  list( APPEND _disabled_features ${_name} )
+  list( REMOVE_DUPLICATES _disabled_features )
+
+  set_property(GLOBAL PROPERTY ENABLED_FEATURES  "${_enabled_features}" )
+  set_property(GLOBAL PROPERTY DISABLED_FEATURES "${_disabled_features}" )
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_fortranlibs.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_fortranlibs.cmake
new file mode 100644
index 0000000..905d020
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_fortranlibs.cmake
@@ -0,0 +1,163 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_fortranlibs
+# ========================
+#
+# Find the Fortran (static) link libraries. ::
+#
+#   ecbuild_find_fortranlibs( [ COMPILER gfortran|pgi|xlf|intel ]
+#                             [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPILER : optional, defaults to gfortran
+#   request a given Fortran compiler (``gfortran``, ``pgi``, ``xlf``, ``intel``)
+#
+# REQUIRED : optional
+#   fail if Fortran libraries were not found
+#
+##############################################################################
+
+macro( ecbuild_find_fortranlibs )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args COMPILER )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT FORTRANLIBS_FOUND ) # don't repeat search
+
+    if( _PAR_COMPILER )
+      set( __known_fcomp 0 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "gfortran" )
+      set( WITH_LIBGFORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "pgi" )
+      set( WITH_PGI_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "xlf" )
+      set( WITH_XL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER MATCHES "intel" )
+      set( WITH_INTEL_FORTRAN 1 )
+      set( __known_fcomp 1 )
+    endif()
+
+    if( _PAR_COMPILER AND NOT __known_fcomp )
+      ecbuild_critical( "unknown fortran compiler ${_PAR_COMPILER}" )
+    endif()
+
+    ### set path from environment variables
+
+    foreach( _fortran_lib PGI XLF LIBGFORTRAN INTEL )
+      if( NOT ${_fortran_lib}_PATH AND NOT "$ENV{${_fortran_lib}_PATH}" STREQUAL "" )
+        set( ${_fortran_lib}_PATH "$ENV{${_fortran_lib}_PATH}" )
+      endif()
+    endforeach()
+
+    set( _flibs_found 0 )
+
+    ### default is to search for gfortran
+
+    if( NOT (WITH_PGI_FORTRAN OR WITH_LIBGFORTRAN OR
+             WITH_XL_FORTRAN OR WITH_INTEL_FORTRAN)
+        AND NOT (DEFINED PGI_PATH OR DEFINED LIBGFORTRAN_PATH OR
+                 DEFINED XLF_PATH OR DEFINED INTEL_PATH) )
+      ecbuild_warn( "Finding fortran libs for unspecified Fortran compiler: default search [ gfortran ]" )
+      set( WITH_LIBGFORTRAN 1 )
+    endif()
+
+    ### actual search ...
+
+    if( WITH_PGI_FORTRAN OR DEFINED PGI_PATH )
+
+      find_package(PGIFortran)
+
+      if( LIBPGIFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${PGIFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "PGI" )
+      endif()
+
+    endif()
+
+    if( WITH_LIBGFORTRAN OR DEFINED LIBGFORTRAN_PATH )
+
+      find_package(LibGFortran)
+
+      if( LIBGFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${GFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "gfortran" )
+      endif()
+
+    endif()
+
+    if( WITH_XL_FORTRAN OR DEFINED XLF_PATH )
+
+      find_package(XLFortranLibs)
+
+      if( LIBXLFORTRAN_FOUND )
+        set( FORTRAN_LIBRARIES ${XLFORTRAN_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "XLF" )
+      endif()
+
+    endif()
+
+    if( WITH_INTEL_FORTRAN OR DEFINED INTEL_PATH )
+
+      find_package(LibIFort)
+
+      if( LIBIFORT_FOUND )
+        set( FORTRAN_LIBRARIES ${IFORT_LIBRARIES} )
+        set( _flibs_found 1 )
+        set( _flibs_txt "Intel" )
+      endif()
+
+    endif()
+
+    ### set found
+
+    if( _flibs_found )
+      set( FORTRANLIBS_FOUND 1 CACHE INTERNAL "Fortran libraries found" )
+      set( FORTRANLIBS_NAME ${_flibs_txt}  CACHE INTERNAL "Fortran library name" )
+      set( FORTRAN_LIBRARIES ${FORTRAN_LIBRARIES} CACHE INTERNAL "Fortran libraries" )
+      ecbuild_info( "Found Fortran libraries: ${_flibs_txt}" )
+    else()
+      set( FORTRANLIBS_FOUND 0 )
+      if( _PAR_REQUIRED )
+        ecbuild_critical( "Failed to find Fortran libraries" )
+      else()
+        ecbuild_warn( "Failed to find Fortran libraries" )
+      endif()
+    endif()
+
+  endif( NOT FORTRANLIBS_FOUND )
+
+endmacro( ecbuild_find_fortranlibs )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_lexyacc.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_lexyacc.cmake
new file mode 100644
index 0000000..3dc8b61
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_lexyacc.cmake
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_lexyacc
+# ====================
+#
+# Find flex and bison (preferred) or lex and yacc.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables can set to skip search for bison or yacc:
+#
+# :SKIP_BISON: do not search for flex and bison
+# :SKIP_YACC:  do not search for lex and yacc
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if flex and bison were found:
+#
+# :FLEX_FOUND:       flex was found
+# :BISON_FOUND:      bison was found
+# :FLEX_EXECUTABLE:  path to the flex executable
+# :BISON_EXECUTABLE: path to the bison executable
+#
+# The following CMake variables are set if lex and yacc were found:
+#
+# :LEXYACC_FOUND:   Found suitable combination of bison, lex, yacc, flex
+# :LEX_FOUND:       lex was found
+# :YACC_FOUND:      yacc was found
+# :LEX_EXECUTABLE:  path to the lex executable
+# :YACC_EXECUTABLE: path to the yacc executable
+#
+##############################################################################
+
+macro( ecbuild_find_lexyacc )
+
+  # find preferably bison or else yacc
+
+  if( NOT SKIP_BISON )
+
+    find_package( BISON )
+    if(BISON_FOUND AND BISON_VERSION VERSION_LESS 2.3 )
+        ecbuild_critical( "Bison found with version ${BISON_VERSION} is less than 2.3.\nPlease define BISON_EXECUTABLE to an appropriate version or define SKIP_BISON to try finding Yacc instead" )
+    endif()
+    find_package( FLEX )
+
+  endif()
+
+  if( NOT BISON_FOUND AND NOT SKIP_YACC )
+
+    find_package( YACC )
+    find_package( LEX  )
+
+  endif()
+
+  set( LEXYACC_FOUND 1 )
+
+  if( NOT YACC_FOUND AND NOT BISON_FOUND ) # neither bison nor yacc were found
+    ecbuild_debug( "Neither bison or yacc were found - at least one is required (together with its lexical analyser" )
+    set( LEXYACC_FOUND 0 )
+  endif()
+
+  if( NOT YACC_FOUND ) # check for both bison & flex together
+    if( BISON_FOUND AND NOT FLEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - flex not found" )
+    endif()
+    if( FLEX_FOUND AND NOT BISON_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both bison and flex are required - bison not found" )
+    endif()
+  endif()
+
+  if( NOT BISON_FOUND ) # check for both yacc & lex together
+    if( YACC_FOUND AND NOT LEX_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - lex not found" )
+    endif()
+    if( LEX_FOUND AND NOT YACC_FOUND )
+      set( LEXYACC_FOUND 0 )
+      ecbuild_debug( "Both yacc and lex are required - yacc not found" )
+    endif()
+  endif()
+
+endmacro( ecbuild_find_lexyacc )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_mpi.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_mpi.cmake
new file mode 100644
index 0000000..85ae5e1
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_mpi.cmake
@@ -0,0 +1,328 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_mpi
+# ================
+#
+# Find MPI and check if MPI compilers successfully compile C/C++/Fortran. ::
+#
+#   ecbuild_find_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+# Input variables
+# ---------------
+#
+# ECBUILD_FIND_MPI : optional, defaults to TRUE
+#   test C/C++/Fortran MPI compiler wrappers (assume working if FALSE)
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if MPI was found: ::
+#
+#   MPI_FOUND
+#   MPI_LIBRARY
+#   MPI_EXTRA_LIBRARY 
+#
+# The following CMake variables are set if C bindings were found: ::
+#
+#   MPI_C_FOUND
+#   MPI_C_COMPILER
+#   MPI_C_COMPILE_FLAGS
+#   MPI_C_INCLUDE_PATH
+#   MPI_C_LIBRARIES
+#   MPI_C_LINK_FLAGS
+#
+# The following CMake variables are set if C++ bindings were found: ::
+#
+#   MPI_CXX_FOUND
+#   MPI_CXX_COMPILER
+#   MPI_CXX_COMPILE_FLAGS
+#   MPI_CXX_INCLUDE_PATH
+#   MPI_CXX_LIBRARIES
+#   MPI_CXX_LINK_FLAGS
+#
+# The following CMake variables are set if Fortran bindings were found: ::
+#
+#   MPI_Fortran_FOUND
+#   MPI_Fortran_COMPILER
+#   MPI_Fortran_COMPILE_FLAGS
+#   MPI_Fortran_INCLUDE_PATH
+#   MPI_Fortran_LIBRARIES
+#   MPI_Fortran_LINK_FLAGS
+#
+##############################################################################
+
+macro( ecbuild_find_mpi )
+
+    # parse parameters
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    # if user defined compilers are MPI compliant, then we use them ...
+    if( NOT DEFINED ECBUILD_FIND_MPI )
+      set( ECBUILD_FIND_MPI TRUE )
+    endif()
+    if( ECBUILD_FIND_MPI )
+
+        # C compiler
+
+        if( CMAKE_C_COMPILER_LOADED AND NOT MPI_C_COMPILER )
+
+            include(CheckCSourceCompiles)
+
+            check_c_source_compiles("
+                #include <mpi.h>
+                int main(int argc, char* argv[])
+                {
+                int rank;
+                MPI_Init(&argc, &argv); 
+                MPI_Comm_rank(MPI_COMM_WORLD, &rank); 
+                MPI_Finalize();
+                return 0;
+                }
+                "
+                C_COMPILER_SUPPORTS_MPI )
+
+            if( C_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C compiler supports MPI -- ${CMAKE_C_COMPILER}" )
+                set( MPI_C_COMPILER ${CMAKE_C_COMPILER} )
+            endif()
+
+        endif()
+
+        # CXX compiler
+
+        if( CMAKE_CXX_COMPILER_LOADED AND NOT MPI_CXX_COMPILER )
+
+            include(CheckCXXSourceCompiles)
+
+            check_cxx_source_compiles("
+                #include <mpi.h>
+                 #include <iostream>
+                 int main(int argc, char* argv[])
+                 {
+                   MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Finalize();
+                   return 0;
+                 }
+                 "
+                 CXX_COMPILER_SUPPORTS_MPI )
+
+            if( CXX_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "C++ compiler supports MPI -- ${CMAKE_CXX_COMPILER}" )
+                set( MPI_CXX_COMPILER ${CMAKE_CXX_COMPILER} )
+            endif()
+
+        endif()
+
+        # Fortran compiler
+
+        if( CMAKE_Fortran_COMPILER_LOADED AND NOT MPI_Fortran_COMPILER )
+
+            include(CheckFortranSourceCompiles)
+
+            check_fortran_source_compiles("
+                program main
+                use MPI
+                integer ierr
+                call MPI_INIT( ierr )
+                call MPI_FINALIZE( ierr )
+                end
+                "
+            Fortran_COMPILER_SUPPORTS_MPI )
+
+            if( Fortran_COMPILER_SUPPORTS_MPI )
+                ecbuild_info( "Fortran compiler supports MPI (F90) -- ${CMAKE_Fortran_COMPILER}" )
+                set( MPI_Fortran_COMPILER ${CMAKE_Fortran_COMPILER} )
+                set( MPI_Fortran_FOUND TRUE )
+            endif()
+
+        endif()
+
+        if( NOT _PAR_REQUIRED )
+            find_package( MPI QUIET )
+        else()
+            find_package( MPI QUIET REQUIRED )
+        endif()
+
+        if( C_COMPILER_SUPPORTS_MPI )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CXX_COMPILER_SUPPORTS_MPI )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( Fortran_COMPILER_SUPPORTS_MPI )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    else()
+
+        # find_package with Cray compiler did not send MPI_<lang>_FOUND
+        if( CMAKE_C_COMPILER_LOADED )
+            set( C_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_C_FOUND TRUE )
+        endif()
+        if( CMAKE_CXX_COMPILER_LOADED )
+            set( CXX_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_CXX_FOUND TRUE )
+        endif()
+        if( CMAKE_Fortran_COMPILER_LOADED )
+            set( Fortran_COMPILER_SUPPORTS_MPI TRUE )
+            set( MPI_Fortran_FOUND TRUE )
+        endif()
+
+    endif( ECBUILD_FIND_MPI )
+
+    # hide these variables from UI
+
+    mark_as_advanced( MPI_LIBRARY MPI_EXTRA_LIBRARY )
+
+    if( NOT _PAR_COMPONENTS )
+      set( _PAR_COMPONENTS C )
+    endif()
+
+    set( MPI_FOUND TRUE )
+    foreach( _lang ${_PAR_COMPONENTS} )
+      if( NOT MPI_${_lang}_FOUND )
+        set( MPI_FOUND FALSE )
+      endif()
+    endforeach()
+
+endmacro( ecbuild_find_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_mpi
+# ==================
+#
+# Find MPI, add include directories and set compiler flags. ::
+#
+#   ecbuild_enable_mpi( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                       [ REQUIRED ] )
+#
+# For each MPI language binding found, set the corresponding compiler flags
+# and add the include directories.
+#
+# See ``ecbuild_find_mpi`` for input and output variables.
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if MPI was not found
+#
+##############################################################################
+
+macro( ecbuild_enable_mpi )
+
+    set( options REQUIRED )
+    set( single_value_args )
+    set( multi_value_args COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_COMPONENTS )
+      set (_PAR_COMPONENTS C )
+    endif()
+
+    if( NOT _PAR_REQUIRED )
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} )
+    else()
+       ecbuild_find_mpi( COMPONENTS ${_PAR_COMPONENTS} REQUIRED )
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_enable_mpi )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_include_mpi
+# ===================
+#
+# Add MPI include directories and set compiler flags, assuming MPI was found.
+#
+# For each MPI language binding found, set corresponding compiler flags and
+# add include directories. ``ecbuild_find_mpi`` must have been called before.
+#
+##############################################################################
+
+macro( ecbuild_include_mpi )
+
+    set( options )
+    set( single_value_args )
+    set( multi_value_args )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_find_mpi(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( MPI_C_FOUND AND NOT C_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_c_source_return )
+        ecbuild_add_c_flags("${MPI_C_COMPILE_FLAGS}")
+        include_directories(${MPI_C_INCLUDE_PATH})
+    endif()
+
+    if( MPI_CXX_FOUND AND NOT CXX_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_cxx_source_return )
+        ecbuild_add_cxx_flags("${MPI_CXX_COMPILE_FLAGS}")
+        include_directories(${MPI_CXX_INCLUDE_PATH})
+    endif()
+
+    if( MPI_Fortran_FOUND AND NOT Fortran_COMPILER_SUPPORTS_MPI )
+        include( ecbuild_check_fortran_source_return )
+        ecbuild_add_fortran_flags("${MPI_Fortran_COMPILE_FLAGS}")
+        include_directories(${MPI_Fortran_INCLUDE_PATH})
+    endif()
+
+endmacro( ecbuild_include_mpi )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_omp.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_omp.cmake
new file mode 100644
index 0000000..3dc6fac
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_omp.cmake
@@ -0,0 +1,259 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for looking for openmp flags
+
+macro( lookup_omp_flags )
+  set(_OMP_FLAG_GNU        "-fopenmp")
+  set(_OMPSTUBS_FLAG_GNU   "-fno-openmp")
+
+  set(_OMP_FLAG_Cray       "-homp")
+  set(_OMPSTUBS_FLAG_Cray  "-hnoomp")
+
+  set(_OMP_FLAG_XL         "-qsmp=omp")
+  set(_OMPSTUBS_FLAG_XL    "-qsmp=noomp")
+
+  set(_OMP_FLAG_Intel      "-qopenmp")
+  set(_OMPSTUBS_FLAG_Intel "-qopenmp-stubs")
+
+  # sample C openmp source code to test
+  set(_OMP_C_TEST_SOURCE
+  "
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    #pragma omp parallel
+    {
+      (void)omp_get_thread_num();
+    }
+    return 0;
+  #else
+    breaks_on_purpose
+  #endif
+  }
+  ")
+  set( _OMP_CXX_TEST_SOURCE ${_OMP_C_TEST_SOURCE} )
+
+
+  # sample C openmp source code to test
+  set(_OMPSTUBS_C_TEST_SOURCE
+  "
+  // Include must be found
+  #include <omp.h>
+  int main() {
+  #ifdef _OPENMP
+    breaks_on_purpose
+  #else
+    #pragma omp parallel
+    {
+      // This pragma should have passed compilation
+      (void)0;
+    }
+    return 0;
+  #endif
+  }
+  ")
+  set( _OMPSTUBS_CXX_TEST_SOURCE ${_OMPSTUBS_C_TEST_SOURCE} )
+
+
+  # sample Fortran openmp source code to test
+  set(_OMP_Fortran_TEST_SOURCE
+  "
+  program main
+    use omp_lib
+  end program
+  ")
+  set( _OMPSTUBS_Fortran_TEST_SOURCE ${_OMP_Fortran_TEST_SOURCE} )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_omp
+# ================
+#
+# Find OpenMP. ::
+#
+#   ecbuild_find_omp( [ COMPONENTS <component1> [ <component2> ... ] ]
+#                     [ REQUIRED ]
+#                     [ STUBS ] )
+#
+# Options
+# -------
+#
+# COMPONENTS : optional, defaults to C
+#   list of required languages bindings
+#
+# REQUIRED : optional
+#   fail if OpenMP was not found
+#
+# STUBS : optional
+#   search for OpenMP stubs
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if OpenMP was found:
+#
+# :OMP_FOUND: OpenMP was found
+#
+# For each language listed in COMPONENTS, the following variables are set:
+#
+# :OMP_<LANG>_FOUND: OpenMP bindings for LANG were found
+# :OMP_<LANG>_FLAGS: OpenMP compiler flags for LANG
+#
+# If the STUBS option was given, all variables are also set with the OMPSTUBS
+# instead of the OMP prefix.
+#
+##############################################################################
+
+macro( ecbuild_find_omp )
+
+  set( options REQUIRED STUBS )
+  set( single_value_args )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT _PAR_COMPONENTS )
+    ecbuild_critical( "No COMPONENTS were specified, looking for OMP.\n Please find with COMPONENTS C CXX Fortran " )
+  endif()
+
+  set( _STUBS "" )
+  if( _PAR_STUBS )
+    set( _STUBS "STUBS" )
+  endif()
+
+  lookup_omp_flags()
+
+  set( OMP${_STUBS}_FOUND TRUE )
+
+  foreach( _LANG ${_PAR_COMPONENTS} )
+
+    if( NOT OMP${_STUBS}_${_LANG}_FLAGS )
+
+      if( DEFINED _OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID} )
+        set( _OMP${_STUBS}_${_LANG}_FLAG "${_OMP${_STUBS}_FLAG_${CMAKE_${_LANG}_COMPILER_ID}}" )
+      endif()
+      if( CMAKE_${_LANG}_COMPILER_LOADED AND _OMP${_STUBS}_${_LANG}_FLAG )
+        set(SAVE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
+        set(CMAKE_REQUIRED_FLAGS "${_OMP${_STUBS}_${_LANG}_FLAG}")
+        include(Check${_LANG}SourceCompiles)
+        set( _SOURCE ${_OMP${_STUBS}_${_LANG}_TEST_SOURCE} )
+        set( _FLAG ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS})
+        if( _LANG STREQUAL "C" )
+          check_c_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "CXX" )
+          check_cxx_source_compiles("${_SOURCE}" ${_FLAG} )
+        endif()
+        if( _LANG STREQUAL "Fortran" )
+          check_fortran_source_compiles("${_SOURCE}" ${_FLAG} SRC_EXT f90)
+        endif()
+        set(CMAKE_REQUIRED_FLAGS "${SAVE_CMAKE_REQUIRED_FLAGS}")
+      endif()
+
+      if( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} )
+        set( OMP${_STUBS}_${_LANG}_FLAGS ${_OMP${_STUBS}_${_LANG}_FLAG} )
+      endif()
+
+    else()
+      set( ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS} TRUE )
+    endif()
+
+
+    set( OMP${_STUBS}_${_LANG}_FIND_QUIETLY TRUE )
+    find_package_handle_standard_args( OMP${_STUBS}_${_LANG} REQUIRED_VARS ${_LANG}_COMPILER_SUPPORTS_OMP${_STUBS}  )
+
+    if( OMP${_STUBS}_FORTRAN_FOUND )
+      set( OMP${_STUBS}_Fortran_FOUND TRUE )
+    endif()
+
+    if( NOT OMP${_STUBS}_${_LANG}_FOUND )
+      set( OMP${_STUBS}_FOUND FALSE )
+    endif()
+
+    if( _PAR_STUBS )
+      set( OMP_${_LANG}_FOUND ${OMPSTUBS_${_LANG}_FOUND} )
+      set( OMP_${_LANG}_FLAGS ${OMPSTUBS_${_LANG}_FLAGS} )
+    endif()
+
+  endforeach()
+
+  if( _PAR_STUBS )
+    set( OMP_FOUND ${OMPSTUBS_FOUND} )
+  endif()
+
+endmacro( ecbuild_find_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_omp
+# ==================
+#
+# Find OpenMP for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP support was detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_omp )
+
+  ecbuild_debug("ecbuild_enable_omp: Trying to enable OpenMP")
+  ecbuild_find_omp( COMPONENTS C CXX Fortran )
+
+  ecbuild_debug_var("OMP_C_FOUND")
+  if( OMP_C_FOUND )
+    ecbuild_debug("Adding ${OMP_C_FLAGS} to CMAKE_C_FLAGS")
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMP_C_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_CXX_FOUND")
+  if( OMP_CXX_FOUND )
+    ecbuild_debug("Adding ${OMP_CXX_FLAGS} to CMAKE_CXX_FLAGS")
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMP_CXX_FLAGS}" )
+  endif()
+
+  ecbuild_debug_var("OMP_Fortran_FOUND")
+  if( OMP_Fortran_FOUND )
+    ecbuild_debug("Adding ${OMP_Fortran_FLAGS} to CMAKE_Fortran_FLAGS")
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMP_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_omp )
+
+##############################################################################
+#.rst:
+#
+# ecbuild_enable_ompstubs
+# =======================
+#
+# Find OpenMP stubs for C, C++ and Fortran and set the compiler flags for each
+# language for which OpenMP stubs were detected.
+#
+##############################################################################
+
+macro( ecbuild_enable_ompstubs )
+
+  ecbuild_find_omp( COMPONENTS C CXX Fortran STUBS )
+
+  if( OMPSTUBS_C_FOUND )
+    set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OMPSTUBS_C_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_CXX_FOUND )
+    set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OMPSTUBS_CXX_FLAGS}" )
+  endif()
+
+  if( OMPSTUBS_Fortran_FOUND )
+    set( CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OMPSTUBS_Fortran_FLAGS}" )
+  endif()
+
+endmacro( ecbuild_enable_ompstubs )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_package.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_package.cmake
new file mode 100644
index 0000000..71be044
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_package.cmake
@@ -0,0 +1,368 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_package
+# ====================
+#
+# Find a package and import its configuration. ::
+#
+#   ecbuild_find_package( NAME <name>
+#                         [ VERSION <version> [ EXACT ] ]
+#                         [ COMPONENTS <component1> [ <component2> ... ] ]
+#                         [ URL <url> ]
+#                         [ DESCRIPTION <description> ]
+#                         [ TYPE <type> ]
+#                         [ PURPOSE <purpose> ]
+#                         [ FAILURE_MSG <message> ]
+#                         [ REQUIRED ]
+#                         [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# COMPONENTS : optional
+#   list of package components to find (behaviour depends on the package)
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   literal string or name of CMake variable describing the package
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   literal string or name of CMake variable describing which functionality
+#   this package enables in the project
+#
+# FAILURE_MSG : optional
+#   literal string or name of CMake variable containing a message to be
+#   appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>`` is
+# the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :DEVELOPER_MODE: if enabled, discover projects parallel in the build tree
+# :<name>_PATH:    install prefix path of the package
+# :<NAME>_PATH:    install prefix path of the package
+# :<name>_DIR:     directory containing the ``<name>-config.cmake`` file
+#                  (usually ``<install-prefix>/share/<name>/cmake``)
+#
+# The environment variables ``<name>_PATH``, ``<NAME>_PATH``, ``<name>_DIR``
+# are taken into account only if the corresponding CMake variables are unset.
+#
+# Usage
+# -----
+#
+# The search proceeds as follows:
+#
+# 1.  If any paths have been specified by the user via CMake or environment
+#     variables as given above or a parallel build tree has been discovered in
+#     DEVELOPER_MODE:
+#
+#     * search for ``<name>-config.cmake`` in those paths only
+#     * search using ``Find<name>.cmake`` (which should respect those paths)
+#     * fail if the package was not found in any of those paths
+#
+# 2.  Search for ``<name>-config.cmake`` in the ``CMAKE_PREFIX_PATH`` and if
+#     DEVELOPER_MODE is enabled also in the user package registry.
+#
+# 3.  Search system paths for ``<name>-config.cmake``.
+#
+# 4.  Search system paths using ``Find<name>.cmake``.
+#
+# 5.  If the package was found, and a minimum version was requested, check if
+#     the version is acceptable and if not, unset ``<NAME>_FOUND``.
+#
+# 6.  Fail if the package was not found and is REQUIRED.
+#
+##############################################################################
+
+macro( ecbuild_find_package )
+
+  set( options REQUIRED QUIET EXACT )
+  set( single_value_args NAME VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args COMPONENTS )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_package(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_NAME  )
+    ecbuild_critical("The call to ecbuild_find_package() doesn't specify the NAME.")
+  endif()
+
+  if( _PAR_EXACT AND NOT _PAR_VERSION )
+    ecbuild_critical("Call to ecbuild_find_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _PAR_REQUIRED )
+    set( _PAR_TYPE REQUIRED )
+  endif()
+
+  # ecbuild_debug_var( _PAR_NAME )
+
+  string( TOUPPER ${_PAR_NAME} pkgUPPER )
+  string( TOLOWER ${_PAR_NAME} pkgLOWER )
+
+  set( _${pkgUPPER}_version "" )
+  if( _PAR_VERSION )
+    set( _${pkgUPPER}_version ${_PAR_VERSION} )
+    if( _PAR_EXACT )
+      set( _${pkgUPPER}_version ${_PAR_VERSION} EXACT )
+    endif()
+  endif()
+
+  # check developer mode (search in cmake cache )
+
+  if( NOT ${DEVELOPER_MODE} )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): Not in DEVELOPER_MODE - do not search package registry or recent GUI build paths")
+    set( NO_DEV_BUILD_DIRS NO_CMAKE_PACKAGE_REGISTRY NO_CMAKE_BUILDS_PATH )
+  endif()
+
+  # in DEVELOPER_MODE we give priority to projects parallel in the build tree
+  # so lets prepend a parallel build tree to the search path if we find it
+
+  if( DEVELOPER_MODE )
+    get_filename_component( _proj_bdir "${CMAKE_BINARY_DIR}/../${pkgLOWER}" ABSOLUTE )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - searching for ${pkgLOWER}-config.cmake in ${_proj_bdir}")
+    if( EXISTS ${_proj_bdir}/${pkgLOWER}-config.cmake )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - found parallel build tree in ${_proj_bdir}")
+      if( ${pkgUPPER}_PATH )
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - ${pkgUPPER}_PATH already set to ${${pkgUPPER}_PATH}, not modifying")
+      else()
+        ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): in DEVELOPER_MODE - setting ${pkgUPPER}_PATH to ${_proj_bdir}")
+        set( ${pkgUPPER}_PATH "${_proj_bdir}" )
+      endif()
+    endif()
+  endif()
+
+  # Read environment variables but ONLY if the corresponding CMake variables are unset
+
+  if( NOT DEFINED ${pkgUPPER}_PATH AND NOT "$ENV{${pkgUPPER}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${pkgUPPER}_PATH=${${pkgUPPER}_PATH} from environment")
+    set( ${pkgUPPER}_PATH "$ENV{${pkgUPPER}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_PATH AND NOT "$ENV{${_PAR_NAME}_PATH}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH} from environment")
+    set( ${_PAR_NAME}_PATH "$ENV{${_PAR_NAME}_PATH}" )
+  endif()
+
+  if( NOT DEFINED ${_PAR_NAME}_DIR AND NOT "$ENV{${_PAR_NAME}_DIR}" STREQUAL "" )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): setting ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR} from environment")
+    set( ${_PAR_NAME}_DIR "$ENV{${_PAR_NAME}_DIR}" )
+  endif()
+
+  # Find packages quietly unless in DEVELOPER_MODE or LOG_LEVEL is DEBUG
+
+  if( NOT DEVELOPER_MODE AND ( ECBUILD_LOG_LEVEL GREATER ${ECBUILD_DEBUG} ) )
+    set( _find_quiet QUIET )
+  endif()
+
+  # search user defined paths first
+
+  if( ${_PAR_NAME}_PATH OR ${pkgUPPER}_PATH OR ${_PAR_NAME}_DIR )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+
+    # 1) search using CONFIG mode -- try to locate a configuration file provided by the package (package-config.cmake)
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 1) search using CONFIG mode -- try to locate ${_PAR_NAME}-config.cmake")
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}):    using hints ${pkgUPPER}_PATH=${${pkgUPPER}_PATH}, ${_PAR_NAME}_PATH=${${_PAR_NAME}_PATH}, ${_PAR_NAME}_DIR=${${_PAR_NAME}_DIR}")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} NO_MODULE ${_find_quiet}
+        COMPONENTS ${_PAR_COMPONENTS}
+        HINTS ${${pkgUPPER}_PATH} ${${_PAR_NAME}_PATH} ${${_PAR_NAME}_DIR}
+        NO_DEFAULT_PATH )
+    endif()
+
+    # 2) search using a file Find<package>.cmake if it exists ( macro should itself take *_PATH into account )
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 2) search using a file Find${_PAR_NAME}.cmake if it exists")
+      find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} MODULE ${_find_quiet}
+                    COMPONENTS ${_PAR_COMPONENTS} )
+    endif()
+
+    # is <package>_PATH was given and we don't find anything then we FAIL
+
+    if( NOT ${_PAR_NAME}_FOUND )
+      if( ${_PAR_NAME}_PATH )
+        ecbuild_critical( "${_PAR_NAME}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${_PAR_NAME}_PATH}'" )
+      endif()
+      if( ${pkgUPPER}_PATH )
+        ecbuild_critical( "${pkgUPPER}_PATH was provided by user but package ${_PAR_NAME} wasn't found at '${${pkgUPPER}_PATH}'" )
+      endif()
+    endif()
+
+  endif()
+
+  # 3) search developer cache and recently configured packages in the CMake GUI if in DEVELOPER_MODE
+  #    otherwise only search CMAKE_PREFIX_PATH and <package>_PATH
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    if (NO_DEV_BUILD_DIRS)
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH")
+    else()
+      ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 3) search CMAKE_PREFIX_PATH and \$${pkgUPPER}_PATH and package registry")
+    endif()
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      HINTS ENV ${pkgUPPER}_PATH
+      ${NO_DEV_BUILD_DIRS}
+      NO_CMAKE_ENVIRONMENT_PATH
+      NO_SYSTEM_ENVIRONMENT_PATH
+      NO_CMAKE_SYSTEM_PATH
+      NO_CMAKE_SYSTEM_PACKAGE_REGISTRY )
+
+  endif()
+
+  # 4) search system paths, for <package>-config.cmake
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 5) search system paths, for ${_PAR_NAME}-config.cmake")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} NO_MODULE
+      COMPONENTS ${_PAR_COMPONENTS}
+      ${NO_DEV_BUILD_DIRS} )
+
+  endif()
+
+  # 5) search system paths, using Find<package>.cmake if it exists
+
+  if( NOT ${_PAR_NAME}_FOUND )
+    ecbuild_debug("ecbuild_find_package(${_PAR_NAME}): 6) search system paths, using Find${_PAR_NAME}.cmake if it exists")
+
+    find_package( ${_PAR_NAME} ${_${pkgUPPER}_version} ${_find_quiet} MODULE
+                  COMPONENTS ${_PAR_COMPONENTS} )
+
+  endif()
+
+  # check version found is acceptable
+
+  if( ${_PAR_NAME}_FOUND )
+    set( _version_acceptable 1 )
+    if( _PAR_VERSION )
+      if( ${_PAR_NAME}_VERSION )
+        if( _PAR_EXACT )
+          if( NOT ${_PAR_NAME}_VERSION VERSION_EQUAL _PAR_VERSION )
+            ecbuild_warn( "${PROJECT_NAME} requires (exactly) ${_PAR_NAME} = ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            set( _version_acceptable 0 )
+          endif()
+        else()
+          if( _PAR_VERSION VERSION_LESS ${_PAR_NAME}_VERSION OR _PAR_VERSION VERSION_EQUAL ${_PAR_NAME}_VERSION )
+            set( _version_acceptable 1 )
+          else()
+            if( NOT _PAR_QUIET )
+              ecbuild_warn( "${PROJECT_NAME} requires ${_PAR_NAME} >= ${_PAR_VERSION} -- found ${${_PAR_NAME}_VERSION}" )
+            endif()
+            set( _version_acceptable 0 )
+          endif()
+        endif()
+      else()
+        if( NOT _PAR_QUIET )
+          ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but no version information, so cannot check if satisfies ${_PAR_VERSION}" )
+        endif()
+        set( _version_acceptable 0 )
+      endif()
+    endif()
+  endif()
+
+  if( ${_PAR_NAME}_FOUND )
+
+    if( _version_acceptable )
+      set( ${pkgUPPER}_FOUND ${${_PAR_NAME}_FOUND} )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${PROJECT_NAME} found ${_PAR_NAME} but with unsuitable version" )
+      endif()
+      set( ${pkgUPPER}_FOUND 0 )
+      set( ${_PAR_NAME}_FOUND 0 )
+    endif()
+
+  endif()
+
+  ### final messages
+
+  if( ${_PAR_NAME}_FOUND OR ${pkgUPPER}_FOUND )
+
+    if( NOT _PAR_QUIET )
+      ecbuild_info( "[${_PAR_NAME}] (${${_PAR_NAME}_VERSION})" )
+      foreach( var in ITEMS INCLUDE_DIR INCLUDE_DIRS DEFINITIONS LIBRARY LIBRARIES )
+        if( ${pkgUPPER}_${var} )
+          ecbuild_info( "   ${pkgUPPER}_${var} : [${${pkgUPPER}_${var}}]" )
+        elseif( ${_PAR_NAME}_${var} )
+          ecbuild_info( "   ${_PAR_NAME}_${var} : [${${_PAR_NAME}_${var}}]" )
+        endif()
+      endforeach()
+    endif()
+
+    if( DEFINED ${_PAR_DESCRIPTION} )
+      set( _PAR_DESCRIPTION ${${_PAR_DESCRIPTION}} )
+    endif()
+    if( DEFINED ${_PAR_PURPOSE} )
+      set( _PAR_PURPOSE ${${_PAR_PURPOSE}} )
+    endif()
+    set_package_properties( ${_PAR_NAME} PROPERTIES
+                            URL "${_PAR_URL}"
+                            DESCRIPTION "${_PAR_DESCRIPTION}"
+                            TYPE "${_PAR_TYPE}"
+                            PURPOSE "${_PAR_PURPOSE}" )
+
+  else()
+
+    if( DEFINED ${_PAR_FAILURE_MSG} )
+      set( _PAR_FAILURE_MSG ${${_PAR_FAILURE_MSG}} )
+    endif()
+    set( _failed_message
+      "  ${PROJECT_NAME} FAILED to find package ${_PAR_NAME}\n"
+      "    Provide location with \"-D${pkgUPPER}_PATH=/...\" or \"-D${_PAR_NAME}_DIR=/...\" \n"
+      "    You may also export environment variables ${pkgUPPER}_PATH or ${_PAR_NAME}_DIR\n"
+      "  Values (note CAPITALISATION):\n"
+      "    ${pkgUPPER}_PATH should contain the path to the install prefix (as in <install>/bin <install>/lib <install>/include)\n"
+      "    ${_PAR_NAME}_DIR should be a directory containing a <package>-config.cmake file (usually <install>/share/<package>/cmake)\n"
+      )
+
+    if( _PAR_REQUIRED )
+      ecbuild_critical( "${_failed_message}!! ${PROJECT_NAME} requires package ${_PAR_NAME} !!\n${_PAR_FAILURE_MSG}" )
+    else()
+      if( NOT _PAR_QUIET )
+        ecbuild_warn( "${_failed_message}\n${_PAR_FAILURE_MSG}" )
+      endif()
+    endif()
+
+  endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_perl.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_perl.cmake
new file mode 100644
index 0000000..b6c1825
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_perl.cmake
@@ -0,0 +1,73 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_perl
+# =================
+#
+# Find perl executable and its version. ::
+#
+#   ecbuild_find_perl( [ REQUIRED ] )
+#
+# Options
+# -------
+#
+# REQUIRED : optional
+#   fail if perl was not found
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if perl was found:
+#
+# :PERL_FOUND:          perl was found
+# :PERL_EXECUTABLE:     path to the perl executable
+# :PERL_VERSION:        perl version
+# :PERL_VERSION_STRING: perl version (same as ``PERL_VERSION``)
+#
+##############################################################################
+
+macro( ecbuild_find_perl )
+
+  # parse parameters
+
+  set( options REQUIRED )
+  set( single_value_args )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_find_perl(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  find_package( Perl )
+
+  if( NOT PERL_EXECUTABLE AND _p_REQUIRED )
+    ecbuild_critical( "Failed to find Perl (REQUIRED)" )
+  endif()
+
+  if( PERL_EXECUTABLE )
+
+    execute_process( COMMAND ${PERL_EXECUTABLE} -V:version OUTPUT_VARIABLE  perl_version_output_variable  RESULT_VARIABLE  perl_version_return )
+    if( NOT perl_version_return )
+      string(REGEX REPLACE "version='([^']+)'.*" "\\1" PERL_VERSION ${perl_version_output_variable})
+    endif()
+
+    # from cmake 2.8.8 onwards
+    if( NOT PERL_VERSION_STRING )
+      set( PERL_VERSION_STRING ${PERL_VERSION} )
+    endif()
+
+    ecbuild_debug("ecbuild_find_perl: found perl version ${PERL_VERSION_STRING} as ${PERL_EXECUTABLE}")
+
+  endif()
+
+endmacro( ecbuild_find_perl )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_find_python.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_find_python.cmake
new file mode 100644
index 0000000..0e9adaa
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_find_python.cmake
@@ -0,0 +1,263 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_find_python
+# ===================
+#
+# Find Python interpreter, its version and the Python libraries. ::
+#
+#   ecbuild_find_python( [ VERSION <version> ] [ REQUIRED ] [ NO_LIBS ] )
+#
+# Options
+# -------
+#
+# VERSION : optional
+#   minimum required version
+#
+# REQUIRED : optional
+#   fail if Python was not found
+#
+# NO_LIBS : optional
+#   only search for the Python interpreter, not the libraries
+#
+# Unless ``NO_LIBS`` is set, the ``python-config`` utility, if found, is used
+# to determine the Python include directories, libraries and link line. Set the
+# CMake variable ``PYTHON_NO_CONFIG`` to use CMake's FindPythonLibs instead.
+#
+# Output variables
+# ----------------
+#
+# The following CMake variables are set if python was found:
+#
+# :PYTHONINTERP_FOUND:    Python interpreter was found
+# :PYTHONLIBS_FOUND:      Python libraries were found
+# :PYTHON_FOUND:          Python was found (both interpreter and libraries)
+# :PYTHON_EXECUTABLE:     Python executable
+# :PYTHON_VERSION_MAJOR:  major version number
+# :PYTHON_VERSION_MINOR:  minor version number
+# :PYTHON_VERSION_PATCH:  patch version number
+# :PYTHON_VERSION_STRING: Python version
+# :PYTHON_INCLUDE_DIRS:   Python include directories
+# :PYTHON_LIBRARIES:      Python libraries
+# :PYTHON_SITE_PACKAGES:  Python site packages directory
+#
+##############################################################################
+
+set( __test_python ${CMAKE_CURRENT_LIST_DIR}/pymain.c )
+
+function( ecbuild_find_python )
+
+    # parse parameters
+
+    set( options REQUIRED NO_LIBS )
+    set( single_value_args VERSION )
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_find_python(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+    if( _p_REQUIRED )
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter (required) ..." )
+      set( _p_REQUIRED REQUIRED )
+    else()
+      ecbuild_debug( "ecbuild_find_python: Searching for Python interpreter ..." )
+      unset( _p_REQUIRED )
+    endif()
+
+    # find python executable
+
+    # Search first without specifying the version, since doing so gives preference to the specified
+    # version even though a never version of the interpreter may be available
+    find_package( PythonInterp ${_p_REQUIRED} )
+
+    # If no suitable version was found, search again with the version specified
+    if( PYTHONINTERP_FOUND AND _p_VERSION )
+      if( _p_VERSION VERSION_GREATER "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}', however version '${_p_VERSION}' is required. Searching again..." )
+        unset( PYTHONINTERP_FOUND )
+        unset( PYTHON_EXECUTABLE )
+        unset( PYTHON_EXECUTABLE CACHE )
+        unset( PYTHON_VERSION_MAJOR )
+        unset( PYTHON_VERSION_MINOR )
+        unset( PYTHON_VERSION_PATCH )
+        unset( PYTHON_VERSION_STRING )
+        find_package( PythonInterp "${_p_VERSION}" ${_p_REQUIRED} )
+      endif()
+    endif()
+
+    set_package_properties( PythonInterp PROPERTIES
+                            URL http://python.org
+                            DESCRIPTION "Python interpreter" )
+
+    set( __required_vars PYTHONINTERP_FOUND )
+
+    if( PYTHONINTERP_FOUND )
+        ecbuild_debug( "ecbuild_find_python: Found Python interpreter version '${PYTHON_VERSION_STRING}' at '${PYTHON_EXECUTABLE}'" )
+
+        # find where python site-packages are ...
+
+        if( PYTHON_EXECUTABLE )
+            execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
+        endif()
+        ecbuild_debug( "ecbuild_find_python: PYTHON_SITE_PACKAGES=${PYTHON_SITE_PACKAGES}" )
+    else()
+        ecbuild_debug( "ecbuild_find_python: could NOT find Python interpreter!" )
+    endif()
+
+    if( PYTHONINTERP_FOUND AND _p_NO_LIBS )
+        ecbuild_debug( "ecbuild_find_python: NOT searching for Python libraries" )
+    elseif( PYTHONINTERP_FOUND )
+        list( APPEND __required_vars PYTHONLIBS_FOUND PYTHON_LIBS_WORKING )
+        ecbuild_debug( "ecbuild_find_python: Searching for Python libraries ..." )
+
+        # find python config
+
+        if( PYTHON_EXECUTABLE AND EXISTS ${PYTHON_EXECUTABLE}-config )
+            set(PYTHON_CONFIG_EXECUTABLE ${PYTHON_EXECUTABLE}-config CACHE PATH "" FORCE)
+        else()
+            get_filename_component( __python_bin_dir ${PYTHON_EXECUTABLE} PATH )
+            find_program( PYTHON_CONFIG_EXECUTABLE
+                          NO_CMAKE_PATH NO_CMAKE_SYSTEM_PATH
+                          NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH
+                          HINTS ${__python_bin_dir}
+                          NAMES python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}-config
+                                python${PYTHON_VERSION_MAJOR}-config
+                                python-config )
+        endif()
+
+        ecbuild_debug( "ecbuild_find_python: found python-config at '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+        # find python libs
+
+        # The OpenBSD python packages have python-config's
+        # that don't reliably report linking flags that will work.
+
+        if( PYTHON_CONFIG_EXECUTABLE AND NOT ( PYTHON_NO_CONFIG OR ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" ) )
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using '${PYTHON_CONFIG_EXECUTABLE}'" )
+
+            if( DEFINED PYTHON_LIBRARY )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY already set to '${PYTHON_LIBRARY}'" )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --prefix
+                              OUTPUT_VARIABLE PYTHON_PREFIX
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_PREFIX=${PYTHON_PREFIX}" )
+
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --ldflags
+                              OUTPUT_VARIABLE PYTHON_LIBRARY
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARY=${PYTHON_LIBRARY}" )
+
+              # Prepend -L and and set the RPATH to the lib directory under the
+              # Python install prefix unless it is a standard system prefix path
+              if( PYTHON_LIBRARY AND PYTHON_PREFIX AND NOT CMAKE_SYSTEM_PREFIX_PATH MATCHES ${PYTHON_PREFIX} )
+                ecbuild_debug( "ecbuild_find_python: Python libraries not in CMAKE_SYSTEM_PREFIX_PATH, prepending PYTHON_PREFIX '${PYTHON_PREFIX}' to PYTHON_LIBRARY" )
+                set( PYTHON_LIBRARY "-L${PYTHON_PREFIX}/lib -Wl,-rpath,${PYTHON_PREFIX}/lib ${PYTHON_LIBRARY}" )
+              endif()
+            endif()
+
+            if( DEFINED PYTHON_INCLUDE_DIR )
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR already set to '${PYTHON_INCLUDE_DIR}'" )
+            elseif(DEFINED PYTHON_INCLUDE_PATH AND NOT DEFINED PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_PATH already set to '${PYTHON_INCLUDE_PATH}'" )
+              ecbuild_deprecate( "ecbuild_find_python: PYTHON_INCLUDE_PATH is deprecated, use PYTHON_INCLUDE_DIR instead!" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_PATH}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+            else()
+              execute_process(COMMAND "${PYTHON_CONFIG_EXECUTABLE}" --includes
+                              OUTPUT_VARIABLE PYTHON_INCLUDE_DIR
+                              OUTPUT_STRIP_TRAILING_WHITESPACE
+                              ERROR_QUIET)
+
+              string(REGEX REPLACE "^[-I]" "" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+              string(REGEX REPLACE "[ ]-I" " " PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}")
+
+              separate_arguments(PYTHON_INCLUDE_DIR)
+              ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR}" )
+              set( PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}" CACHE PATH
+                   "Path to where Python.h is found" FORCE )
+
+            endif()
+
+            set(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}")
+            set(PYTHON_LIBRARIES "${PYTHON_LIBRARY}")
+
+            find_package_handle_standard_args( PythonLibs DEFAULT_MSG
+                                               PYTHON_INCLUDE_DIRS PYTHON_LIBRARIES )
+
+        else() # revert to finding pythonlibs the standard way (cmake macro)
+            ecbuild_debug( "ecbuild_find_python: Searching for Python include directories and libraries using find_package( PythonLibs ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH} ${_p_REQUIRED} )" )
+
+            find_package( PythonLibs "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}" ${_p_REQUIRED} )
+
+            set_package_properties( PythonLibs PROPERTIES
+                                    URL http://python.org
+                                    DESCRIPTION "Python library and header" )
+
+        endif()
+
+        # Remove duplicate include directories
+        list(REMOVE_DUPLICATES PYTHON_INCLUDE_DIRS)
+
+        ecbuild_debug( "ecbuild_find_python: PYTHON_INCLUDE_DIRS=${PYTHON_INCLUDE_DIRS}" )
+        ecbuild_debug( "ecbuild_find_python: PYTHON_LIBRARIES=${PYTHON_LIBRARIES}" )
+
+        if( PYTHON_LIBRARIES AND PYTHON_INCLUDE_DIRS )
+            ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries ..." )
+            # Test if we can link against the Python libraries and include Python.h
+            try_compile( PYTHON_LIBS_WORKING ${CMAKE_CURRENT_BINARY_DIR}
+                         ${__test_python}
+                         CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PYTHON_INCLUDE_DIRS}"
+                         LINK_LIBRARIES ${PYTHON_LIBRARIES}
+                         OUTPUT_VARIABLE __try_compile_output )
+            if( PYTHON_LIBS_WORKING )
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries successful" )
+            else()
+              ecbuild_debug( "ecbuild_find_python: trying to link executable with Python libraries failed\n${__try_compile_output}" )
+            endif()
+
+        else()
+            ecbuild_debug( "ecbuild_find_python: Python library and include diretory not found" )
+        endif()
+
+    endif()
+
+    find_package_handle_standard_args( Python DEFAULT_MSG ${__required_vars} )
+
+    ecbuild_debug_var( PYTHONINTERP_FOUND )
+    ecbuild_debug_var( PYTHON_FOUND )
+    ecbuild_debug_var( PYTHON_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_CONFIG_EXECUTABLE )
+    ecbuild_debug_var( PYTHON_VERSION_MAJOR )
+    ecbuild_debug_var( PYTHON_VERSION_MINOR )
+    ecbuild_debug_var( PYTHON_VERSION_PATCH )
+    ecbuild_debug_var( PYTHON_VERSION_STRING )
+    ecbuild_debug_var( PYTHON_INCLUDE_DIRS )
+    ecbuild_debug_var( PYTHON_LIBRARIES )
+    ecbuild_debug_var( PYTHON_SITE_PACKAGES )
+
+    set( PYTHONINTERP_FOUND    ${PYTHONINTERP_FOUND} PARENT_SCOPE )
+    set( PYTHONLIBS_FOUND      ${PYTHONLIBS_FOUND} PARENT_SCOPE )
+    set( PYTHON_FOUND          ${PYTHON_FOUND} PARENT_SCOPE )
+    set( PYTHON_EXECUTABLE     ${PYTHON_EXECUTABLE} PARENT_SCOPE )
+    set( PYTHON_VERSION_MAJOR  ${PYTHON_VERSION_MAJOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_MINOR  ${PYTHON_VERSION_MINOR} PARENT_SCOPE )
+    set( PYTHON_VERSION_PATCH  ${PYTHON_VERSION_PATCH} PARENT_SCOPE )
+    set( PYTHON_VERSION_STRING ${PYTHON_VERSION_STRING} PARENT_SCOPE )
+    set( PYTHON_INCLUDE_DIRS   ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE )
+    set( PYTHON_LIBRARIES      ${PYTHON_LIBRARIES} PARENT_SCOPE )
+    set( PYTHON_SITE_PACKAGES  ${PYTHON_SITE_PACKAGES} PARENT_SCOPE )
+
+endfunction( ecbuild_find_python )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_generate_config_headers.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_generate_config_headers.cmake
new file mode 100644
index 0000000..8f2d44e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_generate_config_headers.cmake
@@ -0,0 +1,63 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_config_headers
+# ===============================
+#
+# Generates the ecBuild configuration header for the project with the system
+# introspection done by CMake. ::
+#
+#   ecbuild_generate_config_headers( [ DESTINATION <directory> ] )
+#
+# Options
+# -------
+#
+# DESTINATION : optional
+#   installation destination directory
+#
+##############################################################################
+
+function( ecbuild_generate_config_headers )
+
+  # parse parameters
+
+  set( options )
+  set( single_value_args DESTINATION )
+  set( multi_value_args  )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_config_headers(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  # generate list of compiler flags
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${langs} )
+    set( EC_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+  endforeach()
+
+  configure_file( ${ECBUILD_MACROS_DIR}/ecbuild_config.h.in  ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h   )
+
+  # install ecbuild configuration
+
+  set( _destination ${INSTALL_INCLUDE_DIR} )
+  if( _p_DESTINATION )
+    set( _destination ${_p_DESTINATION} )
+  endif()
+
+  install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_ecbuild_config.h DESTINATION ${_destination} )
+
+endfunction( ecbuild_generate_config_headers )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake
new file mode 100644
index 0000000..a127315
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_generate_fortran_interfaces.cmake
@@ -0,0 +1,143 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_fortran_interfaces
+# ===================================
+#
+# Generates interfaces from the Fortran source files. ::
+#
+#   ecbuild_generate_fortran_interfaces()
+#
+# Options
+# -------
+#
+# TARGET : required
+#   target name
+#
+##############################################################################
+
+function( ecbuild_generate_fortran_interfaces )
+
+  find_program( FCM_EXECUTABLE fcm REQUIRED DOC "Fortran interface generator" )
+
+  if( NOT FCM_EXECUTABLE )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: fcm executable not found." )
+  endif()
+
+  set( options )
+  set( single_value_args TARGET DESTINATION PARALLEL INCLUDE_DIRS GENERATED SOURCE_DIR FCM_CONFIG_FILE )
+  set( multi_value_args DIRECTORIES )
+
+  cmake_parse_arguments( P "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if( NOT DEFINED P_TARGET )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: TARGET argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DESTINATION )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DESTINATION argument missing" )
+  endif()
+
+  if( NOT DEFINED P_DIRECTORIES )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: DIRECTORIES argument missing" )
+  endif()
+
+  if( NOT DEFINED P_PARALLEL OR (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") )
+    set( P_PARALLEL 1 )
+  endif()
+
+  ecbuild_debug_var( P_PARALLEL )
+
+  if( NOT DEFINED P_SOURCE_DIR )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: SOURCE_DIR argument missing")
+  endif()
+
+  if( DEFINED P_FCM_CONFIG_FILE )
+    set( FCM_CONFIG_FILE ${P_FCM_CONFIG_FILE} )
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( PROJECT_FCM_CONFIG_FILE "${PROJECT_SOURCE_DIR}/cmake/fcm-make-interfaces.cfg" )
+    if( EXISTS ${PROJECT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${PROJECT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${PROJECT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${PROJECT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  if( NOT FCM_CONFIG_FILE )
+    set( DEFAULT_FCM_CONFIG_FILE "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg" )
+    if( EXISTS ${DEFAULT_FCM_CONFIG_FILE} )
+      set( FCM_CONFIG_FILE ${DEFAULT_FCM_CONFIG_FILE} )
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    else()
+      ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${DEFAULT_FCM_CONFIG_FILE}" )
+    endif()
+  endif()
+
+  ecbuild_debug_var( FCM_CONFIG_FILE )
+
+  if( NOT EXISTS ${FCM_CONFIG_FILE} )
+    ecbuild_error( "ecbuild_generate_fortran_interfaces: needs fcm configuration in ${FCM_CONFIG_FILE}" )
+  endif()
+
+  foreach( _srcdir ${P_DIRECTORIES} )
+    if( _srcdir MATCHES "/$" )
+      ecbuild_critical("ecbuild_generate_fortran_interfaces: directory ${_srcdir} must not end with /")
+    endif()
+    ecbuild_list_add_pattern( LIST fortran_files SOURCE_DIR ${P_SOURCE_DIR} GLOB ${_srcdir}/*.F* )
+  endforeach()
+
+  string( REPLACE ";" " " _srcdirs "${P_DIRECTORIES}" )
+
+  set( _cnt 0 )
+  foreach( file ${_fortran_files} )
+    if( ${${SRC}/file} IS_NEWER_THAN ${${SRC}/file} )
+      set( run_fcm 1 )
+    endif()
+  endforeach()
+
+  foreach( fortran_file ${fortran_files} )
+    #list( APPEND fullpath_fortran_files ${CMAKE_CURRENT_SOURCE_DIR}/${fortran_file} )
+      get_filename_component(base ${fortran_file} NAME_WE)
+      set( interface_file "${CMAKE_CURRENT_BINARY_DIR}/interfaces/include/${base}.intfb.h" )
+      list( APPEND interface_files ${interface_file} )
+      set_source_files_properties( ${interface_file} PROPERTIES GENERATED TRUE )
+      math(EXPR _cnt "${_cnt}+1")
+  endforeach()
+
+  ecbuild_info("Target ${P_TARGET} will generate ${_cnt} interface files using FCM")
+
+
+
+  if( DEFINED P_GENERATED )
+    set( ${P_GENERATED} ${interface_files} PARENT_SCOPE )
+  endif()
+
+  set( include_dir ${CMAKE_CURRENT_BINARY_DIR}/${P_DESTINATION}/interfaces/include )
+  set( ${P_INCLUDE_DIRS} ${include_dir} PARENT_SCOPE )
+
+  execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${include_dir}
+                   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+
+    add_custom_command(
+      OUTPUT  "${P_DESTINATION}/${P_TARGET}.timestamp"
+      COMMAND ${FCM_EXECUTABLE} make -j ${P_PARALLEL} --config-file=${FCM_CONFIG_FILE} interfaces.ns-incl=${_srcdirs} interfaces.source=${P_SOURCE_DIR}
+      COMMAND touch "${P_TARGET}.timestamp"
+      DEPENDS ${fortran_files}
+      COMMENT "Generating ${_cnt} interface files for target ${P_TARGET}"
+      WORKING_DIRECTORY ${P_DESTINATION} VERBATIM )
+
+    add_custom_target( ${P_TARGET} DEPENDS ${P_DESTINATION}/${P_TARGET}.timestamp )
+
+
+endfunction( ecbuild_generate_fortran_interfaces )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_generate_rpc.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_generate_rpc.cmake
new file mode 100644
index 0000000..2eb8605
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_generate_rpc.cmake
@@ -0,0 +1,98 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_rpc
+# ====================
+#
+# Process RPC (Remote Procedure Call) Language files using rpcgen. ::
+#
+#   ecbuild_generate_rpc( SOURCE <file>
+#                         [ TARGET_H <file> ]
+#                         [ TARGET_C <file> ]
+#                         [ DEPENDANT <file1> [ <file2> ... ] ] )
+#
+# Options
+# -------
+#
+# SOURCE : required
+#   RPC source file
+#
+# TARGET_H : optional (required if TARGET_C not given)
+#   name of header file to be generated
+#
+# TARGET_C : optional (required if TARGET_H not given)
+#   name of source file to be generated
+#
+# DEPENDANT : optional
+#  list of files which depend on the generated source and header files
+#
+##############################################################################
+
+macro( ecbuild_generate_rpc )
+
+  set( options )
+  set( single_value_args SOURCE TARGET_H TARGET_C )
+  set( multi_value_args DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_rpc(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_SOURCE  )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the SOURCE file.")
+  endif()
+
+  # optional
+  #    if( NOT _PAR_DEPENDANT )
+  #      ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the DEPENDANT files.")
+  #    endif()
+
+  if( NOT DEFINED _PAR_TARGET_H AND NOT DEFINED _PAR_TARGET_C )
+    ecbuild_critical("The call to ecbuild_generate_rpc() doesn't specify the _PAR_TARGET_H or _PAR_TARGET_C files.")
+  endif()
+
+  find_package( RPCGEN REQUIRED )
+
+  if( DEFINED _PAR_TARGET_H )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}
+      COMMAND ${RPCGEN_EXECUTABLE} -h -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_H}" )
+      endforeach()
+    endif()
+
+  endif()
+
+  if( DEFINED _PAR_TARGET_C )
+
+    add_custom_command(
+      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}
+      COMMAND ${RPCGEN_EXECUTABLE} -c -o ${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C} ${_PAR_SOURCE}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${_PAR_SOURCE} )
+
+    if( DEFINED _PAR_DEPENDANT )
+      foreach( file ${_PAR_DEPENDANT} )
+        set_source_files_properties( ${file} PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_PAR_TARGET_C}" )
+      endforeach()
+    endif()
+
+  endif()
+
+endmacro( ecbuild_generate_rpc )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_generate_yy.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_generate_yy.cmake
new file mode 100644
index 0000000..ea7ec13
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_generate_yy.cmake
@@ -0,0 +1,201 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_generate_yy
+# ===================
+#
+# Process lex/yacc files. ::
+#
+#   ecbuild_generate_yy( YYPREFIX <prefix>
+#                        YACC <file>
+#                        LEX <file>
+#                        DEPENDANT <file1> [ <file2> ... ]
+#                        [ SOURCE_DIR <dir> ]
+#                        [ OUTPUT_DIRECTORY <dir> ]
+#                        [ YACC_TARGET <file> ]
+#                        [ LEX_TARGET <file> ]
+#                        [ YACC_FLAGS <flags> ]
+#                        [ LEX_FLAGS <flags> ]
+#                        [ BISON_FLAGS <flags> ]
+#                        [ FLEX_FLAGS <flags> ] )
+#
+# Options
+# -------
+#
+# YYPREFIX : required
+#   prefix to use for file and function names
+#
+# YACC : required
+#   base name of the yacc source file (without .y extension)
+#
+# LEX : required
+#   base name of the lex source file (without .l extension)
+#
+# DEPENDANT : required
+#  list of files which depend on the generated lex and yacc target files
+#  At least one should be an existing source file (not generated itself).
+#
+# SOURCE_DIR : optional, defaults to CMAKE_CURRENT_SOURCE_DIR
+#   directory where yacc and lex source files are located
+#
+# OUTPUT_DIRECTORY : optional, defaults to CMAKE_CURRENT_BINARY_DIR
+#   output directory for yacc and lex target files
+#
+# YACC_TARGET : optional, defaults to YACC
+#   base name of the generated yacc target file (without .c extension)
+#
+# LEX_TARGET : optional, defaults to LEX
+#   base name of the generated lex target file (without .c extension)
+#
+# YACC_FLAGS : optional, defaults to -t
+#   flags to pass to yacc executable
+#
+# LEX_FLAGS : optional
+#   flags to pass to lex executable
+#
+# BISON_FLAGS : optional, defaults to -t
+#   flags to pass to bison executable
+#
+# FLEX_FLAGS : optional, defaults to -l
+#   flags to pass to flex executable
+#
+##############################################################################
+
+macro( ecbuild_generate_yy )
+
+  ecbuild_find_lexyacc() # find [ yacc|byson ] and [ lex|flex ]
+
+  ecbuild_find_perl( REQUIRED )
+
+  set( options )
+  set( single_value_args YYPREFIX YACC LEX SOURCE_DIR OUTPUT_DIRECTORY YACC_TARGET LEX_TARGET LEX_FLAGS YACC_FLAGS FLEX_FLAGS BISON_FLAGS )
+  set( multi_value_args  DEPENDANT )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_generate_yy(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _PAR_YYPREFIX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YYPREFIX.")
+  endif()
+
+  if( NOT _PAR_YACC  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the YACC file.")
+  endif()
+
+  if( NOT _PAR_LEX  )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the LEX file.")
+  endif()
+
+  if( NOT _PAR_DEPENDANT )
+    ecbuild_critical("The call to ecbuild_generate_yy() doesn't specify the DEPENDANT files.")
+  endif()
+
+  set( BASE ${_PAR_YYPREFIX}_${_PAR_YACC} )
+
+  ## default flags
+
+  if( NOT _PAR_LEX_FLAGS )
+    set( _PAR_LEX_FLAGS "" )
+  endif()
+
+  if( NOT _PAR_FLEX_FLAGS )
+    set( _PAR_FLEX_FLAGS "-l" )
+  endif()
+
+  if( NOT _PAR_YACC_FLAGS )
+    set( _PAR_YACC_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_BISON_FLAGS )
+    set( _PAR_BISON_FLAGS "-t" )
+  endif()
+
+  if( NOT _PAR_YACC_TARGET )
+    set ( _PAR_YACC_TARGET ${_PAR_YACC} )
+  endif()
+
+  if ( NOT _PAR_LEX_TARGET )
+    set ( _PAR_LEX_TARGET ${_PAR_LEX} )
+  endif()
+
+  if( NOT _PAR_SOURCE_DIR )
+    set( _PAR_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} )
+  endif()
+
+  if( NOT _PAR_OUTPUT_DIRECTORY )
+    set( _PAR_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+  else()
+    file( MAKE_DIRECTORY ${_PAR_OUTPUT_DIRECTORY} )
+  endif()
+
+  set( ${BASE}yy_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.c )
+  set( ${BASE}yh_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.tmp.h )
+  set( ${BASE}yl_tmp_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.tmp.c )
+
+  set( ${BASE}yy_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.c )
+  set( ${BASE}yh_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_YACC_TARGET}.h )
+  set( ${BASE}yl_target ${_PAR_OUTPUT_DIRECTORY}/${_PAR_LEX_TARGET}.c )
+
+  if( BISON_FOUND )
+    bison_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_BISON_FLAGS}" )
+  else()
+    yacc_target( ${BASE}_parser ${_PAR_SOURCE_DIR}/${_PAR_YACC}.y ${${BASE}yy_tmp_target} COMPILE_FLAGS "${_PAR_YACC_FLAGS}" )
+  endif()
+
+  if( FLEX_FOUND )
+    flex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_FLEX_FLAGS}" )
+    add_flex_bison_dependency(${BASE}_scanner ${BASE}_parser)
+  else()
+    lex_target( ${BASE}_scanner ${_PAR_SOURCE_DIR}/${_PAR_LEX}.l ${${BASE}yl_tmp_target} COMPILE_FLAGS "${_PAR_LEX_FLAGS}" )
+    add_lex_yacc_dependency(${BASE}_scanner ${BASE}_parser)
+  endif()
+
+  set_source_files_properties(${${BASE}yy_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_tmp_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_tmp_target} GENERATED)
+
+  add_custom_command(OUTPUT  ${${BASE}yy_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yy_tmp_target} ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yy_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yy_target}
+    DEPENDS ${${BASE}yy_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yh_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yh_tmp_target} ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yh_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.h/\\.h/g' ${${BASE}yh_target}
+    DEPENDS ${${BASE}yh_tmp_target}
+    )
+
+  add_custom_command(OUTPUT  ${${BASE}yl_target}
+    COMMAND ${CMAKE_COMMAND} -E copy ${${BASE}yl_tmp_target} ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/yy/${_PAR_YYPREFIX}/g' ${${BASE}yl_target}
+    COMMAND ${PERL_EXECUTABLE} -pi -e 's/\\.tmp\\.c/\\.c/g' ${${BASE}yl_target}
+    DEPENDS ${${BASE}yl_tmp_target}
+    )
+
+  set_source_files_properties(${${BASE}yy_target} GENERATED)
+  set_source_files_properties(${${BASE}yh_target} GENERATED)
+  set_source_files_properties(${${BASE}yl_target} GENERATED)
+
+  foreach( file ${_PAR_DEPENDANT} )
+    if( NOT IS_ABSOLUTE ${file})
+      set( file ${_PAR_SOURCE_DIR}/${file} )
+    endif()
+    set_source_files_properties( ${file} PROPERTIES
+        OBJECT_DEPENDS "${${BASE}yy_target};${${BASE}yh_target};${${BASE}yl_target}" )
+  endforeach()
+
+endmacro( ecbuild_generate_yy )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake
new file mode 100644
index 0000000..acb0b21
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_get_cxx11_flags.cmake
@@ -0,0 +1,74 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_cxx11_flags
+# =======================
+#
+# Set the CMake variable ``${CXX11_FLAGS}`` to the C++11 flags for the current
+# compiler (based on macros from https://github.com/UCL/GreatCMakeCookOff). ::
+#
+#   ecbuild_get_cxx11_flags( CXX11_FLAGS )
+#
+##############################################################################
+
+function( ecbuild_get_cxx11_flags CXX11_FLAGS )
+
+  include(CheckCXXCompilerFlag)
+
+  # On older cmake versions + newer compilers,
+  # the given version of CheckCXXCompilerFlags does not quite work.
+  if(CMAKE_VERSION VERSION_LESS 2.8.9)
+    macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
+       set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}")
+       set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}")
+       CHECK_CXX_SOURCE_COMPILES("int main() { return 0;}" ${_RESULT}
+         # Some compilers do not fail with a bad flag
+         FAIL_REGEX "command line option .* is valid for .* but not for C\\\\+\\\\+" # GNU
+         FAIL_REGEX "unrecognized .*option"                     # GNU
+         FAIL_REGEX "unknown .*option"                          # Clang
+         FAIL_REGEX "ignoring unknown option"                   # MSVC
+         FAIL_REGEX "warning D9002"                             # MSVC, any lang
+         FAIL_REGEX "option.*not supported"                     # Intel
+         FAIL_REGEX "invalid argument .*option"                 # Intel
+         FAIL_REGEX "ignoring option .*argument required"       # Intel
+         FAIL_REGEX "[Uu]nknown option"                         # HP
+         FAIL_REGEX "[Ww]arning: [Oo]ption"                     # SunPro
+         FAIL_REGEX "command option .* is not recognized"       # XL
+         FAIL_REGEX "not supported in this configuration; ignored"       # AIX
+         FAIL_REGEX "File with unknown suffix passed to linker" # PGI
+         FAIL_REGEX "WARNING: unknown flag:"                    # Open64
+         )
+       set (CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}")
+    endmacro ()
+  endif(CMAKE_VERSION VERSION_LESS 2.8.9)
+
+  check_cxx_compiler_flag(-std=c++11 has_std_cpp11)
+  check_cxx_compiler_flag(-std=c++0x has_std_cpp0x)
+  check_cxx_compiler_flag(-hstd=c++11 has_hstd_cpp11)
+  if(MINGW)
+    check_cxx_compiler_flag(-std=gnu++11 has_std_gnupp11)
+    check_cxx_compiler_flag(-std=gnu++0x has_std_gnupp0x)
+  endif(MINGW)
+  if(has_std_gnupp11)
+    set(${CXX11_FLAGS} "-std=gnu++11" PARENT_SCOPE)
+  elseif(has_std_gnupp0x)
+    set(${CXX11_FLAGS} "-std=gnu++0x" PARENT_SCOPE)
+  elseif(has_hstd_cpp11)
+    set(${CXX11_FLAGS} "-hstd=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp11)
+    set(${CXX11_FLAGS} "-std=c++11" PARENT_SCOPE)
+  elseif(has_std_cpp0x)
+    set(${CXX11_FLAGS} "-std=c++0x" PARENT_SCOPE)
+  else()
+    ecbuild_critical("Could not detect C++11 flags")
+  endif(has_std_gnupp11)
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_get_date.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_get_date.cmake
new file mode 100644
index 0000000..0f94d7d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_get_date.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_date
+# ================
+#
+# Set the CMake variable ``${DATE}`` to the current date in the form
+# YYYY.mm.DD. ::
+#
+#   ecbuild_get_date( DATE )
+#
+##############################################################################
+
+macro(ecbuild_get_date RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%d/%m/%Y" OUTPUT_VARIABLE ${RESULT})
+        string(REGEX REPLACE "(..)/(..)/(....).*" "\\3.\\2.\\1" ${RESULT} ${${RESULT}})
+    else()
+        ecbuild_error("date not implemented")
+    endif()
+endmacro(ecbuild_get_date)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_timestamp
+# =====================
+#
+# Set the CMake variable ``${TIMESTAMP}`` to the current date and time in the
+# form YYYYmmDDHHMMSS. ::
+#
+#   ecbuild_get_timestamp( TIMESTAMP )
+#
+##############################################################################
+
+macro(ecbuild_get_timestamp RESULT)
+    if(UNIX)
+        execute_process(COMMAND "date" "+%Y/%m/%d/%H/%M/%S" OUTPUT_VARIABLE _timestamp)
+        string(REGEX REPLACE "(....)/(..)/(..)/(..)/(..)/(..).*" "\\1\\2\\3\\4\\5\\6" ${RESULT} ${_timestamp})
+    else()
+        ecbuild_warn("This is NOT UNIX - timestamp not implemented")
+    endif()
+endmacro(ecbuild_get_timestamp)
+
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_get_resources.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_get_resources.cmake
new file mode 100644
index 0000000..d6c20e4
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_get_resources.cmake
@@ -0,0 +1,52 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# macro for adding a test
+##############################################################################
+
+macro( ecbuild_get_resources )
+
+    set( options                )
+    set( single_value_args TO_DIR  )
+    set( multi_value_args  LIST )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+        ecbuild_critical("Unknown keywords given to ecbuild_get_resources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_LIST )
+        ecbuild_critical( "Missing parameter LIST of resources in macro ecbuild_get_resources()" )
+    endif()
+
+    if( NOT _PAR_TO_DIR )
+		set( _PAR_TO_DIR ${CMAKE_CURRENT_BINARY_DIR} )
+	endif()
+
+    list( LENGTH _PAR_LIST _rsize )
+    math( EXPR _max "${_rsize}-1" )
+    foreach( i RANGE 0 ${_max} 2 )
+
+        math( EXPR in "${i}+1" )
+
+        list( GET _PAR_LIST ${i}  r  )
+        list( GET _PAR_LIST ${in} rh )
+
+#        ecbuild_debug_var( r  )
+#        ecbuild_debug_var( rh )
+
+        get_filename_component( rf ${r} NAME )
+
+        file( DOWNLOAD ${r} ${_PAR_TO_DIR}/${rf} EXPECTED_HASH SHA1=${rh} )
+
+    endforeach()
+
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_get_test_data.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_get_test_data.cmake
new file mode 100644
index 0000000..4945b90
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_get_test_data.cmake
@@ -0,0 +1,427 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# function for downloading test data
+
+function( _download_test_data _p_NAME _p_DIRNAME )
+
+  # TODO: make that 'at ecmwf'
+  #if(1)
+  #unset(ENV{no_proxy})
+  #unset(ENV{NO_PROXY})
+  #set(ENV{http_proxy} "http://proxy.ecmwf.int:3333")
+  #endif()
+
+  # Do not retry downloads by default (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_RETRIES )
+    set( ECBUILD_DOWNLOAD_RETRIES 0 )
+  endif()
+  # Use default timeout of 30s if not specified (ECBUILD-307)
+  if( NOT DEFINED ECBUILD_DOWNLOAD_TIMEOUT )
+    set( ECBUILD_DOWNLOAD_TIMEOUT 30 )
+  endif()
+
+  find_program( CURL_PROGRAM curl )
+  mark_as_advanced(CURL_PROGRAM)
+
+  if( CURL_PROGRAM )
+
+    add_custom_command( OUTPUT ${_p_NAME}
+      COMMENT "(curl) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+      COMMAND ${CURL_PROGRAM} --silent --show-error --fail --output ${_p_NAME}
+              --retry ${ECBUILD_DOWNLOAD_RETRIES}
+              --connect-timeout ${ECBUILD_DOWNLOAD_TIMEOUT}
+              http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+  else()
+
+    find_program( WGET_PROGRAM wget )
+
+    if( WGET_PROGRAM )
+
+      # wget takes the total number of tries, curl the number or retries
+      math( EXPR ECBUILD_DOWNLOAD_RETRIES "${ECBUILD_DOWNLOAD_RETRIES} + 1" )
+
+      add_custom_command( OUTPUT ${_p_NAME}
+        COMMENT "(wget) downloading http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME}"
+        COMMAND ${WGET_PROGRAM} -nv -O ${_p_NAME}
+                -t ${ECBUILD_DOWNLOAD_RETRIES} -T ${ECBUILD_DOWNLOAD_TIMEOUT}
+                http://download.ecmwf.org/test-data/${_p_DIRNAME}/${_p_NAME} )
+
+    else()
+
+      if( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+        ecbuild_warn( "Couldn't find curl neither wget -- cannot download test data from server.\nPlease obtain the test data by other means and pleace it in the build directory." )
+        set( WARNING_CANNOT_DOWNLOAD_TEST_DATA 1 CACHE INTERNAL "Couldn't find curl neither wget -- cannot download test data from server" )
+        mark_as_advanced( WARNING_CANNOT_DOWNLOAD_TEST_DATA )
+      endif()
+
+    endif()
+
+  endif()
+
+endfunction()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_data
+# =====================
+#
+# Download a test data set at build time. ::
+#
+#   ecbuild_get_test_data( NAME <name>
+#                          [ TARGET <target> ]
+#                          [ DIRNAME <dir> ]
+#                          [ MD5 <hash> ]
+#                          [ EXTRACT ]
+#                          [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAME : required
+#   name of the test data file
+#
+# TARGET : optional, defaults to test_data_<name>
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# MD5 : optional, ignored if NOCHECK is given
+#   md5 checksum of the data set to verify. If not given and NOCHECK is *not*
+#   set, download the md5 checksum and verify
+#
+# EXTRACT : optional
+#   extract the downloaded file (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>/<NAME>``
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, the downloaded file is verified against an md5 checksum, either
+# given as the ``MD5`` argument or downloaded from the server otherwise. Use
+# the argument ``NOCHECK`` to disable this check.
+#
+# The default timeout is 30 seconds, which can be overridden with
+# ``ECBUILD_DOWNLOAD_TIMEOUT``. Downloads are by default only tried once, use
+# ``ECBUILD_DOWNLOAD_RETRIES`` to set the number of retries.
+#
+# Examples
+# --------
+#
+# Do not verify the checksum: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib NOCHECK )
+#
+# Checksum agains remote md5 file: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_data( NAME msl.grib MD5 f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_data )
+
+    set( options NOCHECK EXTRACT )
+    set( single_value_args TARGET URL NAME DIRNAME MD5 SHA1)
+    set( multi_value_args  )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    file( RELATIVE_PATH currdir ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} )
+
+    ### check parameters
+
+    if( NOT _p_NAME )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAME")
+    endif()
+
+    if( NOT _p_TARGET )
+      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "test_data_${_p_NAME}")
+#      string( REGEX REPLACE "[^A-Za-z0-9_]" "_" _p_TARGET "${_p_NAME}")
+#      set( _p_TARGET ${_p_NAME} )
+    endif()
+
+    if( NOT _p_DIRNAME )
+      set( _p_DIRNAME ${PROJECT_NAME}/${currdir} )
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_URL )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    # download the data
+
+    _download_test_data( ${_p_NAME} ${_p_DIRNAME} )
+
+    # perform the checksum if requested
+
+    set( _deps ${_p_NAME} )
+
+    if( NOT _p_NOCHECK )
+
+        if( NOT _p_MD5 AND NOT _p_SHA1) # use remote md5
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            _download_test_data( ${_p_NAME}.md5 ${_p_DIRNAME} )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 ${_p_NAME}.md5 )
+
+            list( APPEND _deps  ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+        if( _p_MD5 )
+
+            add_custom_command( OUTPUT ${_p_NAME}.localmd5
+                                COMMAND ${CMAKE_COMMAND} -E md5sum ${_p_NAME} > ${_p_NAME}.localmd5
+                                DEPENDS ${_p_NAME} )
+
+            configure_file( "${ECBUILD_MACROS_DIR}/md5.in" ${_p_NAME}.md5 @ONLY )
+
+            add_custom_command( OUTPUT ${_p_NAME}.ok
+                                COMMAND ${CMAKE_COMMAND} -E compare_files ${_p_NAME}.md5 ${_p_NAME}.localmd5 &&
+                                        ${CMAKE_COMMAND} -E touch ${_p_NAME}.ok
+                                DEPENDS ${_p_NAME}.localmd5 )
+
+            list( APPEND _deps ${_p_NAME}.localmd5 ${_p_NAME}.ok )
+
+        endif()
+
+#        if( _p_SHA1 )
+
+#            find_program( SHASUM NAMES sha1sum shasum )
+#            if( SHASUM )
+#                add_custom_command( OUTPUT ${_p_NAME}.localsha1
+#                                    COMMAND ${SHASUM} ${_p_NAME} > ${_p_NAME}.localsha1 )
+
+#                add_custom_command( OUTPUT ${_p_NAME}.ok
+#                                    COMMAND diff ${_p_NAME}.sha1 ${_p_NAME}.localsha1 && touch ${_p_NAME}.ok )
+
+#                configure_file( "${ECBUILD_MACROS_DIR}/sha1.in" ${_p_NAME}.sha1 @ONLY )
+
+#                list( APPEND _deps ${_p_NAME}.localsha1 ${_p_NAME}.ok )
+#            endif()
+
+#        endif()
+
+    endif()
+
+    add_custom_target( ${_p_TARGET} DEPENDS ${_deps} )
+
+    if( _p_EXTRACT )
+      ecbuild_debug("ecbuild_get_test_data: extracting ${_p_NAME} (post-build for target ${_p_TARGET}")
+      add_custom_command( TARGET ${_p_TARGET} POST_BUILD
+                          COMMAND ${CMAKE_COMMAND} -E tar xv ${_p_NAME} )
+    endif()
+
+endfunction(ecbuild_get_test_data)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_get_test_multidata
+# ==========================
+#
+# Download multiple test data sets at build time. ::
+#
+#   ecbuild_get_test_multidata( NAMES <name1> [ <name2> ... ]
+#                               TARGET <target>
+#                               [ DIRNAME <dir> ]
+#                               [ LABELS <label1> [<label2> ...] ]
+#                               [ EXTRACT ]
+#                               [ NOCHECK ] )
+#
+# curl or wget is required (curl is preferred if available).
+#
+# Options
+# -------
+#
+# NAMES : required
+#   list of names of the test data files
+#
+# TARGET : optional
+#   CMake target name
+#
+# DIRNAME : optional, defaults to <project>/<relative path to current dir>
+#   directory in which the test data resides
+#
+# LABELS : optional
+#   list of labels to assign to the test
+#
+#   Lower case project name and ``download_data`` are always added as labels.
+#
+#   This allows selecting tests to run via ``ctest -L <regex>`` or tests
+#   to exclude via ``ctest -LE <regex>``.
+#
+# EXTRACT : optional
+#   extract downloaded files (supported archives: tar, zip, tar.gz, tar.bz2)
+#
+# NOCHECK : optional
+#   do not verify the md5 checksum of the data file
+#
+# Usage
+# -----
+#
+# Download test data from ``http://download.ecmwf.org/test-data/<DIRNAME>``
+# for each name given in the list of ``NAMES``. Each name may contain a
+# relative path, which is appended to ``DIRNAME`` and may be followed by an
+# md5 checksum, separated with a ``:`` (the name must not contain spaces).
+#
+# If the ``DIRNAME`` argument is not given, the project name followed by the
+# relative path from the root directory to the current directory is used.
+#
+# By default, each downloaded file is verified against an md5 checksum, either
+# given as part of the name as described above or a remote checksum downloaded
+# from the server. Use the argument ``NOCHECK`` to disable this check.
+#
+# Examples
+# --------
+#
+# Do not verify checksums: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir NOCHECK )
+#
+# Checksums agains remote md5 file: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data NAMES foo.grib bar.grib
+#                               DIRNAME test/data/dir )
+#
+# Checksum agains local md5: ::
+#
+#   ecbuild_get_test_multidata( TARGET get_grib_data DIRNAME test/data/dir
+#                               NAMES msl.grib:f69ca0929d1122c7878d19f32401abe9 )
+#
+##############################################################################
+
+function( ecbuild_get_test_multidata )
+
+    set( options EXTRACT NOCHECK )
+    set( single_value_args TARGET DIRNAME )
+    set( multi_value_args  NAMES LABELS )
+
+    cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_p_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_get_test_data(): \"${_p_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    ### check parameters
+
+    if( NOT _p_NAMES )
+      ecbuild_critical("ecbuild_get_test_data() expects a NAMES")
+    endif()
+
+    if( NOT _p_TARGET )
+      ecbuild_critical("ecbuild_get_test_data() expects a TARGET")
+    endif()
+
+#    ecbuild_debug_var( _p_TARGET )
+#    ecbuild_debug_var( _p_NAME )
+#    ecbuild_debug_var( _p_DIRNAME )
+
+    if( _p_EXTRACT )
+        set( _extract EXTRACT )
+    endif()
+
+    if( _p_NOCHECK )
+        set( _nocheck NOCHECK )
+    endif()
+
+    ### prepare file
+
+    set( _script ${CMAKE_CURRENT_BINARY_DIR}/get_data_${_p_TARGET}.cmake )
+
+    file( WRITE ${_script} "
+function(EXEC_CHECK)
+     execute_process(COMMAND \${ARGV} RESULT_VARIABLE CMD_RESULT)
+     if(CMD_RESULT)
+           message(FATAL_ERROR \"Error running ${CMD}\")
+     endif()
+endfunction()\n\n" )
+
+    foreach( _d ${_p_NAMES} )
+
+        string( REGEX MATCH "[^:]+" _f "${_d}" )
+
+        get_filename_component( _file ${_f} NAME )
+        get_filename_component( _dir  ${_f} PATH )
+
+        list( APPEND _path_comps ${_p_DIRNAME} ${_dir} )
+        join( _path_comps "/" _dirname )
+        if( _dirname )
+            set( _dirname DIRNAME ${_dirname} )
+        endif()
+        unset( _path_comps )
+
+        string( REPLACE "." "_" _name "${_file}" )
+        string( REGEX MATCH ":.*"  _md5  "${_d}" )
+        string( REPLACE ":" "" _md5 "${_md5}" )
+
+        if( _md5 )
+            set( _md5 MD5 ${_md5} )
+        endif()
+
+        #ecbuild_debug_var(_f)
+        #ecbuild_debug_var(_file)
+        #ecbuild_debug_var(_dirname)
+        #ecbuild_debug_var(_name)
+        #ecbuild_debug_var(_md5)
+
+        ecbuild_get_test_data(
+            TARGET __get_data_${_p_TARGET}_${_name}
+            NAME ${_file} ${_dirname} ${_md5} ${_extract} ${_nocheck} )
+
+        # The option /fast disables dependency checking on a target, see
+        # https://cmake.org/Wiki/CMake_FAQ#Is_there_a_way_to_skip_checking_of_dependent_libraries_when_compiling.3F
+        if( WIN32 )
+          set( _fast "\fast" )
+        else()
+          set( _fast "/fast" )
+        endif()
+        file( APPEND ${_script}
+              "exec_check( \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target __get_data_${_p_TARGET}_${_name}${_fast} )\n" )
+
+    endforeach()
+
+    if( ENABLE_TESTS )
+      add_test(  NAME ${_p_TARGET} COMMAND ${CMAKE_COMMAND} -P ${_script} )
+      set( _p_LABELS ${PROJECT_NAME_LOWCASE} download_data ${_p_LABELS} )
+      list( REMOVE_DUPLICATES _p_LABELS )
+      set_property( TEST ${_p_TARGET} APPEND PROPERTY LABELS "${_p_LABELS}" )
+    endif()
+
+endfunction(ecbuild_get_test_multidata)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_git.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_git.cmake
new file mode 100644
index 0000000..18aab19
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_git.cmake
@@ -0,0 +1,300 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( ECBUILD_GIT  ON  CACHE BOOL "Turn on/off ecbuild_git() function" )
+
+mark_as_advanced(ECBUILD_GIT)
+
+if( ECBUILD_GIT )
+
+  find_package(Git)
+
+  set( ECMWF_USER $ENV{USER} CACHE STRING "ECMWF git user" )
+  set( ECMWF_GIT  SSH        CACHE STRING "ECMWF git protocol" )
+
+  set( ECMWF_GIT_SSH   "ssh://git@software.ecmwf.int:7999"                  CACHE INTERNAL "ECMWF ssh address" )
+  set( ECMWF_GIT_HTTPS "https://${ECMWF_USER}@software.ecmwf.int/stash/scm" CACHE INTERNAL "ECMWF https address" )
+
+  if( ECMWF_GIT MATCHES "[Ss][Ss][Hh]" )
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_SSH} CACHE INTERNAL "" )
+  else()
+    set( ECMWF_GIT_ADDRESS ${ECMWF_GIT_HTTPS} CACHE INTERNAL "" )
+  endif()
+
+endif()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_git
+# ===========
+#
+# Manages an external Git repository. ::
+#
+#   ecbuild_git( PROJECT <name>
+#                DIR <directory>
+#                URL <giturl>
+#                [ BRANCH <gitbranch> | TAG <gittag> ]
+#                [ UPDATE | NOREMOTE ] )
+#                [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# URL : required
+#   Git URL of the remote repository to clone (see ``git help clone``)
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecbuild_git )
+
+  set( options UPDATE NOREMOTE MANUAL )
+  set( single_value_args PROJECT DIR URL TAG BRANCH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  if( DEFINED _PAR_BRANCH AND DEFINED _PAR_TAG )
+    ecbuild_critical( "Cannot defined both BRANCH and TAG in macro ecbuild_git" )
+  endif()
+
+  if( _PAR_UPDATE AND _PAR_NOREMOTE )
+    ecbuild_critical( "Cannot pass both NOREMOTE and UPDATE in macro ecbuild_git" )
+  endif()
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_git(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( ECBUILD_GIT )
+
+    set( _needs_switch 0 )
+
+    get_filename_component( ABS_PAR_DIR "${_PAR_DIR}" ABSOLUTE )
+    get_filename_component( PARENT_DIR  "${_PAR_DIR}/.." ABSOLUTE )
+
+    ### clone if no directory
+
+    if( NOT EXISTS "${_PAR_DIR}" )
+
+      ecbuild_info( "Cloning ${_PAR_PROJECT} from ${_PAR_URL} into ${_PAR_DIR}...")
+      execute_process(
+        COMMAND ${GIT_EXECUTABLE} "clone" ${_PAR_URL} ${clone_args} ${_PAR_DIR} "-q"
+        RESULT_VARIABLE nok ERROR_VARIABLE error
+        WORKING_DIRECTORY "${PARENT_DIR}")
+      if(nok)
+        ecbuild_critical("${_PAR_DIR} git clone failed:\n  ${GIT_EXECUTABLE} clone ${_PAR_URL} ${clone_args} ${_PAR_DIR} -q\n  ${error}\n")
+      endif()
+      ecbuild_info( "${_PAR_DIR} retrieved.")
+      set( _needs_switch 1 )
+
+    endif()
+
+    ### check current tag and sha1
+
+    if( IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
+                       OUTPUT_VARIABLE _sha1 RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if(nok)
+        ecbuild_info("git rev-parse HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+                       OUTPUT_VARIABLE _current_branch RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+      if( nok OR _current_branch STREQUAL "" )
+        ecbuild_info("git rev-parse --abbrev-ref HEAD on ${_PAR_DIR} failed:\n ${error}")
+      endif()
+
+      execute_process( COMMAND ${GIT_EXECUTABLE} describe --exact-match --abbrev=0
+                       OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                       OUTPUT_STRIP_TRAILING_WHITESPACE  ERROR_STRIP_TRAILING_WHITESPACE
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+
+      if( error MATCHES "no tag exactly matches" OR error MATCHES "No names found" )
+        unset( _current_tag )
+      else()
+        if( nok )
+          ecbuild_info("git describe --exact-match --abbrev=0 on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+      if( NOT _current_tag ) # try nother method
+        execute_process( COMMAND ${GIT_EXECUTABLE} name-rev --tags --name-only ${_sha1}
+                         OUTPUT_VARIABLE _current_tag RESULT_VARIABLE nok ERROR_VARIABLE error
+                         OUTPUT_STRIP_TRAILING_WHITESPACE
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}" )
+        if( nok OR _current_tag STREQUAL "" )
+          ecbuild_info("git name-rev --tags --name-only on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+      endif()
+
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_BRANCH AND NOT "${_current_branch}" STREQUAL "${_PAR_BRANCH}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( NOT _PAR_MANUAL AND DEFINED _PAR_TAG AND NOT "${_current_tag}" STREQUAL "${_PAR_TAG}" )
+      set( _needs_switch 1 )
+    endif()
+
+    if( DEFINED _PAR_BRANCH AND _PAR_UPDATE AND NOT _PAR_NOREMOTE )
+
+      add_custom_target( git_update_${_PAR_PROJECT}
+                         COMMAND "${GIT_EXECUTABLE}" pull -q
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}"
+                         COMMENT "git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR}" )
+
+      set( git_update_targets "git_update_${_PAR_PROJECT};${git_update_targets}" )
+
+    endif()
+
+    ### updates
+
+    if( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+      if( DEFINED _PAR_BRANCH )
+        set ( _gitref ${_PAR_BRANCH} )
+        ecbuild_info("Updating ${_PAR_PROJECT} to head of BRANCH ${_PAR_BRANCH}...")
+      else()
+        ecbuild_info("Updating ${_PAR_PROJECT} to TAG ${_PAR_TAG}...")
+        set ( _gitref ${_PAR_TAG} )
+      endif()
+
+      # fetching latest tags and branches
+
+      if( NOT _PAR_NOREMOTE )
+
+        ecbuild_info("git fetch --all @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+        ecbuild_info("git fetch --all --tags @ ${ABS_PAR_DIR}")
+        execute_process( COMMAND "${GIT_EXECUTABLE}" fetch --all --tags -q
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_warn("git fetch --all --tags in ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      else()
+        ecbuild_info("${_PAR_DIR} marked NOREMOTE : Skipping git fetch")
+      endif()
+
+      # checking out gitref
+
+      ecbuild_info("git checkout ${_gitref} @ ${ABS_PAR_DIR}")
+      execute_process( COMMAND "${GIT_EXECUTABLE}" checkout -q "${_gitref}"
+                       RESULT_VARIABLE nok ERROR_VARIABLE error
+                       WORKING_DIRECTORY "${ABS_PAR_DIR}")
+      if(nok)
+        ecbuild_critical("git checkout ${_gitref} on ${_PAR_DIR} failed:\n  ${GIT_EXECUTABLE} checkout -q ${_gitref}\n  ${error}")
+      endif()
+
+      if( DEFINED _PAR_BRANCH AND _PAR_UPDATE ) #############################################################################
+
+        # Use git pull --ff-only, we WANT this to fail on upstream rebase and
+        # we DON'T want merge commits here!
+        execute_process( COMMAND "${GIT_EXECUTABLE}" pull -q --ff-only
+                         RESULT_VARIABLE nok ERROR_VARIABLE error
+                         WORKING_DIRECTORY "${ABS_PAR_DIR}")
+        if(nok)
+          ecbuild_critical("git pull of branch ${_PAR_BRANCH} on ${_PAR_DIR} failed:\n ${error}")
+        endif()
+
+      endif() ####################################################################################
+
+    endif( _needs_switch AND IS_DIRECTORY "${_PAR_DIR}/.git" )
+
+  endif( ECBUILD_GIT )
+
+endmacro()
+
+##############################################################################
+#.rst:
+#
+# ecbuild_stash
+# =============
+#
+# Manages an external Git repository on ECMWF Stash. ::
+#
+#   ecbuild_stash( PROJECT <name>
+#                  DIR <directory>
+#                  STASH <repository>
+#                  [ BRANCH <gitbranch> | TAG <gittag> ]
+#                  [ UPDATE | NOREMOTE ] )
+#                  [ MANUAL ] )
+#
+# Options
+# -------
+#
+# PROJECT : required
+#   project name for the Git repository to be managed
+#
+# DIR : required
+#   directory to clone the repository into (can be relative)
+#
+# STASH : required
+#   Stash repository in the form <project>/<repository>
+#
+# BRANCH : optional, cannot be combined with TAG
+#   Git branch to check out
+#
+# TAG : optional, cannot be combined with BRANCH
+#   Git tag or commit id to check out
+#
+# UPDATE : optional, requires BRANCH, cannot be combined with NOREMOTE
+#   Create a CMake target update to fetch changes from the remote repository
+#
+# NOREMOTE : optional, cannot be combined with UPDATE
+#   Do not fetch changes from the remote repository
+#
+# MANUAL : optional
+#   Do not automatically switch branches or tags
+#
+##############################################################################
+
+macro( ecmwf_stash )
+
+  set( options )
+  set( single_value_args STASH )
+  set( multi_value_args )
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+  ecbuild_git( URL "${ECMWF_GIT_ADDRESS}/${_PAR_STASH}.git" ${_PAR_UNPARSED_ARGUMENTS} )
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_install_project.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_install_project.cmake
new file mode 100644
index 0000000..113e642
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_install_project.cmake
@@ -0,0 +1,437 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_install_project
+# =======================
+#
+# Set up packaging and export configuration. ::
+#
+#   ecbuild_install_project( NAME <name> [ DESCRIPTION <description> ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   project name used for packaging
+#
+# DESCRIPTION : optional
+#   project description used for packaging
+#
+# Usage
+# -----
+#
+# ``ecbuild_install_project`` should be called at the very end of any ecBuild
+# project (only followed by ``ecbuild_print_summary``), sets up packaging of
+# the project with cpack and exports the configuration and targets for other
+# projects to use.
+#
+# Unless ECBUILD_SKIP_<PNAME>_EXPORT is set, the following files are generated:
+#
+# :<project>-config.cmake:         default project configuration
+# :<project>-config-version.cmake: project version number
+# :<project>-import.cmake:         extra project configuration (optional)
+# :<project>-config.cmake.tpls:    3rd party project configurations
+# :<project>-targets.cmake:        exported targets
+#
+# For ``<project>-import.cmake`` to be exported to build and install tree,
+# ``<project>-import.cmake`` or ``<project>-import.cmake.in`` must exist in
+# the source tree. ``<project>-config.cmake.in`` and
+# ``<project>-config-version.cmake.in`` can be provided in the source tree to
+# override the default templates used to generate ``<project>-config.cmake``
+# and ``<project>-config-version.cmake``.
+#
+# In DEVELOPER_MODE, the build tree location is also added to the CMake user
+# package registry for top level projects.
+#
+# If the project is added as a subdirectory, the following CMake variables
+# are set in the parent scope:
+#
+# :<PROJECT>_FOUND:            set to ``TRUE``
+# :<project>_FOUND:            set to ``TRUE``
+# :<PROJECT>_VERSION:          version string
+# :<project>_VERSION:          version string
+# :<PROJECT>_INCLUDE_DIRS:     list of include directories
+# :<PROJECT>_LIBRARIES:        list of libraries
+# :<PROJECT>_DEFINITIONS:      list of compiler definitions
+# :<PROJECT>_TPLS:             list of 3rd party dependencies
+# :<PROJECT>_TPL_LIBRARIES:    libraries of 3rd party dependencies
+# :<PROJECT>_TPL_DEFINITIONS:  compiler definitions of 3rd party dependencies
+# :<PROJECT>_TPL_INCLUDE_DIRS: include directories of 3rd party dependencies
+# :<PROJECT>_FEATURES:         list of enabled features
+# :<PROJECT>_HAVE_<FEATURE>:   set to 1 for each enabled features
+#
+##############################################################################
+
+function( ecbuild_set_if_not_defined VAR VALUE )
+
+    if(NOT DEFINED ${VAR})
+        set( ${VAR} "${VALUE}" PARENT_SCOPE )
+        # ecbuild_info("Variable not defined, setting ${VAR} => ${VALUE}")
+    # else()
+        # ecbuild_info("${VAR} == ${${VAR}}")
+    endif()
+
+endfunction()
+
+macro( ecbuild_install_project )
+
+    set( options )
+    set( single_value_args NAME DESCRIPTION )
+    set( multi_value_args  COMPONENTS )
+
+    cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+    if(_PAR_UNPARSED_ARGUMENTS)
+      ecbuild_critical("Unknown keywords given to ecbuild_install_project(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+    endif()
+
+    if( NOT _PAR_NAME  )
+      ecbuild_critical("The call to ecbuild_install_project() doesn't specify the NAME.")
+    endif()
+
+    ### EXTRA TARGETS #####################################################
+
+    # added here to avoid adding another macro call at the end of each project,
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        ecbuild_define_libs_and_execs_targets()
+        ecbuild_define_links_target()
+
+    endif()
+
+    ### PACKAGING ########################################################
+
+    set( PNAME ${PROJECT_NAME_CAPS} )
+    set( LNAME ${PROJECT_NAME_LOWCASE} )
+
+    # components
+
+    #    if( DEFINED _PAR_COMPONENTS )
+    #        set(CPACK_COMPONENTS_ALL   "${_PAR_COMPONENTS}")
+    #    else()
+    #        set(CPACK_COMPONENTS_ALL   "${PROJECT_NAME}")
+    #    endif()
+
+    # name, version, etc ...
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_NAME      "${_PAR_NAME}")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VERSION   "${${PNAME}_VERSION_STR}")
+    # Convert "/" to "-" for the case where the version string contains a "/"
+    string( REPLACE "/" "-" CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION} )
+
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_FILE_NAME   "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
+
+    #    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ECMWF") # required for DEB
+    #    set(CPACK_ARCHIVE_COMPONENT_INSTALL "ON")
+    #    set(CPACK_RPM_COMPONENT_INSTALL "ON")
+
+    ecbuild_set_if_not_defined(CPACK_SOURCE_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_GENERATOR "TGZ")
+    ecbuild_set_if_not_defined(CPACK_PACKAGE_VENDOR "ECMWF")
+
+    # short description
+
+    if( _PAR_DESCRIPTION )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_DESCRIPTION}" )
+    else()
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${_PAR_NAME} misses a description" )
+    endif()
+
+    # long description
+
+    if( EXISTS ${PROJECT_SOURCE_DIR}/INSTALL )
+        ecbuild_set_if_not_defined(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/INSTALL")
+    endif()
+    if( EXISTS ${PROJECT_SOURCE_DIR}/LICENSE )
+        ecbuild_set_if_not_defined(CPACK_RESOURCE_FILE_LICENSE    "${PROJECT_SOURCE_DIR}/LICENSE")
+    endif()
+
+    # set(CPACK_PACKAGE_EXECUTABLES ${ECBUILD_ALL_EXES})
+
+    list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES
+          "${PROJECT_SOURCE_DIR}" "."
+          "${ECBUILD_MACROS_DIR}" "cmake/" )
+
+    # what to pack and not
+
+    set(CPACK_SOURCE_IGNORE_FILES
+        /build/
+        /\\\\.git/
+        /\\\\.svn/
+        CMakeLists.txt.user
+        \\\\.swp$
+        p4config
+    )
+
+    # skip the files that were declared as DONT_PACK
+
+    list( APPEND CPACK_SOURCE_IGNORE_FILES ${ECBUILD_DONT_PACK_FILES} )
+
+    # Find the ecbuild toolchain files and include in the source package if found
+    find_path( ECBUILD_TOOLCHAIN_DIR ecmwf-XC30-GNU.cmake
+               PATHS ${ECBUILD_MACROS_DIR}/../toolchains
+                     ${ECBUILD_MACROS_DIR}/../share/ecbuild/toolchains )
+
+    mark_as_advanced( ECBUILD_TOOLCHAIN_DIR )
+
+    if( ECBUILD_TOOLCHAIN_DIR )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_TOOLCHAIN_DIR}" "share/ecbuild/toolchains/" )
+    endif()
+
+    # Find the ecbuild bin directory and include in the source package if found
+    find_program( ECBUILD_SCRIPT ecbuild
+                  PATHS ${ECBUILD_MACROS_DIR}/../bin
+                        ${ECBUILD_MACROS_DIR}/../../../bin )
+
+    mark_as_advanced( ECBUILD_SCRIPT )
+
+    if( ECBUILD_SCRIPT )
+      get_filename_component( ECBUILD_BIN_DIR ${ECBUILD_SCRIPT} PATH )
+      list( APPEND CPACK_SOURCE_INSTALLED_DIRECTORIES "${ECBUILD_BIN_DIR}" "bin/" )
+    endif()
+
+    # cpack config file
+
+    # set(CPACK_INSTALL_CMAKE_PROJECTS "${${PROJECT_NAME}_BINARY_DIR}" "${PROJECT_NAME}" "${CPACK_COMPONENTS_ALL}" "*" )
+
+    include( CPack )
+
+    ### EXPORTS ########################################################
+
+    ecbuild_enabled_features( ${PROJECT_NAME_CAPS}_FEATURES )
+    foreach( _f ${${PNAME}_FEATURES} )
+        set( ${PNAME}_HAVE_${_f} 1 )
+    endforeach()
+
+    ecbuild_info( "${PROJECT_NAME_CAPS}_TPLS: ${${PROJECT_NAME_CAPS}_TPLS}" )
+
+    foreach( _tpl ${${PNAME}_TPLS} )
+        string( TOUPPER ${_tpl} _TPL )
+
+        if( ${_tpl}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIRS} )
+        elseif( ${_tpl}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_tpl}_INCLUDE_DIR} )
+        elseif( ${_TPL}_INCLUDE_DIRS )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIRS} )
+        elseif( ${_TPL}_INCLUDE_DIR )
+            list( APPEND ${PNAME}_TPL_INCLUDE_DIRS ${${_TPL}_INCLUDE_DIR} )
+        endif()
+
+        if( ${_tpl}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARIES} )
+        elseif( ${_tpl}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_tpl}_LIBRARY} )
+        elseif( ${_TPL}_LIBRARIES )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARIES} )
+        elseif( ${_TPL}_LIBRARY )
+            list( APPEND ${PNAME}_TPL_LIBRARIES   ${${_TPL}_LIBRARY} )
+        endif()
+
+        if( ${_tpl}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_tpl}_DEFINITIONS} )
+        elseif( ${_TPL}_DEFINITIONS )
+            list( APPEND ${PNAME}_TPL_DEFINITIONS ${${_TPL}_DEFINITIONS} )
+        endif()
+    endforeach()
+
+    # Deduplicate TPL includes, libs and definitions
+    # The same TPL may indirectly be pulled in multiple times!
+    if( ${PNAME}_TPL_INCLUDE_DIRS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_INCLUDE_DIRS )
+    endif()
+    if( ${PNAME}_TPL_LIBRARIES )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_LIBRARIES )
+    endif()
+    if( ${PNAME}_TPL_DEFINITIONS )
+      list( REMOVE_DUPLICATES ${PNAME}_TPL_DEFINITIONS )
+    endif()
+
+    # Generate the project .cmake config files
+    # All variables here must be (sub)project specific in order to work within bundles
+    if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+        set( _template_config "${ECBUILD_MACROS_DIR}/project-config.cmake.in" )
+        if( EXISTS ${LNAME}-config.cmake.in )
+            set( _template_config "${LNAME}-config.cmake.in" )
+        endif()
+
+        set( _template_config_version "${ECBUILD_MACROS_DIR}/project-config-version.cmake.in" )
+        if( EXISTS ${LNAME}-config-version.cmake.in )
+            set( _template_config_version "${LNAME}-config-version.cmake.in" )
+        endif()
+
+        # project-config-version.cmake -- format ([0-9]+).([0-9]+).([0-9]+)
+
+        set( PACKAGE_VERSION        "${${PNAME}_VERSION}" )
+        set( PACKAGE_GIT_SHA1       "${${PNAME}_GIT_SHA1}" )
+        set( PACKAGE_GIT_SHA1_SHORT "${${PNAME}_GIT_SHA1_SHORT}" )
+
+        configure_file( "${_template_config_version}" "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" @ONLY )
+
+        install( FILES "${PROJECT_BINARY_DIR}/${LNAME}-config-version.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # prepare imutable variables (don't depend on install path)
+
+        if( ${PNAME}_FEATURES )
+          set( CONF_FEATURES ${${PNAME}_FEATURES} )
+        endif()
+
+        set( CONF_LIBRARIES ${${PROJECT_NAME}_ALL_LIBS} )
+        if( ${PNAME}_LIBRARIES )
+            set( CONF_LIBRARIES ${${PNAME}_LIBRARIES} )
+        endif()
+
+        set( CONF_DEFINITIONS "" )
+        if( ${PNAME}_DEFINITIONS )
+           set( CONF_DEFINITIONS ${${PNAME}_DEFINITIONS} )
+        endif()
+
+        set( CONF_TPL_LIBRARIES   "" )
+        if( ${PNAME}_TPL_LIBRARIES )
+           set( CONF_TPL_LIBRARIES ${${PNAME}_TPL_LIBRARIES} )
+        endif()
+
+        set( CONF_TPLS ${${PNAME}_TPLS} )
+
+        set( CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}" )
+        if( ${PNAME}_INCLUDE_DIRS )
+            set( CONF_INCLUDE_DIRS ${${PNAME}_INCLUDE_DIRS} )
+        endif()
+
+        set( CONF_TPL_INCLUDE_DIRS "" )
+        if( ${PNAME}_TPL_INCLUDE_DIRS )
+            set( CONF_TPL_INCLUDE_DIRS ${${PNAME}_TPL_INCLUDE_DIRS} )
+        endif()
+
+        # Generate <project>-import.cmake (if it exists)
+
+        set( CONF_IMPORT_FILE "${LNAME}-import.cmake" )
+
+        # If <project>-import.cmake.in exist in source tree, configure it to
+        # the build tree and install the configured version
+        if( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in - configuring to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}.in"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" @ONLY )
+          install( FILES "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        # Otherwise, if <project>-import.cmake exist in source tree, copy it to
+        # the build tree and install it
+        elseif( EXISTS "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}" )
+          ecbuild_debug( "Found ${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE} - copying to ${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" )
+          configure_file( "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                          "${PROJECT_BINARY_DIR}/${CONF_IMPORT_FILE}" COPYONLY )
+          install( FILES "${PROJECT_SOURCE_DIR}/${CONF_IMPORT_FILE}"
+                   DESTINATION "${INSTALL_CMAKE_DIR}" )
+        else()
+          ecbuild_debug( "No ${CONF_IMPORT_FILE} found in ${PROJECT_SOURCE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use from the build tree
+
+        set( _lname_config "${PROJECT_BINARY_DIR}/${LNAME}-config.cmake")
+
+        # Include directories (may) reference source and build tree and the
+        # config file is marked as coming from a build tree
+        set( _is_build_dir_export ON )
+        configure_file( "${_template_config}" "${_lname_config}" @ONLY )
+
+        # Generate <project>-config.cmake.tpls (if there are any TPLs)
+
+        file( REMOVE ${_lname_config}.tpls.in )
+
+        foreach( _tpl ${${PNAME}_TPLS} )
+
+            string( TOUPPER ${_tpl} TPL )
+
+            if( ${TPL}_IMPORT_FILE ) # ecBuild packages should trigger this if they export themselves
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_IMPORT_FILE}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_IMPORT_FILE )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            elseif( ${TPL}_CONFIG ) # cmake built packages (e.g. CGAL) may have exported their targets
+
+              ecbuild_debug( "Adding TPL ${TPL} import file to ${_lname_config}.tpls.in" )
+                set( __import_file "${${TPL}_CONFIG}" )
+                file( APPEND "${_lname_config}.tpls.in" "if( NOT ${TPL}_CONFIG )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    include( \"${__import_file}\" OPTIONAL )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "    set( ${TPL}_CONFIG \"${__import_file}\" )\n" )
+                file( APPEND "${_lname_config}.tpls.in" "endif()\n" )
+
+            endif()
+
+        endforeach()
+
+        if( EXISTS "${_lname_config}.tpls.in" )
+            configure_file( "${_lname_config}.tpls.in" "${_lname_config}.tpls" @ONLY )
+            install( FILES "${_lname_config}.tpls" DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+        # Generate <project>-config.cmake for use in the install tree
+
+        # Compute path to the include dir relative to the project's CMake dir
+        # where <project>-config.cmake is installed to
+        file( RELATIVE_PATH REL_INCLUDE_DIR "${${PNAME}_FULL_INSTALL_CMAKE_DIR}" "${${PNAME}_FULL_INSTALL_INCLUDE_DIR}" )
+        set( CONF_INCLUDE_DIRS "\${${PNAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" )
+
+        set( _is_build_dir_export OFF )
+        configure_file( "${_template_config}" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" @ONLY )
+        install( FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${LNAME}-config.cmake" DESTINATION "${INSTALL_CMAKE_DIR}" )
+
+        # install the export
+
+        if( ${PROJECT_NAME}_ALL_EXES OR ${PROJECT_NAME}_ALL_LIBS )
+            install( EXPORT ${PROJECT_NAME}-targets
+                     DESTINATION "${INSTALL_CMAKE_DIR}" )
+        endif()
+
+    endif()  # if ( NOT ECBUILD_SKIP_${PNAME}_EXPORT )
+
+    # exports the package for use from the build-tree but only in DEVELOPER_MODE
+    # inserts <package> into the CMake user package registry
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+        if( DEVELOPER_MODE )
+            export( PACKAGE ${PROJECT_NAME} )
+        endif()
+
+    else()
+
+    # export variables for upper projects
+
+        set( ${PNAME}_FOUND             TRUE                          PARENT_SCOPE )
+        set( ${PROJECT_NAME}_FOUND      TRUE                          PARENT_SCOPE )
+        set( ${PNAME}_VERSION           ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1          ${${PNAME}_GIT_SHA1}          PARENT_SCOPE )
+        set( ${PNAME}_GIT_SHA1_SHORT    ${${PNAME}_GIT_SHA1_SHORT}    PARENT_SCOPE )
+        set( ${PROJECT_NAME}_VERSION    ${${PNAME}_VERSION}           PARENT_SCOPE )
+        set( ${PNAME}_INCLUDE_DIRS      ${${PNAME}_INCLUDE_DIRS}      PARENT_SCOPE )
+        set( ${PNAME}_LIBRARIES         ${${PNAME}_LIBRARIES}         PARENT_SCOPE )
+        set( ${PNAME}_DEFINITIONS       ${${PNAME}_DEFINITIONS}       PARENT_SCOPE )
+        set( ${PNAME}_PACKAGES          ${${PNAME}_PACKAGES}          PARENT_SCOPE )
+        set( ${PNAME}_TPLS              ${${PNAME}_TPLS}              PARENT_SCOPE )
+        set( ${PNAME}_TPL_LIBRARIES     ${${PNAME}_TPL_LIBRARIES}     PARENT_SCOPE )
+        set( ${PNAME}_TPL_DEFINITIONS   ${${PNAME}_TPL_DEFINITIONS}   PARENT_SCOPE )
+        set( ${PNAME}_TPL_INCLUDE_DIRS  ${${PNAME}_TPL_INCLUDE_DIRS}  PARENT_SCOPE )
+        set( ${PNAME}_FEATURES          ${${PNAME}_FEATURES}          PARENT_SCOPE )
+        foreach( _f ${${PNAME}_FEATURES} )
+            set( ${PNAME}_HAVE_${_f} ${${PNAME}_HAVE_${_f}} PARENT_SCOPE )
+        endforeach()
+
+    endif()
+
+endmacro( ecbuild_install_project )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_list_add_pattern.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_list_add_pattern.cmake
new file mode 100644
index 0000000..a0f9f34
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_list_add_pattern.cmake
@@ -0,0 +1,102 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_add_pattern
+# ========================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_add_pattern( LIST <input_list>
+#                             GLOB <pattern1> [ <pattern2> ... ]
+#                             [ SOURCE_DIR <source_dir> ]
+#                             [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be appended to
+#
+# GLOB : required
+#   Regex pattern of exclusion
+#
+# SOURCE_DIR : optional
+#   Directory from where to start search
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_add_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST SOURCE_DIR )
+  set( multi_value_args  GLOB )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_add_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_GLOB )
+    ecbuild_critical("The call to ecbuild_list_add_pattern() doesn't specify the GLOB.")
+  endif()
+
+  #####
+
+  set( input_list ${${_p_LIST}} )
+  unset( matched_files )
+
+  foreach( pattern ${_p_GLOB} )
+
+    if( IS_ABSOLUTE ${pattern} )
+      ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern}" )
+      file( GLOB_RECURSE matched_files ${pattern} )
+    else()
+
+      if(_p_SOURCE_DIR)
+        if( IS_ABSOLUTE ${_p_SOURCE_DIR} )
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files ${_p_SOURCE_DIR}/${pattern} )
+        else()
+          ecbuild_debug( "ecbuild_list_add_pattern: Adding ${_p_SOURCE_DIR}/${pattern}" )
+          file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_p_SOURCE_DIR}/${pattern} )
+        endif()
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern: Adding ${pattern} ")
+        file( GLOB_RECURSE matched_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${pattern} )
+      endif()
+
+    endif()
+
+    if(matched_files)
+      ecbuild_debug( "ecbuild_list_add_pattern: Found ${matched_files}" )
+      list( APPEND input_list ${matched_files} )
+      list( REMOVE_DUPLICATES input_list )
+      set( ${_p_LIST} ${input_list} PARENT_SCOPE )
+    else()
+      if(NOT _p_QUIET)
+        ecbuild_warn( "ecbuild_list_add_pattern: no matches found for patterns ${pattern}" )
+      else()
+        ecbuild_debug( "ecbuild_list_add_pattern:no matches found for patterns ${pattern}" )
+      endif()
+    endif()
+
+  endforeach()
+
+
+endfunction(ecbuild_list_add_pattern)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake
new file mode 100644
index 0000000..bfb6ee8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_list_exclude_pattern.cmake
@@ -0,0 +1,88 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_list_exclude_pattern
+# ============================
+#
+# Exclude items from a list that match a list of patterns. ::
+#
+#   ecbuild_list_exclude_pattern( LIST <input_list>
+#                                 REGEX <regex1> [ <regex2> ... ]
+#                                 [ QUIET ] )
+#
+# Options
+# -------
+#
+# LIST : required
+#   list variable to be cleaned
+#
+# REGEX : required
+#   Regex pattern of exclusions
+#
+# QUIET  : optional
+#   Don't warn if patterns don't match
+#
+##############################################################################
+
+function( ecbuild_list_exclude_pattern )
+
+  set( options QUIET )
+  set( single_value_args LIST )
+  set( multi_value_args  REGEX )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_list_exclude_pattern(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_LIST  )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the LIST.")
+  endif()
+
+  if( NOT _p_REGEX )
+    ecbuild_critical("The call to ecbuild_list_exclude_pattern() doesn't specify the REGEX.")
+  endif()
+
+  #####
+
+  set( result "" )
+  set( matches_found 0 )
+
+  # ecbuild_debug_var(_p_REGEX)
+
+  foreach( item ${${_p_LIST}} )
+
+    set( _keep 1 )
+
+    foreach( pattern ${_p_REGEX} )
+        if( ${item} MATCHES ${pattern} )
+            set( _keep 0 )
+            set( matches_found 1 )
+        endif()
+    endforeach()
+    if( _keep )
+        list( APPEND result ${item} )
+#    else()
+#      ecbuild_warn( "removing ${item}" )
+    endif()
+
+  endforeach()
+
+  if( matches_found )
+      set( ${_p_LIST} ${result} PARENT_SCOPE )
+  else()
+    if( NOT _p_QUIET )
+        ecbuild_warn( "ecbuild_list_exclude_pattern: no matches found for patterns ${_p_REGEX} in ${_p_LIST}" )
+    endif()
+  endif()
+
+endfunction(ecbuild_list_exclude_pattern)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake
new file mode 100644
index 0000000..1ba31bc
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_list_extra_search_paths.cmake
@@ -0,0 +1,81 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+############################################################################################
+#
+# macro for adding search paths for a package to a given CMake variable
+#
+# usage: ecbuild_list_extra_search_paths( netcdf4 VARIABLE )
+
+function( ecbuild_list_extra_search_paths pkg var )
+
+  ecbuild_deprecate( " ecbuild_list_extra_search_paths should no longer be"
+                     " used and is going to be removed in a future version of ecBuild." )
+
+	# ecbuild_debug_var( pkg )
+	# ecbuild_debug_var( var )
+
+	string( TOUPPER ${pkg} _PKG )
+
+	# PKG_PATH (upper case)
+
+	if( DEFINED ${_PKG}_PATH AND EXISTS ${${_PKG}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${_PKG}_PATH = ${${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} ${${_PKG}_PATH} )
+	endif()
+
+	# ENV PKG_PATH (upper case)
+
+	if( DEFINED ENV{${_PKG}_PATH} AND EXISTS $ENV{${_PKG}_PATH}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_PATH = $ENV{${_PKG}_PATH} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_PATH} )
+	endif()
+
+	# pkg_PATH (lower case)
+
+	if( DEFINED ${pkg}_PATH AND EXISTS ${${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending ${pkg}_PATH = ${${pkg}_PATH} to ${var}")
+		list( APPEND ${var} ${${pkg}_PATH} )
+	endif()
+
+	# ENV pkg_PATH (lower case)
+
+  if( DEFINED ${pkg}_PATH AND EXISTS $ENV{${pkg}_PATH} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_PATH = $ENV{${pkg}_PATH} to ${var}")
+    list( APPEND ${var} $ENV{${pkg}_PATH} )
+	endif()
+
+	# ENV PKG_DIR (upper case)
+
+	if( DEFINED ENV{${_PKG}_DIR} AND EXISTS $ENV{${_PKG}_DIR}  )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${_PKG}_DIR = $ENV{${_PKG}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${_PKG}_DIR} )
+	endif()
+
+	# ENV pkg_DIR (lower case)
+
+	if( DEFINED ENV{${pkg}_DIR} AND EXISTS $ENV{${pkg}_DIR} )
+    ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): appending \$${pkg}_DIR = $ENV{${pkg}_DIR} to ${var}")
+		list( APPEND ${var} $ENV{${pkg}_DIR} )
+	endif()
+
+	# sanitize the list
+
+	if( ${var} )
+		list( REMOVE_DUPLICATES ${var} )
+	endif()
+
+	# define it out of the function
+
+  ecbuild_debug("ecbuild_list_extra_search_paths(${pkg}): setting ${var} to ${${var}}")
+	set( ${var} ${${var}} PARENT_SCOPE )
+
+# ecbuild_debug_var( ${var} )
+
+endfunction()
+
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_list_macros.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_list_macros.cmake
new file mode 100644
index 0000000..232e5e8
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_list_macros.cmake
@@ -0,0 +1,58 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+# function for concatenating list into a string
+#
+# examples:
+#
+#   set( _paths "foo" "bar" )
+#   join( _paths "/" _mypath )
+#
+#   message( "${_mpath}" ) #  produces "foo/bar"
+
+function( JOIN _listname _glue _output )
+
+	set( _ret "" )
+
+	foreach( _v ${${_listname}} )
+		if( _ret )
+			set(_ret "${_ret}${_glue}${_v}") # append
+		else()
+			set(_ret "${_v}") # init
+		endif()
+	endforeach()
+
+	set(${_output} "${_ret}" PARENT_SCOPE)
+
+endfunction()
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_insert( "mymap" "foo" "bar" )
+#
+
+function( MAP_INSERT _map _key _value )
+	set( "_${_map}_${_key}" "${_value}" PARENT_SCOPE )
+endfunction(MAP_INSERT)
+
+##############################################################################
+# function for inserting a key / value into a map
+#
+# examples:
+#
+#   map_get( "mymap" "foo" VAR )
+#
+
+function( MAP_GET _map _key _var )
+	set( ${_var} "${_${_map}_${_key}}" PARENT_SCOPE )
+endfunction(MAP_GET)
+
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_log.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_log.cmake
new file mode 100644
index 0000000..57ebf0d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_log.cmake
@@ -0,0 +1,249 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# Logging
+# =======
+#
+# ecBuild provides functions for logging based on a log level set by the user,
+# similar to the Python logging module:
+#
+# :ecbuild_debug:     logs a ``STATUS`` message if log level <= ``DEBUG``
+# :ecbuild_info:      logs a ``STATUS`` message if log level <= ``INFO``
+# :ecbuild_warn:      logs a ``WARNING`` message if log level <= ``WARN``
+# :ecbuild_error:     logs a ``SEND_ERROR`` message if log level <= ``ERROR``
+# :ecbuild_critical:  logs a ``FATAL_ERROR`` message if log level <= ``CRITICAL``
+# :ecbuild_deprecate: logs a ``DEPRECATION`` message as a warning
+#                     enable CMAKE_ERROR_DEPRECATED to raise an error instead
+#                     disable CMAKE_WARN_DEPRECATED to hide deprecations
+#
+# Furthermore there are auxilliary functions for outputting CMake variables,
+# CMake lists and environment variables if the log level is ``DEBUG``:
+#
+# :ecbuild_debug_var:      logs given CMake variables if log level <= ``DEBUG``
+# :ecbuild_debug_list:     logs given CMake lists if log level <= ``DEBUG``
+# :ecbuild_debug_env_var:  logs given environment variables if log level <= ``DEBUG``
+# :ecbuild_debug_property: logs given global CMake property if log level <= ``DEBUG``
+#
+# To log a message to the ecBuild log file only at a given log level, use ::
+#
+#   ecbuild_log( <level> <msg> )
+#
+# Input variables
+# ---------------
+#
+# CMake variables controlling logging behaviour:
+#
+# ECBUILD_LOG_FILE : path
+#   set the log file, defaults to ``${CMAKE_BINARY_DIR}/ecbuild.log``
+#
+#   All ecBuild log functions write their messages to this log file with a time
+#   stamp. Messages emitted by CMake directly cannot be logged to file.
+#
+# ECBUILD_LOG_LEVEL : string, one of DEBUG, INFO, WARN, ERROR, CRITICAL, OFF
+#   desired log level, defaults to ``INFO``, ``OFF`` to disable logging
+#
+# ECBUILD_NO_COLOUR : bool
+#   if set, does not colour log output (by default log output is coloured)
+#
+# Usage
+# -----
+#
+# The functions ``ecbuild_debug`` and ``ecbuild_info`` can be used to output
+# messages which are not printed by default. Many ecBuild macros use this
+# facility to log debugging hints. When debugging a CMake run, users can use
+# ``-DECBUILD_LOG_LEVEL=DEBUG`` to get detailed diagnostics.
+#
+##############################################################################
+
+# Define colour escape sequences (not available on Windows)
+if(NOT (WIN32 OR ECBUILD_NO_COLOUR))
+  string(ASCII 27 Esc)
+  set(ColourReset "${Esc}[m")
+  set(ColourBold  "${Esc}[1m")
+  set(Red         "${Esc}[31m")
+  set(Green       "${Esc}[32m")
+  set(Yellow      "${Esc}[33m")
+  set(Blue        "${Esc}[34m")
+  set(Magenta     "${Esc}[35m")
+  set(Cyan        "${Esc}[36m")
+  set(White       "${Esc}[37m")
+  set(BoldRed     "${Esc}[1;31m")
+  set(BoldGreen   "${Esc}[1;32m")
+  set(BoldYellow  "${Esc}[1;33m")
+  set(BoldBlue    "${Esc}[1;34m")
+  set(BoldMagenta "${Esc}[1;35m")
+  set(BoldCyan    "${Esc}[1;36m")
+  set(BoldWhite   "${Esc}[1;37m")
+endif()
+
+set(ECBUILD_DEBUG    10)
+set(ECBUILD_INFO     20)
+set(ECBUILD_WARN     30)
+set(ECBUILD_ERROR    40)
+set(ECBUILD_CRITICAL 50)
+
+if( NOT DEFINED ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( NOT ECBUILD_LOG_LEVEL )
+  set(ECBUILD_LOG_LEVEL 60)
+elseif( ECBUILD_LOG_LEVEL STREQUAL "DEBUG" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_DEBUG})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "INFO" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_INFO})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "WARN" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "ERROR" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_ERROR})
+elseif( ECBUILD_LOG_LEVEL STREQUAL "CRITICAL" )
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_CRITICAL})
+else()
+  message(WARNING "Unknown log level ${ECBUILD_LOG_LEVEL} (valid are DEBUG, INFO, WARN, ERROR, CRITICAL) - using WARN")
+  set(ECBUILD_LOG_LEVEL ${ECBUILD_WARN})
+endif()
+
+if( NOT DEFINED ECBUILD_LOG_FILE )
+  set( ECBUILD_LOG_FILE ${CMAKE_BINARY_DIR}/ecbuild.log )
+endif()
+if( NOT DEFINED CMAKE_ERROR_DEPRECATED AND NOT DEFINED CMAKE_WARN_DEPRECATED )
+  set( CMAKE_WARN_DEPRECATED ON )
+endif()
+
+##############################################################################
+
+function( ecbuild_log LEVEL )
+  string( REPLACE ";" " " MSG "${ARGN}" )
+  string( TIMESTAMP _time )
+  file( APPEND ${ECBUILD_LOG_FILE} "${_time} - ${PROJECT_NAME} - ${LEVEL} - ${MSG}\n" )
+endfunction( ecbuild_log )
+
+##############################################################################
+
+function( ecbuild_debug )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(DEBUG "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    message(STATUS "${Blue}DEBUG - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_debug )
+
+##############################################################################
+
+function( ecbuild_info )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(INFO "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 21)
+    message(STATUS "${MSG}")
+  endif()
+endfunction( ecbuild_info )
+
+##############################################################################
+
+function( ecbuild_warn )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(WARNING "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 31)
+    message(WARNING "${Yellow}WARN - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_warn )
+
+##############################################################################
+
+function( ecbuild_error )
+  string( REPLACE ";" " " MSG "${ARGV}" )
+  ecbuild_log(ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 41)
+    message(SEND_ERROR "${BoldRed}ERROR - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_error )
+
+##############################################################################
+
+function( ecbuild_deprecate )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(DEPRECATION "${MSG}")
+  # DEPRECATION message type was only introduced in CMake 3.0, provide
+  # consistent behaviour for CMake < 3.0
+  if( CMAKE_VERSION VERSION_LESS 3.0 )
+    if( CMAKE_ERROR_DEPRECATED )
+      message(FATAL_ERROR "${BoldRed}DEPRECATION - ${MSG}${ColourReset}")
+    elseif( CMAKE_WARN_DEPRECATED )
+      message(WARNING "${Yellow}DEPRECATION - ${MSG}${ColourReset}")
+    endif()
+  else()
+    message(DEPRECATION "${BoldRed}${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_deprecate )
+
+##############################################################################
+
+function( ecbuild_critical )
+  string(REPLACE ";" " " MSG ${ARGV})
+  ecbuild_log(FATAL_ERROR "${MSG}")
+  if( ECBUILD_LOG_LEVEL LESS 51)
+    message(FATAL_ERROR "${BoldMagenta}CRITICAL - ${MSG}${ColourReset}")
+  endif()
+endfunction( ecbuild_critical )
+
+##############################################################################
+# function for debugging CMake variables
+
+function( ecbuild_debug_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ${VAR} : ${${VAR}}${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging CMake lists
+
+function( ecbuild_debug_list )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "${VAR} : ${${VAR}}")
+    foreach( _elem ${${VAR}} )
+      ecbuild_log( DEBUG "  ${_elem}" )
+    endforeach()
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message( STATUS "${Blue}DEBUG - ${VAR}" )
+      foreach( _elem ${${VAR}} )
+        message( STATUS "  ${_elem}" )
+      endforeach()
+      message(STATUS "${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging environment variables
+
+function( ecbuild_debug_env_var )
+  foreach( VAR ${ARGV} )
+    ecbuild_log(DEBUG "ENV ${VAR} : $ENV{${VAR}}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - ENV ${VAR} [$ENV{${VAR}}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
+
+##############################################################################
+# function for debugging a CMake global property
+
+function( ecbuild_debug_property )
+  foreach( VAR ${ARGV} )
+    get_property( __prop GLOBAL PROPERTY ${VAR} )
+    ecbuild_log(DEBUG "PROPERTY ${VAR} : ${__prop}")
+    if( ECBUILD_LOG_LEVEL LESS 11)
+      message(STATUS "${Blue}DEBUG - PROPERTY ${VAR} [${__prop}]${ColourReset}")
+    endif()
+  endforeach()
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_pkgconfig.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_pkgconfig.cmake
new file mode 100644
index 0000000..5ae273e
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_pkgconfig.cmake
@@ -0,0 +1,421 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+
+# Write transitive list of library dependencies of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_library_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    unset( _location )
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      set( _imported 0 )
+      get_property( _imported TARGET ${_lib} PROPERTY IMPORTED )
+
+      unset( _deps )
+
+      if( _imported )
+
+        get_property( _location TARGET ${_lib} PROPERTY LOCATION )
+        get_property( _configs   TARGET ${_lib} PROPERTY IMPORTED_CONFIGURATIONS )
+        list( REVERSE _configs )
+        list( GET _configs 0 _config)
+        get_property( _deps     TARGET ${_lib} PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES_${_config} )
+        get_property( _locimp   TARGET ${_lib} PROPERTY IMPORTED_LOCATION_${_config} )
+
+      else()
+
+        list( APPEND _location ${_lib} )
+        get_property( _deps TARGET ${_lib} PROPERTY LINK_LIBRARIES )
+
+      endif()
+
+      ecbuild_library_dependencies( _deps_location _deps )
+      list( APPEND _location ${_deps_location} )
+
+    else()
+
+      set( _location ${_lib} )
+
+    endif()
+
+    list( APPEND _dependencies ${_location} )
+
+  endforeach()
+
+  if( _dependencies )
+    list( REVERSE           _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    list( REVERSE           _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_library_dependencies)
+
+##############################################################################
+
+# Write list of include directories of each library in ${libraries}
+# to CMake variable ${dependencies}
+function( ecbuild_include_dependencies dependencies libraries )
+
+  set( _libraries ${${libraries}} )
+
+  foreach( _lib ${_libraries})
+
+    if( TARGET ${_lib} ) # check if this is an existing target
+
+      get_property( _include_dirs TARGET ${_lib} PROPERTY INCLUDE_DIRECTORIES )
+      list( APPEND _dependencies ${_include_dirs} )
+
+    endif()
+
+  endforeach()
+
+  if( _dependencies )
+    list( REMOVE_DUPLICATES _dependencies )
+    set( ${dependencies} ${_dependencies} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_include_dependencies)
+
+##############################################################################
+
+# Transform list of libraries in ${libraries}, ignoring any in ${ignore_libs},
+# and write pkg-config compatible string to CMake variable ${pkgconfig_libs}
+function( ecbuild_pkgconfig_libs pkgconfig_libs libraries ignore_libs )
+
+  set( _libraries ${${libraries}} )
+  set( _ignore_libs ${${ignore_libs}} )
+
+  foreach( _lib ${_libraries} )
+
+    unset( _name )
+    unset( _dir  )
+
+    if( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+      get_filename_component( _name ${_lib} NAME_WE )
+      list( APPEND _pkgconfig_libs "-framework ${_name}" )
+
+    else()
+
+      if( ${_lib} MATCHES "-l.+" )
+
+        string( REGEX REPLACE "^-l" "" _name ${_lib} )
+
+      else()
+
+
+        get_filename_component( _name ${_lib} NAME_WE )
+        get_filename_component( _dir  ${_lib} PATH )
+
+        if( TARGET ${_lib} )
+          get_target_property( _name ${_lib} OUTPUT_NAME )
+        endif()
+        if( NOT _name )
+          set( _name ${_lib} )
+        endif()
+
+        string( REGEX REPLACE "^lib" "" _name ${_name} )
+
+        if( "${_dir}" STREQUAL "/usr/lib" )
+          unset( _dir )
+        endif()
+        if( "${_dir}" STREQUAL "/usr/lib64" )
+          unset( _dir )
+        endif()
+
+      endif()
+
+      set( _set_append TRUE )
+        foreach( _ignore ${_ignore_libs} )
+          if( "${_name}" STREQUAL "${_ignore}" )
+            set( _set_append FALSE )
+          endif()
+      endforeach()
+
+      if( _set_append )
+
+        if( _dir )
+          list( APPEND _pkgconfig_libs "-L${_dir}" "-l${_name}" )
+        else()
+          list( APPEND _pkgconfig_libs "-l${_name}" )
+        endif()
+
+      endif()
+
+    endif( ${_lib} MATCHES ".+/Frameworks/.+" )
+
+  endforeach( _lib ${_libraries} )
+
+  if( _pkgconfig_libs )
+    list( REMOVE_DUPLICATES _pkgconfig_libs )
+    string( REPLACE ";" " " _pkgconfig_libs "${_pkgconfig_libs}" )
+
+    set( ${pkgconfig_libs} ${_pkgconfig_libs} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_libs)
+
+##############################################################################
+
+# Transform list of include directories in ${INCLUDE_DIRS}, ignoring any in
+# ${ignore_includes} and ${${PNAME}_INCLUDE_DIRS}, and write pkg-config
+# compatible string to CMake variable ${INCLUDE}
+function( ecbuild_pkgconfig_include INCLUDE INCLUDE_DIRS ignore_includes )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+
+  set( _ignore_includes ${${ignore_includes}} )
+
+  list( APPEND ignore_include_dirs
+    "/usr/include"
+     ${${PNAME}_INCLUDE_DIRS} # These are build-directory includes
+     ${CMAKE_SOURCE_DIR}  # Ignore private includes referencing source tree
+     ${CMAKE_BINARY_DIR}  # Ignore private includes referencing build tree
+     ${_ignore_includes}
+  )
+
+  foreach( _incdir ${${INCLUDE_DIRS}} )
+
+    foreach( _ignore ${ignore_include_dirs} )
+      if( "${_incdir}" MATCHES "${_ignore}" )
+        unset( _incdir )
+        break()
+      endif()
+    endforeach()
+
+    if( _incdir )
+      list( APPEND _include "-I${_incdir}")
+    endif()
+
+  endforeach()
+
+  if( _include )
+    list( REMOVE_DUPLICATES _include)
+    string( REPLACE ";" " " _include "${_include}")
+    set( ${INCLUDE} ${_include} PARENT_SCOPE )
+  endif()
+
+endfunction(ecbuild_pkgconfig_include)
+
+##############################################################################
+#.rst:
+#
+# ecbuild_pkgconfig
+# =================
+#
+# Create a pkg-config file for the current project. ::
+#
+#   ecbuild_pkgconfig( [ NAME <name> ]
+#                      [ FILENAME <filename> ]
+#                      [ TEMPLATE <template> ]
+#                      [ URL <url> ]
+#                      [ DESCRIPTION <description> ]
+#                      [ LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ IGNORE_INCLUDE_DIRS <dir1> [ <dir2> ... ] ]
+#                      [ IGNORE_LIBRARIES <lib1> [ <lib2> ... ] ]
+#                      [ LANGUAGES <language1> [ <language2> ... ] ]
+#                      [ VARIABLES <variable1> [ <variable2> ... ] ]
+#                      [ NO_PRIVATE_INCLUDE_DIRS ] )
+#
+# Options
+# -------
+#
+# NAME : optional, defaults to lower case name of the project
+#   name to be given to the package
+#
+# FILENAME : optional, defaults to ``<NAME>.pc``
+#   file to be generated, including .pc extension
+#
+# TEMPLATE : optional, defaults to ``${ECBUILD_CMAKE_DIR}/pkg-config.pc.in``
+#   template configuration file to use
+#
+#   This is useful to create customised pkg-config files.
+#
+# URL : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_URL``
+#   url of the package
+#
+# DESCRIPTION : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_DESCRIPTION``
+#   description of the package
+#
+# LIBRARIES : optional, defaults to ``${UPPERCASE_PROJECT_NAME}_LIBRARIES``
+#   list of package libraries
+#
+# IGNORE_INCLUDE_DIRS : optional
+#   list of include directories to ignore
+#
+# IGNORE_LIBRARIES : optional
+#   list of libraries to ignore i.e. those are removed from ``LIBRARIES``
+#
+# VARIABLES : optional
+#   list of additional CMake variables to export to the pkg-config file
+#
+# LANGUAGES : optional, defaults to all loaded languages
+#   list of languages to use. Accepted languages: C CXX Fortran
+#
+# NO_PRIVATE_INCLUDE_DIRS
+#   do not add include directories of dependencies to Cflags
+#
+#   This is mainly useful for Fortran only packages, when only modules need
+#   to be added to Cflags.
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables are used as default values for some of the
+# options listed above, where ``PNAME`` is the project name in upper case:
+#
+# :<PNAME>_LIBRARIES:    list of libraries to export
+# :<PNAME>_DESCRIPTION:  package description
+# :<PNAME>_URL:          package URL
+# :<PNAME>_VERSION:      package version
+# :<PNAME>_GIT_SHA1:     Git revision
+#
+# Usage
+# -----
+#
+# It is good practice to provide a separate pkg-config file for each library a
+# package exports. This can be achieved as follows: ::
+#
+#   foreach( _lib ${${PNAME}_LIBRARIES} )
+#     if( TARGET ${_lib} )
+#       ecbuild_pkgconfig( NAME ${_lib}
+#                          DESCRIPTION "..."
+#                          URL "..."
+#                          LIBRARIES ${_lib} )
+#     endif()
+#   endforeach()
+#
+##############################################################################
+
+function( ecbuild_pkgconfig )
+
+  set( options REQUIRES NO_PRIVATE_INCLUDE_DIRS )
+  set( single_value_args FILENAME NAME TEMPLATE URL DESCRIPTION )
+  set( multi_value_args LIBRARIES IGNORE_INCLUDE_DIRS IGNORE_LIBRARIES VARIABLES LANGUAGES )
+
+  cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  string( TOUPPER ${PROJECT_NAME} PNAME )
+  string( TOLOWER ${PROJECT_NAME} LNAME )
+
+  if(_PAR_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_add_executable(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  unset( PKGCONFIG_LANGUAGES )
+  if( NOT _PAR_LANGUAGES )
+    if( CMAKE_C_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES C )
+    endif()
+    if( CMAKE_CXX_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES CXX )
+    endif()
+    if( CMAKE_Fortran_COMPILER_LOADED )
+      list( APPEND PKGCONFIG_LANGUAGES Fortran )
+    endif()
+  else()
+    foreach( _lang ${_PAR_LANGUAGES} )
+      if( CMAKE_${_lang}_COMPILER_LOADED )
+        list( APPEND PKGCONFIG_LANGUAGES ${_lang} )
+      endif()
+    endforeach()
+  endif()
+
+  foreach( _lang ${PKGCONFIG_LANGUAGES} )
+    set( PKGCONFIG_HAVE_${_lang} 1 )
+  endforeach()
+
+  set( LIBRARIES ${${PNAME}_LIBRARIES} )
+  if( _PAR_LIBRARIES )
+    set( LIBRARIES ${_PAR_LIBRARIES} )
+  endif()
+
+  if( CMAKE_CXX_COMPILER_LOADED )
+   set( _linker_lang CXX )
+  elseif( CMAKE_C_COMPILER_LOADED )
+   set( _linker_lang C )
+  elseif( CMAKE_Fortran_COMPILER_LOADED )
+   set( _linker_lang Fortran )
+  endif()
+
+  set( RPATH_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_${_linker_lang}_FLAG} )
+
+  set( PKGCONFIG_MOD_FLAG ${CMAKE_Fortran_MODPATH_FLAG} )
+
+  if( NOT PKGCONFIG_MOD_FLAG )
+    set( PKGCONFIG_MOD_FLAG "-I" )
+  endif()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS LIBRARIES _PAR_IGNORE_LIBRARIES )
+
+  ecbuild_library_dependencies( _libraries LIBRARIES )
+  foreach( _lib ${LIBRARIES} )
+    list( REMOVE_ITEM _libraries ${_lib} )
+  endforeach()
+
+  ecbuild_pkgconfig_libs( PKGCONFIG_LIBS_PRIVATE _libraries _PAR_IGNORE_LIBRARIES )
+
+  if( NOT _PAR_NO_PRIVATE_INCLUDE_DIRS )
+    ecbuild_include_dependencies( _include_dirs LIBRARIES )
+    ecbuild_pkgconfig_include( PKGCONFIG_CFLAGS _include_dirs _PAR_IGNORE_INCLUDE_DIRS )
+  endif()
+
+  set( PKGCONFIG_INCLUDE "-I\${includedir}" )
+  if( PKGCONFIG_HAVE_Fortran )
+    set( PKGCONFIG_INCLUDE "${PKGCONFIG_INCLUDE} ${PKGCONFIG_MOD_FLAG}\${fmoddir}" )
+  endif()
+
+  if( NOT _PAR_TEMPLATE )
+    set( _PAR_TEMPLATE "${ECBUILD_MACROS_DIR}/pkg-config.pc.in" )
+  endif()
+
+  set( PKGCONFIG_NAME ${LNAME} )
+  if( _PAR_NAME )
+    set( PKGCONFIG_NAME ${_PAR_NAME} )
+  endif()
+
+  if( NOT _PAR_FILENAME )
+    set( _PAR_FILENAME "${PKGCONFIG_NAME}.pc" )
+  endif()
+
+  set( PKGCONFIG_DESCRIPTION ${${PNAME}_DESCRIPTION} )
+  if( _PAR_DESCRIPTION )
+    set( PKGCONFIG_DESCRIPTION ${_PAR_DESCRIPTION} )
+  endif()
+
+  set( PKGCONFIG_URL ${${PNAME}_URL} )
+  if( _PAR_URL )
+    set( PKGCONFIG_URL ${_PAR_URL} )
+  endif()
+
+  set( PKGCONFIG_VERSION ${${PNAME}_VERSION} )
+  set( PKGCONFIG_GIT_TAG ${${PNAME}_GIT_SHA1} )  # For now set it to a commit id
+
+  if( _PAR_VARIABLES )
+    set( PKGCONFIG_VARIABLES "\n### Features:\n\n")
+    foreach( _var ${_PAR_VARIABLES} )
+      set( PKGCONFIG_VARIABLES "${PKGCONFIG_VARIABLES}${_var}=${${_var}}\n" )
+    endforeach()
+  endif()
+
+  configure_file( ${_PAR_TEMPLATE} "${CMAKE_BINARY_DIR}/${_PAR_FILENAME}" @ONLY )
+  ecbuild_info( "pkg-config file created: ${_PAR_FILENAME}" )
+
+  install( FILES ${CMAKE_BINARY_DIR}/${_PAR_FILENAME}
+           DESTINATION ${INSTALL_LIB_DIR}/pkgconfig/
+           COMPONENT utilities )
+
+endfunction(ecbuild_pkgconfig)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_policies.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_policies.cmake
new file mode 100644
index 0000000..df2b40f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_policies.cmake
@@ -0,0 +1,67 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#
+# ecBuild Policies
+# ================
+#
+# NOTE: This file needs to be included with NO_POLICY_SCOPE or it will have no
+#       effect!
+# NOTE: Policies 1 through 17 will be set to NEW by requiring CMake 2.8.4 i.e.
+#       calling cmake_minimum_required( VERSION 2.8.4 FATAL_ERROR )
+#
+##############################################################################
+
+# allow for empty spaces around library names 
+if( POLICY CMP0004 )
+    cmake_policy( SET CMP0004 OLD )
+endif()
+
+# Allow use of the LOCATION target property.
+if( POLICY CMP0026 )
+    cmake_policy( SET CMP0026 OLD )
+endif()
+
+# for macosx use @rpath in a target’s install name
+if( POLICY CMP0042 )
+    cmake_policy( SET CMP0042 NEW )
+    set( CMAKE_MACOSX_RPATH ON )
+endif()
+
+# Error on non-existent target in get_target_property
+if( POLICY CMP0045 )
+    cmake_policy( SET CMP0045 NEW )
+endif()
+
+# Error on non-existent dependency in add_dependencies
+if( POLICY CMP0046 )
+    cmake_policy( SET CMP0046 NEW )
+endif()
+
+# Do not manage VERSION variables in project command
+if( POLICY CMP0048 )
+  cmake_policy( SET CMP0048 OLD )
+endif()
+
+# Disallow add_custom_command SOURCE signatures
+if( POLICY CMP0050 )
+    cmake_policy( SET CMP0050 NEW )
+endif()
+
+# Reject source and build dirs in installed INTERFACE_INCLUDE_DIRECTORIES
+if( POLICY CMP0052 )
+    cmake_policy( SET CMP0052 NEW )
+endif()
+
+# inside if() don't dereference variables if they are quoted
+# e.g. "VAR" is not dereferenced
+#      "${VAR}" is dereference only once
+if( POLICY CMP0054 )
+    cmake_policy( SET CMP0054 NEW )
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_print_summary.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_print_summary.cmake
new file mode 100644
index 0000000..c863e60
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_print_summary.cmake
@@ -0,0 +1,116 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_print_summary
+# =====================
+#
+# Print a summary of the project, build environment and enabled features. ::
+#
+#   ecbuild_print_summary()
+#
+# If ``project_summary.cmake`` exist in the source root directory, a project
+# summary is printed by including this file.
+#
+# For a top level project, a summary of the build environment and a feature
+# summary are also printed.
+#
+##############################################################################
+
+macro( ecbuild_print_summary )
+
+  if( EXISTS ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Project ${PROJECT_NAME} summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    include( ${PROJECT_SOURCE_DIR}/project_summary.cmake )
+
+  endif()
+
+  if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    get_property( langs GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+    ecbuild_info( "---------------------------------------------------------" )
+    if( NOT ${DEVELOPER_MODE} )
+      ecbuild_info( "Build summary" )
+    else()
+      ecbuild_info( "Build summary -- ( DEVELOPER_MODE )" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    ecbuild_info( "system : [${BUILD_SITE}] [${CMAKE_SYSTEM}] [${EC_OS_NAME}.${EC_OS_BITS}]" )
+    ecbuild_info( "processor        : [${CMAKE_SYSTEM_PROCESSOR}]" )
+    if( EC_BIG_ENDIAN )
+      ecbuild_info( "endiness         : Big Endian -- IEEE [${IEEE_BE}]" )
+    endif()
+    if( EC_LITTLE_ENDIAN )
+      ecbuild_info( "endiness         : Little Endian -- IEEE [${IEEE_LE}]" )
+    endif()
+    ecbuild_info( "build type       : [${CMAKE_BUILD_TYPE}]" )
+    ecbuild_info( "timestamp        : [${EC_BUILD_TIMESTAMP}]" )
+    ecbuild_info( "install prefix   : [${CMAKE_INSTALL_PREFIX}]" )
+    ecbuild_info( "  bin dir        : [${${PNAME}_FULL_INSTALL_BIN_DIR}]" )
+    ecbuild_info( "  lib dir        : [${${PNAME}_FULL_INSTALL_LIB_DIR}]" )
+    ecbuild_info( "  include dir    : [${${PNAME}_FULL_INSTALL_INCLUDE_DIR}]" )
+    ecbuild_info( "  data dir       : [${${PNAME}_FULL_INSTALL_DATA_DIR}]" )
+    ecbuild_info( "  cmake dir      : [${${PNAME}_FULL_INSTALL_CMAKE_DIR}]" )
+    if( EC_LINK_DIR )
+      ecbuild_info( "links prefix     : [${EC_LINK_DIR}]" )
+    endif()
+    ecbuild_info( "---------------------------------------------------------" )
+
+    foreach( lang ${langs} )
+      ecbuild_info( "${lang} -- ${CMAKE_${lang}_COMPILER_ID} ${CMAKE_${lang}_COMPILER_VERSION}"  )
+      ecbuild_info( "    compiler   : ${CMAKE_${lang}_COMPILER}" )
+      ecbuild_info( "    flags      : ${CMAKE_${lang}_FLAGS} ${CMAKE_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} ${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+      ecbuild_info( "    link flags : ${CMAKE_${lang}_LINK_FLAGS}" )
+    endforeach()
+
+    ecbuild_info( "linker : ${CMAKE_LINKER}")
+    ecbuild_info( "ar     : ${CMAKE_AR}")
+    ecbuild_info( "ranlib : ${CMAKE_RANLIB}")
+    ecbuild_info( "link flags" )
+    ecbuild_info( "    executable [${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    shared lib [${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "    static lib [${CMAKE_MODULE_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}]" )
+    ecbuild_info( "install rpath  : ${CMAKE_INSTALL_RPATH}" )
+
+    get_directory_property( defs COMPILE_DEFINITIONS )
+
+    ecbuild_info( "common definitions: ${defs}" )
+
+    ### FEATURE SUMMARY
+
+    ecbuild_info( "---------------------------------------------------------" )
+    ecbuild_info( "Feature summary" )
+    ecbuild_info( "---------------------------------------------------------" )
+
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8.6" )
+      set( __what ALL )
+    else()
+      set( __what ALL INCLUDE_QUIET_PACKAGES )
+    endif()
+
+    # Print feature summary
+    feature_summary( WHAT ${__what} )
+    # Write feature summary to ecbuild.log
+    feature_summary( WHAT ${__what} FILENAME ${ECBUILD_LOG_FILE} APPEND )
+
+    ### WARNINGS
+
+    # issue warnings / errors in case there are unused project files
+    ecbuild_warn_unused_files()
+
+  endif()
+
+endmacro( ecbuild_print_summary )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_project_files.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_project_files.cmake
new file mode 100644
index 0000000..12c07c2
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_project_files.cmake
@@ -0,0 +1,75 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+# resert the variable on each configure
+set( EC_UNUSED_FILES "" CACHE INTERNAL "unused files" )
+
+##############################################################################
+# finds project files and adds them to the passed variable
+
+macro( ecbuild_find_files_recursive aFileList )
+
+list( APPEND ecbuild_project_extensions c cc cpp cxx ) # for the moment skip ( h hh )
+
+foreach( aExt ${ecbuild_project_extensions} )
+  set( globPatterns ${globPatterns} *.${aExt} )
+endforeach()
+
+# This globs for only one pattern at a time
+# Shell extglob patterns are unfortunately not supported.
+file( GLOB_RECURSE ${aFileList} ${globPatterns} )
+
+endmacro()
+
+##############################################################################
+# finds the unused files on all the project
+function( ecbuild_find_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    ecbuild_find_files_recursive( cwdFiles )
+
+    # this list will be kept
+    set( EC_PROJECT_FILES ${EC_PROJECT_FILES} ${cwdFiles} CACHE INTERNAL "" )
+    # this list will be progressevely emptied
+    set( EC_UNUSED_FILES  ${EC_UNUSED_FILES}  ${cwdFiles} CACHE INTERNAL "" )
+  endif()
+
+endfunction()
+
+##############################################################################
+# removed used files from unused list
+macro( ecbuild_declare_project_files )
+
+  # Only do this if we actually care to warn about unused files
+  if( CHECK_UNUSED_FILES )
+    foreach( _afile ${ARGV} )
+
+      # ecbuild_debug_var( _afile )
+
+      get_property( _src_gen SOURCE ${_afile} PROPERTY GENERATED )
+
+      if( NOT _src_gen )
+
+        get_filename_component( _abspath ${_afile} ABSOLUTE )
+
+        # check for existance of all declared files
+        if( EXISTS ${_abspath} )
+            list( REMOVE_ITEM EC_UNUSED_FILES ${_abspath} )
+        else()
+        ecbuild_critical( "In directory ${CMAKE_CURRENT_SOURCE_DIR} file ${_afile} was declared in CMakeLists.txt but not found" )
+        endif()
+      endif()
+
+    endforeach()
+
+    # rewrite the unused file list in cache
+    set( EC_UNUSED_FILES ${EC_UNUSED_FILES} CACHE INTERNAL "unused files" )
+  endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake
new file mode 100644
index 0000000..93aab17
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_remove_fortran_flags.cmake
@@ -0,0 +1,64 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_remove_fortran_flags
+# ============================
+#
+# Remove Fortran compiler flags from ``CMAKE_Fortran_FLAGS``. ::
+#
+#   ecbuild_remove_fortran_flags( <flag1> [ <flag2> ... ] [ BUILD <build> ] )
+#
+# Options
+# -------
+#
+# BUILD : optional
+#   remove flags from ``CMAKE_Fortran_FLAGS_<build>`` instead of
+#   ``CMAKE_Fortran_FLAGS``
+#
+##############################################################################
+
+include( CheckFortranCompilerFlag )
+macro( ecbuild_remove_fortran_flags m_flags )
+
+  set( _flags ${m_flags} )
+  if( _flags AND CMAKE_Fortran_COMPILER_LOADED )
+
+    set( single_value_args BUILD )
+    set( multi_value_args )
+    cmake_parse_arguments( _PAR "" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} )
+
+    string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS )
+
+    if( _PAR_BUILD )
+      string( TOUPPER ${_PAR_BUILD} _PAR_BUILD_CAPS )
+    endif()
+
+    if( _PAR_BUILD AND (CMAKE_BUILD_TYPE_CAPS MATCHES "${_PAR_BUILD_CAPS}") )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${_PAR_BUILD} ${CMAKE_Fortran_FLAGS_${_PAR_BUILD}})
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed from build type ${_PAR_BUILD}" )
+      endforeach()
+
+    elseif( NOT _PAR_BUILD )
+
+      foreach( _flag ${_flags} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS} ${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS}} )
+        string(REGEX REPLACE " *${_flag} *" " " CMAKE_Fortran_FLAGS ${CMAKE_Fortran_FLAGS} )
+        ecbuild_debug( "Fortran FLAG [${_flag}] removed" )
+      endforeach()
+
+    endif()
+
+  endif()
+  unset( _flags )
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_requires_macro_version.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_requires_macro_version.cmake
new file mode 100644
index 0000000..8ff5617
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_requires_macro_version.cmake
@@ -0,0 +1,27 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_requires_macro_version
+# ==============================
+#
+# Check that the ecBuild version satisfied a given minimum version or fail. ::
+#
+#   ecbuild_requires_macro_version( <minimum-version> )
+#
+##############################################################################
+
+macro( ecbuild_requires_macro_version req_vrs )
+
+	if( ECBUILD_MACRO_VERSION VERSION_LESS ${req_vrs} )
+		ecbuild_critical( "${PROJECT_NAME} needs ecbuild macro version >= ${req_vrs}" )
+	endif()
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_separate_sources.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_separate_sources.cmake
new file mode 100644
index 0000000..69b6810
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_separate_sources.cmake
@@ -0,0 +1,103 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_separate_sources
+# ========================
+#
+# Separate a given list of sources according to language. ::
+#
+#   ecbuild_separate_sources( TARGET <name>
+#                             SOURCES <source1> [ <source2> ... ] )
+#
+# Options
+# -------
+#
+# TARGET : required
+#   base name for the CMake output variables to set
+#
+# SOURCES : required
+#   list of source files to separate
+#
+# Output variables
+# ----------------
+#
+# If any file of the following group of extensions is present in the list of
+# sources, the corresponding CMake variable is set:
+#
+# :<target>_h_srcs:       source files with extension .h, .hxx, .hh, .hpp, .H
+# :<target>_c_srcs:       source files with extension .c
+# :<target>_cxx_srcs:     source files with extension .cc, .cxx, .cpp, .C
+# :<target>_fortran_srcs: source files with extension .f, .F, .for, f77, .f90,
+#                                                     .f95, .F77, .F90, .F95
+# :<target>_cuda_srcs:    source files with extension .cu
+#
+##############################################################################
+
+function( ecbuild_separate_sources )
+
+	set( options )
+	set( single_value_args TARGET  )
+	set( multi_value_args  SOURCES )
+
+	cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+	if(_PAR_UNPARSED_ARGUMENTS)
+	  ecbuild_critical("Unknown keywords given to ecbuild_separate_sources(): \"${_PAR_UNPARSED_ARGUMENTS}\"")
+	endif()
+
+	if( NOT _PAR_TARGET  )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the TARGET.")
+	endif()
+
+	if( NOT _PAR_SOURCES )
+	  ecbuild_critical("The call to ecbuild_separate_sources() doesn't specify the SOURCES.")
+	endif()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.h$|\\.hxx$|\\.hh$|\\.hpp$|\\.H$)")
+			list( APPEND ${_PAR_TARGET}_h_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.c$)")
+			list( APPEND ${_PAR_TARGET}_c_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.cc$|\\.cxx$|\\.cpp$|\\.C$)")
+			list( APPEND ${_PAR_TARGET}_cxx_srcs ${src} )
+		endif()
+	endforeach()
+
+	foreach( src ${_PAR_SOURCES} )
+		if(${src} MATCHES "(\\.f$|\\.F$|\\.for$|\\.f77$|\\.f90$|\\.f95$|\\.f03$|\\.f08$|\\.F77$|\\.F90$|\\.F95$|\\.F03$|\\.F08$)")
+			list( APPEND ${_PAR_TARGET}_fortran_srcs ${src} )
+		endif()
+	endforeach()
+
+    foreach( src ${_PAR_SOURCES} )
+        if(${src} MATCHES "(\\.cu$)")
+            list( APPEND ${_PAR_TARGET}_cuda_srcs ${src} )
+        endif()
+    endforeach()
+
+    set_source_files_properties( ${${_PAR_TARGET}_fortran_srcs} PROPERTIES LANGUAGE Fortran )
+
+    set( ${_PAR_TARGET}_h_srcs       "${${_PAR_TARGET}_h_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_c_srcs       "${${_PAR_TARGET}_c_srcs}"       PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cxx_srcs     "${${_PAR_TARGET}_cxx_srcs}"     PARENT_SCOPE )
+    set( ${_PAR_TARGET}_fortran_srcs "${${_PAR_TARGET}_fortran_srcs}" PARENT_SCOPE )
+    set( ${_PAR_TARGET}_cuda_srcs    "${${_PAR_TARGET}_cuda_srcs}"    PARENT_SCOPE )
+
+
+endfunction( ecbuild_separate_sources )
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_setup_test_framework.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_setup_test_framework.cmake
new file mode 100644
index 0000000..b4b3ccb
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_setup_test_framework.cmake
@@ -0,0 +1,72 @@
+ecbuild_add_option( FEATURE TESTS
+                    DEFAULT ON
+                    DESCRIPTION "Enable the unit tests" )
+
+if( ENABLE_TESTS AND CMAKE_CXX_COMPILER_LOADED )
+
+  # Try to find compiled boost
+
+  # BOOST_ROOT or BOOSTROOT should take precedence on the search for location
+  if( BOOST_ROOT OR BOOSTROOT OR DEFINED ENV{BOOST_ROOT} OR DEFINED ENV{BOOSTROOT} )
+    set( CMAKE_PREFIX_PATH ${BOOST_ROOT} ${BOOSTROOT} $ENV{BOOST_ROOT} $ENV{BOOSTROOT} ${CMAKE_PREFIX_PATH} )
+  endif()
+
+  set( Boost_USE_MULTITHREADED  ON )
+  #   set( Boost_DEBUG              ON )
+
+  find_package( Boost 1.47.0 COMPONENTS unit_test_framework )
+
+  set( ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/include" )
+
+  if( Boost_FOUND AND Boost_UNIT_TEST_FRAMEWORK_LIBRARY )
+
+    set( HAVE_BOOST_UNIT_TEST 1 )
+    set( BOOST_UNIT_TEST_FRAMEWORK_LINKED 1 )
+
+    ecbuild_info( "Using Boost for unit tests:\n    INC [${Boost_INCLUDE_DIRS}]\n    LIB [${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}]" )
+
+  else()
+
+    ecbuild_info( "Boost unit test framework -- NOT FOUND" )
+
+    set( HAVE_BOOST_UNIT_TEST 0 )
+
+    # set( BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY 1 )
+    # comment out this when ecbuild packs boost unit test inside...
+    # list( APPEND ECBUILD_BOOST_HEADER_DIRS "${CMAKE_CURRENT_LIST_DIR}/contrib/boost-1.55/include" )
+    # set( HAVE_BOOST_UNIT_TEST 1 )
+
+  endif()
+
+endif()
+
+if( ENABLE_TESTS )
+
+  # CTest has built-in support for running with memcheck
+  # (https://cmake.org/cmake/help/latest/manual/ctest.1.html#ctest-memcheck-step)
+  # via `ctest -T memcheck`, however by default memcheck does not exit with a
+  # non-zero error code if any issues are found.
+  #
+  # CTest will run ${MEMORYCHECK_COMMAND} with ${MEMORYCHECK_COMMAND_OPTIONS}.
+  # Suppressions are read from ${MEMORYCHECK_SUPPRESSIONS_FILE} if given.
+
+  find_program( MEMORYCHECK_COMMAND valgrind )
+  ecbuild_debug_var( MEMORYCHECK_COMMAND )
+
+  if( NOT MEMORYCHECK_COMMAND_OPTIONS )
+    set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --error-exitcode=1"
+         CACHE STRING "Options passed to memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_COMMAND_OPTIONS )
+
+  if( NOT MEMORYCHECK_SUPPRESSIONS_FILE AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/valgrind_suppress.txt" )
+    set( MEMORYCHECK_SUPPRESSIONS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.supp"
+         CACHE FILEPATH "Suppressions file to be used with memcheck command" )
+  endif()
+  ecbuild_debug_var( MEMORYCHECK_SUPPRESSIONS_FILE )
+
+else()
+
+  ecbuild_info("Tests have been disabled")
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_source_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_source_flags.cmake
new file mode 100644
index 0000000..bd63258
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_source_flags.cmake
@@ -0,0 +1,34 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+set( __gen_source_flags ${CMAKE_CURRENT_LIST_DIR}/gen_source_flags.py )
+
+# Calls gen_source_flags.py to generate a CMake file with the per
+# source file flags for a given target.
+function( ecbuild_source_flags OUT TARGET DEFAULT_FLAGS SOURCES )
+
+  if( NOT PYTHONINTERP_FOUND OR PYTHON_VERSION VERSION_LESS 2.7 )
+    find_package( PythonInterp 2.7 REQUIRED )
+  endif()
+
+  set( OUTFILE ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_source_flags.cmake )
+
+  if( ECBUILD_LOG_LEVEL LESS 11)
+    set( __debug "--debug" )
+  endif()
+  execute_process( COMMAND ${PYTHON_EXECUTABLE} ${__gen_source_flags}
+                           ${ECBUILD_SOURCE_FLAGS} ${OUTFILE} "${DEFAULT_FLAGS}"
+                           ${SOURCES} "${__debug}"
+                   RESULT_VARIABLE __res )
+
+  if( __res GREATER 0 )
+    ecbuild_error( "ecbuild_source_flags: failed generating source flags for target ${TARGET} from ${ECBUILD_SOURCE_FLAGS}" )
+  endif()
+  set( ${OUT} ${OUTFILE} PARENT_SCOPE )
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_system.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_system.cmake
new file mode 100644
index 0000000..16c0299
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_system.cmake
@@ -0,0 +1,277 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+########################################################################################################
+# disallow in-source build
+
+if( EXISTS ${CMAKE_SOURCE_DIR}/CMakeCache.txt ) # check for failed attempts to build within the source tree
+    message( FATAL_ERROR "Project ${PROJECT_NAME} contains a CMakeCache.txt inside source tree [${CMAKE_SOURCE_DIR}/CMakeCache.txt].\n Please remove it and
+    make sure that source tree is prestine and clean of unintended files, before retrying." )
+endif()
+
+get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+if(${srcdir} STREQUAL ${bindir})
+    message("######################################################")
+    message("You are attempting to build in your source directory (${srcdir}).")
+    message("You must run cmake from a different build directory.")
+    message("######################################################")
+    message( FATAL_ERROR "${PROJECT_NAME} requires an out of source build.\n Please create a separate build directory and run 'cmake path/to/project [options]' from there.")
+endif()
+
+########################################################################################################
+# ecbuild versioning support
+
+set( ECBUILD_CMAKE_MINIMUM "2.8.10" )
+if( ${CMAKE_VERSION} VERSION_LESS ${ECBUILD_CMAKE_MINIMUM} )
+    message(FATAL_ERROR "${PROJECT_NAME} requires at least CMake ${ECBUILD_CMAKE_MINIMUM} -- you are using ${CMAKE_COMMAND} [${CMAKE_VERSION}]\n Please, get a newer version of CMake @ www.cmake.org" )
+endif()
+
+set( ECBUILD_MACROS_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "where ecbuild system is" )
+
+include( "${ECBUILD_MACROS_DIR}/VERSION.cmake" )
+
+set( ecbuild_VERSION_STR "${ECBUILD_VERSION_STR}" )
+
+# Set policies
+include( ecbuild_policies NO_POLICY_SCOPE )
+
+# set capitalised project name
+
+string( TOUPPER ${PROJECT_NAME} PROJECT_NAME_CAPS )
+string( TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWCASE )
+
+########################################################################################################
+# include our cmake macros, but only do so if this is the top project
+if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+
+    # hostname of where we build
+
+    site_name( BUILD_SITE )
+    mark_as_advanced( BUILD_SITE )
+    mark_as_advanced( BUILD_TESTING )
+
+    set( ECBUILD_PROJECTS  "" CACHE INTERNAL "list of ecbuild (sub)projects that use ecbuild" )
+
+    # Include log macros since these are used right away
+    include( ecbuild_log )
+
+    execute_process( COMMAND env OUTPUT_VARIABLE __env )
+    ecbuild_debug( "---------------------------------------------------------" )
+    ecbuild_debug( "Environment:" )
+    ecbuild_debug( "---------------------------------------------------------\n${__env}" )
+    ecbuild_debug( "---------------------------------------------------------" )
+
+    ecbuild_info( "ecbuild   ${ecbuild_VERSION_STR}\t${ECBUILD_MACROS_DIR}" )
+    ecbuild_info( "cmake     ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}\t${CMAKE_COMMAND}" )
+
+    if( CMAKE_TOOLCHAIN_FILE )
+      ecbuild_info( "toolchain ${CMAKE_TOOLCHAIN_FILE}" )
+    endif()
+
+    if( ECBUILD_CONFIG )
+      ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+    endif()
+
+    if( ECBUILD_CACHE )
+      include( ${ECBUILD_CACHE} )
+      ecbuild_info( "cache     ${ECBUILD_CACHE}" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+    # clear the build dir exported targets file (only on the top project)
+
+    set( TOP_PROJECT_TARGETS_FILE "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-targets.cmake" CACHE INTERNAL "" )
+    file( REMOVE ${TOP_PROJECT_TARGETS_FILE} )
+
+    # add backport support for versions up too 2.8.4
+    if( ${CMAKE_VERSION} VERSION_LESS "2.8" )
+    set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/2.8" ${CMAKE_MODULE_PATH} )
+    endif()
+
+    # add extra macros from external contributions
+    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib" )
+
+    # would bring FindEigen in, so for the moment keep it out
+    # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/contrib/GreatCMakeCookOff" )
+
+    ############################################################################################
+    # define valid build types
+
+    include(ecbuild_define_build_types)
+
+    ############################################################################################
+    # add cmake macros
+
+    include(AddFileDependencies)
+
+    include(CheckTypeSize)
+    include(CheckIncludeFile)
+    include(CheckIncludeFiles)
+
+    include(CheckFunctionExists)
+    include(CheckSymbolExists)
+
+    include(CheckCCompilerFlag)
+    include(CheckCSourceCompiles)
+    include(CheckCSourceRuns)
+
+    include(CMakeParseArguments)
+
+    # include(CMakePrintSystemInformation) # available in cmake 2.8.4
+
+    if( CMAKE_CXX_COMPILER_LOADED )
+        include(CheckIncludeFileCXX)
+        include(CheckCXXCompilerFlag)
+        include(CheckCXXSourceCompiles)
+        include(CheckCXXSourceRuns)
+    endif()
+
+    if( CMAKE_Fortran_COMPILER_LOADED )
+        set( CMAKE_Fortran_MODULE_DIRECTORY  ${CMAKE_BINARY_DIR}/module CACHE PATH "directory for all fortran modules." )
+        include(CheckFortranFunctionExists)
+        if( CMAKE_C_COMPILER_LOADED AND ENABLE_FORTRAN_C_INTERFACE )
+            include(FortranCInterface)
+        endif()
+        set( EC_HAVE_FORTRAN 1 )
+    endif()
+
+    include(FeatureSummary) # support features in cmake
+
+    include(TestBigEndian)
+
+    ############################################################################################
+    # backport of cmake > 2.8.4 functions
+
+    if( "${CMAKE_VERSION}" VERSION_LESS "2.8.6" )
+        include( ${CMAKE_CURRENT_LIST_DIR}/2.8/CMakePushCheckState.cmake )
+    else()
+        include(CMakePushCheckState)
+    endif()
+
+    ############################################################################################
+    # add our macros
+
+    include( ecbuild_list_macros )
+    include( ecbuild_list_add_pattern )
+    include( ecbuild_list_exclude_pattern )
+
+    include( ecbuild_check_c_source_return )
+    include( ecbuild_check_cxx_source_return )
+    include( ecbuild_check_cxx11 )
+    include( ecbuild_check_fortran_source_return )
+
+    include( ecbuild_requires_macro_version )
+    include( ecbuild_get_date )
+    include( ecbuild_add_persistent )
+    include( ecbuild_generate_config_headers )
+    include( ecbuild_generate_rpc )
+    include( ecbuild_generate_yy )
+    include( ecbuild_generate_fortran_interfaces )
+    include( ecbuild_echo_targets )
+    include( ecbuild_features )
+    include( ecbuild_add_option )
+    include( ecbuild_add_library )
+    include( ecbuild_add_executable )
+    include( ecbuild_append_to_rpath )
+    include( ecbuild_download_resource )
+    include( ecbuild_get_test_data )
+    include( ecbuild_add_c_flags )
+    include( ecbuild_add_cxx_flags )
+    include( ecbuild_add_cxx11_flags )
+    include( ecbuild_get_cxx11_flags )
+    include( ecbuild_check_fortran )
+    include( ecbuild_add_fortran_flags )
+    include( ecbuild_add_test )
+    include( ecbuild_add_resources )
+    include( ecbuild_get_resources )
+    include( ecbuild_dont_pack )
+    include( ecbuild_project_files )
+    include( ecbuild_declare_project )
+    include( ecbuild_install_project )
+    include( ecbuild_separate_sources )
+    include( ecbuild_find_package )
+    include( ecbuild_use_package )
+    include( ecbuild_list_extra_search_paths )
+    include( ecbuild_add_extra_search_paths )
+    include( ecbuild_print_summary )
+    include( ecbuild_warn_unused_files )
+    include( ecbuild_find_mpi )
+    include( ecbuild_find_omp )
+    include( ecbuild_find_perl )
+    include( ecbuild_find_python )
+    include( ecbuild_find_lexyacc )
+    include( ecbuild_find_fortranlibs )
+    include( ecbuild_git )
+    include( ecbuild_enable_fortran )
+    include( ecbuild_source_flags )
+    include( ecbuild_target_flags )
+    include( ecbuild_bundle )
+    include( ecbuild_pkgconfig )
+    include( ecbuild_cache )
+    include( ecbuild_remove_fortran_flags )
+
+    include( ${CMAKE_CURRENT_LIST_DIR}/contrib/GetGitRevisionDescription.cmake )
+
+    ############################################################################################
+    # kickstart the build system
+
+    if( ECBUILD_CONFIG )
+      include( ${ECBUILD_CONFIG} )
+    endif()
+
+    ecbuild_prepare_cache()
+
+    include( ecbuild_define_options )               # define build options
+    include( ecbuild_compiler_flags )               # compiler flags
+    include( ecbuild_check_compiler )               # check for compiler characteristics
+    include( ecbuild_check_os )                     # check for os characteristics
+    include( ecbuild_check_functions )              # check for available functions
+    include( ecbuild_define_paths )                 # defines installation paths
+    include( ecbuild_define_libs_and_execs_target ) # defines the top level execs and libs
+    include( ecbuild_define_links_target )          # defines the links target
+    include( ecbuild_setup_test_framework )         # setup test framework
+    include( ecbuild_define_uninstall )             # define uninstall target
+
+    ecbuild_flush_cache()
+
+    ############################################################################################
+    # Testing
+
+    include(CTest)                 # add cmake testing support
+    enable_testing()
+
+    # keep this until we modify the meaning to 'check' if installation worked
+    add_custom_target( check COMMAND ${CMAKE_CTEST_COMMAND} )
+
+    ############################################################################################
+    # define the build timestamp, unless the user provided one via EC_BUILD_TIMESTAMP
+
+    if( NOT DEFINED EC_BUILD_TIMESTAMP )
+        ecbuild_get_timestamp( EC_BUILD_TIMESTAMP )
+        set( EC_BUILD_TIMESTAMP  "${EC_BUILD_TIMESTAMP}" CACHE INTERNAL "Build timestamp" )
+    endif()
+
+    ecbuild_info( "---------------------------------------------------------" )
+
+else()
+
+    # Allow subprojects with different compilation flags. This could be done by defining
+    #     set( ECBUILD_C_FLAGS_DEBUG "-O0" )
+    # or
+    #     set( ECBUILD_CONFIG "<subproject-config>.cmake" )
+    if( ECBUILD_CONFIG )
+        ecbuild_info( "---------------------------------------------------------" )
+        ecbuild_info( "config    ${ECBUILD_CONFIG}" )
+        include( ${ECBUILD_CONFIG} )
+    endif()
+    include( ecbuild_compiler_flags )
+
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_target_flags.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_target_flags.cmake
new file mode 100644
index 0000000..b6527d1
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_target_flags.cmake
@@ -0,0 +1,91 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_target_flags
+# ====================
+#
+# Override compiler flags for a given target. ::
+#
+#   ecbuild_target_flags( <target> <c_flags> <cxx_flags> <fortran_flags> )
+#
+# Required arguments:
+#
+# :target:        Target name
+# :c_flags:       Target specific C flags (can be empty)
+# :cxx_flags:     Target specific CXX flags (can be empty)
+# :fortran_flags: Target specific Fortran flags (can be empty)
+#
+# There are 3 cases, only the first applicable case takes effect:
+#
+# 1.  Use custom rules from user specified ``ECBUILD_COMPILE_FLAGS`` file and
+#     append target specific flags.
+#
+# 2.  Use JSON rules from user specified ``ECBUILD_SOURCE_FLAGS`` file and
+#     append target specific flags.
+#
+# 3.  Only the target specific flags are applied to all matching source files.
+#
+##############################################################################
+
+function( ecbuild_target_flags target c_flags cxx_flags fortran_flags )
+
+  get_property( languages GLOBAL PROPERTY ENABLED_LANGUAGES )
+
+  foreach( lang ${languages} )
+
+    string( TOLOWER ${lang} l )
+
+    if( ${target}_${l}_srcs )
+
+      # 1) Override compile flags from user specified CMake file
+      if( ECBUILD_COMPILE_FLAGS )
+
+        # Project specific flags for current language and optionally build type
+        set( pflags "${${PNAME}_${lang}_FLAGS} ${${PNAME}_${lang}_FLAGS_${CMAKE_BUILD_TYPE_CAPS}}" )
+
+        foreach( src ${${target}_${l}_srcs} )
+          get_property( oflags SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS )
+          get_property( oflags_btype SOURCE ${src} PROPERTY OVERRIDE_COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+          # Override compile flags for source file?
+          if( oflags OR oflags_btype )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${oflags} ${oflags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): overriding flags for ${src} with '${oflags} ${oflags_btype}'" )
+          # Otherwise append source file specific flags to project specific and target specific flags
+          else()
+            get_property( flags SOURCE ${src} PROPERTY COMPILE_FLAGS )
+            get_property( flags_btype SOURCE ${src} PROPERTY COMPILE_FLAGS_${CMAKE_BUILD_TYPE_CAPS} )
+            set_source_files_properties( ${src} PROPERTIES COMPILE_FLAGS "${pflags} ${${l}_flags} ${flags} ${flags_btype}" )
+            ecbuild_debug( "ecbuild_target_flags(${target}): setting flags for ${src} to '${pflags} ${${l}_flags} ${flags} ${flags_btype}'" )
+          endif()
+        endforeach()
+
+      # 2) Override compile flags from user specified JSON file
+      elseif( ECBUILD_SOURCE_FLAGS )
+        ecbuild_source_flags( ${target}_${lang}_SOURCE_FLAGS
+                              ${target}_${l}
+                              "${${l}_flags}"
+                              "${${target}_${l}_srcs}" )
+
+        ecbuild_debug("ecbuild_target_flags(${target}): setting source file ${lang} flags from ${${target}_${lang}_SOURCE_FLAGS}")
+        include( ${${target}_${lang}_SOURCE_FLAGS} )
+
+      # 3) Use target specific compile flags
+      elseif( ${l}_flags )
+
+        set_source_files_properties( ${${target}_${l}_srcs} PROPERTIES COMPILE_FLAGS "${${l}_flags}" )
+        ecbuild_debug("ecbuild_target_flags(${target}): setting flags for '${${target}_${l}_srcs}' to '${${l}_flags}'")
+
+      endif()
+    endif()
+
+  endforeach()
+
+endfunction()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_uninstall.cmake.in b/ecbuild/share/ecbuild/cmake/ecbuild_uninstall.cmake.in
new file mode 100644
index 0000000..33b1949
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_uninstall.cmake.in
@@ -0,0 +1,28 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+
+if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt")
+  file(READ "@CMAKE_CURRENT_BINARY_DIR@/extra_install.txt" __files)
+  string(REGEX REPLACE "\n" ";" __files "${__files}")
+  list(APPEND files ${__files})
+endif()
+
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif()
+  else()
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif()
+endforeach(file)
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_use_package.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_use_package.cmake
new file mode 100644
index 0000000..d54e459
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_use_package.cmake
@@ -0,0 +1,341 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_use_package
+# ===================
+#
+# Add a project from a source directory, a subdirectory or search for it. ::
+#
+#   ecbuild_use_package( PROJECT <name>
+#                        [ VERSION <version> [ EXACT ] ]
+#                        [ URL <url> ]
+#                        [ DESCRIPTION <description> ]
+#                        [ TYPE <type> ]
+#                        [ PURPOSE <purpose> ]
+#                        [ FAILURE_MSG <message> ]
+#                        [ REQUIRED ]
+#                        [ QUIET ] )
+#
+# Options
+# -------
+#
+# NAME : required
+#   package name (used as ``Find<name>.cmake`` and ``<name>-config.cmake``)
+#
+# VERSION : optional
+#   minimum required package version
+#
+# EXACT : optional, requires VERSION
+#   require the exact version rather than a minimum version
+#
+# URL : optional
+#   homepage of the package (shown in summary and stored in the cache)
+#
+# DESCRIPTION : optional
+#   string describing the package (shown in summary and stored in the cache)
+#
+# TYPE : optional, one of RUNTIME|OPTIONAL|RECOMMENDED|REQUIRED
+#   type of dependency of the project on this package (defaults to OPTIONAL)
+#
+# PURPOSE : optional
+#   string describing which functionality this package enables in the project
+#
+# FAILURE_MSG : optional
+#   string to be appended to the failure message if the package is not found
+#
+# REQUIRED : optional
+#   fail if package cannot be found
+#
+# QUIET : optional
+#   do not output package information if found
+#
+# Input variables
+# ---------------
+#
+# The following CMake variables influence the behaviour if set (``<name>``
+# is the package name as given, ``<NAME>`` is the capitalised version):
+#
+# :<NAME>_SOURCE:    path to source directory for package
+# :SUBPROJECT_DIRS:  list of additional paths to search for package source
+#
+# See also ``ecbuild_find_package`` for additional CMake variables relevant
+# when search for the package (step 6 below).
+#
+# Usage
+# -----
+#
+# Use another CMake project as a dependency by either building it from source
+# i.e. adding its source directory as a subdirectory or searching for it. This
+# transparently deals with the case where the project has already been included
+# e.g. because multiple projects with shared dependencies are built together.
+#
+# The search proceeds as follows:
+#
+# 1.  If ``SUBPROJECT_DIRS`` is set, each directory in the list is searched
+#     for a subdirectory <name> and ``<NAME>_SOURCE`` is set to the first one
+#     found (if any).
+#
+# 2.  If ``<NAME>_SOURCE`` is set, check if this directory is a CMake project
+#     (contains ``CMakeLists.txt`` and fail if not.
+#
+# 3.  Otherwise, check if the current directory has a ``<name>`` subdirectory.
+#
+# 4.  If the project has not been previously marked as found or added as a
+#     subdirectory and a project source directory has been found in steps 1-3
+#     add this subdirectory.
+#
+# 5.  If the project has been marked as found, check the version.
+#
+# 6.  Otherwise, search for the project using ``ecbuild_find_package``.
+#
+##############################################################################
+
+macro( ecbuild_use_package )
+
+  set( options            REQUIRED QUIET EXACT )
+  set( single_value_args  PROJECT VERSION URL DESCRIPTION TYPE PURPOSE FAILURE_MSG )
+  set( multi_value_args )
+
+  cmake_parse_arguments( _p "${options}" "${single_value_args}" "${multi_value_args}"  ${_FIRST_ARG} ${ARGN} )
+
+  if(_p_UNPARSED_ARGUMENTS)
+    ecbuild_critical("Unknown keywords given to ecbuild_use_package(): \"${_p_UNPARSED_ARGUMENTS}\"")
+  endif()
+
+  if( NOT _p_PROJECT  )
+    ecbuild_critical("The call to ecbuild_use_package() doesn't specify the PROJECT.")
+  endif()
+
+  if( _p_EXACT AND NOT _p_VERSION )
+    ecbuild_critical("Call to ecbuild_use_package() requests EXACT but doesn't specify VERSION.")
+  endif()
+
+  # If the package is required, set TYPE to REQUIRED
+  # Due to shortcomings in CMake's argument parser, passing TYPE REQUIRED has no effect
+  if( _p_REQUIRED )
+    set( _p_TYPE REQUIRED )
+  endif()
+
+  # try to find the package as a subproject and build it
+
+  string( TOUPPER ${_p_PROJECT} pkgUPPER )
+
+  # user defined dir with subprojects
+
+  if( NOT DEFINED ${pkgUPPER}_SOURCE AND DEFINED SUBPROJECT_DIRS )
+    ecbuild_warn("ecbuild_use_package(): setting SUBPROJECT_DIRS is deprecated")
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): scanning subproject directories ${SUBPROJECT_DIRS}")
+    foreach( dir ${SUBPROJECT_DIRS} )
+      if( EXISTS ${dir}/${_p_PROJECT} AND EXISTS ${dir}/${_p_PROJECT}/CMakeLists.txt )
+        ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):   setting ${pkgUPPER}_SOURCE to ${dir}/${_p_PROJECT}")
+        set( ${pkgUPPER}_SOURCE "${dir}/${_p_PROJECT}" )
+      endif()
+    endforeach()
+  endif()
+
+  # user defined path to subproject
+
+  if( DEFINED ${pkgUPPER}_SOURCE )
+
+    if( NOT EXISTS ${${pkgUPPER}_SOURCE} OR NOT EXISTS ${${pkgUPPER}_SOURCE}/CMakeLists.txt )
+      ecbuild_critical("User defined source directory '${${pkgUPPER}_SOURCE}' for project '${_p_PROJECT}' does not exist or does not contain a CMakeLists.txt file.")
+    endif()
+
+    set( ${pkgUPPER}_subproj_dir_ "${${pkgUPPER}_SOURCE}" )
+
+  else() # default is 'dropped in' subdirectory named as project
+
+    if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT} AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}/CMakeLists.txt )
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): found ${_p_PROJECT} in subdirectory ${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}")
+      set( ${pkgUPPER}_subproj_dir_ "${CMAKE_CURRENT_SOURCE_DIR}/${_p_PROJECT}" )
+    endif()
+
+  endif()
+
+  # check if was already added as subproject ...
+
+  set( _just_added 0 )
+  set( _do_version_check 0 )
+  set( _source_description "" )
+
+  list( FIND ECBUILD_PROJECTS ${_p_PROJECT} _ecbuild_project_${pkgUPPER} )
+
+  if( NOT _ecbuild_project_${pkgUPPER} EQUAL "-1" )
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 1 )
+  else()
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): ${_p_PROJECT} was not previously added as a subproject")
+    set( ${pkgUPPER}_previous_subproj_ 0 )
+  endif()
+
+  # solve capitalization issues
+
+  if( ${_p_PROJECT}_FOUND AND NOT ${pkgUPPER}_FOUND )
+    set( ${pkgUPPER}_FOUND 1 )
+  endif()
+  if( ${pkgUPPER}_FOUND AND NOT ${_p_PROJECT}_FOUND )
+    set( ${_p_PROJECT}_FOUND 1 )
+  endif()
+
+  # Case 1) project was NOT previously added as subproject and is NOT already FOUND
+
+  if( NOT ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ )
+
+    # check if SUBPROJDIR is set
+
+    if( DEFINED ${pkgUPPER}_subproj_dir_ )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 1) project was NOT previously added as subproject and is NOT already FOUND")
+
+      # check version is acceptable
+      set( _just_added 1 )
+      set( _do_version_check 1 )
+      set( _source_description "sub-project ${_p_PROJECT} (sources)" )
+
+      # add as a subproject
+
+      set( ${pkgUPPER}_subproj_dir_ ${${pkgUPPER}_subproj_dir_} CACHE PATH "Path to ${_p_PROJECT} source directory" )
+      mark_as_advanced( ${pkgUPPER}_subproj_dir_ )
+
+      set( ECBUILD_PROJECTS ${ECBUILD_PROJECTS} ${_p_PROJECT} CACHE INTERNAL "" )
+
+      ecbuild_debug("ecbuild_use_package(${_p_PROJECT}):    ${_p_PROJECT} found in subdirectory ${${pkgUPPER}_subproj_dir_}")
+      add_subdirectory( ${${pkgUPPER}_subproj_dir_} ${_p_PROJECT} )
+
+      set( ${_p_PROJECT}_BASE_DIR ${CMAKE_BINARY_DIR} )
+
+      set( ${pkgUPPER}_FOUND 1 )
+      set( ${_p_PROJECT}_VERSION ${${pkgUPPER}_VERSION} )
+
+      list( APPEND ${pkgUPPER}_INCLUDE_DIRS ${${pkgUPPER}_TPL_INCLUDE_DIRS} )
+
+    endif()
+
+  endif()
+
+  # Case 2) project was already added as subproject, so is already FOUND -- BUT must check version acceptable
+
+  if( ${pkgUPPER}_previous_subproj_ )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 2) project was already added as subproject, check version is acceptable")
+
+    if( NOT ${pkgUPPER}_FOUND )
+      ecbuild_critical( "${_p_PROJECT} was already included as sub-project but ${pkgUPPER}_FOUND isn't set -- this is likely a BUG in ecbuild" )
+    endif()
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "already existing sub-project ${_p_PROJECT} (sources)" )
+
+  endif()
+
+  # Case 3) project was NOT added as subproject, but is FOUND -- so it was previously found as a binary ( either build or install tree )
+
+  if( ${pkgUPPER}_FOUND AND NOT ${pkgUPPER}_previous_subproj_ AND NOT _just_added )
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 3) project was NOT previously added as subproject, but is FOUND")
+
+    # check version is acceptable
+    set( _do_version_check 1 )
+    set( _source_description "previously found package ${_p_PROJECT} (binaries)" )
+
+  endif()
+
+  # test version for Cases 1,2,3
+
+  # ecbuild_debug_var( _p_PROJECT )
+  # ecbuild_debug_var( _p_VERSION )
+  # ecbuild_debug_var( ${pkgUPPER}_VERSION )
+  # ecbuild_debug_var( ${_p_PROJECT}_VERSION )
+  # ecbuild_debug_var( _just_added )
+  # ecbuild_debug_var( _do_version_check )
+  # ecbuild_debug_var( _source_description )
+  # ecbuild_debug_var( ${pkgUPPER}_FOUND )
+  # ecbuild_debug_var( ${pkgUPPER}_previous_subproj_ )
+
+  if( _p_VERSION AND _do_version_check )
+    if( _p_EXACT )
+      if( NOT ${_p_PROJECT}_VERSION VERSION_EQUAL _p_VERSION )
+        ecbuild_critical( "${PROJECT_NAME} requires (exactly) ${_p_PROJECT} = ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    else()
+      if( _p_VERSION VERSION_LESS ${_p_PROJECT}_VERSION OR _p_VERSION VERSION_EQUAL ${_p_PROJECT}_VERSION )
+        ecbuild_info( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected as ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      else()
+        ecbuild_critical( "${PROJECT_NAME} requires ${_p_PROJECT} >= ${_p_VERSION} -- detected only ${_source_description} ${${_p_PROJECT}_VERSION}" )
+      endif()
+    endif()
+  endif()
+
+  # Case 4) is NOT FOUND so far, NOT as sub-project (now or before), and NOT as binary neither
+  #         so try to find precompiled binaries or a build tree
+
+  if( ${pkgUPPER}_FOUND )
+    # Only set package properties if ecbuild_find_package, which itself calls
+    # set_package_properties, is not subsequently called since doing so would
+    # duplicate the purpose
+    set_package_properties( ${_p_PROJECT} PROPERTIES
+                            URL "${_p_URL}"
+                            DESCRIPTION "${_p_DESCRIPTION}"
+                            TYPE "${_p_TYPE}"
+                            PURPOSE "${_p_PURPOSE}" )
+  else()
+
+    ecbuild_debug("ecbuild_use_package(${_p_PROJECT}): 4) project has NOT been added as a subproject and is NOT already FOUND")
+
+    set( _opts )
+    if( _p_VERSION )
+      list( APPEND _opts VERSION ${_p_VERSION} )
+    endif()
+    if( _p_EXACT )
+      list( APPEND _opts EXACT )
+    endif()
+    if( _p_REQUIRED )
+      list( APPEND _opts REQUIRED )
+    endif()
+    if( _p_URL )
+      list( APPEND _opts URL ${_p_URL} )
+    endif()
+    if( _p_DESCRIPTION )
+      list( APPEND _opts DESCRIPTION "${_p_DESCRIPTION}" )
+    endif()
+    if( _p_TYPE )
+      list( APPEND _opts TYPE ${_p_TYPE} )
+    endif()
+    if( _p_PURPOSE )
+      list( APPEND _opts PURPOSE "${_p_PURPOSE}" )
+    endif()
+    if( _p_FAILURE_MSG )
+      ecbuild_debug_var( _p_FAILURE_MSG )
+      list( APPEND _opts FAILURE_MSG "${_p_FAILURE_MSG}" )
+    endif()
+
+    ecbuild_find_package( NAME ${_p_PROJECT} ${_opts} )
+
+    if( ${_p_PROJECT}_FOUND )
+      set( ${pkgUPPER}_FOUND ${${_p_PROJECT}_FOUND} )
+    endif()
+
+  endif()
+
+  if( ${pkgUPPER}_FOUND )
+    list( APPEND ${PROJECT_NAME_CAPS}_TPLS ${_p_PROJECT} )
+    list( REMOVE_DUPLICATES ${PROJECT_NAME_CAPS}_TPLS )
+  endif()
+
+  ### for when we change this macro to a function()
+  # set_parent_scope( ${pkgUPPER}_FOUND )
+  # set_parent_scope( ${_p_PROJECT}_FOUND )
+  # set_parent_scope( ${pkgUPPER}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_VERSION )
+  # set_parent_scope( ${_p_PROJECT}_BINARY_DIR )
+
+endmacro()
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_version.h.in b/ecbuild/share/ecbuild/cmake/ecbuild_version.h.in
new file mode 100644
index 0000000..89b0e33
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_version.h.in
@@ -0,0 +1,20 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ecbuild_version_h
+#define ecbuild_version_h
+
+#define ECBUILD_VERSION "@ECBUILD_VERSION@"
+
+#define ECBUILD_MAJOR_VERSION @ECBUILD_MAJOR_VERSION@
+#define ECBUILD_MINOR_VERSION @ECBUILD_MINOR_VERSION@
+#define ECBUILD_PATCH_VERSION @ECBUILD_PATCH_VERSION@
+
+#endif // ecbuild_version_h
diff --git a/ecbuild/share/ecbuild/cmake/ecbuild_warn_unused_files.cmake b/ecbuild/share/ecbuild/cmake/ecbuild_warn_unused_files.cmake
new file mode 100644
index 0000000..7d330d7
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/ecbuild_warn_unused_files.cmake
@@ -0,0 +1,77 @@
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+##############################################################################
+#.rst:
+#
+# ecbuild_warn_unused_files
+# =========================
+#
+# Print warnings about unused source files in the project. ::
+#
+#   ecbuild_warn_unused_files()
+#
+# If the CMake variable ``CHECK_UNUSED_FILES`` is set, ecBuild will keep track
+# of any source files (.c, .cc, .cpp, .cxx) which are not part of a CMake
+# target. If set, this macro reports unused files if any have been found. This
+# is considered a fatal error unless ``UNUSED_FILES_LEVEL`` is set to a value
+# different from ``ERROR``.
+#
+# .. note ::
+#
+#   Enabling ``CHECK_UNUSED_FILES`` can slow down the CMake configure time
+#   considerably!
+#
+##############################################################################
+
+macro( ecbuild_warn_unused_files )
+
+    if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME ) # only for top level project
+    
+      # if cache file with unused files exists remove it
+      set( UNUSED_FILE "${CMAKE_BINARY_DIR}/UnusedFiles.txt" )
+      if( EXISTS ${UNUSED_FILE} )
+              file( REMOVE ${UNUSED_FILE} )
+      endif()
+    
+      if( CHECK_UNUSED_FILES ) # to check or not to check...
+    
+          if( NOT DEFINED UNUSED_FILES_LEVEL ) # to err or not...
+              set( UNUSED_FILES_LEVEL "ERROR" )
+          endif()
+    
+          # if unused files where found, put the list on the file
+          if( EC_UNUSED_FILES )
+    
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+            ecbuild_info(" Unused source files found:")
+            foreach( AFILE ${EC_UNUSED_FILES} )
+              ecbuild_info("     ${AFILE}")
+              file( APPEND ${UNUSED_FILE} "${AFILE}\n" )
+            endforeach()
+            ecbuild_info("")
+            ecbuild_info(" List dumped to ${UNUSED_FILE}")
+            ecbuild_info("")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info(" !!!--- ${UNUSED_FILES_LEVEL} ---!!! ")
+            ecbuild_info("")
+    
+            if( UNUSED_FILES_LEVEL STREQUAL "ERROR" )
+              ecbuild_critical( "\n Aborted build system configuration. \n Add unused files to the build system or remove them." )
+            endif()
+    
+          endif()
+    
+      endif()
+    
+    endif()
+
+endmacro( ecbuild_warn_unused_files )
diff --git a/ecbuild/share/ecbuild/cmake/fcm-make-interfaces.cfg b/ecbuild/share/ecbuild/cmake/fcm-make-interfaces.cfg
new file mode 100644
index 0000000..a73363a
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fcm-make-interfaces.cfg
@@ -0,0 +1,31 @@
+# FCM configuration file used to auto-generate interface files
+# for F77 and F90 files.
+# Interface files will have the extention ".intfb.h"
+# Results will be in a directory "interfaces/include" relative to cwd
+
+# Usage: fcm make --config-file=<path -to-this-file> \
+#                 interfaces.ns-incl="<space-sep-list-of-dirs>"
+
+$SRC{?}  = $HERE
+
+step.class[interfaces] = build
+steps  = interfaces
+
+interfaces.target{task}     = ext-iface
+interfaces.target{category} = include
+
+interfaces.source = $SRC
+
+# Exclude all
+interfaces.ns-excl = /
+
+# Include some
+# interfaces.ns-incl = <list of dirs passed at command-line>
+
+# Extention of interface files
+interfaces.prop{file-ext.f90-interface} = .intfb.h
+
+# Do not follow includes
+interfaces.prop{no-dep.f.module} = *
+interfaces.prop{no-dep.include} = *
+
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake b/ecbuild/share/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake
new file mode 100644
index 0000000..992964f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/CheckFortranFeatures.cmake
@@ -0,0 +1,167 @@
+###############################################################################
+# checks
+set(Fortran_FEATURE_CHECK_DIR ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "fortran file directory")
+
+MACRO(fortran_check_single_feature FEATURE_NAME FEATURE_NUMBER RESULT_VAR)
+  IF (NOT DEFINED ${RESULT_VAR})
+    SET(_bindir "${CMAKE_BINARY_DIR}/fortran_feature_tests/fortran_${FEATURE_NAME}")
+
+    IF (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME}-N${FEATURE_NUMBER})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\" (N${FEATURE_NUMBER})")
+    ELSE (${FEATURE_NUMBER})
+      SET(_SRCFILE_BASE ${Fortran_FEATURE_CHECK_DIR}/${FEATURE_NAME})
+      SET(_LOG_NAME "\"${FEATURE_NAME}\"")
+    ENDIF (${FEATURE_NUMBER})
+    ecbuild_info("Checking Fortran support for ${_LOG_NAME}")
+
+    SET(_SRCFILE "${_SRCFILE_BASE}.F90")
+    SET(_SRCFILE_FAIL "${_SRCFILE_BASE}_fail.F90")
+    SET(_SRCFILE_FAIL_COMPILE "${_SRCFILE_BASE}_fail_compile.F90")
+
+    IF (CROSS_COMPILING)
+      try_compile(${RESULT_VAR} "${_bindir}" "${_SRCFILE}")
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_compile(${RESULT_VAR} "${_bindir}_fail" "${_SRCFILE_FAIL}")
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ELSE (CROSS_COMPILING)
+      try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+          "${_bindir}" "${_SRCFILE}")
+      IF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} TRUE)
+      ELSE (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+        SET(${RESULT_VAR} FALSE)
+      ENDIF (_COMPILE_RESULT_VAR AND NOT _RUN_RESULT_VAR)
+      IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+        try_run(_RUN_RESULT_VAR _COMPILE_RESULT_VAR
+            "${_bindir}_fail" "${_SRCFILE_FAIL}")
+        IF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} TRUE)
+        ELSE (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+          SET(${RESULT_VAR} FALSE)
+        ENDIF (_COMPILE_RESULT_VAR AND _RUN_RESULT_VAR)
+      ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL})
+    ENDIF (CROSS_COMPILING)
+    IF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+      try_compile(_TMP_RESULT "${_bindir}_fail_compile" "${_SRCFILE_FAIL_COMPILE}")
+      IF (_TMP_RESULT)
+        SET(${RESULT_VAR} FALSE)
+      ELSE (_TMP_RESULT)
+        SET(${RESULT_VAR} TRUE)
+      ENDIF (_TMP_RESULT)
+    ENDIF (${RESULT_VAR} AND EXISTS ${_SRCFILE_FAIL_COMPILE})
+
+    IF (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- works")
+    ELSE (${RESULT_VAR})
+      ecbuild_info("Checking Fortran support for ${_LOG_NAME} -- not supported")
+    ENDIF (${RESULT_VAR})
+    SET(${RESULT_VAR} ${${RESULT_VAR}} CACHE INTERNAL "Fortran support for ${_LOG_NAME}")
+  ENDIF (NOT DEFINED ${RESULT_VAR})
+ENDMACRO(fortran_check_single_feature)
+
+# Find list of all features
+function(fortran_find_all_features outvar)
+  FILE(GLOB ALL_Fortran_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/*.F90")
+  set(OUTPUT_VARIABLES)
+  foreach(filename ${ALL_Fortran_FEATURE_FILES})
+    get_filename_component(filename ${filename} NAME_WE)
+    string(REGEX REPLACE "_fail_compile" "" filename "${filename}")
+    string(REGEX REPLACE "_fail" "" filename "${filename}")
+    string(REGEX REPLACE "-N[0-9]*" "" filename "${filename}")
+    set(OUTPUT_VARIABLES ${OUTPUT_VARIABLES} ${filename})
+  endforeach()
+  list(REMOVE_DUPLICATES OUTPUT_VARIABLES)
+  set(${outvar} ${OUTPUT_VARIABLES} PARENT_SCOPE)
+endfunction()
+
+# Parses input and separates into arguments before REQUIRED and after REQUIRED.
+# Arguments before REQUIRED are OPTIONALS.
+# Arguments after REQUIRED are REQUIRED.
+# If no arguments, then sets output OPTIONALS to ALLFEATURES.
+function(parse_input_features ALLFEATURES OPTIONALS REQUIRED ERRORS)
+
+  if("${ARGN}" STREQUAL "")
+    set(${OPTIONALS} ${ALLFEATURES} PARENT_SCOPE)
+    set(${REQUIRED} "" PARENT_SCOPE)
+  else()
+    set(REQUIRED_FEATURES)
+    set(OPTIONAL_FEATURES)
+    set(UNKNOWN_FEATURES)
+    set(result_type OPTIONAL_FEATURES)
+    foreach(feature ${ARGN})
+      if(${feature} STREQUAL "REQUIRED")
+        set(result_type REQUIRED_FEATURES)
+      else()
+        list(FIND ALLFEATURES ${feature} feature_was_found)
+
+        if(feature_was_found EQUAL -1)
+          list(APPEND UNKNOWN_FEATURES ${feature})
+        else()
+          list(APPEND ${result_type} ${feature})
+        endif()
+
+      endif(${feature} STREQUAL "REQUIRED")
+    endforeach()
+
+    set(${OPTIONALS} ${OPTIONAL_FEATURES} PARENT_SCOPE)
+    set(${REQUIRED} ${REQUIRED_FEATURES} PARENT_SCOPE)
+    set(${ERRORS} ${UNKNOWN_FEATURES} PARENT_SCOPE)
+  endif("${ARGN}" STREQUAL "")
+endfunction(parse_input_features)
+
+# Figures out name and number of feature
+# then calls macro that does the work
+macro(_figure_out_fortran_feature current_feature)
+  # Find set of files that match current_feature, excepting _fail and _fail_compile.
+  file(GLOB ALL_FEATURE_FILES "${Fortran_FEATURE_CHECK_DIR}/${current_feature}*.F90")
+  foreach(filename ${ALL_FEATURE_FILES})
+    if(filename MATCHES "_fail")
+      list(REMOVE_ITEM ALL_FEATURE_FILES ${filename})
+    endif()
+  endforeach()
+
+  list(LENGTH ALL_FEATURE_FILES NFILES)
+  if(NOT ${NFILES} EQUAL 1)
+    ecbuild_critical("[Fortran] Expected to find only one feature. Found ${NFILES} -- ${ALL_FEATURE_FILES}.")
+  endif(NOT ${NFILES} EQUAL 1)
+
+  # Now we know which file corresponds to option.
+  get_filename_component(basename ${ALL_FEATURE_FILES} NAME_WE)
+  # If has feature number, extract it
+  set(number "")
+  if(basename MATCHES "-N[0-9]*$")
+    string(REGEX REPLACE "${current_feature}-N" "" number "${basename}")
+  endif()
+  # Then call macro
+  string(TOUPPER ${current_feature} UPPER_OPTIONAL)
+  set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+  fortran_check_single_feature(${current_feature} "${number}" ${VARNAME})
+endmacro(_figure_out_fortran_feature)
+
+function(fortran_feature_check)
+
+  # find all features
+  fortran_find_all_features(ALL_Fortran_FEATURES)
+
+  # Parses input to this function.
+  parse_input_features("${ALL_Fortran_FEATURES}" OPTIONALS REQUIRED ERRORS ${ARGN})
+  if(NOT ${ERRORS} STREQUAL "")
+    ecbuild_info("[Fortran] The following features are unknown: ${ERRORS}.")
+  endif()
+
+  # Check optional features
+  foreach(current_feature ${OPTIONALS})
+    _figure_out_fortran_feature(${current_feature})
+  endforeach(current_feature ${ARGN})
+
+  # Check required features
+  foreach(current_feature ${REQUIRED})
+    _figure_out_fortran_feature(${current_feature})
+    set(VARNAME HAS_Fortran_${UPPER_OPTIONAL})
+    if(NOT ${VARNAME})
+      ecbuild_critical("[Fortran] Required feature ${current_feature} is not available.")
+    endif(NOT ${VARNAME})
+  endforeach(current_feature ${REQUIRED})
+
+endfunction(fortran_feature_check)
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/c_size_t.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/c_size_t.F90
new file mode 100644
index 0000000..3c47136
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/c_size_t.F90
@@ -0,0 +1,8 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_size_t, c_int, c_long
+
+write(0,*) "c_int    = ",c_int
+write(0,*) "c_long   = ",c_long
+write(0,*) "c_size_t = ",c_size_t
+
+end program
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/c_sizeof.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/c_sizeof.F90
new file mode 100644
index 0000000..fc1be41
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/c_sizeof.F90
@@ -0,0 +1,3 @@
+program test_c_sizeof
+use, intrinsic :: iso_c_binding, only : c_sizeof
+end program
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_interface.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_interface.F90
new file mode 100644
index 0000000..d59a1c3
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_interface.F90
@@ -0,0 +1,54 @@
+module constructor
+
+implicit none
+
+TYPE :: AnimalType
+  private
+  integer :: m_age
+contains
+  procedure :: age
+  procedure :: speak
+ENDTYPE
+
+! Declare constructor as interface with same name as type
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+contains
+
+function AnimalType__ctor(age) result(self)
+  type(AnimalType) :: self
+  integer :: age
+  write(0,'(A)') "Constructor Animal"
+  self%m_age = age
+end function
+
+function age(self)
+  class(AnimalType), intent(inout) :: self
+  integer :: age
+  age = self%m_age
+end function
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(A)') "Animal::speak not overridden"
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_constructor
+use constructor
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8)
+
+  write(0,'(A,I0)') "age = ",animal%age()
+
+  call animal%speak()
+
+end program
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_io.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_io.F90
new file mode 100644
index 0000000..47a98b0
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/derivedtype_io.F90
@@ -0,0 +1,42 @@
+module write_module
+
+implicit none
+
+TYPE :: AnimalType
+  integer :: m_age
+  integer :: m_paws
+contains
+  procedure :: writetype
+  generic :: write(formatted) => writetype
+ENDTYPE
+
+contains
+
+subroutine writetype(animal, unit, iotype, v_list, iostat, iomsg)
+  ! Argument names here from the std, but you can name them differently.
+  class(AnimalType), intent(in) :: animal ! Object to write.
+  integer, intent(in) :: unit             ! Internal unit to write to.
+  character(*), intent(in) :: iotype      ! LISTDIRECTED or DTxxx
+  integer, intent(in) :: v_list(:)        ! parameters from fmt spec.
+  integer, intent(out) :: iostat          ! non zero on error, etc.
+  character(*), intent(inout) :: iomsg    ! define if iostat non zero.
+
+  write (unit, "(A)", IOSTAT=iostat, IOMSG=iomsg) &
+      "I am a dog"
+end subroutine writetype
+
+end module
+
+! ------------------------------------------------------------------------
+
+program test_write
+use write_module
+implicit none
+
+  type(AnimalType) :: animal
+
+  animal = AnimalType(8,4)
+
+  write(0,'(A,DT)') 'Custom writing: ',animal
+
+end program
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/finalization.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/finalization.F90
new file mode 100644
index 0000000..5bacd5f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/finalization.F90
@@ -0,0 +1,141 @@
+module final_module
+
+implicit none
+
+integer :: final_counted = 0
+integer :: destroy_counted = 0
+
+TYPE :: AnimalType
+  character(len=20), private :: m_kind = "unidentified"
+  logical :: constructed = .false.
+contains
+  procedure :: speak
+  final :: AnimalType__dtor
+ENDTYPE
+
+interface AnimalType
+  module procedure AnimalType__ctor
+end interface
+
+interface assignment(=)
+  module procedure AnimalType__assignment
+end interface
+
+contains
+
+subroutine speak(self)
+  class(AnimalType), intent(in) :: self
+  write(0,'(2A)') "I am a ",self%m_kind
+end subroutine
+
+subroutine AnimalType__dtor(self)
+  type(AnimalType), intent(inout) :: self
+
+  write(0,'(2A)') "Final animal ",self%m_kind
+  final_counted = final_counted + 1
+
+  ! Destruction guard needed for portability
+  if( self%constructed ) then
+    write(0,'(2A)') "    Destroy animal ",self%m_kind
+    destroy_counted = destroy_counted + 1
+  endif
+end subroutine
+
+function AnimalType__ctor(animaltype_) result(self)
+  type(AnimalType) :: self
+  character(len=*) :: animaltype_
+  self%m_kind = animaltype_
+  write(0,'(3A,I0)') "Constructing animal ",self%m_kind, " -- address = ",loc(self)
+  self%constructed = .true.
+end function
+
+subroutine AnimalType__assignment(animal_out,animal_in)
+  type(AnimalType), intent(out) :: animal_out
+  class(AnimalType), intent(in) :: animal_in
+  write(0,'(3A,I0,A,I0)') '   Copying ',animal_in%m_kind, " -- from address ", loc(animal_in), " to address ", loc(animal_out)
+  animal_out%m_kind = animal_in%m_kind
+  animal_out%constructed = animal_in%constructed
+end subroutine
+
+end module
+
+! ------------------------------------------------------------------------
+
+subroutine scope_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: cat
+
+  dog = AnimalType("dog")  ! Cray       : final called on temporary AnimalType("dog"); missing final call on dog before assignment
+                           ! Intel      : final called on dog before assignment; and on temporary AnimalType("dog")
+                           ! PGI 14.4   : final NOT called at all, possibly compiler bug
+                           ! GNU 4.9    : final called on dog before assignment; missing call on temporary AnimalType("dog")
+  call dog%speak()
+
+  ! final called on dog when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+subroutine assignment_test
+use final_module
+implicit none
+
+  type(AnimalType) :: dog
+  type(AnimalType) :: animal
+
+  dog = AnimalType("dog")    ! final called on dog before assignment
+  call dog%speak()
+  write(0,'(A)') "-- animal = dog"
+  animal = dog               ! final called on animal before assignment
+  call animal%speak()
+
+  ! final called on dog when out of scope
+  ! final called on animal when out of scope
+end subroutine
+
+! -------------------------------------------------------
+
+program test_final
+use final_module
+implicit none
+  logical :: test_failed = .false.
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin scope_test"
+  call scope_test
+  write(0,'(A)') "<<<<<< end scope_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 1 ) then
+    test_failed = .true.
+    write(0,'(A)') "ASSERTION FAILED: destroy_counted < 1"
+  endif
+
+  final_counted = 0
+  destroy_counted = 0
+
+  write(0,'(A)') " "
+  write(0,'(A)') ">>>>>> begin assignment_test"
+  call assignment_test
+  write(0,'(A)') "<<<<<< end assignment_test"
+  write(0,'(A)') " "
+
+  write(0,'(A,I0)') "final_counted = ", final_counted
+  write(0,'(A,I0)') "destroy_counted = ", destroy_counted
+
+  if( destroy_counted < 2 ) then
+    test_failed = .true.
+    write(0,*) "ASSERTION FAILED: destroy_counted < 2"
+  endif
+  if( test_failed ) STOP 1
+
+end program
diff --git a/ecbuild/share/ecbuild/cmake/fortran_features/submodules.F90 b/ecbuild/share/ecbuild/cmake/fortran_features/submodules.F90
new file mode 100644
index 0000000..3a2261f
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/fortran_features/submodules.F90
@@ -0,0 +1,35 @@
+module sb_module
+implicit none
+integer :: a = 1
+
+interface
+  module subroutine sb
+  end subroutine
+end interface
+
+contains
+end module sb_module
+
+! -------------------------------------------------------
+
+submodule (sb_module) sb_submod1
+implicit none
+integer :: b = 2
+
+contains
+
+module subroutine sb()
+  a = b
+end subroutine
+
+end submodule sb_submod1
+
+! -------------------------------------------------------
+
+program test_submodule
+use sb_module
+implicit none
+write(0,*) a
+call sb()
+write(0,*) a
+end program
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/gen_source_flags.py b/ecbuild/share/ecbuild/cmake/gen_source_flags.py
new file mode 100644
index 0000000..c08ad08
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/gen_source_flags.py
@@ -0,0 +1,84 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+"""
+Generate .cmake file to set source-file specific compiler flags based on
+rules defined in a JSON file.
+"""
+
+from argparse import ArgumentParser
+from fnmatch import fnmatch
+import logging
+from json import JSONDecoder
+from os import path
+
+log = logging.getLogger('gen_source_flags')
+
+
+def match(source, pattern, op, flags, indent=0):
+    if fnmatch(source, pattern):
+
+        suff = '' if op[0] in ('+', '=', '/') else ' (nested pattern)'
+        log.debug('%s-> pattern "%s" matches "%s"%s',
+                  ' ' * (indent + 1), pattern, source, suff)
+
+        if op[0] == "+":
+            flags += [flag for flag in op[1:] if flag not in flags]
+            log.debug('%sappending %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "=":
+            flags = op[1:]
+            log.debug('%ssetting %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        elif op[0] == "/":
+            flags = [flag for flag in flags if flag not in op[1:]]
+            log.debug('%sremoving %s --> flags: %s', ' ' * (indent + 2), op[1:], flags)
+
+        else:  # Nested rule
+            log.debug('%sapplying nested rules for "%s" (flags: %s)',
+                      ' ' * (indent + 2), pattern, flags)
+            for nested_pattern, nested_op in op:
+                flags = match(source, nested_pattern, nested_op, flags, indent + 2)
+
+    return flags
+
+
+def generate(rules, out, default_flags, sources, debug=False):
+    logging.basicConfig(level=logging.DEBUG if debug else logging.INFO,
+                        format='-- %(levelname)s - %(name)s: %(message)s')
+
+    with open(path.expanduser(rules)) as f:
+        rules = JSONDecoder(object_pairs_hook=list).decode(f.read())
+
+    with open(path.expanduser(out), 'w') as f:
+        for source in sources:
+            log.debug('%s (default flags: "%s")', source, default_flags)
+            flags = default_flags.split()
+            for pattern, op in rules:
+                flags = match(source, pattern, op, flags)
+
+            if flags:
+                log.debug(' ==> setting flags for %s to %s', source, ' '.join(flags))
+                f.write('set_source_files_properties(%s PROPERTIES COMPILE_FLAGS "%s")\n'
+                        % (source, ' '.join(flags)))
+            else:
+                log.debug(' ==> flags for %s empty', source)
+
+
+def main():
+    """Parse arguments"""
+    parser = ArgumentParser(description=__doc__)
+    parser.add_argument('rules', metavar='RULES.json', help='JSON rules file')
+    parser.add_argument('out', metavar='OUT.cmake', help='CMake script to generate')
+    parser.add_argument('default_flags', help='Default compiler flags to use')
+    parser.add_argument('sources', metavar='file', nargs='+', help='Path to file to apply rules to')
+    parser.add_argument('--debug', '-d', action='store_true', help='Log debug messages')
+    generate(**vars(parser.parse_args()))
+
+if __name__ == '__main__':
+    main()
diff --git a/ecbuild/share/ecbuild/cmake/include/ecbuild/boost_test_framework.h b/ecbuild/share/ecbuild/cmake/include/ecbuild/boost_test_framework.h
new file mode 100644
index 0000000..ba48689
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/include/ecbuild/boost_test_framework.h
@@ -0,0 +1,17 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifdef BOOST_UNIT_TEST_FRAMEWORK_HEADER_ONLY
+#include <boost/test/included/unit_test.hpp>
+#else
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+#endif
+
diff --git a/ecbuild/share/ecbuild/cmake/md5.in b/ecbuild/share/ecbuild/cmake/md5.in
new file mode 100644
index 0000000..bf8aeb0
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/md5.in
@@ -0,0 +1 @@
+ at _p_MD5@  @_p_NAME@
diff --git a/ecbuild/share/ecbuild/cmake/pkg-config.pc.in b/ecbuild/share/ecbuild/cmake/pkg-config.pc.in
new file mode 100644
index 0000000..e6d903d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/pkg-config.pc.in
@@ -0,0 +1,35 @@
+# This pkg-config file is generated by ecbuild_pkgconfig()
+# with template ecbuild/cmake/pkg-config.pc.in
+
+git_tag=@PKGCONFIG_GIT_TAG@
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
+libdir=${prefix}/@INSTALL_LIB_DIR@
+includedir=${prefix}/@INSTALL_INCLUDE_DIR@
+bindir=${prefix}/@INSTALL_BIN_DIR@
+fmoddir=${prefix}/@INSTALL_INCLUDE_DIR@
+
+CC=@CMAKE_C_COMPILER@
+CXX=@CMAKE_CXX_COMPILER@
+FC=@CMAKE_Fortran_COMPILER@
+
+rpath=@RPATH_FLAG@${libdir}
+
+libs=-L${libdir} ${rpath} @PKGCONFIG_LIBS@
+
+libs_private=@PKGCONFIG_LIBS_PRIVATE@
+
+cflags=@PKGCONFIG_INCLUDE@ @PKGCONFIG_CFLAGS@
+ at PKGCONFIG_VARIABLES@
+#====================================================================
+Name: @PKGCONFIG_NAME@
+Description: @PKGCONFIG_DESCRIPTION@
+URL: @PKGCONFIG_URL@
+Version: @PKGCONFIG_VERSION@
+Libs: ${libs}
+Libs.private: ${libs_private}
+Requires: @PKGCONFIG_REQUIRES@
+Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@
+Cflags: ${cflags}
+#====================================================================
diff --git a/ecbuild/share/ecbuild/cmake/project-config-version.cmake.in b/ecbuild/share/ecbuild/cmake/project-config-version.cmake.in
new file mode 100644
index 0000000..802a2fe
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/project-config-version.cmake.in
@@ -0,0 +1,12 @@
+set(PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# check whether the requested PACKAGE_FIND_VERSION is compatible
+
+if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
+  set(PACKAGE_VERSION_COMPATIBLE FALSE)
+else()
+  set(PACKAGE_VERSION_COMPATIBLE TRUE)
+  if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
+    set(PACKAGE_VERSION_EXACT TRUE)
+  endif()
+endif()
\ No newline at end of file
diff --git a/ecbuild/share/ecbuild/cmake/project-config.cmake.in b/ecbuild/share/ecbuild/cmake/project-config.cmake.in
new file mode 100644
index 0000000..0e1e83b
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/project-config.cmake.in
@@ -0,0 +1,97 @@
+# Config file for the @PROJECT_NAME@ package
+# Defines the following variables:
+#
+#  @PNAME at _INCLUDE_DIRS   - include directories
+#  @PNAME at _DEFINITIONS    - preprocessor definitions
+#  @PNAME at _LIBRARIES      - libraries to link against
+#  @PNAME at _FEATURES       - list of enabled features
+#  @PNAME at _VERSION        - version of the package
+#  @PNAME at _GIT_SHA1       - Git revision of the package
+#  @PNAME at _GIT_SHA1_SHORT - short Git revision of the package
+#
+# Also defines @PROJECT_NAME@ third-party library dependencies:
+#  @PNAME at _TPLS             - package names of  third-party library dependencies
+#  @PNAME at _TPL_INCLUDE_DIRS - include directories
+#  @PNAME at _TPL_DEFINITIONS  - preprocessor definitions
+#  @PNAME at _TPL_LIBRARIES    - libraries to link against
+
+### compute paths
+
+get_filename_component(@PNAME at _CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
+
+set( @PNAME at _SELF_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@" )
+set( @PNAME at _SELF_DEFINITIONS  "@CONF_DEFINITIONS@" )
+set( @PNAME at _SELF_LIBRARIES    "@CONF_LIBRARIES@" )
+
+set( @PNAME at _TPLS              "@CONF_TPLS@" )
+set( @PNAME at _TPL_INCLUDE_DIRS  "@CONF_TPL_INCLUDE_DIRS@" )
+set( @PNAME at _TPL_DEFINITIONS   "@CONF_TPL_DEFINITIONS@" )
+set( @PNAME at _TPL_LIBRARIES     "@CONF_TPL_LIBRARIES@" )
+
+set( @PNAME at _VERSION           "@PACKAGE_VERSION@" )
+set( @PNAME at _GIT_SHA1          "@PACKAGE_GIT_SHA1@" )
+set( @PNAME at _GIT_SHA1_SHORT    "@PACKAGE_GIT_SHA1_SHORT@" )
+
+### export include paths as absolute paths
+
+set( @PNAME at _INCLUDE_DIRS "" )
+foreach( path ${@PNAME at _SELF_INCLUDE_DIRS} )
+  get_filename_component( abspath ${path} ABSOLUTE )
+  list( APPEND @PNAME at _INCLUDE_DIRS ${abspath} )
+endforeach()
+list( APPEND @PNAME at _INCLUDE_DIRS ${@PNAME at _TPL_INCLUDE_DIRS} )
+
+### export definitions
+
+set( @PNAME at _DEFINITIONS      ${@PNAME at _SELF_DEFINITIONS} ${@PNAME at _TPL_DEFINITIONS} )
+
+### export list of all libraries
+
+set( @PNAME at _LIBRARIES        ${@PNAME at _SELF_LIBRARIES}   ${@PNAME at _TPL_LIBRARIES}   )
+
+### export the features provided by the package
+
+set( @PNAME at _FEATURES    "@CONF_FEATURES@" )
+foreach( _f ${@PNAME at _FEATURES} )
+  set( @PNAME at _HAVE_${_f} 1 )
+endforeach()
+
+# Has this configuration been exported from a build tree?
+set( @PNAME at _IS_BUILD_DIR_EXPORT @_is_build_dir_export@ )
+
+if( EXISTS ${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@ )
+  set( @PNAME at _IMPORT_FILE "${@PNAME at _CMAKE_DIR}/@CONF_IMPORT_FILE@" )
+  include( ${@PNAME at _IMPORT_FILE} )
+endif()
+
+# here goes the imports of the TPL's
+
+include( ${CMAKE_CURRENT_LIST_FILE}.tpls OPTIONAL )
+
+# insert definitions for IMPORTED targets
+
+if( NOT @PROJECT_NAME at _BINARY_DIR )
+
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    include( "@TOP_PROJECT_TARGETS_FILE@" OPTIONAL )
+  else()
+    include( "${@PNAME at _CMAKE_DIR}/@PROJECT_NAME at -targets.cmake" OPTIONAL )
+  endif()
+
+endif()
+
+# publish this file as imported
+
+set( @PNAME at _IMPORT_FILE ${CMAKE_CURRENT_LIST_FILE} )
+mark_as_advanced( @PNAME at _IMPORT_FILE )
+
+# set @PROJECT_NAME at _BASE_DIR for final installations or build directories
+
+if( NOT @PROJECT_NAME@ )
+  if( @PNAME at _IS_BUILD_DIR_EXPORT )
+    set( @PROJECT_NAME at _BASE_DIR @CMAKE_BINARY_DIR@ )
+  else()
+    get_filename_component( abspath ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE )
+    set( @PROJECT_NAME at _BASE_DIR ${abspath} )
+  endif()
+endif()
diff --git a/ecbuild/share/ecbuild/cmake/pymain.c b/ecbuild/share/ecbuild/cmake/pymain.c
new file mode 100644
index 0000000..823d57d
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/pymain.c
@@ -0,0 +1,5 @@
+#include <Python.h>
+
+int main() {
+  return 0;
+}
diff --git a/ecbuild/share/ecbuild/cmake/sg.pl b/ecbuild/share/ecbuild/cmake/sg.pl
new file mode 100755
index 0000000..f8c8e31
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/sg.pl
@@ -0,0 +1,573 @@
+#!/usr/bin/perl
+#!/usr/local/share/perl56
+
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+use strict;
+
+#use Data::Dumper;
+use File::Basename;
+
+#$Data::Dumper::Indent = 1;
+# $ARGV[0] = "test.cc";
+# $ARGV[0] = "/usr/include/g++-3/stl_pair.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_vector.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_list.h";
+# $ARGV[0] = "/usr/include/g++-3/stl_map.h";
+# $ARGV[0] = "x.cc";
+# $ARGV[0] = "/usr/include/g++-3/std/bastring.h";
+
+# script takes 3 parameters:
+# (1) file to process
+my $file = $ARGV[0];
+# (2) [optional] directory to place the generated .b file
+my $base = $ARGV[1];
+# (3) [optional] c++ namespace 
+my $namespace = $ARGV[2];
+
+# no argv[1] passed, take basedir from file
+if( $base eq "" )
+{
+	$base = dirname($file);
+}
+
+# no argv[1] passed, take basedir from file
+if( $namespace eq "" )
+{
+    $namespace = "eclib"
+}
+
+my @c = parser::parse($file);
+#print Dumper(\@c);
+
+
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(STDOUT,">$base/$n.b") || die "$base/$n.b: $!";
+
+	my @init1;
+	push @init1, map { "$_(b)" } $c->super;
+	push @init1, map { "$_(b(\&$_))" } $c->members;
+
+	my $col1;
+	$col1=":\n" if(@init1);
+	my $init1 = join(",\n",map {"\t$_"} @init1);
+
+	my @init2;
+	push @init2, map { "$_(b(\"$n\"))" } $c->super;
+	push @init2, map { "$_(b(\"$n\",\"$_\"))" } $c->members;
+
+	my $col2;
+	$col2=":\n" if(@init2);
+	my $init2 = join(",\n",map {"\t$_"} @init2);
+
+
+	my @s = map { "${_}::describe(s,depth+1)"      } $c->super;
+	my @m = map { "${namespace}::_describe(s,depth+1,\"$_\",$_)" } $c->members;
+	my $d = join(";\n\t","${namespace}::_startClass(s,depth,specName())", at s, at m,"${namespace}::_endClass(s,depth,specName())");
+
+	my @s = map { "${_}::_export(h)"      } $c->super;
+	my @m = map { "${namespace}::_export(h,\"$_\",$_)" } $c->members;
+	my $D = join(";\n\t","${namespace}::_startClass(h,\"$n\")", at s, at m,"${namespace}::_endClass(h,\"$n\")");
+
+	my $spec = "\"$n\"";
+	my @tmpl = $c->template;
+
+	my $spec_type = "const char*";
+
+	if(@tmpl)
+	{
+		$spec_type = "std::string";
+		my $x = join("+ ',' + ",  map { "Traits<$_>::name()"; } @tmpl);
+		$spec = <<"EOS";
+        std::string("$n<\") + $x + ">"
+EOS
+		$spec =~ s/\n/ /g;
+	}
+
+	my $isa = "${namespace}::Isa::add(t,specName());";
+	foreach my $s ( $c->super )
+	{
+		$isa = "${s}::isa(t);$isa";
+	}
+
+	my $schema;
+	@s = map { "${_}::schema(s)"      } $c->super;
+	@m = map { $a=$_->[0]; $b=$_->[1]; "s.member(\"$a\",member_size($n,$a),member_offset($n,$a),\"$b\")" } $c->members_types;
+	$schema = join(";\n\t","s.start(specName(),sizeof($n))", at s, at m,"s.end(specName())");
+
+	print <<"EOF";
+
+${n}(${namespace}::Bless& b)$col1$init1
+{
+}
+
+${n}(${namespace}::Evolve b)$col2$init2
+{
+}
+
+static ${spec_type} specName()      { return ${spec}; }
+static void isa(TypeInfo* t)  { ${isa} }
+static ${namespace}::Isa* isa()             { return ${namespace}::Isa::get(specName());  }
+
+static void schema(${namespace}::Schema& s)
+{
+	$schema;
+}
+
+EOF
+
+if(!$c->has_method("describe"))
+{
+print <<"EOF";
+
+void describe(std::ostream& s,int depth = 0) const {
+	$d;
+}
+
+
+EOF
+}
+
+print <<"EOF";
+
+void _export(${namespace}::Exporter& h) const { 
+	$D;
+}
+
+
+EOF
+
+}
+if(0)
+{
+foreach my $c ( @c )
+{
+	my $n = $c->name;
+	open(OUT,">${n}.b");
+	select OUT;
+	print "static void schema(${namespace}::Schema& s) {\n";
+	foreach my $x ( $c->super )
+	{
+		print "${x}::schema(s);\n";
+		#print "s(\"$x\", 0,sizeof($x));\n";
+	}
+	foreach my $x ( $c->members )
+	{
+		print "s(\"${n}::$x\",offsetof($n,$x),sizeof(&(($n*)0)->$x));\n";
+	}
+	print "}\n";
+}
+}
+package parser;
+use Carp;
+my @TOKENS;
+sub parse {
+	my ($file) = @_;
+	local $/ = undef;
+	open(IN,"<$file") || croak "$file: $!";
+	my $x = <IN>;
+	close(IN);
+	$x =~ s/^#.*$//mg;
+	$x =~ s/\/\/.*$//mg;
+	@TOKENS =
+		grep { length($_);                }
+		map  { /\W/ ? split('',$_) :  $_; }
+		map  { s/\s//g; $_;               }
+		split(/\b/, $x );
+
+	my @c;
+	my $x;
+	while($x = consume_until("(typedef|template|class|struct)"))
+	{
+		if($x eq 'typedef')
+		{
+			consume_until(";");
+			next;
+		}
+
+		if($x eq 'template')
+		{
+			push @c, parse_template();
+		}
+		else
+		{
+			push @c, parse_class();
+		}
+	}
+	return grep { defined $_; } @c;
+}
+
+sub parse_template {
+	my @tmp = template_args();
+	return parse_class(@tmp) if(next_is("(class|struct)"));
+}
+sub template_args {
+	my @tmp;
+	expect_next("<");
+	for(;;)
+	{
+		expect_next("(class|bool|int)");
+		push @tmp, next_ident();
+		if(next_is("="))
+		{
+			my $x = consume_until('(,|\>|\<)');
+			unshift @TOKENS,$x;
+			while($x eq '<')
+			{
+				consume_block('<','>');
+				$x = consume_until('(,|\>|\<)');
+				unshift @TOKENS,$x;
+			}
+		}
+		last unless(next_is(","));
+	}
+	expect_next(">");
+	return @tmp;
+}
+sub parse_class {
+	my (@tmp) = @_;
+	my $self = {};
+	my $name = next_ident();
+	$self->{name}     = $name;
+	$self->{template} = \@tmp if(@tmp);
+	# Foreward declaration
+	return if(next_is(";"));
+	if(next_is(":"))
+	{
+		for(;;)
+		{
+			ignore_while("(public|private|protected|virtual)");
+			push @{$self->{super}}, next_ident();
+			last unless(next_is(","));
+		}
+	}
+	expect_next('{');
+	while(!peek_next('}'))
+	{
+		# print "... : $TOKENS[0], $TOKENS[1], ... \n";
+		if(next_is('\/'))
+		{
+			if(next_is('\*'))
+			{
+				while(!next_is('\/'))
+				{
+					consume_until('\*');
+				}
+				next;
+			}
+			else
+			{
+				unshift @TOKENS, "/";	
+			}
+
+		}
+
+		if(next_is("(public|private|protected)"))
+		{
+			expect_next(":");
+			next;
+		}
+
+		if(next_is("friend"))
+		{
+			my $x = consume_until("(;|{)");
+			if($x eq "{")
+			{
+				unshift @TOKENS, $x;
+				consume_block('{','}');
+			}
+			next;
+		}
+
+		# next_is("explicit");
+		if(next_is("(typedef|using|typename|enum)"))
+		{
+			consume_until(";");
+			next;
+		}
+		if(next_is("(class|struct)"))
+		{
+			push @{$self->{classes}}, parse_class();
+			next;
+		}
+		my %m;
+		while(next_is("template"))
+		{
+			push @{ $m{template} } , template_args();
+		}
+
+		my @x;
+#		push @x,"~" while(next_is('\~'));
+
+		$m{explicit} = 1 if(next_is("explicit"));
+		$m{static}   = 1 if(next_is("static"));
+		$m{virtual}  = 1 if(next_is("virtual"));
+		my $x;
+		while($x = next_is_ident())
+		{
+			# print "--- : $x\n";
+			push @x, $x;
+			push @x,'*' while(next_is('\*'));
+			push @x,'&' while(next_is('\&'));
+			$m{name} = $x;
+			# int a,b,*c; does not work
+			my $s;
+			if($s = next_is('(,|;|=)'))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				if(exists $m{static})
+				{
+					push @{$self->{class_members}}, \%m;
+				}
+				else
+				{
+					push @{$self->{members}}, \%m;
+				}
+				consume_until(";") if($s eq '=');
+				last;
+			}
+			if(peek_next('\('))
+			{
+				pop @x;
+				$m{type} = make_type(@x);
+				my @args = consume_block('(',')');
+				shift @args;
+				pop @args;
+				my @a;
+				my $n = 0;
+				my @z;
+				foreach my $a ( @args )
+				{
+					if($a eq ',' && $n == 0)
+					{
+						push @a, make_type(@z);
+						@z = ();
+						next;
+					}
+					$n++ if($a eq '<');	
+					$n++ if($a eq '(');	
+					$n-- if($a eq ')');	
+					$n-- if($a eq '>');	
+					push @z,$a;
+				}
+				push @a, make_type(@z) if(@z);
+				$m{const} = 1 if(next_is("const"));
+				$m{args}  = \@a;
+				if(exists $m{static})
+				{
+					push @{$self->{class_methods}}, \%m;
+				}
+				else
+				{
+					push @{$self->{methods}}, \%m;
+				}
+				# print "f: $x\n";
+
+				if(next_is(':'))
+				{
+					# print "{: $TOKENS[0]\n";
+					consume_until('\{');
+					unshift @TOKENS, '{';
+					consume_block('{','}');
+					# print "}: $TOKENS[0]\n";
+				}
+				else
+				{
+					if(peek_next('\{'))
+					{
+						consume_block('{','}');
+					}
+					else
+					{
+						if(next_is("="))
+						{
+							expect_next("0");
+							$m{abstract} = 1;
+						}
+						expect_next(";");
+					}
+				}
+				last;
+			}
+		}
+
+	}
+	expect_next("}");
+	expect_next(";");
+	return bless($self,"class");
+}
+sub consume_until {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		return $x if($x =~ /^$r$/);
+	}
+	return undef;
+}
+sub consume_block {
+	my ($bra,$ket) = @_;
+	my $n = 0;
+	my @x;
+	croak "@TOKENS" unless($bra eq $TOKENS[0]);
+	while(@TOKENS)
+	{
+		my $x = shift @TOKENS;
+		$n++ if($x eq $bra);
+		$n-- if($x eq $ket);
+		push @x,$x;
+		return @x if($n == 0);
+	}
+}
+sub ignore_while {
+	my ($r) = @_;
+	while(@TOKENS)
+	{
+		return unless($TOKENS[0] =~ /^$r$/);
+		shift @TOKENS;
+	}
+}
+sub expect_next {
+	my ($r) = @_;
+	my $ident = shift @TOKENS;
+	croak "$ident is not $r" unless($ident =~ /^$r$/);
+	return $ident;
+}
+sub next_ident {
+	my $x = next_is_ident();
+	croak "not an ident " unless($x);
+	return $x;
+}
+sub next_is {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return shift @TOKENS;
+	}
+	return undef;
+}
+sub next_is_ident {
+	my $op = next_is("operator");
+	if($op)
+	{
+		my $x;
+		if($x = next_is("(new|delete)"))
+		{
+			$op .= " $x";
+			if(next_is('\['))
+			{
+				expect_next('\]');
+				$op .= "[]";
+			}
+			return $op;
+		}
+
+		if(next_is('\('))
+		{
+			expect_next('\)');
+			$op .= "()";
+		}
+		my $z;
+		while($z = next_is('[\-+/\*\[\]<>=!]'))
+		{
+			$op .= $z;
+		}
+		return $op;
+	}
+	my $y = next_is('\~');
+	my $x = next_is('\w+');
+	if($x)
+	{
+		$x = "$y$x";
+		if(peek_next("<"))
+		{
+			my @x = consume_block("<",">");
+			$x .= join("", at x);
+		}
+
+		if(next_is(":"))
+		{
+			if(next_is(":"))
+			{
+				my $z = next_is_ident();
+				return "${x}::${z}";
+			}
+			else
+			{
+				unshift @TOKENS, ":";
+			}
+		}
+	}
+	return $x;
+}
+sub peek_next {
+	my ($r) = @_;
+	if($TOKENS[0] =~ /^$r$/)
+	{
+		return 1;
+	}
+	return 0;
+}
+sub make_type {
+	my (@a) = @_;
+	my $p;
+	my @x;
+	foreach my $a ( @a )
+	{
+		push @x, " " if($p =~ /^(\w+|\&|\*)$/ && $a =~ /^\w+$/);
+		push @x, $a;
+		$p = $a;
+	}
+	my $s = join('', at x);
+	$s =~ s/>>/> >/g;
+	return $s;
+}
+package class;
+sub name {
+	my ($self) = @_;
+	return $self->{name};
+}
+sub super {
+	my ($self) = @_;
+	return $self->{super} ? @{$self->{super}} : ();
+}
+
+sub members {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub methods {
+	my ($self) = @_;
+	my @x = $self->{methods} ? @{$self->{methods}} : ();
+	return map { $_->{name} } @x;
+}
+
+sub has_method {
+	my ($self,$name) = @_;
+	return grep { $_ eq $name } $self->methods;
+}
+
+sub members_types {
+	my ($self) = @_;
+	my @x = $self->{members} ? @{$self->{members}} : ();
+	return map { [ $_->{name}, $_->{type} ]  } @x;
+}
+
+sub template {
+	my ($self) = @_;
+	return $self->{template} ? @{$self->{template}} : ();
+}
+1;
+
+
diff --git a/ecbuild/share/ecbuild/cmake/sha1.in b/ecbuild/share/ecbuild/cmake/sha1.in
new file mode 100644
index 0000000..8e744a7
--- /dev/null
+++ b/ecbuild/share/ecbuild/cmake/sha1.in
@@ -0,0 +1 @@
+ at _p_SHA1@  @_p_NAME@
diff --git a/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake
new file mode 100644
index 0000000..e3ef29c
--- /dev/null
+++ b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake
@@ -0,0 +1,49 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  Cray )
+CMAKE_FORCE_CXX_COMPILER     ( CC  Cray )
+CMAKE_FORCE_Fortran_COMPILER ( ftn Cray )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-homp" )
+set( OMP_CXX_FLAGS           "-homp" )
+set( OMP_Fortran_FLAGS       "-homp" )
+
+set( OMPSTUBS_C_FLAGS        "-hnoomp" )
+set( OMPSTUBS_CXX_FLAGS      "-hnoomp" )
+set( OMPSTUBS_Fortran_FLAGS  "-hnoomp" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr -Ktrap=fp" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Ktrap=fp -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Ktrap=fp -Wl,-Map,loadmap -Wl,--as-needed" )
+set( ECBUILD_CXX_IMPLICIT_LINK_LIBRARIES "$ENV{CC_X86_64}/lib/x86-64/libcray-c++-rts.so" CACHE STRING "" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake
new file mode 100644
index 0000000..84dbd7b
--- /dev/null
+++ b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake
@@ -0,0 +1,52 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  GNU )
+CMAKE_FORCE_CXX_COMPILER     ( CC  GNU )
+CMAKE_FORCE_Fortran_COMPILER ( ftn GNU )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-fopenmp" )
+set( OMP_CXX_FLAGS           "-fopenmp" )
+set( OMP_Fortran_FLAGS       "-fopenmp" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -ftrapv" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -ftrapv" )
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-ffree-line-length-none -O0 -g -fcheck=bounds -fbacktrace -finit-real=snan -ffpe-trap=invalid,zero,overflow" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Wl,-Map,loadmap -Wl,--as-needed" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake
new file mode 100644
index 0000000..a890886
--- /dev/null
+++ b/ecbuild/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake
@@ -0,0 +1,73 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  Intel )
+CMAKE_FORCE_CXX_COMPILER     ( CC  Intel )
+CMAKE_FORCE_Fortran_COMPILER ( ftn Intel )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-openmp -openmp-threadprivate=compat" )
+set( OMP_CXX_FLAGS           "-openmp -openmp-threadprivate=compat" )
+set( OMP_Fortran_FLAGS       "-openmp -openmp-threadprivate=compat" )
+
+####################################################################
+# COMMON FLAGS
+####################################################################
+
+# for diagnostics:
+#  -diag-enable=vec -diag-file -Winline
+
+set( ECBUILD_C_FLAGS       "-fp-speculation=strict -fp-model precise -traceback")
+set( ECBUILD_CXX_FLAGS     "-fp-speculation=strict -fp-model precise -traceback" )
+set( ECBUILD_Fortran_FLAGS "-fp-speculation=strict -fp-model source  -convert big_endian -assume byterecl -traceback -fpe0" )
+
+####################################################################
+# BIT REPRODUCIBLE FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_BIT        "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_CXX_FLAGS_BIT      "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_Fortran_FLAGS_BIT  "-O2 -xAVX -finline-function -finline-limit=500 -align array64byte" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -traceback -fp-trap=common" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -traceback -fp-trap=common" )
+# -check all implies -check bounds
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-O0 -g -traceback -warn all -heap-arrays -fpe-all=0 -fpe:0 -check all" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Wl,-Map,loadmap -Wl,--as-needed" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/ecbuild/share/ecbuild/toolchains/ichec-fionn-Intel.cmake b/ecbuild/share/ecbuild/toolchains/ichec-fionn-Intel.cmake
new file mode 100644
index 0000000..184dfb5
--- /dev/null
+++ b/ecbuild/share/ecbuild/toolchains/ichec-fionn-Intel.cmake
@@ -0,0 +1,67 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( icc  Intel )
+CMAKE_FORCE_CXX_COMPILER     ( icpc  Intel )
+CMAKE_FORCE_Fortran_COMPILER ( ifort Intel )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-openmp -openmp-threadprivate=compat" )
+set( OMP_CXX_FLAGS           "-openmp -openmp-threadprivate=compat" )
+set( OMP_Fortran_FLAGS       "-openmp -openmp-threadprivate=compat" )
+
+####################################################################
+# COMMON FLAGS
+####################################################################
+
+# for diagnostics:
+#  -diag-enable=vec -diag-file -Winline
+
+set( ECBUILD_C_FLAGS       "-fp-speculation=strict -fp-model precise -traceback")
+set( ECBUILD_CXX_FLAGS     "-fp-speculation=strict -fp-model precise -traceback" )
+set( ECBUILD_Fortran_FLAGS "-fp-speculation=strict -fp-model source  -convert big_endian -assume byterecl -traceback -fpe0" )
+
+####################################################################
+# BIT REPRODUCIBLE FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_BIT        "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_CXX_FLAGS_BIT      "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_Fortran_FLAGS_BIT  "-O2 -xAVX -finline-function -finline-limit=500 -align array64byte" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -traceback -fp-trap=common" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -traceback -fp-trap=common" )
+# -check all implies -check bounds
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-O0 -g -traceback -warn all -heap-arrays -fpe-all=0 -fpe:0 -check all" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_C_LINK_FLAGS        "-Wl,-Map,load.map -Wl,--as-needed" )
+set( ECBUILD_CXX_LINK_FLAGS      "-Wl,-Map,load.map -Wl,--as-needed" )
+set( ECBUILD_Fortran_LINK_FLAGS  "-Wl,-Map,load.map -Wl,--as-needed" )
+
+###################################################################
+# 
+# Serial versions of these packages (need to specify intel_mpi versions? )
+###################################################################
+
+set( FFTW_PATH    "/ichec/packages/fftw/intel/3.3.4")
+set( NETCDF_PATH  "/ichec/packages/netcdf/intel/4.4.0")
+set( HDF5_PATH    "/ichec/packages/hdf5/intel/1.8.16")
diff --git a/ecbuild/share/ecmwf_license_header.txt b/ecbuild/share/ecmwf_license_header.txt
new file mode 100644
index 0000000..67f88d9
--- /dev/null
+++ b/ecbuild/share/ecmwf_license_header.txt
@@ -0,0 +1,9 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. In
+ * applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
diff --git a/ecbuild/tools/CMakeLists.txt b/ecbuild/tools/CMakeLists.txt
new file mode 100644
index 0000000..8580e31
--- /dev/null
+++ b/ecbuild/tools/CMakeLists.txt
@@ -0,0 +1,13 @@
+list( APPEND ecbuild_bin_files
+ecbuild
+git-meld
+git-mproj
+check_install.sh 
+apply_license.sh
+license.pl )
+
+add_custom_target( ecbuild_bin SOURCES ${ecbuild_bin_files} )
+
+install( PROGRAMS ecbuild DESTINATION ${INSTALL_BIN_DIR} )
+
+#	install( PROGRAMS ${ecbuild_bin_files} DESTINATION ${INSTALL_BIN_DIR} )
diff --git a/ecbuild/tools/apply_license.sh b/ecbuild/tools/apply_license.sh
new file mode 100755
index 0000000..6c932d5
--- /dev/null
+++ b/ecbuild/tools/apply_license.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+if [ -z $1 ]
+then
+    echo "apply_license.sh"
+    echo "usage: $0 [dir] [dir] ... "
+    echo "dir - directory where to search"
+    exit 1
+fi
+
+for f in $( find $DIRS $* \(   \
+            -iname "*.java" \
+        -or -iname "*.xml" \
+        -or -iname "*.sh"  \
+        -or -iname "*.pl"  \
+        -or -iname "*.pm"  \
+        -or -iname "*.py"  \
+        -or -iname "*.js"  \
+        -or -iname "*.c"   \
+        -or -iname "*.cpp" \
+        -or -iname "*.cxx" \
+        -or -iname "*.cc"  \
+        -or -iname "*.h"   \
+        -or -iname "*.hh"  \
+        -or -iname "*.hpp" \
+        -or -iname "*.l"   \
+        -or -iname "*.y"   \
+        -or -iname "*.f"   \
+        -or -iname "*.F"   \
+        -or -iname "*.for" \
+        -or -iname "*.f77" \
+        -or -iname "*.f90" \
+        -or -iname "*.cmake" \
+        -or -iname "*.css"   \
+        -or -iname "*.sql"   \
+        -or -iname "*.properties"  \
+        -or -iname "*.def" \
+ \) -print -follow | grep -v "\.git/" | grep -v "\.svn/" )
+do
+#  echo $f
+  license.pl -u $f
+done
+
+#|  sed "s/ /\\\ /g" | \
+#xargs echo
+
+exit
diff --git a/ecbuild/tools/check_install.sh b/ecbuild/tools/check_install.sh
new file mode 100755
index 0000000..4e5369c
--- /dev/null
+++ b/ecbuild/tools/check_install.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+chksm="md5sum"
+manifest_file="manifest.txt"
+install_proc="echo_install"
+
+#------------------------------------------------------------------------------
+
+function abspath {
+    if [[ -d "$1" ]]
+    then
+        pushd "$1" >/dev/null
+        pwd
+        popd >/dev/null
+    elif [[ -e $1 ]]
+    then
+        pushd $(dirname $1) >/dev/null
+        echo $(pwd)/$(basename $1)
+        popd >/dev/null
+    else
+        echo "error: path $1 does not exist!" >&2
+        return 127
+    fi
+}
+
+function echo_install {
+  echo "$1 -> $2"
+}
+
+#------------------------------------------------------------------------------
+
+if [ -z $2 ]
+then
+    echo "usage: $0 <src_dir> <target_dir>"
+    exit 1
+fi
+
+[[ ! -z $3 ]] && install_proc="$3"
+
+SRC=$(abspath $1)
+TGT=$(abspath $2)
+
+manifest="$SRC/$manifest_file"
+
+#------------------------------------------------------------------------------
+
+[[ ! -f $manifest ]] && echo "error: cannot find manifest.txt in dir $SRC" && exit 1
+
+cd $SRC
+
+for f in $(cat $manifest )
+do
+
+  [[ ! -f "$SRC/$f" ]] && echo "error: $SRC/$f does not exist" && exit 1
+
+  install="no"
+
+  if [[ ! -f "$TGT/$f" ]]
+  then
+    install="yes"
+  else
+    srcsum=$( $chksm "$SRC/$f" | cut -d" " -f1 )
+    tgtsum=$( $chksm "$TGT/$f" | cut -d" " -f1 )
+
+    if [ $srcsum != "$tgtsum" ]
+    then
+      install="yes"
+    fi
+  fi
+
+   if [ $install == "yes" ]
+   then
+     $install_proc $SRC/$f $TGT/$f
+   fi
+
+done
diff --git a/ecbuild/tools/git-meld b/ecbuild/tools/git-meld
new file mode 100755
index 0000000..3f1e353
--- /dev/null
+++ b/ecbuild/tools/git-meld
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+meld $2 $5
diff --git a/ecbuild/tools/git-mproj b/ecbuild/tools/git-mproj
new file mode 100755
index 0000000..80cf794
--- /dev/null
+++ b/ecbuild/tools/git-mproj
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# (C) Copyright 1996-2017 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+#
+# git-proj: status, branch, checkout
+#
+
+txtbld=$(tput bold)             #  bold
+bldblu=${txtbld}$(tput setaf 6) #  bold cyan
+txtrst=$(tput sgr0)             #  reset
+
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="$dashless <command> [options...]"
+
+SUBDIRECTORY_OK="yes"
+
+PATH=$(git --exec-path):$PATH
+. git-sh-setup
+
+require_work_tree_exists
+
+# go to top
+
+cd_to_toplevel
+cd ..
+
+# check option for number of levels
+nlevels=$(git config --global --int --get mproj.levels)
+if [ "$nlevels" != "" ]
+then
+    nlevels="-maxdepth $nlevels"
+fi
+
+# do it
+
+for d in $( find $PWD $nlevels -type d -iname ".git" )
+do
+    gdir=$(dirname $d)
+    proj=$(basename $gdir)
+    pushd $gdir > /dev/null
+    branch=$(git symbolic-ref --short HEAD)
+    echo -e "${bldblu}---> $proj ($branch)${txtrst}"
+    git "$@"
+    popd > /dev/null
+done
+
+
diff --git a/ecbuild/tools/license.pl b/ecbuild/tools/license.pl
new file mode 100755
index 0000000..3d97841
--- /dev/null
+++ b/ecbuild/tools/license.pl
@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+use strict;
+
+my $LICENSE = <<"EOF";
+(C) Copyright 1996-2017 ECMWF.
+
+This software is licensed under the terms of the Apache Licence Version 2.0
+which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+In applying this licence, ECMWF does not waive the privileges and immunities
+granted to it by virtue of its status as an intergovernmental organisation
+nor does it submit to any jurisdiction.
+EOF
+
+my %COMMENTS = (
+
+		java       => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		xml        => { start => "<!--\n", end => "-->\n\n" , after => "\<\?xml[^<]*\>" },
+#		xsd        => { start => "<!--\n", end => "-->\n\n" },
+#		jsp        => { start => "<!--\n", end => "-->\n\n" },
+		sh         => { comment => "# ", end => "\n", after => "^#!/.*\n" },
+		pl         => { comment => "# ", end => "\n", after => "^#!/.*\n" },
+		pm         => { comment => "# ", end => "\n", after => "^#!/.*\n" },
+		py         => { comment => "# ", end => "\n", after => "^#!/.*\n" },
+		js         => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		c          => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		cc         => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		cpp        => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		cxx        => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		h          => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		hh         => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		hpp        => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		l          => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		'y'        => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		'f'        => { comment => "C ", end => "C\n\n" }, # assume f77
+		'F'        => { comment => "C ", end => "C\n\n" }, # assume f77
+		'for'      => { comment => "C ", end => "C\n\n" }, # assume f77
+		'f77'      => { comment => "C ", end => "C\n\n" },
+		'f90'      => { comment => "! ", end => "!\n\n" },
+		cmake      => { end => "\n", comment => "# " },
+		css        => { start => "/*\n"  , end => " */\n\n"  , comment => " * " },
+		sql        => { comment => "-- ", end => "\n" },
+		properties => { comment => "# ", end => "\n" },
+		def        => { comment => "# ", end => "\n" },
+
+		);
+
+my %cmdargs = map { $_ => 1 } @ARGV;
+
+foreach my $file ( @ARGV )
+{
+    next if( $file eq "-u" or $file eq "--update" );
+
+#   my $doit=0;
+    my $doit=1;
+
+	$file =~ /\.(\w+)$/;
+	my $ext = $1;
+
+	my $c = $COMMENTS{$ext};
+
+	unless($c)
+	{
+		print "$file: unsupported extension. File ignored\n";
+		next;
+	}
+
+	open(IN,"<$file") or die "$file: $!";
+	my @text = <IN>;
+	close(IN);
+
+	if(join("", at text) =~ /icensed under the/gs)
+	{
+        if( exists( $cmdargs{"-u"} ) or exists( $cmdargs{"--update"} ) )
+        {
+            # lets update the year if needed
+            my $currentyear = (localtime)[5] + 1900;
+            if($doit)
+            {
+                print("$file: updating license year to $currentyear\n");
+                system("perl -pi -e 's/Copyright ([0-9]{4})-[0-9]{4} ECMWF/Copyright \$1-$currentyear ECMWF/' $file");
+            }
+        }
+        else
+        {
+            print "$file: License already stated. File ignored\n";
+        }
+		next;
+	}
+
+	open(OUT,">$file.tmp") or die "$file.tmp: $!";
+
+	if($c->{after})
+	{
+		my @x;
+		my $re = $c->{after};
+		loop: while(@text)
+		{
+			if($text[0] =~ m/$re/)
+			{
+				print OUT @x, shift @text;
+				@x = ();
+				last loop;
+			}
+			push @x,shift @text;
+		}
+		@text = (@x, at text);
+	}
+
+	print OUT $c->{start};
+	foreach my $line ( split("\n",$LICENSE) )
+	{
+		print OUT $c->{comment}, $line,"\n";
+	}
+	print OUT $c->{end};
+
+	print OUT @text;
+	close(OUT) or die "$file: $!";
+
+    if($doit)
+    {
+        use File::Copy qw(cp);
+        use File::Compare qw(compare_text compare);
+
+        if(compare_text("$file.tmp",$file))
+        {
+            print "UPDATING file $file\n";
+            system("p4 edit $file") unless(-w $file);
+#s            cp($file,"$file.old") or die "cp($file,$file.old): $!";
+            cp("$file.tmp",$file) or die "cp($file.tmp,$file): $!";
+        }
+    }
+    else
+    {
+        print "IGNORING file $file\n";
+    }
+
+    unlink("$file.tmp");
+
+
+}
diff --git a/ecbuild/tox.ini b/ecbuild/tox.ini
new file mode 100644
index 0000000..e44b810
--- /dev/null
+++ b/ecbuild/tox.ini
@@ -0,0 +1,2 @@
+[flake8]
+ignore = E501
diff --git a/eckit/.clang-format b/eckit/.clang-format
new file mode 100644
index 0000000..2e78ae7
--- /dev/null
+++ b/eckit/.clang-format
@@ -0,0 +1,4 @@
+Language:        Cpp
+BasedOnStyle:  Google
+ColumnLimit:     120
+AccessModifierOffset: -2
\ No newline at end of file
diff --git a/eckit/.gitignore b/eckit/.gitignore
new file mode 100644
index 0000000..db6c96a
--- /dev/null
+++ b/eckit/.gitignore
@@ -0,0 +1,8 @@
+.tags*
+CMakeLists.txt.user*
+*.autosave
+doc/html
+doc/latex
+*.sublime-workspace
+.*.sw*
+.*un~
diff --git a/eckit/AUTHORS b/eckit/AUTHORS
new file mode 100644
index 0000000..fcab634
--- /dev/null
+++ b/eckit/AUTHORS
@@ -0,0 +1,23 @@
+eckit authors
+=============
+
+The main development of eckit is done through at the
+
+       European Centre for Medium-Range Weather Forecasts
+               (ECMWF - http://www.ecmwf.int )
+
+
+Main programmers:
+=================
+
+Baudouin Raoult
+Manuel Fuentes
+Tiago Quintino
+Piotr Kuchta
+Florian Rathgeber
+
+Past developers
+===============
+
+Thanks for contributions from:
+==============================
diff --git a/eckit/CMakeLists.txt b/eckit/CMakeLists.txt
new file mode 100644
index 0000000..9ed8656
--- /dev/null
+++ b/eckit/CMakeLists.txt
@@ -0,0 +1,217 @@
+########################################################################################################################
+#
+#   eckit - ECMWF C++ Toolkit
+#
+########################################################################################################################
+
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+project( eckit CXX )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.4 )
+
+set( PERSISTENT_NAMESPACE "eckit" CACHE INTERNAL "" ) # needed for generating .b files for persistent support
+
+###############################################################################
+# local project
+
+ecbuild_declare_project()
+
+###############################################################################
+# some variables/options of this project
+
+### Experimental features
+
+ecbuild_add_option( FEATURE EXPERIMENTAL
+                    DEFAULT ON
+                    DESCRIPTION "Experimental features Armadillo, CUDA, MKL, ViennaCL (not used in production)" )
+
+### eckit::mpi
+
+ecbuild_add_option( FEATURE MPI
+                    DEFAULT ON
+                    DESCRIPTION "Use system MPI libraries"
+                    REQUIRED_PACKAGES "MPI COMPONENTS CXX" )
+
+### eckit::cmd
+
+ecbuild_add_option( FEATURE ECKIT_CMD
+                    DESCRIPTION "Utilities for administration tools"
+                    REQUIRED_PACKAGES "LEXYACC" )
+
+### Eigen
+
+ecbuild_add_option( FEATURE EIGEN
+                    DESCRIPTION "Eigen linear algebra library"
+                    REQUIRED_PACKAGES Eigen3 )
+
+set_package_properties( Eigen3 PROPERTIES
+                        TYPE RECOMMENDED
+                        DESCRIPTION "C++ template library for linear algebra"
+                        PURPOSE "Dense and sparse matrix operations" )
+
+if( ENABLE_EIGEN )
+    # TODO: we should add here a test that we can compile Eigen without problems
+    # TODO: don't use contrib just yet -- sort out the legal stuff first
+    # set( ECKIT_CONTRIB_EIGEN 1 )
+endif()
+
+### Armadillo
+
+ecbuild_add_option( FEATURE ARMADILLO
+                    CONDITION HAVE_EXPERIMENTAL
+                    DESCRIPTION "Armadillo linear algebra library"
+                    REQUIRED_PACKAGES Armadillo )
+
+set_package_properties( Armadillo PROPERTIES
+                        DESCRIPTION "C++ linear algebra"
+                        TYPE RECOMMENDED
+                        PURPOSE "Dense matrix operations" )
+
+### LAPACK
+
+ecbuild_add_option( FEATURE LAPACK
+                    DESCRIPTION "Linear Algebra PACKage"
+                    REQUIRED_PACKAGES "LAPACK" )
+
+### OpenSSL (for SHA1)
+
+ecbuild_add_option( FEATURE SSL
+                    DESCRIPTION "OpenSSL support"
+                    TYPE RECOMMENDED
+                    PURPOSE "OpenSSL provides crypto and hashing algorithms including SHA1"
+                    REQUIRED_PACKAGES OpenSSL )
+
+ecbuild_info("OpenSSL ${OPENSSL_FOUND} ${OPENSSL_VERSION} : libs ${OPENSSL_LIBRARIES} incs ${OPENSSL_INCLUDE_DIR}")
+
+### xxHash
+
+ecbuild_add_option( FEATURE XXHASH
+                    DESCRIPTION "Use xxHash for non-crypto hashing in data integrity checks"
+                    PURPOSE     "xxHash support"
+                    REQUIRED_PACKAGES "PROJECT xxHash" )
+
+ecbuild_info("xxHash ${XXHASH_FOUND}  : libs ${XXHASH_LIBRARIES} incs ${XXHASH_INCLUDE_DIRS}")
+
+#### CUDA
+
+ecbuild_add_option( FEATURE CUDA
+                    CONDITION HAVE_EXPERIMENTAL
+                    DESCRIPTION "CUDA GPU linear algebra operations"
+                    REQUIRED_PACKAGES CUDA )
+
+### MKL
+
+ecbuild_add_option( FEATURE MKL
+                    CONDITION HAVE_EXPERIMENTAL
+                    DESCRIPTION "MKL linear algebra library"
+                    REQUIRED_PACKAGES MKL )
+
+### ViennaCL
+
+ecbuild_add_option( FEATURE VIENNACL
+                    CONDITION HAVE_EXPERIMENTAL
+                    DESCRIPTION "ViennaCL OpenCL linear algebra operations"
+                    REQUIRED_PACKAGES ViennaCL )
+
+set_package_properties( ViennaCL PROPERTIES
+                        DESCRIPTION "linear algebra library for computations on many-core architectures"
+                        TYPE RECOMMENDED
+                        PURPOSE "Dense and sparse matrix operations on OpenCL devices" )
+
+### async io support
+
+find_package( AIO )
+set_package_properties( AIO PROPERTIES TYPE RECOMMENDED PURPOSE "support for asynchronous IO" )
+
+### thread library ( preferably pthreads )
+
+set( CMAKE_THREAD_PREFER_PTHREAD 1 )
+find_package( Threads REQUIRED )
+
+### Curses (for CmdApplication)
+
+find_package( Curses REQUIRED )
+
+###############################################################################
+# checks
+
+# check thread library is pthreads
+
+if( NOT ${CMAKE_USE_PTHREADS_INIT} )
+    message( FATAL_ERROR "Only pthreads supported - thread library found is [${CMAKE_THREAD_LIBS_INIT}]" )
+endif()
+
+# fail if we dont have bool
+if( NOT EC_HAVE_CXX_BOOL )
+    message( FATAL_ERROR "eckit cannot build -- c++ compiler does not support bool" )
+endif()
+# fail if we dont have sstream
+if( NOT EC_HAVE_CXX_SSTREAM )
+    message( FATAL_ERROR "eckit cannot build -- c++ compiler does not support stringstream" )
+endif()
+
+############################################################################################
+# export package info
+
+set( ECKIT_INCLUDE_DIRS   ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src )
+set( ECKIT_LIBRARIES      eckit eckit_geometry eckit_linalg eckit_maths eckit_web )
+
+if( HAVE_ECKIT_CMD)
+  list( APPEND ECKIT_LIBRARIES eckit_cmd )
+endif()
+
+if( HAVE_ECKIT_MPI )
+  list( APPEND ECKIT_LIBRARIES eckit_mpi )
+endif()
+
+if( HAVE_EIGEN AND ECKIT_CONTRIB_EIGEN )
+  list( APPEND ECKIT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/contrib )
+endif()
+
+if( HAVE_EXPERIMENTAL )
+
+  list( APPEND ECKIT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/experimental )
+
+endif()
+
+get_directory_property( ECKIT_DEFINITIONS COMPILE_DEFINITIONS )
+
+############################################################################################
+# sources
+
+ecbuild_find_project_files()
+
+include_directories( ${ECKIT_INCLUDE_DIRS} )
+if( HAVE_EIGEN )
+    include_directories( ${EIGEN3_INCLUDE_DIR} )
+endif()
+
+add_subdirectory( bamboo )
+add_subdirectory( contrib )
+add_subdirectory( doc )
+add_subdirectory( src )
+add_subdirectory( regressions )
+
+ecbuild_add_resources( TARGET ${PROJECT_NAME}_top_files SOURCES
+                       TODO AUTHORS README NOTICE LICENSE INSTALL ChangeLog COPYING )
+
+############################################################################################
+# finalize
+
+foreach( _lib ${ECKIT_LIBRARIES} )
+  if( TARGET ${_lib} )
+    ecbuild_pkgconfig( NAME ${_lib}
+                       DESCRIPTION "ECMWF C++ Toolkit - ${_lib} library"
+                       URL "https://software.ecmwf.int/wiki/display/ECKIT/ecKit"
+                       LIBRARIES ${_lib} )
+  endif()
+endforeach()
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/eckit/COPYING b/eckit/COPYING
new file mode 100644
index 0000000..f4ed7ed
--- /dev/null
+++ b/eckit/COPYING
@@ -0,0 +1,201 @@
+                                Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2007-2012 European Centre for Medium-Range Weather Forecasts (ECMWF)
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/eckit/ChangeLog b/eckit/ChangeLog
new file mode 100755
index 0000000..e69de29
diff --git a/eckit/INSTALL b/eckit/INSTALL
new file mode 100644
index 0000000..e69de29
diff --git a/eckit/LICENSE b/eckit/LICENSE
new file mode 100644
index 0000000..475dcc3
--- /dev/null
+++ b/eckit/LICENSE
@@ -0,0 +1,190 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   Copyright 1996-2017 ECMWF
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/eckit/NOTICE b/eckit/NOTICE
new file mode 100644
index 0000000..72128be
--- /dev/null
+++ b/eckit/NOTICE
@@ -0,0 +1,13 @@
+eckit
+=====
+
+Copyright 1996-2017 ECMWF
+
+This product is developed by the
+
+  Development Section, European Centre for Medium-Range Weather Forecasts (ECMWF) - http://www.ecmwf.int
+
+Below is a list of software packages which are used inside eckit:
+
+- No external package in currently included
+
diff --git a/eckit/README b/eckit/README
new file mode 100644
index 0000000..fdd1edf
--- /dev/null
+++ b/eckit/README
@@ -0,0 +1,26 @@
+eckit README
+============
+
+1. Getting the source
+=====================
+
+The tarball of the source can be downloaded from 
+
+     https://software.ecmwf.int/wiki/display/eckit/Releases
+
+
+2. Installation
+===============
+
+Please read the installation guide:
+
+  https://software.ecmwf.int/wiki/display/eckit/Building
+
+
+3. License
+==========
+
+Please read COPYING and LICENSE.
+
+
+
diff --git a/eckit/TODO b/eckit/TODO
new file mode 100644
index 0000000..883c556
--- /dev/null
+++ b/eckit/TODO
@@ -0,0 +1,10 @@
+TODO
+
+ - remove architecture dependent headers
+ - add control of behavior on Exception
+     - output on constructor
+     - backtrace on contructor
+     - abort on constructor
+     - abort on double exception
+
+ - Allow finer control on logging format ( eg [%time][%pid] message )
diff --git a/eckit/VERSION.cmake b/eckit/VERSION.cmake
new file mode 100644
index 0000000..c9c3fa3
--- /dev/null
+++ b/eckit/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.16.3")
diff --git a/eckit/bamboo/CMakeLists.txt b/eckit/bamboo/CMakeLists.txt
new file mode 100644
index 0000000..18b8087
--- /dev/null
+++ b/eckit/bamboo/CMakeLists.txt
@@ -0,0 +1,4 @@
+file( GLOB_RECURSE bamboo_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" )
+
+ecbuild_add_resources(  TARGET ${PROJECT_NAME}_bamboo
+						SOURCES_DONT_PACK ${bamboo_files} )
diff --git a/eckit/contrib/CMakeLists.txt b/eckit/contrib/CMakeLists.txt
new file mode 100644
index 0000000..bfdbe70
--- /dev/null
+++ b/eckit/contrib/CMakeLists.txt
@@ -0,0 +1,14 @@
+### Eigen
+
+if( HAVE_EIGEN AND ECKIT_CONTRIB_EIGEN )
+
+	install( DIRECTORY eigen3 DESTINATION ${INSTALL_INCLUDE_DIR} )
+
+	### pack all contrib directory
+
+	file( GLOB_RECURSE contrib_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" )
+
+	ecbuild_add_resources(  TARGET ${PROJECT_NAME}_contribs
+							SOURCES_PACK ${contrib_files} )
+endif()
+
diff --git a/eckit/doc/CMakeLists.txt b/eckit/doc/CMakeLists.txt
new file mode 100644
index 0000000..fa3b0be
--- /dev/null
+++ b/eckit/doc/CMakeLists.txt
@@ -0,0 +1,18 @@
+ecbuild_add_option(
+  FEATURE DOCS
+  DESCRIPTION "Generate reference documentation"
+  REQUIRED_PACKAGES "Doxygen" )
+
+if( HAVE_DOCS )
+
+  set( ECKIT_DOC eckit_doc )
+  if( PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME )
+    set( ECKIT_DOC doc )
+  endif()
+
+  set( DOXYFILE Doxyfile CACHE INTERNAL "Doxygen filename" )
+  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${DOXYFILE}.in "${CMAKE_CURRENT_BINARY_DIR}/${DOXYFILE}" @ONLY )
+  add_custom_target( ${ECKIT_DOC}
+  COMMAND doxygen ${CMAKE_CURRENT_BINARY_DIR}/${DOXYFILE}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
+endif()
diff --git a/eckit/doc/Doxyfile.in b/eckit/doc/Doxyfile.in
new file mode 100644
index 0000000..0b11a77
--- /dev/null
+++ b/eckit/doc/Doxyfile.in
@@ -0,0 +1,2280 @@
+# Doxyfile 1.8.5
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "ECKIT"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-
+# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en,
+# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish,
+# Turkish, Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = ../src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/eckit/eckit.sublime-project b/eckit/eckit.sublime-project
new file mode 100644
index 0000000..168c355
--- /dev/null
+++ b/eckit/eckit.sublime-project
@@ -0,0 +1,29 @@
+{
+	"folders":
+	[
+		{
+            "file_exclude_patterns": [".tags", ".tags_sorted_by_file", ".gemtags","CMakeLists.txt.user*"],
+			"follow_symlinks": true,
+			"path": "."
+		}
+	],
+	"build_systems": [
+        {
+            "working_dir": "${project_path}/../../build/eckit",
+            "cmd": [
+                "make"
+            ],
+            "file_regex": "([/\\w\\-\\.]+):(\\d+):(\\d+:)?",
+            "name": "ecbuild"
+        }
+    ],
+    "SublimeLinter":
+    {
+        "linters":
+        {
+            "cpplint": {
+                "filter": "-whitespace/line_length,-whitespace/blank_line,-runtime/references"
+            },
+        }
+    }
+}
diff --git a/eckit/project_summary.cmake b/eckit/project_summary.cmake
new file mode 100644
index 0000000..c61dac1
--- /dev/null
+++ b/eckit/project_summary.cmake
@@ -0,0 +1,16 @@
+if( MPI_CXX_FOUND )
+
+	message( STATUS "MPI")
+
+	message( STATUS "	MPI_CXX_COMPILER      : [${MPI_CXX_COMPILER}]")
+	message( STATUS "	MPI_CXX_INCLUDE_PATH  : [${MPI_CXX_INCLUDE_PATH}]")
+	message( STATUS "	MPI_CXX_LIBRARIES     : [${MPI_CXX_LIBRARIES}]")
+
+	message( STATUS "	MPIEXEC               : [${MPIEXEC}]")
+
+endif()
+
+if(CURSES_FOUND)
+  message( STATUS " Curses   includes : [${CURSES_INCLUDE_DIR}]" )
+  message( STATUS "          libs     : [${CURSES_LIBRARIES}]" )
+endif()
diff --git a/eckit/regressions/CMakeLists.txt b/eckit/regressions/CMakeLists.txt
new file mode 100644
index 0000000..8e18b8e
--- /dev/null
+++ b/eckit/regressions/CMakeLists.txt
@@ -0,0 +1,17 @@
+list( APPEND regressions ECKIT-175 )
+
+foreach( r ${regressions} )
+
+  configure_file( ${r}.sh.in ${r}.sh @ONLY )
+
+  ecbuild_add_executable(
+    NOINSTALL
+    TARGET   ${r}.x
+    SOURCES  ${r}.cc
+    LIBS     eckit )
+
+  ecbuild_add_test(
+    TYPE     SCRIPT
+    COMMAND  ${r}.sh )
+
+endforeach()
diff --git a/eckit/regressions/ECKIT-175.cc b/eckit/regressions/ECKIT-175.cc
new file mode 100644
index 0000000..bbb67de
--- /dev/null
+++ b/eckit/regressions/ECKIT-175.cc
@@ -0,0 +1,108 @@
+
+/*
+
+BUG ECKIT-175
+=============
+
+    when Main::initialise(argc,argv) is *not*
+    called or a Tool is *not* used,
+    then everything is OK, and the 
+    FileTarget gets destructed as opposed to
+    when Main::initialise(argc,argv) *is* called,
+    or a Tool *is* used.
+
+    Program expects an integer argument. Possible values: 
+    0 :
+        Test does *not* initialise a Main::instance()
+        Everything works as expected.
+
+    1 :
+        Test explicitely starts with Main::initialise(argc,argv)
+        Buffers should be flushed but aren't
+    2 :
+        Test uses a eckit::Tool to initialise a Main::instance()
+        Buffers should be flushed but aren't
+
+Note:
+    The destructor ~FileTarget() is modified to print
+    a message when invoked, so don't merge this
+
+*/
+
+#include <map>
+#include <cstdlib>
+#include <iostream>
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/log/Log.h"
+
+using namespace eckit;
+
+void myrun() {
+
+  Log::setFile("ECKIT-175.out");
+
+  Log::info() << "Flushed message in myrun()" << std::endl;
+  Log::info() << "NON Flushed message in myrun()\n";
+
+}
+
+class MyTool : public Tool {
+public:
+  MyTool(int argc, char **argv) : Tool(argc,argv) {}
+  virtual ~MyTool() {}
+  virtual void run() { myrun(); }
+};
+
+
+enum { NO_INIT = 1, INIT = 2, TOOL = 3 };
+
+
+int main(int argc, char **argv) {
+
+    if( argc != 2 ) {
+      std::cout << "Usage: " << argv[0] << " <case> " << std::endl;
+      std::cout << "       Case values: 1 2 3" << std::endl;
+      return 1;
+    }
+
+    int test = std::atoi(argv[1]);
+
+//    std::cerr << "###########################################################################" << std::endl;
+
+//    Log::print(std::cerr);
+
+//    std::cerr << "###########################################################################" << std::endl;
+
+    switch( test ) {
+
+        case 1: {
+            myrun();
+        break;
+        }
+
+        case 2: {
+            Main::initialise(argc,argv);
+            myrun();
+            break;
+        }
+
+        case 3: {
+            MyTool tool(argc,argv);
+            tool.start();
+            break;
+        }
+
+        default:
+            exit(1);
+            break;
+    }
+
+//    std::cerr << "###########################################################################" << std::endl;
+
+//    Log::print(std::cerr);
+
+//    std::cerr << "###########################################################################" << std::endl;
+
+    return 0;
+}
diff --git a/eckit/regressions/ECKIT-175.sh.in b/eckit/regressions/ECKIT-175.sh.in
new file mode 100755
index 0000000..d392338
--- /dev/null
+++ b/eckit/regressions/ECKIT-175.sh.in
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+set -eux
+
+cd @CMAKE_CURRENT_BINARY_DIR@
+
+
+### Case 1
+
+rm -f ECKIT-175.out && ./ECKIT-175.x 1
+
+out=$(cat ECKIT-175.out | grep "NON Flushed message in myrun")
+
+[[ -z ${out:-} ]] && echo "Failed" && exit 1
+
+
+### Case 2
+
+rm -f ECKIT-175.out && ./ECKIT-175.x 2
+
+out=$(cat ECKIT-175.out | grep "NON Flushed message in myrun")
+
+[[ -z ${out:-} ]] && echo "Failed" && exit 1
+
+
+### Case 3
+
+rm -f ECKIT-175.out && ./ECKIT-175.x 3
+
+out=$(cat ECKIT-175.out | grep "NON Flushed message in myrun")
+
+[[ -z ${out:-} ]] && echo "Failed" && exit 1
+
+
+
+echo "OK"
diff --git a/eckit/src/CMakeLists.txt b/eckit/src/CMakeLists.txt
new file mode 100644
index 0000000..1b95c3d
--- /dev/null
+++ b/eckit/src/CMakeLists.txt
@@ -0,0 +1,19 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+### sources
+
+add_subdirectory( eckit  )
+
+if( HAVE_EXPERIMENTAL )
+  add_subdirectory( experimental )
+endif()
+
+add_subdirectory( apps )
+
+add_subdirectory( tests )
diff --git a/eckit/src/apps/CMakeLists.txt b/eckit/src/apps/CMakeLists.txt
new file mode 100644
index 0000000..492bc4c
--- /dev/null
+++ b/eckit/src/apps/CMakeLists.txt
@@ -0,0 +1,4 @@
+ecbuild_add_executable( TARGET      eckit_version
+                        SOURCES     eckit_version.cc
+                        LIBS        eckit )
+
diff --git a/eckit/src/apps/eckit_version.cc b/eckit/src/apps/eckit_version.cc
new file mode 100644
index 0000000..083f53f
--- /dev/null
+++ b/eckit/src/apps/eckit_version.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/config/Resource.h"
+#include "eckit/log/Log.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Version : public Tool {
+public:
+
+    Version(int argc, char **argv): Tool(argc, argv) {}
+
+    ~Version() {}
+
+    virtual void run();
+};
+
+void Version::run()
+{
+    if ( Resource<bool>("-long", false) )
+    {
+        Log::info() << "eckit" << std::endl
+                    << "    version: " << eckit_version() << std::endl
+                    << "    version str: " << eckit_version_str() << std::endl
+                    << "    sha1: " << eckit_git_sha1() << std::endl;
+
+        if ( Resource<bool>("-build", false) )
+        {
+            Log::info() << "    build type  : " << ECKIT_BUILD_TYPE << std::endl
+                        << "    timestamp   : " << ECKIT_BUILD_TIMESTAMP << std::endl
+                        << "    op. system  : " << ECKIT_OS_NAME << " (" << ECKIT_OS_STR << ")"  << std::endl
+                        << "    processor   : " << ECKIT_SYS_PROCESSOR  << std::endl
+                        << "    c compiler  : " << ECKIT_C_COMPILER_ID << " " << ECKIT_C_COMPILER_VERSION << std::endl
+                        << "      flags     : " << ECKIT_C_FLAGS << std::endl
+                        << "    c++ compiler: " << ECKIT_CXX_COMPILER_ID << " " << ECKIT_CXX_COMPILER_VERSION << std::endl
+                        << "      flags     : " << ECKIT_CXX_FLAGS << std::endl;
+        }
+    }
+    else
+    {
+        Log::info() << eckit_version() << std::endl;
+    }
+}
+
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit;
+
+int main(int argc, char **argv)
+{
+    Version app(argc, argv);
+    return app.start();
+}
+
diff --git a/eckit/src/cppcheck-suppress b/eckit/src/cppcheck-suppress
new file mode 100644
index 0000000..37c6916
--- /dev/null
+++ b/eckit/src/cppcheck-suppress
@@ -0,0 +1,3 @@
+unusedStructMember:src/eckit/memory/MapAllocator.cc
+unusedStructMember:src/eckit/memory/MemoryPool.cc
+unusedFunction:*
diff --git a/eckit/src/eckit/CMakeLists.txt b/eckit/src/eckit/CMakeLists.txt
new file mode 100644
index 0000000..531c53c
--- /dev/null
+++ b/eckit/src/eckit/CMakeLists.txt
@@ -0,0 +1,686 @@
+### config headers
+
+ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/eckit )
+
+configure_file( eckit_config.h.in   eckit_config.h  )
+configure_file( eckit_version.h.in  eckit_version.h )
+
+install(FILES
+			${CMAKE_CURRENT_BINARY_DIR}/eckit_config.h
+			${CMAKE_CURRENT_BINARY_DIR}/eckit_version.h
+		DESTINATION
+			${INSTALL_INCLUDE_DIR}/eckit )
+
+configure_file( eckit_version.cc.in  eckit_version.cc )
+
+### eckit sources
+
+list( APPEND eckit_srcs eckit.h eckit_version.cc )
+
+list( APPEND eckit_container_srcs
+
+container/BTree.h
+container/BTree.cc
+
+container/Cache.h
+container/CacheLRU.h
+container/CacheLRU.cc
+
+container/CacheManager.h
+container/CacheManager.cc
+container/DenseMap.h
+container/StatCollector.h
+container/Recycler.h
+container/ClassExtent.h
+container/KDMemory.h
+container/KDMapped.cc
+container/KDMapped.h
+
+container/KDTree.h
+container/BSPTree.h
+
+container/MappedArray.h
+container/MappedArray.cc
+
+container/SharedMemArray.h
+container/SharedMemArray.cc
+
+container/sptree/SPIterator.h
+container/sptree/SPNode.h
+
+container/sptree/SPNodeQueue.h
+container/sptree/SPTree.h
+container/sptree/SPValue.h
+container/sptree/SPNodeInfo.h
+container/sptree/SPMetadata.h
+
+container/kdtree/KDNode.h
+container/kdtree/KDNode.cc
+container/kdtree/KDNode.cc
+
+container/bsptree/BSPNode.h
+container/bsptree/BSPNode.cc
+container/bsptree/BSPHyperPlane.h
+
+
+)
+
+list( APPEND eckit_io_srcs
+io/Length.h
+io/Length.cc
+io/DataHandle.h
+io/DataHandle.cc
+io/Select.h
+io/Select.cc
+io/CommandStream.h
+io/CommandStream.cc
+io/Offset.h
+io/Offset.cc
+io/DataBlob.h
+io/DataBlob.cc
+io/Buffer.cc
+io/Buffer.h
+io/Buffer.cc
+io/BufferCache.h
+io/BufferCache.cc
+io/FileLocker.h
+io/FileLocker.cc
+io/FileLock.h
+io/FileLock.cc
+io/FilePool.h
+io/FilePool.cc
+io/ResizableBuffer.h
+io/ResizableBuffer.cc
+io/DblBuffer.h
+io/DblBuffer.cc
+io/HandleBuf.h
+io/HandleBuf.cc
+io/StdFile.h
+io/StdFile.cc
+io/StdPipe.h
+io/StdPipe.cc
+io/StdioBuf.h
+io/StdioBuf.cc
+io/FileBase.h
+io/FileBase.cc
+io/TransferWatcher.h
+io/TransferWatcher.cc
+io/MoverTransfer.h
+io/MoverTransfer.cc
+io/SockBuf.h
+io/SockBuf.cc
+io/AIOHandle.h
+io/AIOHandle.cc
+io/SharedHandle.h
+io/SharedHandle.cc
+io/BufferedHandle.h
+io/BufferedHandle.cc
+io/MemoryHandle.h
+io/MemoryHandle.cc
+io/EmptyHandle.h
+io/EmptyHandle.cc
+io/FTPHandle.h
+io/FTPHandle.cc
+io/FileDescHandle.h
+io/FileDescHandle.cc
+io/FileHandle.h
+io/FileHandle.cc
+io/MarsFSHandle.h
+io/MarsFSHandle.cc
+io/MarsFSPartHandle.h
+io/MarsFSPartHandle.cc
+io/PartHandle.h
+io/PartHandle.cc
+io/MultiPartHandle.h
+io/MultiPartHandle.cc
+io/StatsHandle.h
+io/StatsHandle.cc
+io/MoverHandle.h
+io/MoverHandle.cc
+io/MultiHandle.h
+io/MultiHandle.cc
+io/PartFileHandle.h
+io/PartFileHandle.cc
+io/PipeHandle.h
+io/PipeHandle.cc
+io/StdFileHandle.h
+io/StdFileHandle.cc
+io/RawFileHandle.cc
+io/RawFileHandle.h
+io/TCPHandle.h
+io/TCPHandle.cc
+io/TCPSocketHandle.h
+io/TCPSocketHandle.cc
+io/TeeHandle.h
+io/TeeHandle.cc
+io/HandleHolder.h
+io/HandleHolder.cc
+io/cluster/ClusterDisks.h
+io/cluster/ClusterDisks.cc
+io/cluster/ClusterNode.h
+io/cluster/ClusterNode.cc
+io/cluster/ClusterNodes.h
+io/cluster/ClusterNodes.cc
+io/cluster/NodeInfo.h
+io/cluster/NodeInfo.cc )
+
+list( APPEND eckit_filesystem_srcs
+filesystem/FileSystemSize.h
+filesystem/PathName.h
+filesystem/PathName.cc
+filesystem/BasePathNameT.h
+filesystem/BasePathNameT.cc
+filesystem/BasePathName.h
+filesystem/LocalPathName.h
+filesystem/LocalPathName.cc
+filesystem/FileManager.h
+filesystem/FileManager.cc
+filesystem/FileName.h
+filesystem/FileName.cc
+filesystem/FileSpace.h
+filesystem/FileSpace.cc
+filesystem/FileSpaceStrategies.h
+filesystem/FileSpaceStrategies.cc
+filesystem/FileSystem.h
+filesystem/FileSystem.cc
+filesystem/TempFile.h
+filesystem/TempFile.cc
+filesystem/TmpFile.h
+filesystem/TmpFile.cc
+filesystem/marsfs/MarsFSFile.h
+filesystem/marsfs/MarsFSFile.cc
+filesystem/marsfs/MarsFSPath.h
+filesystem/marsfs/MarsFSPath.cc
+filesystem/marsfs/MarsFSClient.h
+filesystem/marsfs/MarsFSClient.cc )
+
+list( APPEND eckit_thread_srcs
+    thread/AutoLock.h
+    thread/Mutex.cc
+    thread/Mutex.h
+    thread/MutexCond.cc
+    thread/MutexCond.h
+    thread/Once.h
+    thread/StaticMutex.cc
+    thread/StaticMutex.h
+    thread/Thread.cc
+    thread/Thread.h
+    thread/ThreadControler.cc
+    thread/ThreadControler.h
+    thread/ThreadPool.cc
+    thread/ThreadPool.h
+    thread/ThreadSingleton.h
+)
+
+list( APPEND eckit_config_srcs
+config/Configurable.cc
+config/Configurable.h
+config/Configuration.cc
+config/Configuration.h
+config/Configured.cc
+config/Configured.h
+config/EtcTable.cc
+config/EtcTable.h
+config/JSONConfiguration.cc
+config/JSONConfiguration.h
+config/LibEcKit.cc
+config/LibEcKit.h
+config/LocalConfiguration.cc
+config/LocalConfiguration.h
+config/Parametrisation.h
+config/Resource.h
+config/Resource.cc
+config/ResourceBase.cc
+config/ResourceMgr.cc
+config/ResourceMgr.h
+)
+
+list( APPEND eckit_runtime_srcs
+runtime/Task.h
+runtime/Task.cc
+runtime/TaskID.h
+runtime/Main.h
+runtime/Main.cc
+runtime/Application.h
+runtime/Application.cc
+runtime/Tool.h
+runtime/Tool.cc
+runtime/Library.h
+runtime/Library.cc
+runtime/Monitor.h
+runtime/Monitor.cc
+runtime/Monitorable.h
+runtime/Monitorable.cc
+runtime/TaskInfo.h
+runtime/TaskInfo.cc
+runtime/PipeApplication.h
+runtime/PipeApplication.cc
+runtime/PipeHandler.h
+runtime/PipeHandler.cc
+runtime/ProcessControler.h
+runtime/ProcessControler.cc
+runtime/Dispatcher.h
+runtime/Pipe.h
+runtime/ProducerConsumer.h )
+
+list( APPEND eckit_log_srcs
+log/BigNum.h
+log/BigNum.cc
+log/Bytes.h
+log/Bytes.cc
+log/CallbackTarget.h
+log/CallbackTarget.cc
+log/TimeStampTarget.h
+log/TimeStampTarget.cc
+log/PrefixTarget.h
+log/PrefixTarget.cc
+log/WrapperTarget.h
+log/WrapperTarget.cc
+log/IndentTarget.h
+log/IndentTarget.cc
+log/Channel.h
+log/Channel.cc
+log/OStreamTarget.h
+log/OStreamTarget.cc
+log/LineBasedTarget.h
+log/LineBasedTarget.cc
+log/TeeTarget.h
+log/TeeTarget.cc
+log/StatusTarget.h
+log/StatusTarget.cc
+log/MessageTarget.h
+log/MessageTarget.cc
+log/ChannelBuffer.h
+log/ChannelBuffer.cc
+log/CodeLocation.h
+log/CodeLocation.cc
+log/ColouringTarget.h
+log/ColouringTarget.cc
+log/Colour.h
+log/Colour.cc
+log/ETA.h
+log/ETA.cc
+log/FileTarget.h
+log/FileTarget.cc
+log/Log.h
+log/Log.cc
+log/OStreamHandle.h
+log/Plural.h
+log/Progress.h
+log/Progress.cc
+log/RotationTarget.h
+log/RotationTarget.cc
+log/SavedStatus.h
+log/SavedStatus.cc
+log/Seconds.h
+log/Seconds.cc
+log/Statistics.h
+log/Statistics.cc
+log/LogTarget.h
+log/LogTarget.cc
+log/Timer.h
+log/Timer.cc
+log/ResourceUsage.h
+log/ResourceUsage.cc
+log/TimeStamp.h
+log/TimeStamp.cc
+log/UserChannel.h
+log/UserChannel.cc
+)
+
+list( APPEND eckit_exception_srcs
+exception/Exceptions.h
+exception/Exceptions.cc )
+
+list( APPEND eckit_types_srcs
+types/Types.h
+types/Types.cc
+types/Double.h
+types/Double.cc
+types/Fraction.h
+types/Fraction.cc
+types/FixedString.h
+types/FloatCompare.h
+types/FloatCompare.cc
+types/ClimateDate.h
+types/ClimateDate.cc
+types/Coord.h
+types/Coord.cc
+types/DateTime.h
+types/DateTime.cc
+types/DayOfYear.h
+types/DayOfYear.cc
+types/Grid.h
+types/Grid.cc
+types/Metadata.h
+types/Metadata.cc
+types/Month.h
+types/Month.cc
+types/TimeInterval.h
+types/TimeInterval.cc
+types/Date.h
+types/Date.cc
+types/UUID.h
+types/UUID.cc
+types/Time.h
+types/Time.cc
+types/VerifyingDate.h
+types/VerifyingDate.cc
+)
+
+list( APPEND eckit_parser_srcs
+parser/StreamParser.h
+parser/StreamParser.cc
+parser/StringTools.h
+parser/StringTools.cc
+parser/Tokenizer.h
+parser/Tokenizer.cc
+parser/JSON.h
+parser/JSON.cc
+parser/JSONParser.h
+parser/JSONParser.cc
+parser/JSONDataBlob.h
+parser/JSONDataBlob.cc
+parser/JSONMetadata.h
+parser/JSONMetadata.cc )
+
+list( APPEND eckit_value_srcs
+value/BoolContent.h
+value/BoolContent.cc
+value/CompositeParams.cc
+value/CompositeParams.h
+value/Content.h
+value/Content.cc
+value/DateContent.h
+value/DateContent.cc
+value/DispatchParams.h
+value/DoubleContent.h
+value/DoubleContent.cc
+value/Expression.h
+value/ListContent.h
+value/ListContent.cc
+value/MapContent.h
+value/MapContent.cc
+value/NilContent.h
+value/NilContent.cc
+value/NumberContent.h
+value/NumberContent.cc
+value/Params.cc
+value/Params.h
+value/Properties.h
+value/Properties.cc
+value/ScopeParams.cc
+value/ScopeParams.h
+value/StringContent.h
+value/StringContent.cc
+value/Value.h
+value/Value.cc )
+
+list( APPEND eckit_os_srcs
+os/Stat.h
+os/System.h
+os/System.cc
+os/AutoUmask.h
+os/SemLocker.h
+os/SemLocker.cc
+os/Semaphore.h
+os/Semaphore.cc
+os/SignalHandler.h
+os/SignalHandler.cc
+os/Password.h
+os/Password.cc
+os/BackTrace.h
+os/BackTrace.cc
+os/AutoAlarm.h
+os/AutoAlarm.cc
+os/SharedInt.h
+os/SharedInt.cc )
+
+list( APPEND eckit_net_srcs
+net/TCPClient.h
+net/TCPClient.cc
+net/TCPSocket.h
+net/TCPSocket.cc
+net/TCPStream.h
+net/TCPStream.cc
+net/NetAddress.h
+net/NetAddress.cc
+net/NetService.h
+net/NetService.cc
+net/NetUser.h
+net/NetUser.cc
+net/Telnetable.h
+net/Telnetable.cc
+net/Telnet.h
+net/Telnet.cc
+net/TelnetUser.h
+net/Connector.h
+net/Connector.cc
+net/TCPServer.h
+net/TCPServer.cc
+net/Port.h
+net/Port.cc )
+
+list( APPEND eckit_serialisation_srcs
+serialisation/Stream.h
+serialisation/Stream.cc
+serialisation/Streamable.h
+serialisation/Streamable.cc
+serialisation/FileStream.h
+serialisation/FileStream.cc
+serialisation/MemoryStream.h
+serialisation/MemoryStream.cc
+serialisation/HandleStream.h
+serialisation/PipeStream.h
+serialisation/PipeStream.cc
+serialisation/Reanimator.h
+serialisation/Reanimator.cc
+serialisation/ReanimatorBase.cc )
+
+list( APPEND eckit_persist_srcs
+persist/Exporter.h
+persist/Exporter.cc
+persist/DumpLoad.h
+persist/DumpLoad.cc
+persist/Isa.cc )
+
+list( APPEND eckit_utils_srcs
+utils/Translator.h
+utils/Translator.cc
+utils/Hash.h
+utils/Hash.cc
+utils/MD5.h
+utils/MD5.cc
+utils/Regex.h
+utils/Regex.cc
+utils/RendezvousHash.h
+utils/RendezvousHash.cc
+utils/HyperCube.h
+utils/HyperCube.cc
+utils/RLE.h
+utils/RLE.cc )
+
+if(ECKIT_HAVE_SSL)
+    list( APPEND eckit_utils_srcs
+      utils/MD4.h
+      utils/MD4.cc
+      utils/SHA1.h
+      utils/SHA1.cc
+    )
+endif()
+
+if(ECKIT_HAVE_XXHASH)
+    list( APPEND eckit_utils_srcs
+      utils/xxHash.h
+      utils/xxHash.cc
+    )
+endif()
+
+list( APPEND eckit_memory_srcs
+memory/Builder.h
+memory/Builder.cc
+memory/Counted.cc
+memory/Counted.h
+memory/Factory.h
+memory/Owned.h
+memory/MemoryPool.h
+memory/MemoryPool.cc
+memory/SharedPtr.h
+memory/Padded.h
+memory/MapAllocator.h
+memory/MapAllocator.cc
+memory/NonCopyable.h
+memory/NonCopyable.cc
+memory/ScopedPtr.h )
+
+list( APPEND eckit_compat_srcs
+compat/Inited.h
+compat/StrStream.h
+)
+
+list( APPEND eckit_maths_srcs
+maths/Functions.h
+maths/Functions.cc
+)
+
+
+list( APPEND eckit_system_srcs
+    system/Library.cc
+    system/Library.h
+    system/ResourceUsage.cc
+    system/ResourceUsage.h
+    system/SystemInfo.cc
+    system/SystemInfo.h
+)
+
+set(ECKIT_SYSTEM_EXTRA_LIBS "")
+if( EC_OS_NAME STREQUAL "linux" )
+    list( APPEND eckit_system_srcs system/SystemInfoLinux.h system/SystemInfoLinux.cc )
+elseif( EC_OS_NAME STREQUAL "freebsd" )
+    list( APPEND eckit_system_srcs system/SystemInfoFreeBSD.h system/SystemInfoFreeBSD.cc )
+    list( APPEND ECKIT_SYSTEM_EXTRA_LIBS "execinfo" ) # Add in support for backtrace on FreeBSD
+elseif( EC_OS_NAME STREQUAL "macosx" )
+    list( APPEND eckit_system_srcs system/SystemInfoMacOSX.h system/SystemInfoMacOSX.cc )
+else()
+    ecbuild_warn("eckit does not support SystemInfo for OS '${EC_OS_NAME}'")
+endif()
+
+list( APPEND eckit_bases_srcs
+bases/Watcher.h
+bases/Watcher.cc
+bases/Loader.h
+bases/Loader.cc )
+
+list( APPEND eckit_transaction_srcs
+    transaction/TxnEvent.h
+    transaction/TxnEvent.cc
+    transaction/TxnLog.h
+    transaction/TxnLog.cc
+)
+
+list( APPEND eckit_testing_srcs
+testing/Setup.h
+)
+
+list( APPEND eckit_dirs
+	bases
+	compat
+	config
+	container
+	exception
+	filesystem
+	io
+	log
+    maths
+	memory
+	net
+	os
+	parser
+	persist
+	runtime
+	serialisation
+	testing
+	thread
+	transaction
+    types
+    testing
+    utils
+	value
+    system
+)
+
+foreach( dir ${eckit_dirs} )
+  list( APPEND eckit_srcs ${eckit_${dir}_srcs} )
+endforeach()
+
+list( APPEND eckit_templates
+					container/CacheLRU.cc
+                    container/BTree.cc
+                    container/MappedArray.cc
+                    container/SharedMemArray.cc
+                    container/bsptree/BSPNode.cc
+					container/kdtree/KDNode.cc
+					container/sptree/SPNode.cc
+					filesystem/BasePathNameT.cc
+					io/FileBase.cc
+					config/Resource.cc
+					runtime/PipeHandler.cc
+					serialisation/Reanimator.cc
+					transaction/TxnLog.cc
+					types/Types.cc
+					utils/RLE.cc
+)
+
+list( APPEND eckit_persistent
+					io/Length.h
+					io/Offset.h
+					types/ClimateDate.h
+					types/Date.h
+					types/DateTime.h
+					types/DayOfYear.h
+					types/Double.h
+					types/Grid.h
+					types/Month.h
+					types/Time.h
+					types/VerifyingDate.h
+)
+
+### eckit library
+
+ecbuild_add_library(TARGET eckit
+					INSTALL_HEADERS LISTED
+					HEADER_DESTINATION
+						${INSTALL_INCLUDE_DIR}/eckit
+					SOURCES
+						${eckit_srcs}
+					GENERATED
+						eckit_version.cc
+					TEMPLATES
+						${eckit_templates}
+					PERSISTENT
+						${eckit_persistent}
+                    PRIVATE_INCLUDES
+                        "${XXHASH_INCLUDE_DIRS}"
+                    INCLUDES
+                        "${OPENSSL_INCLUDE_DIR}"
+                        "${AIO_INCLUDE_DIRS}"
+					LIBS
+                        ${OPENSSL_LIBRARIES}
+                        ${XXHASH_LIBRARIES}
+                        ${AIO_LIBRARIES}
+                        ${CMAKE_THREAD_LIBS_INIT}
+                        ${CMAKE_DL_LIBS}
+                        ${ECKIT_SYSTEM_EXTRA_LIBS} )
+
+### sub-directories
+
+if( HAVE_ECKIT_CMD )
+  add_subdirectory( cmd )
+endif()
+
+add_subdirectory( geometry )
+add_subdirectory( linalg )
+add_subdirectory( maths )
+add_subdirectory( mpi )
+add_subdirectory( option )
+add_subdirectory( web )
diff --git a/eckit/src/eckit/bases/Loader.cc b/eckit/src/eckit/bases/Loader.cc
new file mode 100644
index 0000000..1f38fb2
--- /dev/null
+++ b/eckit/src/eckit/bases/Loader.cc
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/bases/Loader.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Loader::Loader():
+  ClassExtent< Loader >(this)
+{
+}
+
+Loader::~Loader()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/bases/Loader.h b/eckit/src/eckit/bases/Loader.h
new file mode 100644
index 0000000..1cb04a2
--- /dev/null
+++ b/eckit/src/eckit/bases/Loader.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Loader.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Loader_h
+#define eckit_Loader_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/container/ClassExtent.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Loader : public ClassExtent<Loader> {
+
+public: // methods
+
+	Loader();
+	virtual ~Loader();
+
+	virtual void execute() = 0;
+
+private: // methods
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/bases/Watcher.cc b/eckit/src/eckit/bases/Watcher.cc
new file mode 100644
index 0000000..a7d3532
--- /dev/null
+++ b/eckit/src/eckit/bases/Watcher.cc
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/bases/Watcher.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+struct DummyWatcher : public Watcher {
+	void watch() {}
+};
+
+Watcher& Watcher::dummy()
+{
+	static DummyWatcher x;
+	return x;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/bases/Watcher.h b/eckit/src/eckit/bases/Watcher.h
new file mode 100644
index 0000000..f52b5f8
--- /dev/null
+++ b/eckit/src/eckit/bases/Watcher.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Watcher.h
+// Baudouin Raoult - ECMWF Jun 98
+
+#ifndef eckit_Watcher_h
+#define eckit_Watcher_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Watcher {
+public:
+
+// -- Methods
+   virtual ~Watcher() {}
+
+	virtual void watch() = 0;
+
+// -- Class methods
+
+	static Watcher& dummy();
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/AliasCmd.cc b/eckit/src/eckit/cmd/AliasCmd.cc
new file mode 100644
index 0000000..f3a531d
--- /dev/null
+++ b/eckit/src/eckit/cmd/AliasCmd.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/AliasCmd.h"
+#include "eckit/cmd/CmdParser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+AliasCmd::AliasCmd() : CmdResource("alias") {
+}
+
+//-----------------------------------------------------------------------------
+
+AliasCmd::~AliasCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void AliasCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    if (arg.exists(2))
+        CmdParser::alias(arg[1], arg[2]);
+    else if (arg.exists(1))
+        CmdParser::alias(arg[1]);
+    else
+        CmdParser::alias();
+}
+
+//-----------------------------------------------------------------------------
+
+void AliasCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg AliasCmd::usage(const std::string& cmd) const {
+    return Arg("<name>") + Arg("<value>");
+}
+
+//-----------------------------------------------------------------------------
+
+static AliasCmd aliasCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/AliasCmd.h b/eckit/src/eckit/cmd/AliasCmd.h
new file mode 100644
index 0000000..f82caff
--- /dev/null
+++ b/eckit/src/eckit/cmd/AliasCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   AliasCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_AliasCmd_H
+#define eckit_cmd_AliasCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class AliasCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    AliasCmd();
+
+    // -- Destructor
+
+    ~AliasCmd();
+
+private:
+    // No copy allowed
+
+    AliasCmd(const AliasCmd&);
+    AliasCmd& operator=(const AliasCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/Arg.cc b/eckit/src/eckit/cmd/Arg.cc
new file mode 100644
index 0000000..0208ead
--- /dev/null
+++ b/eckit/src/eckit/cmd/Arg.cc
@@ -0,0 +1,255 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/Arg.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ArgContent {
+public:
+    virtual ~ArgContent(){};
+    virtual void print(std::ostream&, bool) const = 0;
+    virtual ArgContent* clone() const = 0;
+    virtual void completion(const std::vector<std::string>&, std::vector<std::string>&) = 0;
+    virtual void consume(std::vector<std::string>&) = 0;
+};
+
+class ArgContentEmpty : public ArgContent {
+    virtual void print(std::ostream& s, bool) const { s << ""; }
+    virtual ArgContent* clone() const { return new ArgContentEmpty(); }
+
+    virtual void completion(const std::vector<std::string>&, std::vector<std::string>&) {}
+    virtual void consume(std::vector<std::string>&) {}
+};
+
+class ArgContentOption : public ArgContent {
+    std::string name_;
+    Arg::Type type_;
+
+    virtual void print(std::ostream& s, bool) const { s << name_; }
+    virtual ArgContent* clone() const { return new ArgContentOption(name_, type_); }
+
+    virtual void completion(const std::vector<std::string>& s, std::vector<std::string>& r) {
+        //  cerr << "Here" << std::endl;
+        if (name_.find(s[0]) == 0) {
+            r.push_back(name_);
+        }
+    }
+
+    virtual void consume(std::vector<std::string>& v) {
+        bool more = true;
+        while (more) {
+            more = false;
+            for (std::vector<std::string>::iterator j = v.begin(); j != v.end(); ++j)
+                if ((*j) == name_) {
+                    more = true;
+                    v.push_back("** marker **");
+                    if ((*(j + 1))[0] != '-') v.erase(j + 1);
+                    v.erase(j);
+                    break;
+                }
+        }
+    }
+
+public:
+    ArgContentOption(const std::string& name, Arg::Type type) : name_(name), type_(type) {}
+};
+
+class ArgContentParam : public ArgContent {
+    std::string name_;
+    Arg::Type type_;
+
+    virtual void print(std::ostream& s, bool) const { s << name_; }
+    virtual ArgContent* clone() const { return new ArgContentParam(name_, type_); }
+
+    virtual void completion(const std::vector<std::string>& s, std::vector<std::string>& r) {}
+
+    virtual void consume(std::vector<std::string>&) {}
+
+public:
+    ArgContentParam(const std::string& name, Arg::Type type) : name_(name), type_(type) {}
+};
+
+class ArgContentOptional : public ArgContent {
+    eckit::ScopedPtr<ArgContent> content_;
+
+    virtual void print(std::ostream& s, bool) const {
+        s << "[";
+        content_->print(s, false);
+        s << "]";
+    }
+    virtual ArgContent* clone() const { return new ArgContentOptional(content_.get()); }
+
+    virtual void completion(const std::vector<std::string>& s, std::vector<std::string>& r) {
+        content_->completion(s, r);
+    }
+
+    virtual void consume(std::vector<std::string>& s) { content_->consume(s); }
+
+public:
+    ArgContentOptional(ArgContent* c) : content_(c->clone()) {}
+};
+
+//------------------------------------------------------------------------------------------------------------------------
+
+template <class T>
+class ArgContentList : public ArgContent {
+protected:
+    std::vector<ArgContent*> list_;
+
+    virtual ArgContent* clone() const;
+    void push(ArgContent*);
+    virtual void completion(const std::vector<std::string>& s, std::vector<std::string>& r);
+    virtual void consume(std::vector<std::string>& s);
+
+public:
+    ArgContentList(ArgContent*, ArgContent*);
+    ArgContentList(const std::vector<ArgContent*>&);
+    ~ArgContentList();
+};
+
+template <class T>
+void ArgContentList<T>::push(ArgContent* a) {
+    ArgContentList* e = dynamic_cast<T*>(a);
+    if (e) {
+        for (size_t i = 0; i < e->list_.size(); i++) list_.push_back(e->list_[i]->clone());
+    } else {
+        list_.push_back(a->clone());
+    }
+}
+
+template <class T>
+ArgContentList<T>::ArgContentList(ArgContent* a1, ArgContent* a2) {
+    push(a1);
+    push(a2);
+}
+
+template <class T>
+ArgContentList<T>::~ArgContentList() {
+    for (size_t i = 0; i < list_.size(); i++) {
+        delete list_[i];
+        list_[i] = 0;
+    }
+}
+
+template <class T>
+ArgContentList<T>::ArgContentList(const std::vector<ArgContent*>& list)
+    : list_(list) {
+    for (size_t i = 0; i < list_.size(); i++) list_[i] = list_[i]->clone();
+}
+
+template <class T>
+ArgContent* ArgContentList<T>::clone() const {
+    return new T(list_);
+}
+
+template <class T>
+void ArgContentList<T>::completion(const std::vector<std::string>& s, std::vector<std::string>& r) {
+    for (size_t i = 0; i < list_.size(); i++) list_[i]->completion(s, r);
+}
+
+template <class T>
+void ArgContentList<T>::consume(std::vector<std::string>& s) {
+    for (size_t i = 0; i < list_.size(); i++) list_[i]->consume(s);
+}
+
+//=================================
+
+class ArgContentExclusive : public ArgContentList<ArgContentExclusive> {
+
+    void print(std::ostream& s, bool bra) const {
+        std::string p = "";
+        if (bra) s << "(";
+        for (size_t i = 0; i < list_.size(); i++) {
+            s << p;
+            list_[i]->print(s, true);
+            p = " | ";
+        }
+        if (bra) s << ")";
+    }
+
+public:
+    ArgContentExclusive(ArgContent* a1, ArgContent* a2) : ArgContentList<ArgContentExclusive>(a1, a2) {}
+    ArgContentExclusive(const std::vector<ArgContent*>& a) : ArgContentList<ArgContentExclusive>(a) {}
+};
+
+//==============================================================
+
+class ArgContentInclusive : public ArgContentList<ArgContentInclusive> {
+
+    void print(std::ostream& s, bool) const {
+        std::string p = "";
+        for (size_t i = 0; i < list_.size(); i++) {
+            s << p;
+            list_[i]->print(s, true);
+            p = " ";
+        }
+    }
+
+public:
+    ArgContentInclusive(ArgContent* a1, ArgContent* a2) : ArgContentList<ArgContentInclusive>(a1, a2) {}
+    ArgContentInclusive(const std::vector<ArgContent*>& a) : ArgContentList<ArgContentInclusive>(a) {}
+};
+
+//=============================================================================
+
+Arg::Arg() : content_(new ArgContentEmpty()) {
+}
+
+Arg::Arg(ArgContent* c) : content_(c) {
+}
+
+Arg::Arg(const std::string& name, Type type)
+    : content_(name[0] == '-' ? (ArgContent*)new ArgContentOption(name, type)
+                              : (ArgContent*)new ArgContentParam(name, type)) {
+}
+
+Arg::Arg(const Arg& other) : content_(other.content_->clone()) {
+}
+
+Arg::~Arg() {
+}
+
+void Arg::print(std::ostream& s) const {
+    content_->print(s, false);
+}
+
+Arg Arg::operator~() {
+    return Arg(new ArgContentOptional(content_.get()));
+}
+
+Arg operator|(const Arg& a1, const Arg& a2) {
+    return Arg(new ArgContentExclusive(a1.content_.get(), a2.content_.get()));
+}
+
+Arg operator+(const Arg& a1, const Arg& a2) {
+    return Arg(new ArgContentInclusive(a1.content_.get(), a2.content_.get()));
+}
+
+Arg& Arg::operator=(const Arg& other) {
+    content_.reset(other.content_->clone());
+    return *this;
+}
+
+std::vector<std::string> Arg::completion(std::vector<std::string>& s) {
+    s.erase(s.begin());
+    std::vector<std::string> result;
+    content_->consume(s);
+    content_->completion(s, result);
+    return result;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/Arg.h b/eckit/src/eckit/cmd/Arg.h
new file mode 100644
index 0000000..fce2a96
--- /dev/null
+++ b/eckit/src/eckit/cmd/Arg.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   Arg.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_Arg_H
+#define eckit_cmd_Arg_H
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/ScopedPtr.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ArgContent;
+
+class Arg {
+public:
+    enum Type { number, text, path, tape, file, expression, boolean, ellipsis };
+
+    // -- Contructors
+
+    Arg();
+    Arg(const std::string& option, Type = boolean);
+
+    // -- Destructor
+
+    ~Arg();
+
+    // -- Copy
+
+    Arg(const Arg&);
+    Arg& operator=(const Arg&);
+
+    // -- Operators
+
+    Arg operator~();
+
+    // -- Methods
+    std::vector<std::string> completion(std::vector<std::string>&);
+
+protected:
+    // -- Methods
+
+    void print(std::ostream&) const;
+
+private:
+    Arg(ArgContent*);
+
+    // -- Members
+
+    eckit::ScopedPtr<ArgContent> content_;
+
+    // -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s, const Arg& p) {
+        p.print(s);
+        return s;
+    }
+
+    friend Arg operator|(const Arg&, const Arg&);
+    //  friend Arg operator,(const Arg&,const Arg&);
+    friend Arg operator+(const Arg&, const Arg&);
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/CMakeLists.txt b/eckit/src/eckit/cmd/CMakeLists.txt
new file mode 100644
index 0000000..4905bac
--- /dev/null
+++ b/eckit/src/eckit/cmd/CMakeLists.txt
@@ -0,0 +1,78 @@
+list( APPEND eckit_cmd_srcs
+cmdlib.h
+AliasCmd.cc
+AliasCmd.h
+Arg.cc
+Arg.h
+CmdApplication.cc
+CmdApplication.h
+CmdArg.cc
+CmdArg.h
+CmdParser.cc
+CmdParser.h
+CmdResource.cc
+CmdResource.h
+ConfigCmd.cc
+ConfigCmd.h
+DirCmd.cc
+DirCmd.h
+EchoCmd.cc
+EchoCmd.h
+EnvironmentCmd.cc
+EnvironmentCmd.h
+ExportCmd.cc
+ExportCmd.h
+JSONCmd.cc
+JSONCmd.h
+HistoryCmd.cc
+HistoryCmd.h
+KillCmd.cc
+KillCmd.h
+LockCmd.cc
+LockCmd.h
+ManCmd.cc
+ManCmd.h
+MemoryCmd.cc
+MemoryCmd.h
+PsCmd.cc
+PsCmd.h
+QuitCmd.cc
+QuitCmd.h
+RemoteCmd.cc
+RemoteCmd.h
+RemoteCommandable.cc
+RemoteCommandable.h
+RemoteCommander.cc
+RemoteCommander.h
+RemoteCommandUser.cc
+RemoteCommandUser.h
+SleepCmd.cc
+SleepCmd.h
+StartCmd.cc
+StartCmd.h
+StatusCmd.cc
+StatusCmd.h
+StopCmd.cc
+StopCmd.h
+TailCmd.cc
+TailCmd.h
+TermBuf.cc
+TermBuf.h
+term.c
+UpTimeCmd.cc
+UpTimeCmd.h
+UserInput.h
+UserInput.cc
+)
+
+ecbuild_generate_yy( YYPREFIX eckit_cmd_
+                     YACC cmdsy
+                     LEX  cmdsl
+                     DEPENDANT CmdParser.cc )
+
+ecbuild_add_library( TARGET             eckit_cmd
+                     INSTALL_HEADERS    LISTED
+                     SOURCES            ${eckit_cmd_srcs}
+                     HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/eckit/cmd
+                     INCLUDES           ${CURSES_INCLUDE_DIR}
+                     LIBS               eckit ${CURSES_LIBRARIES} )
diff --git a/eckit/src/eckit/cmd/CmdApplication.cc b/eckit/src/eckit/cmd/CmdApplication.cc
new file mode 100644
index 0000000..7cdcf71
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdApplication.cc
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   CmdApplication.cc
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#include <unistd.h>
+
+#include "eckit/cmd/CmdApplication.h"
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/SockBuf.h"
+#include "eckit/net/TCPServer.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Monitor.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+CmdApplication::CmdApplication() {
+}
+
+CmdApplication::~CmdApplication() {
+}
+
+std::string CmdApplication::prompt() const {
+    return name() + "% ";
+}
+
+std::string CmdApplication::name() const {
+    return Main::instance().name();
+}
+
+void CmdApplication::startup(std::ostream& out) {
+    StringList rc;
+
+    rc.push_back("~/." + name() + "rc");
+    rc.push_back("~/etc/" + name() + "rc");
+
+    for (StringList::const_iterator it = rc.begin(); it != rc.end(); ++it) {
+        PathName path(*it);
+        if (path.exists()) {
+            Log::info() << "Startup " << path << std::endl;
+            std::ifstream in(path.localPath());
+            CmdParser::parse(in, out, *this);
+        }
+    }
+}
+
+void CmdApplication::userMode() {
+    std::string command = Resource<std::string>("-command", "");
+
+    Monitor::instance().stoppable(false);
+
+    CmdParser::prompt(false);
+    startup(std::cout);
+
+    PathName file = Resource<PathName>("-f", "");
+    bool fail = Resource<bool>("-fail", false);
+
+    if (command != "") {
+        Log::info() << "command: " << command << std::endl;
+        try {
+            CmdParser::parse(command, std::cout);
+        } catch (std::exception& e) {
+            if (fail) throw e;
+
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is ignored" << std::endl;
+        }
+        return;
+    }
+
+    bool interactive = Resource<bool>("-interactive", ::isatty(1) || ::isatty(0));
+
+    CmdParser::prompt(::isatty(1));
+    CmdParser::prompt(interactive);
+    if (file.exists()) {
+        std::ifstream in(file.localPath());
+        CmdParser::parse(in, std::cout, *this);
+    } else {
+        PathName home("~");
+        Log::info() << name() << " home is " << home << std::endl;
+
+        CmdParser::console(interactive && ::isatty(0) && ::isatty(1));
+
+        while (std::cin) {
+            try {
+                CmdParser::parse(std::cin, std::cout, *this);
+                break;
+            } catch (std::exception& e) {
+                if (fail) throw e;
+
+                Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+                Log::error() << "** Exception is ignored" << std::endl;
+            }
+        }
+    }
+}
+
+void CmdApplication::serveMode(long port) {
+    Log::info() << "Offering remote commands on port " << port << std::endl;
+
+    TCPServer server(port);
+
+    for (;;) {
+        TCPSocket p = server.accept();
+        Log::info() << "Connection from " << p.remoteHost() << std::endl;
+
+        SockBuf buf(p);
+        std::ostream out(&buf);
+        std::istream in(&buf);
+
+        out << "Welcome " << p.remoteHost() << std::endl;
+
+        CmdParser::parse(in, out, *this);
+    }
+}
+
+void CmdApplication::execute() {
+    long port = Resource<long>("-serve", 0);
+    if (port)
+        serveMode(port);
+    else
+        userMode();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/CmdApplication.h b/eckit/src/eckit/cmd/CmdApplication.h
new file mode 100644
index 0000000..d398b4c
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdApplication.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   CmdApplication.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_cmd_CmdApplication_H
+#define eckit_cmd_CmdApplication_H
+
+#include "eckit/cmd/CmdParser.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class CmdApplication : public Prompter {
+public:
+    CmdApplication();
+    virtual ~CmdApplication();
+
+protected:
+    void execute();
+
+private:
+    virtual std::string prompt() const;
+    virtual std::string name() const;
+
+    void startup(std::ostream&);
+
+    void serveMode(long);
+    void userMode();
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/CmdArg.cc b/eckit/src/eckit/cmd/CmdArg.cc
new file mode 100644
index 0000000..cef86ae
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdArg.cc
@@ -0,0 +1,189 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/CmdArg.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec CmdArg::classSpec_ = {
+    &Streamable::classSpec(), "CmdArg",
+};
+Reanimator<CmdArg> CmdArg::reanimator_;
+
+void CmdArg::encode(Stream& s) const {
+    Streamable::encode(s);
+
+    long size = args_.size();
+    s << size;
+
+    for (CmdMap::const_iterator i = args_.begin(); i != args_.end(); ++i) {
+        std::string lhs = (*i).first;
+        Value rhs = (*i).second;
+        s << lhs;
+        s << rhs;
+    }
+}
+
+CmdArg::CmdArg(Stream& s) : Streamable(s) {
+    long size;
+    s >> size;
+
+    for (int i = 0; i < size; ++i) {
+        std::string first;
+        s >> first;
+
+        Value second(s);
+        args_[first] = second;
+    }
+}
+
+CmdArg::CmdArg() {
+}
+
+CmdArg::~CmdArg() {
+}
+
+CmdArg::CmdArg(const CmdArg& other) : args_(other.args_) {
+}
+
+CmdArg& CmdArg::operator=(const CmdArg& other) {
+    args_ = other.args_;
+    return *this;
+}
+
+const Value& CmdArg::get(const std::string& key) const {
+    static Value empty;
+    return get(key, empty);
+}
+
+const Value& CmdArg::get(const std::string& key, const Value& def) const {
+    CmdMap::const_iterator j = args_.find(key);
+    if (j == args_.end()) {
+        return def;
+    } else {
+        return (*j).second;
+    }
+}
+
+void CmdArg::set(const std::string& key, const Value& val) {
+    args_[key] = val;
+}
+
+const Value& CmdArg::operator[](const std::string& s) const {
+    return get(s);
+}
+
+const Value& CmdArg::operator[](const long l) const {
+    return get(Translator<long, std::string>()(l));
+}
+
+Value& CmdArg::operator[](const std::string& s) {
+    return args_[s];
+}
+
+Value& CmdArg::operator[](const long l) {
+    return args_[Translator<long, std::string>()(l)];
+}
+
+void CmdArg::print(std::ostream& out) const {
+    for (CmdMap::const_iterator i = args_.begin(); i != args_.end(); ++i)
+        out << (*i).first << " = " << (*i).second << std::endl;
+}
+
+std::vector<std::string> CmdArg::args() const {
+    std::vector<std::string> result;
+    for (CmdMap::const_iterator i = args_.begin(); i != args_.end(); ++i) result.push_back((*i).first);
+    return result;
+}
+
+void CmdArg::erase() {
+    args_.erase(args_.begin(), args_.end());
+}
+
+void CmdArg::erase(long from, long to) {
+    // Move the remaining, removing the trailing
+    CmdMap::iterator j;
+    long i = from;
+    while ((j = args_.find(Translator<long, std::string>()(i))) != args_.end()) {
+        CmdMap::iterator k = args_.find(Translator<long, std::string>()(to + 1 - from + i));
+        if (k != args_.end())
+            (*j).second = (*k).second;
+        else
+            args_.erase(j);
+        ++i;
+    }
+}
+
+void CmdArg::erase(const std::string& s) {
+    CmdMap::iterator j = args_.find(s);
+    if (j != args_.end()) args_.erase(j);
+}
+
+void CmdArg::operator+=(const CmdArg& other) {
+    Log::debug() << "Appending " << other << std::endl;
+    Log::debug() << "To " << std::endl << *this << std::endl;
+    CmdArg tmp = other;
+
+    // Find where we start adding
+    long i = 0;
+    CmdMap::iterator j;
+    while ((j = args_.find(Translator<long, std::string>()(i))) != args_.end()) ++i;
+
+    // Dont copy arg[0]
+    tmp.args_.erase(tmp.args_.find("0"));
+
+    // Add the numerical arguments
+    long k = 1;
+    for (CmdMap::iterator l = tmp.args_.find(Translator<long, std::string>()(k)); l != tmp.args_.end();
+         ++k, l = tmp.args_.find(Translator<long, std::string>()(k))) {
+        (*this)[i + k - 1] = (*l).second;
+        tmp.args_.erase(l);
+    }
+
+    // Add the rest
+    for (CmdMap::iterator m = tmp.args_.begin(); m != tmp.args_.end(); ++m) args_[(*m).first] = (*m).second;
+}
+
+bool CmdArg::exists(const std::string& key) const {
+    return (args_.find(key) != args_.end());
+}
+
+bool CmdArg::exists(long l) const {
+    std::string key = Translator<long, std::string>()(l);
+    return (args_.find(key) != args_.end());
+}
+
+size_t CmdArg::size() const {
+    return args_.size();
+}
+
+const Value& CmdArg::operator()(const std::string& key, Value defaultval) {
+    CmdMap::const_iterator j = args_.find(key);
+    if (j == args_.end()) {
+        set(key, defaultval);
+        return args_[key];
+    } else
+        return (*j).second;
+}
+
+const Value& CmdArg::operator()(const long lkey, Value defaultval) {
+    std::string key = Translator<long, std::string>()(lkey);
+    return operator()(key, defaultval);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/CmdArg.h b/eckit/src/eckit/cmd/CmdArg.h
new file mode 100644
index 0000000..3fe5729
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdArg.h
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_cmd_CmdArg_H
+#define eckit_cmd_CmdArg_H
+
+#include "eckit/serialisation/Streamable.h"
+#include "eckit/value/Value.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/cmd/cmdlib.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class CmdArg : public eckit::Streamable {
+public:
+    // -- Contructors
+
+    CmdArg();
+    CmdArg(eckit::Stream&);
+
+    // -- Destructor
+
+    ~CmdArg();
+
+    // -- Copy
+
+    CmdArg(const CmdArg&);
+    CmdArg& operator=(const CmdArg&);
+
+    // -- Operators
+
+    void operator+=(const CmdArg&);
+
+    // -- Methods
+
+    const eckit::Value& operator[](const std::string&) const;
+    const eckit::Value& operator[](const long) const;
+    eckit::Value& operator[](const std::string&);
+    eckit::Value& operator[](const long);
+
+    const eckit::Value& operator()(const std::string&, eckit::Value);
+    const eckit::Value& operator()(const long, eckit::Value);
+
+    size_t size() const;
+
+    void erase();
+    void erase(long, long);
+    void erase(const std::string&);
+
+    bool exists(const std::string&) const;
+    bool exists(long) const;
+
+    std::vector<std::string> args() const;
+
+    // From Streamble
+
+    virtual void encode(eckit::Stream&) const;
+    virtual const eckit::ReanimatorBase& reanimator() const { return reanimator_; }
+
+    // access the value or its default
+    const eckit::Value& get(const std::string&, const eckit::Value&) const;
+
+    template <typename T>
+    const eckit::Value get(const std::string& name, const std::string& resource, const T& def) const {
+        T v = eckit::Resource<T>(resource, def);
+        return get(name, v);
+    }
+
+protected:
+    // -- Methods
+
+    void print(std::ostream&) const;
+
+    // Because the use of 'Accessor'
+    const eckit::Value& get(const std::string&) const;
+    void set(const std::string&, const eckit::Value&);
+
+private:
+    // -- Members
+
+    typedef std::map<std::string, eckit::Value, std::less<std::string> > CmdMap;
+
+    CmdMap args_;
+
+    // -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s, const CmdArg& p) {
+        p.print(s);
+        return s;
+    }
+
+    // These have no sense. Only to let the STL have a Stack of CmdArg
+    friend bool operator<(const CmdArg& lhs, const CmdArg& rhs) { return lhs.args_ < rhs.args_; }
+    friend bool operator==(const CmdArg& lhs, const CmdArg& rhs) { return lhs.args_ == rhs.args_; }
+
+    // -- Class members
+
+    static eckit::ClassSpec classSpec_;
+    static eckit::Reanimator<CmdArg> reanimator_;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/CmdParser.cc b/eckit/src/eckit/cmd/CmdParser.cc
new file mode 100644
index 0000000..6d16ab2
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdParser.cc
@@ -0,0 +1,501 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/CmdParser.h"
+#include "eckit/cmd/CmdResource.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/runtime/Main.h"
+
+#define YY_INPUT(buf, result, max_size)                  \
+    {                                                    \
+        int c = CmdParser::input();                      \
+        result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
+    }
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+struct EventNotFound : public Exception {
+    EventNotFound(const std::string& s) : Exception(s + ": Event not found") {}
+};
+
+typedef std::stack<CmdParser*, std::vector<CmdParser*> > ParserStack;
+typedef std::stack<CmdArg, std::vector<CmdArg> > CmdArgStack;
+
+static std::ostream* out_ = &std::cout;
+
+// On command line
+static CmdArg arg_;
+static CmdArg variables_;
+static long param_;
+static std::string command_;
+static std::list<int> buffer_;
+static bool prompt_ = true;
+static bool console_ = false;
+
+typedef std::map<char, bool, std::less<char> > FlagMap;
+static FlagMap flags_;
+
+// Alias are shared for every parser
+static CmdArg theAlias_;
+static CmdArg environment_;
+
+// The history
+typedef std::vector<std::string> History;
+static History history_;
+
+// For Lex
+static bool eckit_cmd_debug_;
+
+static long pos_ = 0;
+
+//-----------------------------------------------------------------------------
+
+namespace CmdYacc {
+
+void eckit_cmd_error(const char* msg);
+
+#include "eckit/cmd/cmdsy.c"
+int eckit_cmd_wrap(void) {
+    return 1;
+}
+
+void eckit_cmd_error(const char* msg) {
+    Log::error() << msg << " line " << eckit_cmd_lineno << std::endl;
+    if (flags_['e']) {
+        Main::instance().terminate();
+    }
+}
+
+#undef input
+#undef unput
+#undef output
+
+}  // namespace CmdYacc
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::parse(const std::string& line, std::ostream& out) {
+    command_ = line;
+    pos_ = 0;
+    out_ = &out;
+
+    if (command_.size() > 0) {
+        // Prepare for parse
+        try {
+            repeat();
+            substitute();
+            unAlias();
+
+            buffer_.resize(command_.size());
+            std::copy(command_.begin(), command_.end(), buffer_.begin());
+            buffer_.push_back(0);
+
+            // std::cout << command_ << std::endl;
+
+            CmdYacc::eckit_cmd_parse();
+
+        } catch (EventNotFound& e) {
+            (*out_) << e.what() << std::endl;
+        } catch (std::exception& e) {
+            throw;
+        }
+    }
+    reset();
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::parse(std::istream& in, std::ostream& out, const Prompter& prompter) {
+    char l[102400];
+    CmdYacc::eckit_cmd_debug = eckit_cmd_debug_;
+
+    out_ = &out;
+    in.tie(out_);
+
+    prompt(prompter);
+    reset();
+
+    while (true) {
+        if(console_) {
+            const char* p = UserInput::getUserInput(prompter.prompt().c_str(), &CmdResource::completion);
+            if(!p) {
+                break;
+            }
+            strncpy(l,p,sizeof(l));
+        }
+        else
+        {
+            if(!in.getline(l, sizeof(l))) {
+                break;
+            }
+        }
+        char* p = l;
+        while (p && *p == ' ') ++p;
+
+        command_ = p;
+        pos_ = 0;
+
+        if (command_.size() > 0) {
+            // Prepare for parse
+            try {
+                repeat();
+                substitute();
+                unAlias();
+
+                // Print command if different from input
+                if (command_ != l) (*out_) << command_ << std::endl;
+
+                buffer_.resize(command_.size());
+                std::copy(command_.begin(), command_.end(), buffer_.begin());
+                buffer_.push_back(0);
+
+                if (prompt_) historize();
+
+                CmdYacc::eckit_cmd_parse();
+
+            } catch (EventNotFound& e) {
+                (*out_) << e.what() << std::endl;
+            } catch (std::exception& e) {
+                throw;
+            }
+        }
+
+        prompt(prompter);
+        reset();
+    }
+}
+
+#if 0
+void CmdParser::eckit_cmd_error(char* msg)
+{
+    Log::error() << msg << " line " << eckit_cmd_lineno << std::endl;
+	if(flags_['e'])
+	{
+		Application::terminate();
+	}
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::prompt(const Prompter& prompter) {
+
+    if (prompt_)
+        (*out_) << prompter.prompt() << "%" << history_.size() + 1 << "> " << std::flush;
+
+    Log::status() << "Idle..." << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::historize() {
+    Tokenizer tokenize(" \t\n");
+    std::vector<std::string> tokens;
+    tokenize(command_, tokens);
+
+    if (tokens.size() > 0) {
+        history_.push_back(command_);
+        Log::debug() << "History: " << command_ << std::endl;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::shell(const std::string& s) {
+    if (s.length() > 0)
+        ::system(s.c_str());
+    else
+        ::system("$SHELL");
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::history(const long n, std::ostream& out) {
+    long size = history_.size();
+    long from = n ? size - n : 0;
+
+    if (from < 0) from = 0;
+    for (int i = from; i < size; i++) out << std::setw(4) << i + 1 << "  " << history_[i] << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::environment(std::ostream& out) {
+    out << environment_;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::environment(const std::string& lhs, const std::string& rhs) {
+    environment_[lhs] = rhs;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::unAlias() {
+    Tokenizer tokenize(" \t\n");
+    std::vector<std::string> tokens;
+    tokenize(command_, tokens);
+
+    if (theAlias_.exists(tokens[0])) {
+        std::string aliased = theAlias_[tokens[0]];
+
+        Log::debug() << "Expanding '" << tokens[0] << "' to '" << aliased << std::endl;
+
+        for (size_t i = 1; i < tokens.size(); ++i) aliased += " " + tokens[i];
+
+        command_ = aliased;
+
+        Log::debug() << "New command '" << command_ << "'" << std::endl;
+        unAlias();
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+int hIndex(const std::string& cmd, std::vector<std::string>& h, std::string& pre, std::string& post) {
+    std::string::size_type i = cmd.find('!');
+    if (i != std::string::npos) {
+        const char* p = (cmd.c_str() + i + 1);  // Skip '!'
+        int size = h.size();
+
+        pre = cmd.substr(0, i);
+        Log::debug() << "In repeat() command" << std::endl;
+
+        if (!p) return size;
+
+        if (*p == '!') {
+            post = ++p;
+            return size - 1;
+        }
+
+        if ((*p == '-') || (isdigit((int)(*p)))) {
+            int idx = atoi(p);
+            int start = (idx <= 0) ? size : -1;
+
+            if (*p == '-') ++p;
+            while (isdigit((int)(*p))) ++p;
+
+            post = p;
+            return start + idx;
+        }
+
+        for (int i = size - 1; i >= 0; --i)
+            if (h[i].find(p, 0) == 0) return i;
+
+        return size;
+    }
+
+    return -1;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::repeat() {
+    Tokenizer tokenize(" \t\n");
+    std::vector<std::string> tokens;
+    tokenize(command_, tokens);
+
+    if (!tokens.size()) return;
+
+    std::string pre(""), post("");
+    int which = hIndex(tokens[0], history_, pre, post);
+
+    if (which >= 0) {
+        if (static_cast<size_t>(which) < history_.size()) {
+            std::string newcmd = pre + history_[which];
+            newcmd += post;
+            for (size_t i = 1; i < tokens.size(); ++i) newcmd += " " + tokens[i];
+
+            command_ = newcmd;
+            Log::debug() << "New command '" << command_ << "'" << std::endl;
+        } else {
+            throw EventNotFound(tokens[0]);
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::substitute() {
+    if (command_[0] == '^') {
+        Tokenizer tokenize("^");
+        std::vector<std::string> tokens;
+        tokenize(command_, tokens);
+
+        if (tokens.size() == 2) {
+            std::string lastcmd = history_[history_.size() - 1];
+            int i = lastcmd.find(tokens[0]);
+
+            command_ = lastcmd.substr(0, i) + tokens[1] + lastcmd.substr(i + tokens[0].size());
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::run(void (*proc)(CmdResource*, CmdArg&, std::istream&, std::ostream&)) {
+    Log::debug() << "Executing " << std::endl << arg_ << std::endl;
+
+    if (flags_['x']) Log::info() << command_ << std::flush;
+
+    if (!flags_['n'])
+        if (!CmdResource::run(proc, arg_, std::cin, *out_))
+            if (flags_['e']) Main::instance().terminate();
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::alias(const std::string& name, const std::string& cmd) {
+    theAlias_[name] = cmd;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::alias(const std::string& name) {
+    (*out_) << theAlias_[name] << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::alias() {
+    (*out_) << theAlias_ << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::aliasCompletion(const std::string& text, std::vector<std::string>& result) {
+    std::vector<std::string> aliases = theAlias_.args();
+    for (size_t i = 0; i < aliases.size(); i++)
+        if (aliases[i].find(text) == 0) result.push_back(aliases[i]);
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::function(const std::string& lines) {
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::flag(const char flag, bool value) {
+    Log::debug() << "Setting flag " << flag << " to " << value << std::endl;
+    flags_[flag] = value;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::flags(const std::string& s) {
+    std::string flags = "exntuv";
+
+    for (size_t j = 0; j < flags.length(); ++j) flags_[flags[j]] = false;
+
+    Tokenizer tokenize(" ");
+    std::vector<std::string> tokens;
+    tokenize(s, tokens);
+
+    for (size_t i = 0; i < tokens.size(); ++i) {
+        if (tokens[i].length() == 2) {
+            char begin = tokens[i][0];
+            if (begin == '-') flags_[tokens[i][1]] = true;
+            if (begin == '+') flags_[tokens[i][1]] = false;
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::reset() {
+    param_ = 0;
+    arg_.erase();
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::addCmd(const char* s) {
+    command_ += s;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::addCmd(const char c) {
+    command_ += c;
+}
+
+//-----------------------------------------------------------------------------
+
+int CmdParser::input() {
+    if (!buffer_.size()) return ';';
+
+    int n = buffer_.front();
+    buffer_.erase(buffer_.begin());
+    return n;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::unput(int n) {
+    buffer_.push_front(n);
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::output(int n) {
+    (*out_) << char(n);
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::arg(long n, const Value& v) {
+    arg_[n] = v;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::arg(const std::string& s, const Value& v) {
+    arg_[s] = v;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::arg(const Value& v) {
+    arg_[++param_] = v;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::var(const std::string& s) {
+    arg_[++param_] = variables_.exists(s) ? std::string(variables_[s]) : std::string(environment_[s]);
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::var(const std::string& s, const Value& v) {
+    variables_[s] = v;
+}
+
+//-----------------------------------------------------------------------------
+
+void CmdParser::prompt(bool p) {
+    prompt_ = p;
+}
+
+void CmdParser::console(bool c) {
+    console_ = c;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/CmdParser.h b/eckit/src/eckit/cmd/CmdParser.h
new file mode 100644
index 0000000..5d9c492
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdParser.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   CmdParser.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_cmd_CmdParser_H
+#define eckit_cmd_CmdParser_H
+
+#include <iostream>
+#include <vector>
+
+#include "eckit/cmd/UserInput.h"
+
+namespace eckit {
+
+class CmdArg;
+class CmdResource;
+class PathName;
+class Value;
+
+//-----------------------------------------------------------------------------
+
+class Prompter {
+public:
+    virtual std::string prompt() const = 0;
+};
+
+class CmdParser {
+public:
+    // -- Methods
+
+    static void parse(std::istream&, std::ostream&, const Prompter& prompter);
+    static void parse(const std::string&, std::ostream&);
+    static void parse(const eckit::PathName&, std::ostream&);
+
+    static void unAlias();
+
+    // For commands over shell things
+    static void history(const long, std::ostream&);
+    static void environment(std::ostream&);
+    static void environment(const std::string&, const std::string&);
+    static void alias();
+    static void alias(const std::string&);
+    static void alias(const std::string&, const std::string&);
+    static void aliasCompletion(const std::string&, std::vector<std::string>&);
+    static void function(const std::string&);
+    static void flag(const char, bool);
+
+    static int input();
+    static void unput(int);
+    static void output(int);
+
+    // On commad line
+    static void prompt(const Prompter&);
+    static void reset();
+    static void addCmd(const char* s);
+    static void addCmd(const char c);
+    static void historize();
+
+    static void prompt(bool);
+    static void console(bool);
+
+    // On startup
+    static void flags(const std::string&);
+
+    // Built-in
+    static void repeat();
+    static void substitute();
+    static void shell(const std::string&);
+
+    static void arg(const std::string&, const eckit::Value&);
+    static void arg(long, const eckit::Value&);
+    static void arg(const eckit::Value&);
+
+    static void var(const std::string&, const eckit::Value&);
+    static void var(const std::string&);
+
+    static void run(void (*)(eckit::CmdResource*, eckit::CmdArg&));
+    static void run(void (*)(eckit::CmdResource*, eckit::CmdArg&, std::istream&, std::ostream&));
+
+private:
+    // No copy allowed
+
+    CmdParser(const CmdParser&);
+    CmdParser& operator=(const CmdParser&);
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/CmdResource.cc b/eckit/src/eckit/cmd/CmdResource.cc
new file mode 100644
index 0000000..bf2d8bd
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdResource.cc
@@ -0,0 +1,299 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/cmd/CmdArg.h"
+#include "eckit/cmd/CmdResource.h"
+#include "eckit/cmd/TermBuf.h"
+#include "eckit/cmd/CmdParser.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/config/Resource.h"
+#include "eckit/os/SignalHandler.h"
+#include "eckit/io/StdPipe.h"
+#include "eckit/io/StdioBuf.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/os/SignalHandler.h"
+
+// TODO: remember to add a mutex
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+CmdResource::Map* CmdResource::resources_ = 0;
+
+// ============================================================
+
+CmdResource::CmdResource(const std::string& s) {
+    Tokenizer tokenize(",");
+    std::vector<std::string> tokens;
+    tokenize(s, tokens);
+
+    if (!resources_) resources_ = new Map();
+    for (size_t i = 0; i < tokens.size(); i++) (*resources_)[tokens[i]] = this;
+}
+
+CmdResource::~CmdResource() {
+    // Should do something here...
+}
+
+void CmdResource::loop(CmdResource* cmd, CmdArg& arg, std::istream& in, std::ostream& o) {
+    long s = eckit::Resource<long>("loopDelay", 2);
+    TermBuf buf(o);
+
+    struct Hide {
+        Hide() { Monitor::instance().show(false); }
+        ~Hide() { Monitor::instance().show(true); }
+    };
+
+    //	Hide dontshow;
+
+    buf.init();
+    buf.clear();
+    std::ostream out(&buf);
+
+    // SignalHandler interrupt;
+    std::string strcmd(arg[0]);
+
+    for (;;) {
+        // Log::status() << "Executing '" << strcmd << "'" << std::endl;
+        buf.home();
+        cmd->execute(in, out, arg);
+        buf.clearEOS();
+        ::sleep(s);
+        SignalHandler::checkInterrupt();
+    }
+}
+
+void CmdResource::command(CmdResource* cmd, CmdArg& arg, std::istream& in, std::ostream& out) {
+    cmd->execute(in, out, arg);
+    out << std::flush;
+}
+
+void CmdResource::print(std::ostream& out) const {
+    Map* m = resources_;
+
+    out << "Contents: " << std::endl;
+    for (Map::iterator i = m->begin(); i != m->end(); ++i) out << (*i).first << " = " << (*i).second << std::endl;
+}
+
+std::vector<std::string> CmdResource::completion(const std::string& c) {
+    Map* m = resources_;
+    std::vector<std::string> result;
+
+    for (Map::iterator i = m->begin(); i != m->end(); ++i) {
+        std::string cmd = (*i).first;
+        if (cmd.find(c) == 0) {
+            result.push_back(cmd);
+        }
+    }
+
+    CmdParser::aliasCompletion(c, result);
+
+    std::sort(result.begin(), result.end());
+
+    return result;
+}
+
+std::vector<std::string> CmdResource::completion(const std::vector<std::string>& r) {
+    std::vector<std::string> result;
+    std::vector<std::string> copy(r);
+
+    Map* m = resources_;
+    Map::iterator j = m->find(r[0]);
+
+    if (j != m->end()) {
+        Arg a((*j).second->usage(r[0]));
+        result = a.completion(copy);
+    }
+
+    return result;
+}
+
+// Called by UserInput
+bool CmdResource::completion(const char* line, int pos, char* insert, int insertmax) {
+
+    std::vector<std::string> c;
+    const char *p = line;
+    int n = 0;
+
+    c.push_back("");
+    while(true) {
+
+        if(n == pos) {
+            std::vector<std::string> v;
+            if(c.size() == 1) {
+                v = completion(c[0]);
+            }
+            else {
+                v = completion(c);
+            }
+
+
+            if(v.size() == 1) {
+                for(size_t i = c.back().length(); i < v[0].length() && i < size_t(insertmax); i++) {
+                    *insert++ = v[0][i];
+                }
+                *insert = 0;
+                return true;
+            }
+
+            // Copy matches
+
+            int k = 0;
+            for(size_t i = 0; i < v.size() ; i++) {
+
+                if(i && k < insertmax) {
+                    *insert++ = ' ';
+                    k++;
+                }
+
+                for(size_t j = 0; j < v[i].length() && k < insertmax; j++, k++) {
+                    *insert++ = v[i][j];
+                }
+                *insert = 0;
+            }
+            return false;
+        }
+
+        if(*p == 0) {
+            break;
+        }
+
+        if(*p == ' ') {
+            c.push_back("");
+        }
+        else {
+            c.back().push_back(*p);
+        }
+        p++;
+        n++;
+    }
+
+    return false; // silence compiler warning
+}
+
+void CmdResource::help(std::ostream& out, const std::string& cmdname) {
+    Map* m = resources_;
+
+    if (cmdname == "") {
+        for (Map::iterator i = m->begin(); i != m->end(); ++i) {
+            std::string cmd = (*i).first;
+            out << " " << cmd << std::setw(16 - cmd.length()) << std::setfill(' ') << " ";
+            //(*i).second->help(out);
+            out << " " << (*i).second->usage(cmd);
+            out << std::endl;
+        }
+    } else {
+        Map::iterator j = m->find(cmdname);
+
+        if (j != m->end()) {
+            CmdResource* cmd = (*j).second;
+            out << cmdname;
+            out << std::setw(15 - cmdname.length()) << std::setfill(' ') << " ";
+            out << cmd->usage(cmdname) << std::endl;
+            out << std::endl;
+            cmd->help(out);
+            out << std::endl;
+        } else
+            out << " '" << cmdname << "' not found" << std::endl;
+    }
+}
+
+bool CmdResource::run(void (*proc)(CmdResource*, CmdArg&, std::istream&, std::ostream&), CmdArg& arg, std::istream& in,
+                      std::ostream& out) {
+    static bool fail = eckit::Resource<bool>("-fail", false);
+
+    const std::string strcmd = arg[0];
+
+    Map* m = resources_;
+    Map::iterator j = m->find(strcmd);
+
+    if (j != m->end()) {
+        CmdResource* cmd = (*j).second;
+        Log::status() << "Executing '" << strcmd << "'" << std::endl;
+        try {
+            SignalHandler interrupt;
+            proc(cmd, arg, in, out);
+            return true;
+        } catch (Abort& e) {
+            if (fail) throw e;
+        } catch (std::exception& e) {
+            if (fail) throw e;
+
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is ignored" << std::endl;
+        }
+    } else {
+        if (fail) throw eckit::SeriousBug(strcmd + ": command not found");
+        out << "'" << strcmd << "': command not found" << std::endl;
+    }
+
+    return false;
+}
+
+void CmdResource::redirect(CmdResource* cmd, CmdArg& args, std::istream& in, std::ostream&) {
+    Tokenizer tokenize(" ");
+    std::vector<std::string> tokens;
+    tokenize(std::string(args[">"]), tokens);
+
+    std::string file = tokens[0];
+    std::ofstream out(file.c_str());
+
+    if (!out) throw CantOpenFile(file);
+
+    CmdArg newargs = args;
+    newargs.erase(">");
+
+    cmd->execute(in, out, newargs);
+
+    out.close();
+    if (out.bad()) throw WriteError(file);
+}
+
+void CmdResource::append(CmdResource* cmd, CmdArg& args, std::istream& in, std::ostream&) {
+    Tokenizer tokenize(" ");
+    std::vector<std::string> tokens;
+    tokenize(std::string(args[">>"]), tokens);
+
+    std::string file = tokens[0];
+    std::ofstream out(file.c_str(), std::ios::app);
+    if (!out) throw CantOpenFile(file);
+
+    CmdArg newargs = args;
+    newargs.erase(">>");
+
+    cmd->execute(in, out, newargs);
+
+    out.close();
+    if (out.bad()) throw WriteError(file);
+}
+
+void CmdResource::pipe(CmdResource* cmd, CmdArg& args, std::istream& in, std::ostream&) {
+    const std::string to = args["|"];
+
+    StdPipe pipe(to, "w");
+    StdioBuf buf(pipe);
+    std::ostream out(&buf);
+
+    CmdArg newargs = args;
+    newargs.erase("|");
+
+    cmd->execute(in, out, newargs);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/CmdResource.h b/eckit/src/eckit/cmd/CmdResource.h
new file mode 100644
index 0000000..b469c7d
--- /dev/null
+++ b/eckit/src/eckit/cmd/CmdResource.h
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_cmd_CmdResource_H
+#define eckit_cmd_CmdResource_H
+
+#include "eckit/eckit.h"
+#include "eckit/memory/NonCopyable.h"
+
+#include "eckit/cmd/Arg.h"
+#include "eckit/cmd/CmdArg.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class CmdResource : private eckit::NonCopyable {
+
+    typedef void (*Proc)(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+
+public:
+    // -- Contructors
+
+    CmdResource(const std::string&);
+
+    // -- Destructor
+
+    virtual ~CmdResource();
+
+    // -- Class methods
+
+    static bool run(Proc, CmdArg&, std::istream&, std::ostream&);
+
+    // Procs called from CmdParser
+    static void command(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+    static void loop(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+    static void pipe(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+    static void redirect(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+    static void append(CmdResource*, CmdArg&, std::istream&, std::ostream&);
+
+    static std::vector<std::string> completion(const std::string&);
+    static std::vector<std::string> completion(const std::vector<std::string>&);
+    static bool completion(const char*, int pos, char*, int);
+
+protected:  // methods
+    virtual void execute(std::istream&, std::ostream&, CmdArg&) = 0;
+
+    virtual Arg usage(const std::string& cmd) const = 0;
+    virtual void help(std::ostream&) const = 0;
+
+    virtual void print(std::ostream&) const;
+
+    // -- Class methods
+
+    static void help(std::ostream&, const std::string&);
+
+private:  // members
+    typedef std::map<std::string, CmdResource*, std::less<std::string> > Map;
+
+    static Map* resources_;
+
+private:  // friends
+    friend std::ostream& operator<<(std::ostream& o, const CmdResource& cmd) {
+        cmd.print(o);
+        return o;
+    }
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/ConfigCmd.cc b/eckit/src/eckit/cmd/ConfigCmd.cc
new file mode 100644
index 0000000..99062ba
--- /dev/null
+++ b/eckit/src/eckit/cmd/ConfigCmd.cc
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/ConfigCmd.h"
+#include "eckit/config/Configurable.h"
+#include "eckit/config/ResourceMgr.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ConfigCmd::ConfigCmd() : CmdResource("config") {
+}
+
+ConfigCmd::~ConfigCmd() {
+}
+
+void ConfigCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    if (arg.exists(1) && arg.exists(2)) {
+        ResourceMgr::instance().set(arg[1], arg[2]);
+        Configurable::reconfigureAll();
+    }
+
+    Configurable::dumpAllResources(out);
+}
+
+void ConfigCmd::help(std::ostream& out) const {
+}
+
+Arg ConfigCmd::usage(const std::string& cmd) const {
+    return ~(Arg("<resource>", Arg::text) + Arg("<value>", Arg::text));
+}
+
+static ConfigCmd configCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/ConfigCmd.h b/eckit/src/eckit/cmd/ConfigCmd.h
new file mode 100644
index 0000000..586e92b
--- /dev/null
+++ b/eckit/src/eckit/cmd/ConfigCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   ConfigCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_ConfigCmd_H
+#define eckit_cmd_ConfigCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ConfigCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    ConfigCmd();
+
+    // -- Destructor
+
+    ~ConfigCmd();
+
+private:
+    // No copy allowed
+
+    ConfigCmd(const ConfigCmd&);
+    ConfigCmd& operator=(const ConfigCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/DirCmd.cc b/eckit/src/eckit/cmd/DirCmd.cc
new file mode 100644
index 0000000..870b2ab
--- /dev/null
+++ b/eckit/src/eckit/cmd/DirCmd.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <limits.h>
+#include <unistd.h>
+
+#include "eckit/cmd/DirCmd.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+DirCmd::DirCmd() : CmdResource("cd,pwd") {
+}
+
+//-----------------------------------------------------------------------------
+
+DirCmd::~DirCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void DirCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    if (arg[0] == "cd") {
+        std::string p("~");
+        if (arg.exists(1)) {
+            p = std::string(arg[1]);
+        }
+        SYSCALL(chdir(PathName(p).localPath()));
+    }
+
+    char buf[PATH_MAX];
+    const char* p = getcwd(buf, sizeof(buf));
+    if (p)
+        out << p << std::endl;
+    else
+        throw FailedSystemCall("getcwd");
+}
+
+//-----------------------------------------------------------------------------
+
+void DirCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg DirCmd::usage(const std::string& cmd) const {
+    return cmd == "cd" ? ~Arg("<path>") : Arg();
+}
+
+//-----------------------------------------------------------------------------
+
+static DirCmd dirCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/DirCmd.h b/eckit/src/eckit/cmd/DirCmd.h
new file mode 100644
index 0000000..89545ca
--- /dev/null
+++ b/eckit/src/eckit/cmd/DirCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   DirCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_DirCmd_H
+#define eckit_cmd_DirCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DirCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    DirCmd();
+
+    // -- Destructor
+
+    ~DirCmd();
+
+private:
+    // No copy allowed
+
+    DirCmd(const DirCmd&);
+    DirCmd& operator=(const DirCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/EchoCmd.cc b/eckit/src/eckit/cmd/EchoCmd.cc
new file mode 100644
index 0000000..e0355c8
--- /dev/null
+++ b/eckit/src/eckit/cmd/EchoCmd.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/EchoCmd.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+EchoCmd::EchoCmd() : CmdResource("echo") {
+}
+
+//-----------------------------------------------------------------------------
+
+EchoCmd::~EchoCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void EchoCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    std::string sep = "";
+    for (size_t i = 1; i < arg.size(); i++) {
+        Value var = arg[i];  // Otherwise is Accessor<>.print()
+        out << sep << var;
+        sep = " ";
+    }
+    out << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void EchoCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg EchoCmd::usage(const std::string& cmd) const {
+    return Arg("<any> ...");
+}
+
+//-----------------------------------------------------------------------------
+
+static EchoCmd echoCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/EchoCmd.h b/eckit/src/eckit/cmd/EchoCmd.h
new file mode 100644
index 0000000..4271e62
--- /dev/null
+++ b/eckit/src/eckit/cmd/EchoCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   EchoCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_EchoCmd_H
+#define eckit_cmd_EchoCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class EchoCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    EchoCmd();
+
+    // -- Destructor
+
+    ~EchoCmd();
+
+private:
+    // No copy allowed
+
+    EchoCmd(const EchoCmd&);
+    EchoCmd& operator=(const EchoCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/EnvironmentCmd.cc b/eckit/src/eckit/cmd/EnvironmentCmd.cc
new file mode 100644
index 0000000..086e89d
--- /dev/null
+++ b/eckit/src/eckit/cmd/EnvironmentCmd.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/CmdParser.h"
+#include "eckit/cmd/EnvironmentCmd.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+EnvironmentCmd::EnvironmentCmd() : CmdResource("environment,env") {
+}
+
+//-----------------------------------------------------------------------------
+
+EnvironmentCmd::~EnvironmentCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void EnvironmentCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    CmdParser::environment(out);
+}
+
+//-----------------------------------------------------------------------------
+
+void EnvironmentCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg EnvironmentCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+//-----------------------------------------------------------------------------
+
+static EnvironmentCmd environmentCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/EnvironmentCmd.h b/eckit/src/eckit/cmd/EnvironmentCmd.h
new file mode 100644
index 0000000..e421d66
--- /dev/null
+++ b/eckit/src/eckit/cmd/EnvironmentCmd.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   EnvironmentCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_EnvironmentCmd_H
+#define eckit_cmd_EnvironmentCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class EnvironmentCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    EnvironmentCmd();
+
+    // -- Destructor
+
+    ~EnvironmentCmd();
+
+private:
+    // No copy allowed
+
+    EnvironmentCmd(const EnvironmentCmd&);
+    EnvironmentCmd& operator=(const EnvironmentCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/ExportCmd.cc b/eckit/src/eckit/cmd/ExportCmd.cc
new file mode 100644
index 0000000..7a06d3e
--- /dev/null
+++ b/eckit/src/eckit/cmd/ExportCmd.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/CmdParser.h"
+#include "eckit/cmd/ExportCmd.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ExportCmd::ExportCmd() : CmdResource("export,setenv") {
+}
+
+//-----------------------------------------------------------------------------
+
+ExportCmd::~ExportCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void ExportCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    CmdParser::environment(arg[1], arg[2]);
+}
+
+//-----------------------------------------------------------------------------
+
+void ExportCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg ExportCmd::usage(const std::string& cmd) const {
+    return Arg("<name>") + Arg("<value>");
+}
+
+//-----------------------------------------------------------------------------
+
+static ExportCmd exportCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/ExportCmd.h b/eckit/src/eckit/cmd/ExportCmd.h
new file mode 100644
index 0000000..7f03c98
--- /dev/null
+++ b/eckit/src/eckit/cmd/ExportCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   ExportCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_ExportCmd_H
+#define eckit_cmd_ExportCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ExportCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    ExportCmd();
+
+    // -- Destructor
+
+    ~ExportCmd();
+
+private:
+    // No copy allowed
+
+    ExportCmd(const ExportCmd&);
+    ExportCmd& operator=(const ExportCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/HistoryCmd.cc b/eckit/src/eckit/cmd/HistoryCmd.cc
new file mode 100644
index 0000000..f3325d1
--- /dev/null
+++ b/eckit/src/eckit/cmd/HistoryCmd.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit_config.h"
+
+#include "eckit/cmd/HistoryCmd.h"
+
+#include "eckit/cmd/CmdParser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static HistoryCmd history;
+
+//-----------------------------------------------------------------------------
+
+HistoryCmd::HistoryCmd() : CmdResource("history,h") {
+}
+
+//-----------------------------------------------------------------------------
+
+HistoryCmd::~HistoryCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void HistoryCmd::execute(std::istream& in, std::ostream& out, CmdArg& args) {
+    long long lines = 0;
+    if (args.exists(1)) lines = args[1];
+    CmdParser::history(lines, out);
+}
+
+//-----------------------------------------------------------------------------
+
+void HistoryCmd::help(std::ostream& out) const {
+    out << "lists the history of entered commands";
+}
+
+//-----------------------------------------------------------------------------
+
+Arg HistoryCmd::usage(const std::string& cmd) const {
+    return Arg("<lines>", Arg::number);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/HistoryCmd.h b/eckit/src/eckit/cmd/HistoryCmd.h
new file mode 100644
index 0000000..0e376f4
--- /dev/null
+++ b/eckit/src/eckit/cmd/HistoryCmd.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   HistoryCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_HistoryCmd_H
+#define eckit_cmd_HistoryCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class HistoryCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    HistoryCmd();
+
+    // -- Destructor
+
+    ~HistoryCmd();
+
+private:
+    // No copy allowed
+
+    HistoryCmd(const HistoryCmd&);
+    HistoryCmd& operator=(const HistoryCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream& out, CmdArg&);
+    virtual void help(std::ostream&) const;
+
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/JSONCmd.cc b/eckit/src/eckit/cmd/JSONCmd.cc
new file mode 100644
index 0000000..dd8500f
--- /dev/null
+++ b/eckit/src/eckit/cmd/JSONCmd.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/JSONCmd.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/runtime/Monitorable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+JSONCmd::JSONCmd() : CmdResource("json") {
+}
+
+JSONCmd::~JSONCmd() {
+}
+
+void JSONCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    JSON j(out);
+    Monitorable::allJSON(j);
+}
+
+void JSONCmd::help(std::ostream&) const {
+}
+
+Arg JSONCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+static JSONCmd jsonCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/JSONCmd.h b/eckit/src/eckit/cmd/JSONCmd.h
new file mode 100644
index 0000000..a7ede6e
--- /dev/null
+++ b/eckit/src/eckit/cmd/JSONCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   JSONCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_JSONCmd_H
+#define eckit_cmd_JSONCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class JSONCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    JSONCmd();
+
+    // -- Destructor
+
+    ~JSONCmd();
+
+private:
+    // No copy allowed
+
+    JSONCmd(const JSONCmd&);
+    JSONCmd& operator=(const JSONCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/KillCmd.cc b/eckit/src/eckit/cmd/KillCmd.cc
new file mode 100644
index 0000000..52fcec3
--- /dev/null
+++ b/eckit/src/eckit/cmd/KillCmd.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+
+#include "eckit/cmd/KillCmd.h"
+#include "eckit/runtime/Monitor.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+KillCmd::KillCmd() : CmdResource("kill") {
+}
+
+//-----------------------------------------------------------------------------
+
+KillCmd::~KillCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void KillCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+
+    for (size_t i = 1; i < arg.size(); ++i) {
+
+        Value task(arg[i]);
+
+        if (task.isNil()) {
+            // usage(out);
+            return;
+        }
+
+        if (task.isNumber()) {
+            // MarsId or Unix process ID
+            unsigned long marsid = (long long)task;
+            if (marsid > info.size())
+                kill(marsid, out);
+            else
+                kill(info[marsid].pid(), out);
+        }
+
+        if (task.isString()) {
+            // Name. Look for Unix process ID
+            std::string name = task;
+            for (unsigned long i = 0; i < info.size(); ++i)
+                if (info[i].busy(true) && info[i].application() == name)
+                    kill(info[i].pid(), out);
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void KillCmd::kill(pid_t pid, std::ostream& out) const {
+    static pid_t me = ::getpid();
+    if (pid == me)
+        out << pid << ": Suicide avoided ;-)" << std::endl;
+    else {
+        if (::kill(pid, SIGTERM))
+            out << Log::syserr << std::endl;
+        else
+            out << pid << ": Killed" << std::endl;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void KillCmd::help(std::ostream& out) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg KillCmd::usage(const std::string& cmd) const {
+    return Arg("<name>", Arg::text) | Arg("<pid>", Arg::number) | Arg("<task>", Arg::number);
+}
+
+//-----------------------------------------------------------------------------
+
+static KillCmd killCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/KillCmd.h b/eckit/src/eckit/cmd/KillCmd.h
new file mode 100644
index 0000000..bede376
--- /dev/null
+++ b/eckit/src/eckit/cmd/KillCmd.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   KillCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_KillCmd_H
+#define eckit_cmd_KillCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class KillCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    KillCmd();
+
+    // -- Destructor
+
+    ~KillCmd();
+
+private:
+    // No copy allowed
+
+    KillCmd(const KillCmd&);
+    KillCmd& operator=(const KillCmd&);
+
+    // -- Methods
+
+    void kill(pid_t, std::ostream&) const;
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/LockCmd.cc b/eckit/src/eckit/cmd/LockCmd.cc
new file mode 100644
index 0000000..861c820
--- /dev/null
+++ b/eckit/src/eckit/cmd/LockCmd.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/LockCmd.h"
+
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+LockCmd::LockCmd() : CmdResource("lock,unlock") {
+}
+
+//-----------------------------------------------------------------------------
+
+LockCmd::~LockCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void LockCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    PathName path("~/locks/admin/cron");
+
+    if (std::string(arg[0]) == "lock") {
+        if (path.exists()) {
+            out << "already locked (" << path << " exists)" << std::endl;
+        } else {
+            path.touch();
+            out << "locked (" << path << " touched)" << std::endl;
+            // make the prompt reflect the state
+        }
+    } else  // unlock
+    {
+        if (path.exists()) {
+            path.unlink();
+            out << "unlocked (" << path << " removed)" << std::endl;
+        } else {
+            out << "not locked (" << path << " missing)" << std::endl;
+        }
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void LockCmd::man(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+void LockCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg LockCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+//-----------------------------------------------------------------------------
+
+static LockCmd lockCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/LockCmd.h b/eckit/src/eckit/cmd/LockCmd.h
new file mode 100644
index 0000000..a298dea
--- /dev/null
+++ b/eckit/src/eckit/cmd/LockCmd.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_cmd_LockCmd_H
+#define eckit_cmd_LockCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class LockCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    LockCmd();
+
+    // -- Destructor
+
+    ~LockCmd();
+
+private:
+    // No copy allowed
+
+    LockCmd(const LockCmd&);
+    LockCmd& operator=(const LockCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+    virtual void man(std::ostream&) const;
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+inline void destroy(LockCmd**) {
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif  // LockCmd_H
diff --git a/eckit/src/eckit/cmd/ManCmd.cc b/eckit/src/eckit/cmd/ManCmd.cc
new file mode 100644
index 0000000..bda6371
--- /dev/null
+++ b/eckit/src/eckit/cmd/ManCmd.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/ManCmd.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static ManCmd l;
+
+//-----------------------------------------------------------------------------
+
+ManCmd::ManCmd() : CmdResource("man,help") {
+}
+
+//-----------------------------------------------------------------------------
+
+ManCmd::~ManCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void ManCmd::execute(std::istream&, std::ostream& out, CmdArg& args) {
+    CmdResource::help(out, args(1, ""));
+}
+
+//-----------------------------------------------------------------------------
+
+void ManCmd::help(std::ostream& out) const {
+    out << "manual pages for commands";
+}
+
+//-----------------------------------------------------------------------------
+
+Arg ManCmd::usage(const std::string& cmd) const {
+    return ~Arg("<command>");
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/ManCmd.h b/eckit/src/eckit/cmd/ManCmd.h
new file mode 100644
index 0000000..bfd672c
--- /dev/null
+++ b/eckit/src/eckit/cmd/ManCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   ManCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_ManCmd_H
+#define eckit_cmd_ManCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ManCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    ManCmd();
+
+    // -- Destructor
+
+    ~ManCmd();
+
+private:
+    // No copy allowed
+
+    ManCmd(const ManCmd&);
+    ManCmd& operator=(const ManCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream& out, CmdArg&);
+    virtual void help(std::ostream&) const;
+
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/MemoryCmd.cc b/eckit/src/eckit/cmd/MemoryCmd.cc
new file mode 100644
index 0000000..fed96a3
--- /dev/null
+++ b/eckit/src/eckit/cmd/MemoryCmd.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/MemoryCmd.h"
+#include "eckit/memory/MemoryPool.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+MemoryCmd::MemoryCmd() : CmdResource("memory") {
+}
+
+MemoryCmd::~MemoryCmd() {
+}
+
+void MemoryCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    MemoryPool::info(out);
+}
+
+void MemoryCmd::help(std::ostream&) const {
+}
+
+Arg MemoryCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+static MemoryCmd MemoryCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/MemoryCmd.h b/eckit/src/eckit/cmd/MemoryCmd.h
new file mode 100644
index 0000000..ece0c94
--- /dev/null
+++ b/eckit/src/eckit/cmd/MemoryCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   MemoryCmd.h
+/// @author Manuel Fuentes
+/// @date   Sep 1998
+
+#ifndef eckit_cmd_MemoryCmd_H
+#define eckit_cmd_MemoryCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MemoryCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    MemoryCmd();
+
+    // -- Destructor
+
+    ~MemoryCmd();
+
+private:
+    // No copy allowed
+
+    MemoryCmd(const MemoryCmd&);
+    MemoryCmd& operator=(const MemoryCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/PsCmd.cc b/eckit/src/eckit/cmd/PsCmd.cc
new file mode 100644
index 0000000..730a153
--- /dev/null
+++ b/eckit/src/eckit/cmd/PsCmd.cc
@@ -0,0 +1,248 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/PsCmd.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/Colour.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static PsCmd ps;
+
+//-----------------------------------------------------------------------------
+
+PsCmd::PsCmd() : CmdResource("ps") {
+}
+
+//-----------------------------------------------------------------------------
+
+PsCmd::~PsCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void PsCmd::display(std::ostream& out, TaskInfo& info, long tasknb, const std::string& grep) const {
+    char st = info.state();
+
+    if (info.max() != info.min()) {
+        out << Colour::green;
+    }
+
+    if (st == 'T' || st == 'V' || st == 'P' || st == 'X') {
+        out << Colour::cyan;
+    }
+
+    if (st == 'Z' || st == 'L') {
+        out << Colour::magenta;
+    }
+
+    if (std::string(info.status()).find("queued") != std::string::npos) {
+        out << Colour::yellow;
+    }
+
+    if (grep.length()) {
+        if (std::string(info.status()).find(grep) != std::string::npos) {
+            out << Colour::red << Colour::bold;
+        }
+    }
+
+    for (int i = 0; i < info.depth(); i++) out << "   ";
+
+    // name column
+    std::string app = info.name();
+    out << app;
+
+    int n = info.depth() * 3 + app.length();
+    for (int i = 0; i < 16 - n; i++) out << ' ';
+
+    // Idle column
+    long age = ::time(0) - info.last();
+
+    long s = age % 60;
+    age /= 60;
+    long m = age % 60;
+    age /= 60;
+    long h = age % 24;
+    age /= 24;
+    long d = age;
+
+    if (d)
+        out << std::setw(d > 1 ? 2 : 3) << d << "day" << (d > 1 ? "s" : "");
+    else if (h || m)
+        out << std::setw(3) << h << ":" << std::setw(2) << std::setfill('0') << m << std::setfill(' ');
+    else
+        out << std::setw(6) << s;
+
+    // State flag
+    out << ' ' << info.state();
+    // Pid column
+    out << std::setw(9) << info.pid();
+    // Task column
+    out << "   " << std::setfill('0') << std::setw(3) << tasknb << std::setfill(' ');
+    // ID column
+    out << std::setw(11) << info.taskID();
+    // Info column
+    out << std::setw(22) << info.message();
+    // Request column
+    out << "  " << info.status();
+
+    // Progress indicator
+    if (info.max() != info.min()) {
+        double x = (info.val() - info.min()) * 100.0 / (info.max() - info.min());
+        out << ' ' << x << '%';
+    }
+
+    out << std::endl;
+
+    out << Colour::reset;
+}
+
+//-----------------------------------------------------------------------------
+
+static void get(int n, std::vector<int>& v) {
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+    if (n != -1) {
+        get(info[n].parent(), v);
+        v.push_back(n);
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+static std::string get(int n) {
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+
+    if (info[n].parent() != -1) return get(info[n].parent());
+
+    return info[n].name();
+}
+
+//-----------------------------------------------------------------------------
+
+static bool sortTasks(int n1, int n2) {
+
+    std::string s1 = get(n1);
+    std::string s2 = get(n2);
+
+    if (s1 != s2) return s1 < s2;
+
+    std::vector<int> v1;
+    std::vector<int> v2;
+
+    get(n1, v1);
+    get(n2, v2);
+
+    return v1 < v2;
+}
+
+//-----------------------------------------------------------------------------
+
+static bool isChild(Monitor::TaskArray& info, const std::string& name, int task) {
+    int parent = info[task].parent();
+    if ((parent == -1) && (name == info[task].application())) return true;
+
+    if (parent != -1) return isChild(info, name, parent);
+
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+
+static bool isParent(Monitor::TaskArray& info, int taskid, int task) {
+    int parenttask = info[task].parent();
+    int parentid = info[taskid].parent();
+    if (task == taskid) return true;
+
+    return ((parenttask != -1) && isParent(info, taskid, parenttask)) ||
+           ((parentid != -1) && isParent(info, parentid, task));
+}
+
+//-----------------------------------------------------------------------------
+
+void PsCmd::execute(std::istream& in, std::ostream& out, CmdArg& args) {
+    std::string grep;
+
+    std::vector<int> t;
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+
+    std::vector<std::string> tasks;
+    std::vector<int> taskids;
+    std::vector<int> pids;
+    bool all = true;
+
+    if (args.exists("grep")) {
+        grep = std::string(args["grep"]);
+    }
+
+    for (size_t i = 1; i < args.size(); ++i)
+        if (args.exists(i)) {
+            all = false;
+            if (args[i].isNumber()) {
+                int id = args[i];
+                if (info.size() >= static_cast<unsigned long>(id))
+                    taskids.push_back(id);
+                else
+                    pids.push_back(id);
+            } else
+                tasks.push_back(args[i]);
+        }
+
+    for (size_t j = 0; j < info.size(); j++)
+        if (info[j].busy(true) && info[j].show()) t.push_back(j);
+
+    std::sort(t.begin(), t.end(), sortTasks);
+
+    out << Colour::bold;
+    out << "name              Idle        Pid   Task       ID   Info      Request" << std::endl;
+    out << "---------------------------------------------------------------------" << std::endl;
+    out << Colour::reset;
+
+    if (all)
+        for (size_t j = 0; j < t.size(); j++) display(out, info[t[j]], t[j], grep);
+    else
+        for (size_t j = 0; j < t.size(); j++) {
+            for (Ordinal i = 0; i < tasks.size(); ++i)
+                if (isChild(info, tasks[i], t[j])) {
+                    display(out, info[t[j]], t[j], grep);
+                    break;
+                }
+            for (Ordinal k = 0; k < taskids.size(); ++k)
+                if (isParent(info, taskids[k], t[j])) {
+                    display(out, info[t[j]], t[j], grep);
+                    break;
+                }
+            for (Ordinal l = 0; l < pids.size(); ++l)
+                if (pids[l] == info[t[j]].pid()) {
+                    display(out, info[t[j]], t[j], grep);
+                    break;
+                }
+        }
+}
+
+//-----------------------------------------------------------------------------
+
+void PsCmd::help(std::ostream& out) const {
+    out << "as the UNIX counterpart";
+}
+
+//-----------------------------------------------------------------------------
+
+Arg PsCmd::usage(const std::string& cmd) const {
+    return ~Arg("-grep", Arg::text) + Arg("<name> ...", Arg::text);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/PsCmd.h b/eckit/src/eckit/cmd/PsCmd.h
new file mode 100644
index 0000000..7458428
--- /dev/null
+++ b/eckit/src/eckit/cmd/PsCmd.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   PsCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_PsCmd_H
+#define eckit_cmd_PsCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+namespace eckit {
+
+class TaskInfo;
+
+//-----------------------------------------------------------------------------
+
+class PsCmd : public eckit::CmdResource {
+public:
+
+    PsCmd();
+
+    virtual ~PsCmd();
+
+private: // methods
+
+    void display(std::ostream&, eckit::TaskInfo&, long, const std::string&) const;
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream& out, eckit::CmdArg&);
+    virtual void help(std::ostream&) const;
+
+    virtual eckit::Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/QuitCmd.cc b/eckit/src/eckit/cmd/QuitCmd.cc
new file mode 100644
index 0000000..a8ff7e8
--- /dev/null
+++ b/eckit/src/eckit/cmd/QuitCmd.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/QuitCmd.h"
+#include "eckit/runtime/Application.h"
+#include "eckit/runtime/Main.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static QuitCmd quit;
+
+//-----------------------------------------------------------------------------
+
+QuitCmd::QuitCmd() : CmdResource("quit,exit") {
+}
+
+//-----------------------------------------------------------------------------
+
+QuitCmd::~QuitCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void QuitCmd::execute(std::istream&, std::ostream& out, CmdArg&) {
+    out << "Bye" << std::endl;
+    Application::instance().terminate();
+}
+
+//-----------------------------------------------------------------------------
+
+void QuitCmd::help(std::ostream& out) const {
+    out << "quits the '" << Main::instance().name() << "'";
+}
+
+//-----------------------------------------------------------------------------
+
+Arg QuitCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/QuitCmd.h b/eckit/src/eckit/cmd/QuitCmd.h
new file mode 100644
index 0000000..439b6d2
--- /dev/null
+++ b/eckit/src/eckit/cmd/QuitCmd.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   QuitCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_QuitCmd_H
+#define eckit_cmd_QuitCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class QuitCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    QuitCmd();
+
+    // -- Destructor
+
+    ~QuitCmd();
+
+private:
+    // No copy allowed
+
+    QuitCmd(const QuitCmd&);
+    QuitCmd& operator=(const QuitCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream& out, CmdArg&);
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/RemoteCmd.cc b/eckit/src/eckit/cmd/RemoteCmd.cc
new file mode 100644
index 0000000..c6fc7ed
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCmd.cc
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/RemoteCmd.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPStream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+RemoteCmd::RemoteCmd() : CmdResource("remote") {
+}
+
+//-----------------------------------------------------------------------------
+
+RemoteCmd::~RemoteCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void RemoteCmd::execute(std::istream& in, std::ostream& out, CmdArg& arg) {
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+
+    Value a(arg[1]);
+    long taskid = -1;
+    int port = 0;
+    std::string host;
+
+    if (a.isNil()) {
+        out << "An application or task id must be specified" << std::endl;
+        return;
+    }
+
+    if (a.isString()) {
+        std::string name = arg[1];
+        for (unsigned long j = 0; j < info.size(); j++) {
+            if (info[j].busy(true) && info[j].application() == name && info[j].parent() == -1) {
+                taskid = j;
+                break;
+            }
+        }
+    }
+
+    if (a.isNumber()) taskid = (long long)a;
+
+    if (taskid < 0 || static_cast<unsigned long>(taskid) >= info.size()) {
+        if (a.isString()) out << a << ": task not found" << std::endl;
+        if (a.isNumber()) out << taskid << ": task Id not found" << std::endl;
+        return;
+    }
+
+    if (info[taskid].busy(true)) {
+        port = info[taskid].port();
+        host = info[taskid].host();
+    }
+
+    if (port == 0 || host == "") {
+        out << "Application '" << taskid << "' cannot be reached" << std::endl;
+        return;
+    }
+
+    TCPStream remote(TCPClient().connect(host, port));
+
+    Log::debug() << "RemoteCommand to " << host << ":" << port << std::endl;
+
+    try {
+        CmdArg remotecmd = arg;
+        remotecmd.erase(0, 1);
+        remotecmd.encode(remote);
+
+        char buf[1024 * 4];
+        int len = 1024 * 4;
+        while ((len = remote.read(buf, sizeof(buf))) > 0)
+            std::copy(buf, buf + len, std::ostream_iterator<char>(out, ""));
+        out << std::flush;
+    } catch (std::exception& e) {
+        // Suppose the server has finished
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void RemoteCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg RemoteCmd::usage(const std::string& cmd) const {
+    return Arg("<process>") + Arg("<command>...");
+}
+
+//-----------------------------------------------------------------------------
+
+static RemoteCmd remoteCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/RemoteCmd.h b/eckit/src/eckit/cmd/RemoteCmd.h
new file mode 100644
index 0000000..bbf6eb6
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   RemoteCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_RemoteCmd_H
+#define eckit_cmd_RemoteCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class RemoteCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    RemoteCmd();
+
+    // -- Destructor
+
+    ~RemoteCmd();
+
+private:
+    // No copy allowed
+
+    RemoteCmd(const RemoteCmd&);
+    RemoteCmd& operator=(const RemoteCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/RemoteCommandUser.cc b/eckit/src/eckit/cmd/RemoteCommandUser.cc
new file mode 100644
index 0000000..8830ee2
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommandUser.cc
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/CmdResource.h"
+#include "eckit/cmd/RemoteCommandUser.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+RemoteCommandUser::RemoteCommandUser(TCPSocket& protocol) : NetUser(protocol), from_(protocol_.remoteHost()) {
+}
+
+//-----------------------------------------------------------------------------
+
+RemoteCommandUser::~RemoteCommandUser() {
+}
+
+//-----------------------------------------------------------------------------
+
+void RemoteCommandUser::serve(Stream& s, std::istream& in, std::ostream& out) {
+
+    Log::debug() << "Starting a remote command connection " << std::endl;
+
+    Monitor::instance().kind("monitor");
+    Monitor::instance().name("monitor");
+
+    CmdArg cmd(s);
+
+    Log::debug() << "Got command: " << cmd << std::endl;
+    //	CmdResource::dispatch(in,out);
+    CmdResource::run(CmdResource::command, cmd, in, out);
+
+    Log::debug() << "Exiting remote command ..." << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/RemoteCommandUser.h b/eckit/src/eckit/cmd/RemoteCommandUser.h
new file mode 100644
index 0000000..c03a129
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommandUser.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   RemoteCommandUser.h
+/// @author Manuel Fuentes
+/// @date   Jul 1996
+
+#ifndef eckit_cmd_RemoteCommandUser_H
+#define eckit_cmd_RemoteCommandUser_H
+
+#include "eckit/net/NetUser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class RemoteCommandUser : public eckit::NetUser {
+public:
+    RemoteCommandUser(eckit::TCPSocket&);
+    virtual ~RemoteCommandUser();
+    static void terminate(RemoteCommandUser& other) { other.stop(); }
+
+private:
+    virtual void serve(eckit::Stream&, std::istream&, std::ostream&);
+    std::string from_;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/RemoteCommandable.cc b/eckit/src/eckit/cmd/RemoteCommandable.cc
new file mode 100644
index 0000000..f416e2f
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommandable.cc
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/RemoteCommandable.h"
+#include "eckit/cmd/RemoteCommander.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+RemoteCommandable::RemoteCommandable(int port) :
+    commander_(new RemoteCommander(port)) {
+    commander_.start();
+}
+
+//-----------------------------------------------------------------------------
+
+RemoteCommandable::~RemoteCommandable() {
+    commander_.stop();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/RemoteCommandable.h b/eckit/src/eckit/cmd/RemoteCommandable.h
new file mode 100644
index 0000000..cf1d997
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommandable.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   RemoteCommand.h
+/// @author Manuel Fuentes
+/// @date   Jul 1996
+
+#ifndef eckit_cmd_RemoteCommandable_H
+#define eckit_cmd_RemoteCommandable_H
+
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// A RemoteCommand-able object
+
+class RemoteCommandable : private eckit::NonCopyable {
+public:
+    // -- Contructors
+
+    RemoteCommandable(int port = 0);
+
+    // -- Destructor
+
+    ~RemoteCommandable();
+
+private:
+    // -- Members
+
+    eckit::ThreadControler commander_;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/RemoteCommander.cc b/eckit/src/eckit/cmd/RemoteCommander.cc
new file mode 100644
index 0000000..d4e5862
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommander.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/RemoteCommander.h"
+#include "eckit/cmd/RemoteCommandUser.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/config/Resource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+RemoteCommander::RemoteCommander(int p) : NetService(p) {
+    Monitor::instance().port(port());
+    std::string host = Resource<std::string>("localBindingAddr", "localHost");
+    Monitor::instance().host(host);
+}
+
+//-----------------------------------------------------------------------------
+
+RemoteCommander::~RemoteCommander() {
+}
+
+//-----------------------------------------------------------------------------
+
+NetUser* RemoteCommander::newUser(TCPSocket& protocol) {
+    return new RemoteCommandUser(protocol);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/RemoteCommander.h b/eckit/src/eckit/cmd/RemoteCommander.h
new file mode 100644
index 0000000..50dafd5
--- /dev/null
+++ b/eckit/src/eckit/cmd/RemoteCommander.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   RemoteCommander.h
+/// @author Manuel Fuentes
+/// @date   Jul 1996
+
+#ifndef eckit_cmd_RemoteCommander_H
+#define eckit_cmd_RemoteCommander_H
+
+#include "eckit/net/NetService.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class RemoteCommander : public NetService {
+public:
+    // -- Contructors
+
+    RemoteCommander(int);
+
+    // -- Destructor
+
+    ~RemoteCommander();
+
+private:
+    // -- Overridden methods
+
+    // From NetService
+
+    virtual NetUser* newUser(TCPSocket&);
+    virtual std::string name() { return "monitor"; }
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/SleepCmd.cc b/eckit/src/eckit/cmd/SleepCmd.cc
new file mode 100644
index 0000000..879db0c
--- /dev/null
+++ b/eckit/src/eckit/cmd/SleepCmd.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/cmd/SleepCmd.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+SleepCmd::SleepCmd() : CmdResource("sleep") {
+}
+
+//-----------------------------------------------------------------------------
+
+SleepCmd::~SleepCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void SleepCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    long t = arg[1];
+
+    ::sleep(t);
+}
+
+//-----------------------------------------------------------------------------
+
+void SleepCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg SleepCmd::usage(const std::string& cmd) const {
+    return Arg("<duration>", Arg::number);
+}
+
+//-----------------------------------------------------------------------------
+
+static SleepCmd sleepCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/SleepCmd.h b/eckit/src/eckit/cmd/SleepCmd.h
new file mode 100644
index 0000000..a46ad43
--- /dev/null
+++ b/eckit/src/eckit/cmd/SleepCmd.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   SleepCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_SleepCmd_H
+#define eckit_cmd_SleepCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class SleepCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    SleepCmd();
+
+    // -- Destructor
+
+    ~SleepCmd();
+
+private:
+    // No copy allowed
+
+    SleepCmd(const SleepCmd&);
+    SleepCmd& operator=(const SleepCmd&);
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/StartCmd.cc b/eckit/src/eckit/cmd/StartCmd.cc
new file mode 100644
index 0000000..5016094
--- /dev/null
+++ b/eckit/src/eckit/cmd/StartCmd.cc
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/StartCmd.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/config/Resource.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StartCmd::StartCmd() : CmdResource("start") {
+}
+
+//-----------------------------------------------------------------------------
+
+StartCmd::~StartCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void StartCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    std::string app = arg["1"];
+
+    if (app == "all") {
+        std::string all = Resource<std::string>("allApplications", "mars,reader,flusher,cleaner,httpsvr,safety");
+
+        Tokenizer token(", \t");
+        std::vector<std::string> names;
+        token(all, names);
+
+        for (std::vector<std::string>::iterator i = names.begin(); i != names.end(); ++i)
+            start(out, *i);
+    } else
+        start(out, app);
+}
+
+//-----------------------------------------------------------------------------
+
+void StartCmd::start(std::ostream& out, const std::string& app) const {
+    Monitor::instance().start(app);
+}
+
+//-----------------------------------------------------------------------------
+
+void StartCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg StartCmd::usage(const std::string& cmd) const {
+    return Arg("all") | Arg("<name>", Arg::text);
+}
+
+//-----------------------------------------------------------------------------
+
+static StartCmd startCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/StartCmd.h b/eckit/src/eckit/cmd/StartCmd.h
new file mode 100644
index 0000000..91eece2
--- /dev/null
+++ b/eckit/src/eckit/cmd/StartCmd.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   StartCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_StartCmd_H
+#define eckit_cmd_StartCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StartCmd : public eckit::CmdResource {
+public:
+    // -- Contructors
+
+    StartCmd();
+
+    // -- Destructor
+
+    ~StartCmd();
+
+private:
+    // No copy allowed
+
+    StartCmd(const StartCmd&);
+    StartCmd& operator=(const StartCmd&);
+
+    // -- Methods
+
+    void start(std::ostream&, const std::string&) const;
+
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, eckit::CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual eckit::Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/StatusCmd.cc b/eckit/src/eckit/cmd/StatusCmd.cc
new file mode 100644
index 0000000..2e34537
--- /dev/null
+++ b/eckit/src/eckit/cmd/StatusCmd.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/StatusCmd.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Monitorable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StatusCmd::StatusCmd() : CmdResource("status") {
+}
+
+StatusCmd::~StatusCmd() {
+}
+
+void StatusCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    setformat(out, Log::monitorFormat);
+    Monitorable::allStatuses(out);
+}
+
+void StatusCmd::help(std::ostream&) const {
+}
+
+Arg StatusCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+static StatusCmd statusCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/StatusCmd.h b/eckit/src/eckit/cmd/StatusCmd.h
new file mode 100644
index 0000000..1dd6665
--- /dev/null
+++ b/eckit/src/eckit/cmd/StatusCmd.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   StatusCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_StatusCmd_H
+#define eckit_cmd_StatusCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StatusCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    StatusCmd();
+
+    // -- Destructor
+
+    ~StatusCmd();
+
+private:
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/StopCmd.cc b/eckit/src/eckit/cmd/StopCmd.cc
new file mode 100644
index 0000000..e83db00
--- /dev/null
+++ b/eckit/src/eckit/cmd/StopCmd.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/cmd/StopCmd.h"
+#include "eckit/runtime/Monitor.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StopCmd::StopCmd() : CmdResource("stop") {
+}
+
+//-----------------------------------------------------------------------------
+
+StopCmd::~StopCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void StopCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    std::string app = arg[1];
+    bool all = false;
+
+    if (app == "all") all = true;
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+    for (unsigned long j = 0; j < info.size(); j++)
+        if (info[j].busy(true) && (all || app == info[j].application()))
+            if (info[j].pid() != getpid()) info[j].stop();
+}
+
+//-----------------------------------------------------------------------------
+
+void StopCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg StopCmd::usage(const std::string& cmd) const {
+    return Arg("all") | Arg("<name>", Arg::text);
+}
+
+//-----------------------------------------------------------------------------
+
+static StopCmd stopCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/StopCmd.h b/eckit/src/eckit/cmd/StopCmd.h
new file mode 100644
index 0000000..c062795
--- /dev/null
+++ b/eckit/src/eckit/cmd/StopCmd.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   StopCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_StopCmd_H
+#define eckit_cmd_StopCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StopCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    StopCmd();
+
+    // -- Destructor
+
+    ~StopCmd();
+
+private:
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/TailCmd.cc b/eckit/src/eckit/cmd/TailCmd.cc
new file mode 100644
index 0000000..fd37dda
--- /dev/null
+++ b/eckit/src/eckit/cmd/TailCmd.cc
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/cmd/TailCmd.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/config/Resource.h"
+#include "eckit/os/SignalHandler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static TailCmd tail;
+
+//-----------------------------------------------------------------------------
+
+TailCmd::TailCmd() : CmdResource("tail") {
+}
+
+//-----------------------------------------------------------------------------
+
+TailCmd::~TailCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void TailCmd::execute(std::istream&, std::ostream& out, CmdArg& args) {
+    long long pid = -1;
+    long long lines = Resource<long>("tailCmd", 10);
+    bool follow = false;
+
+    if (args.exists(1)) pid = args[1];
+
+    if (args.exists("f")) {
+        follow = true;
+        pid = args["f"];
+    }
+
+    if (args.exists("n")) {
+        lines = args["n"];
+    }
+
+    if (pid == -1) {
+        // usage(out); out << std::endl;
+        return;
+    }
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+
+    if (!info[pid].busy(true)) {
+        out << pid << ": No such process" << std::endl;
+        return;
+    }
+
+    unsigned long where = 0;
+
+    static char buffer[10240];
+
+    unsigned long pos = where;
+    unsigned long len = info[pid].text(buffer, sizeof(buffer), pos);
+
+    if (len) {
+        long nl = 0;
+        buffer[len] = 0;
+        int j = 0;
+        for (j = (buffer[len - 1] == '\n') ? len - 2 : len; j >= 0 && nl < lines; j--)
+            if (buffer[j] == '\n') nl++;
+
+        if (j != 0) j++;
+
+        out << &(buffer[j]) << std::flush;
+        where = pos;
+    }
+
+    SignalHandler controlC;
+
+    time_t last = info[pid].last();
+    while (info[pid].busy(true) && follow) {
+        if (last != info[pid].last()) {
+            pos = where;
+            len = info[pid].text(buffer, sizeof(buffer), pos);
+            if (len) {
+                buffer[len] = 0;
+                out << buffer << std::flush;
+                where = pos;
+            }
+            last = info[pid].last();
+        } else
+            ::usleep(1000);
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void TailCmd::help(std::ostream& out) const {
+    out << "as the UNIX counterpart";
+}
+
+//-----------------------------------------------------------------------------
+
+Arg TailCmd::usage(const std::string& cmd) const {
+    return ~Arg("-f") + Arg("<task>", Arg::number);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/TailCmd.h b/eckit/src/eckit/cmd/TailCmd.h
new file mode 100644
index 0000000..ebd2eaf
--- /dev/null
+++ b/eckit/src/eckit/cmd/TailCmd.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   TailCmd.h
+/// @author Manuel Fuentes
+/// @date   Jan 1997
+
+#ifndef TailCmd_H
+#define TailCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TailCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    TailCmd();
+
+    // -- Destructor
+
+    ~TailCmd();
+
+private:
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream& out, CmdArg&);
+    virtual void help(std::ostream&) const;
+
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/TermBuf.cc b/eckit/src/eckit/cmd/TermBuf.cc
new file mode 100644
index 0000000..6999145
--- /dev/null
+++ b/eckit/src/eckit/cmd/TermBuf.cc
@@ -0,0 +1,88 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/TermBuf.h"
+
+extern "C" {
+int TermPutChar(int);
+void TermInit();
+void TermClear();
+void TermHome();
+void TermClearEOL();
+void TermClearEOS();
+}
+
+static std::ostream* os = 0;
+
+int TermPutChar(int c) {
+    *os << char(c);
+    return c;
+}
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TermBuf::TermBuf(std::ostream& o) : out_(o) {
+    setp(buffer_, buffer_ + sizeof(buffer_));
+}
+
+TermBuf::~TermBuf() {
+    sync();
+}
+
+int TermBuf::sync() {
+    for (char* c = pbase(); c != pptr(); ++c) {
+        if (*c == '\n') clearEOL();
+        out_ << *c;
+    }
+    setp(pbase(), epptr());
+    out_ << std::flush;
+    return 0;
+}
+
+int TermBuf::overflow(int c) {
+    sync();
+    if (c == EOF) return 0;
+
+    sputc(c);
+    return 0;
+}
+
+void TermBuf::init() {
+    os = &out_;
+    TermInit();
+}
+
+void TermBuf::clear() {
+    os = &out_;
+    TermClear();
+}
+
+void TermBuf::home() {
+    os = &out_;
+    TermHome();
+}
+
+void TermBuf::clearEOL() {
+    os = &out_;
+    TermClearEOL();
+}
+
+void TermBuf::clearEOS() {
+    os = &out_;
+    TermClearEOS();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/TermBuf.h b/eckit/src/eckit/cmd/TermBuf.h
new file mode 100644
index 0000000..bc9d72c
--- /dev/null
+++ b/eckit/src/eckit/cmd/TermBuf.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   TermBuf.h
+/// @author Manuel Fuentes
+/// @date   Aug 1996
+
+#ifndef eckit_cmd_TermBuf_H
+#define eckit_cmd_TermBuf_H
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TermBuf : public std::streambuf {
+public:
+    // -- Contructors
+
+    TermBuf(std::ostream&);
+
+    // -- Destructor
+
+    ~TermBuf();
+
+    // -- Class Methods
+
+    void init();
+    void clear();
+    void home();
+    void clearEOL();
+    void clearEOS();
+
+private:
+    // No copy allowed
+
+    TermBuf(const TermBuf&);
+    TermBuf& operator=(const TermBuf&);
+
+    // -- Members
+
+    char buffer_[1024];
+    std::ostream& out_;
+
+    // -- Methods
+
+    virtual int overflow(int c);
+    virtual int sync();
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/UpTimeCmd.cc b/eckit/src/eckit/cmd/UpTimeCmd.cc
new file mode 100644
index 0000000..96b1bef
--- /dev/null
+++ b/eckit/src/eckit/cmd/UpTimeCmd.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/cmd/UpTimeCmd.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/Seconds.h"
+#include "eckit/log/TimeStamp.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+UpTimeCmd::UpTimeCmd() : CmdResource("uptime") {
+}
+
+//-----------------------------------------------------------------------------
+
+UpTimeCmd::~UpTimeCmd() {
+}
+
+//-----------------------------------------------------------------------------
+
+void UpTimeCmd::execute(std::istream&, std::ostream& out, CmdArg& arg) {
+    time_t now = ::time(0);
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+    for (unsigned long j = 0; j < info.size(); j++)
+        if (info[j].busy(true) && (info[j].parent() == -1))
+            out << TimeStamp(info[j].start()) << " " << info[j].application()
+                << " " << Seconds(now - info[j].start()) << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+void UpTimeCmd::help(std::ostream&) const {
+}
+
+//-----------------------------------------------------------------------------
+
+Arg UpTimeCmd::usage(const std::string& cmd) const {
+    return Arg();
+}
+
+//-----------------------------------------------------------------------------
+
+static UpTimeCmd uptimeCmd;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/UpTimeCmd.h b/eckit/src/eckit/cmd/UpTimeCmd.h
new file mode 100644
index 0000000..dfcee0c
--- /dev/null
+++ b/eckit/src/eckit/cmd/UpTimeCmd.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   UpTimeCmd.h
+/// @author Baudouin Raoult
+/// @date   Jan 1997
+
+#ifndef eckit_cmd_UpTimeCmd_H
+#define eckit_cmd_UpTimeCmd_H
+
+#include "eckit/cmd/CmdResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class UpTimeCmd : public CmdResource {
+public:
+    // -- Contructors
+
+    UpTimeCmd();
+
+    // -- Destructor
+
+    ~UpTimeCmd();
+
+private:
+    // -- Overridden methods
+
+    // From CmdResource
+
+    virtual void execute(std::istream&, std::ostream&, CmdArg&);
+
+    virtual void help(std::ostream&) const;
+    virtual Arg usage(const std::string& cmd) const;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/UserInput.cc b/eckit/src/eckit/cmd/UserInput.cc
new file mode 100644
index 0000000..6e023be
--- /dev/null
+++ b/eckit/src/eckit/cmd/UserInput.cc
@@ -0,0 +1,659 @@
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <signal.h>
+
+#include "eckit/cmd/UserInput.h"
+
+
+namespace eckit {
+
+struct termios save;
+static bool inited = false;
+
+
+enum {
+    ESC = 0x1B,
+    BACKSPACE = 0x7F,
+    TAB = 0x9,
+    CR = 0XD,
+    CONTROL_A = 0x1,
+    CONTROL_B = 0x2,
+    CONTROL_C = 0x3,
+    CONTROL_D = 0x4,
+    CONTROL_E = 0x5,
+    CONTROL_F = 0x6,
+    CONTROL_G = 0x7,
+    CONTROL_H = 0x8,
+    CONTROL_I = 0x9,
+    CONTROL_J = 0xA,
+    CONTROL_K = 0xB,
+    CONTROL_L = 0xC,
+    CONTROL_M = 0xD,
+    CONTROL_N = 0xE,
+    CONTROL_O = 0xF,
+    CONTROL_P = 0x10,
+    CONTROL_Q = 0x11,
+    CONTROL_R = 0x12,
+    CONTROL_S = 0x13,
+    CONTROL_T = 0x14,
+    CONTROL_U = 0x15,
+    CONTROL_V = 0x16,
+    CONTROL_W = 0x17,
+    CONTROL_X = 0x18,
+    CONTROL_Y = 0x19,
+    CONTROL_Z = 0x1A,
+
+    // Virtual codes
+    UP_ARROW = 1000,
+    DOWN_ARROW = 1001,
+    LEFT_ARROW = 1002,
+    RIGHT_ARROW = 1003,
+    HOME = 1004,
+    END = 1005,
+    DELETE = 1006,
+    INSERT = 1007,
+    PAGE_UP = 1008,
+    PAGE_DOWN = 1009
+};
+
+
+static void exitRaw() {
+    if (inited) {
+        tcsetattr(0, TCSAFLUSH, &save);
+    }
+}
+
+static void enterRaw() {
+
+    if (!isatty(0)) {
+        return;
+    }
+
+    if (tcgetattr(0, &save) < 0) {
+        perror("tcgetattr");
+    }
+
+    struct termios raw = save;
+
+    cfmakeraw(&raw);
+
+    // Re-enable ^C and ^Z
+
+    // raw.c_lflag |= ISIG;
+
+    raw.c_cc[VMIN] = 1; // On char at a time
+    raw.c_cc[VTIME] = 0;
+
+    if (!inited) {
+        atexit(&exitRaw);
+        inited = true;
+    }
+
+    if (tcsetattr(0, TCSAFLUSH, &raw) < 0) {
+        perror("tcsetattr");
+    }
+}
+
+typedef struct entry {
+    struct entry *next;
+    struct entry *prev;
+    char *line;
+    char *edit;
+    int len;
+} entry;
+
+static entry *history = NULL;
+
+typedef struct context {
+    const char *prompt;
+    char *clipboard;
+    entry *curr;
+    int pos;
+    int mark;
+    bool overwrite;
+    bool eof;
+    bool tab;
+    UserInput::completion_proc completion;
+} context;
+
+static bool processCode(int c, context *s);
+
+static void output(const context *s) {
+    char *buffer = (char *)malloc(strlen(s->prompt) + strlen(s->curr->edit) + 20);
+    sprintf(buffer, "\r%s%s\033[0K\r\033[%luC", s->prompt, s->curr->edit, strlen(s->prompt) + s->pos);
+    write(1, buffer, strlen(buffer));
+    free(buffer);
+}
+
+static entry *current(entry *e) {
+    if (!e->edit) {
+        e->edit = (char *)malloc(e->len);
+        strcpy(e->edit, e->line);
+    }
+    return e;
+}
+
+static void del(context *s) {
+    if (s->pos > 0) {
+        int i;
+        char *line = s->curr->edit;
+        int len = strlen(line);
+        for (i = s->pos - 1; i < len; i++) {
+            line[i] = line[i + 1];
+        }
+        s->pos --;
+        line[len] = 0;
+    }
+}
+
+static void ins(context *s, char c) {
+    char *line = s->curr->edit;
+    int len = strlen(line);
+
+    if (len + 2 <= s->curr->len ) {
+        char *old = s->curr->edit;
+        s->curr->len += 80;
+        s->curr->edit = (char *)calloc(s->curr->len, 1);
+        strcpy(s->curr->edit, old);
+        free(old);
+        line = s->curr->edit;
+    }
+
+    if (!s->overwrite) {
+        int i;
+        int len = strlen(line);
+        for (i = len; i >= s->pos; i--) {
+            line[i + 1] = line[i];
+        }
+    }
+    line[s->pos++] = c;
+}
+
+static char nextChar() {
+    char c;
+    if (read(0, &c, 1) != 1) {
+        return 0;
+    }
+    return c;
+}
+
+static struct {
+    const char *sequence;
+    int code;
+} escapes[] = {
+    {"[A", UP_ARROW, },
+    {"[B", DOWN_ARROW, },
+    {"[C", RIGHT_ARROW, },
+    {"[D", LEFT_ARROW, },
+    {"OH", HOME, },
+    {"OF", END, },
+    {"[1~", HOME, },
+    {"[2~", INSERT, },
+    {"[3~", DELETE, },
+    {"[4~", END, },
+    {"[5~", PAGE_UP, },
+    {"[6~", PAGE_DOWN, },
+};
+
+static void esc(context *s) {
+    int len = sizeof(escapes) / sizeof(escapes[0]);
+    int i = 0;
+    int j = 0;
+    char buf[80];
+    memset(buf, 0, sizeof(buf));
+    while (1) {
+        int partial = 0;
+        buf[j++] = nextChar();
+        for (i = 0; i < len; i++) {
+            if (strncmp(escapes[i].sequence, buf, j) == 0) {
+                partial++;
+            }
+            if (strcmp(escapes[i].sequence, buf) == 0) {
+                processCode(escapes[i].code, s);
+                return;
+            }
+        }
+
+        if (partial == 0) {
+            break;
+        }
+    }
+
+    /* No match: output all charaters */
+    for (i = 0; i < j; i++) {
+        processCode(buf[i], s);
+    }
+}
+
+static bool processCode(int c, context *s) {
+
+    char *p, *q;
+
+    if (c != TAB) {
+        s->tab = false;
+    }
+
+    switch (c) {
+
+    case 0:
+        s->curr->edit[0] = 0;
+        s->eof = true;
+        return true;
+
+    case CONTROL_D:
+        if (strlen(s->curr->edit)) {
+            return processCode(BACKSPACE, s);
+        } else {
+            return processCode(0, s);
+        }
+        break;
+
+    case BACKSPACE:
+    case CONTROL_H:
+        del(s);
+        break;
+
+    case DELETE:
+        s->pos++;
+        del(s);
+        break;
+
+    case TAB:
+        if (s->completion) {
+            char insert[10240];
+            char *p = insert;
+            memset(insert, 0, sizeof(insert));
+
+            if (s->completion(s->curr->edit, s->pos, insert, sizeof(insert) - 1)) {
+                while (*p) {
+                    ins(s, *p);
+                    p++;
+                }
+                if (insert[0] && (size_t(s->pos) == strlen(s->curr->edit))) {
+                    ins(s, ' ');
+                }
+            } else {
+                if (s->tab) { // Second TAB
+                    write(1, "\r\n", 2);
+                    write(1, insert, strlen(insert));
+                    write(1, "\r\n", 2);
+                    s->tab = false;
+                } else {
+                    s->tab = true;
+                }
+            }
+
+        }
+        break;
+
+    case CONTROL_U:
+        p = s->curr->edit + s->pos;
+        q = s->curr->edit;
+
+        if (s->clipboard) {
+            free(s->clipboard);
+        }
+        s->clipboard = strdup(s->curr->edit);
+        strncpy(s->clipboard, s->curr->edit, s->pos);
+        s->clipboard[s->pos] = 0;
+
+        while (*p) {
+            *q++ = *p++;
+        }
+        *q = 0;
+        s->pos = 0;
+        break;
+
+    case ESC:
+        esc(s);
+        break;
+
+    case CONTROL_L:
+        write(1, "\033[2J\033[H", 7);
+        break;
+
+    case UP_ARROW:
+    case CONTROL_P:
+        if (s->curr->prev) {
+            s->curr = current(s->curr->prev);
+            s->pos = strlen(s->curr->edit);
+        }
+        break;
+
+    case DOWN_ARROW:
+    case CONTROL_N:
+        if (s->curr->next) {
+            s->curr = current(s->curr->next);
+            s->pos = strlen(s->curr->edit);
+        }
+        break;
+
+    case RIGHT_ARROW:
+    case CONTROL_F:
+        s->pos++;
+        if (size_t(s->pos) > strlen(s->curr->edit)) {
+            s->pos = strlen(s->curr->edit);
+        }
+        break;
+
+    case LEFT_ARROW:
+    case CONTROL_B:
+        s->pos--;
+        if (s->pos < 0) {
+            s->pos = 0;
+        }
+        break;
+
+    case CONTROL_K:
+        if (s->clipboard) {
+            free(s->clipboard);
+        }
+        s->clipboard = strdup(s->curr->edit);
+        strcpy(s->clipboard, s->curr->edit + s->pos);
+        s->curr->edit[s->pos] = 0;
+        break;
+
+    case CONTROL_A:
+    case HOME:
+        s->pos = 0;
+        break;
+
+    case CONTROL_E:
+    case END:
+        s->pos = strlen(s->curr->edit);
+        break;
+
+    case PAGE_UP:
+        break;
+
+    case PAGE_DOWN:
+        break;
+
+    case INSERT:
+        s->overwrite = !s->overwrite;
+        break;
+
+    case CONTROL_J:
+    case CR:
+        write(1, "\r\n", 2);
+        return true;
+        break;
+
+    case CONTROL_C:
+        write(1, "\r\n", 2);
+        s->pos = 0;
+        return processCode(0, s); // CONTROL_C behaves as CONTROL_D -- for backward compatibility to previous marsadm
+        break;
+
+    case CONTROL_G:
+        break;
+
+    case CONTROL_O:
+        break;
+
+    case CONTROL_Q:
+        break;
+
+    case CONTROL_R:
+        // TODO: backward search history
+        break;
+
+    case CONTROL_S:
+        // TODO: foreward search history
+        break;
+
+    case CONTROL_T:
+        if (strlen(s->curr->edit) > 1) {
+            int n = s->pos;
+            if (size_t(s->pos) == strlen(s->curr->edit)) {
+                n--;
+            }
+            if (n >= 1) {
+                char c = s->curr->edit[n];
+                s->curr->edit[n] = s->curr->edit[n - 1];
+                s->curr->edit[n - 1] = c;
+            }
+
+        }
+        break;
+
+    case CONTROL_V:
+        // TODO: escape next control char
+        break;
+
+    case CONTROL_W:
+        // TODO: delete word before
+        break;
+
+    case CONTROL_X:
+        switch (nextChar()) {
+        case CONTROL_X: {
+            int tmp = s->pos;
+            s->pos = s->mark;
+            s->mark = tmp;
+
+            if (size_t(s->pos) > strlen(s->curr->edit)) {
+                s->pos = strlen(s->curr->edit);
+            }
+        }
+        break;
+        }
+        break;
+
+    case CONTROL_Y:
+        if (s->clipboard) {
+            char *p = s->clipboard;
+            while (*p) {
+                ins(s, *p++);
+            }
+        }
+        break;
+
+    case CONTROL_Z:
+        exitRaw();
+        kill(0, SIGTSTP);
+        enterRaw();
+        break;
+
+    default:
+        if (isprint(c)) {
+            ins(s, c);
+        }
+        break;
+
+    }
+    return false;
+}
+
+void UserInput::printHistory(int max) {
+    entry *last = NULL;
+    entry *e;
+
+    if (max == 0) {
+        max = INT_MAX;
+    }
+
+    e = history;
+    while (e && max-- > 0) {
+        last = e;
+        e = e->prev;
+    }
+
+    while (last) {
+        printf("%s\n", last->line);
+        last = last->next;
+    }
+}
+
+void UserInput::saveHistory(const char *path, int max) {
+
+    entry *last = NULL;
+    entry *e;
+
+    FILE *f = ::fopen(path, "w");
+
+    if (!f) {
+        perror(path);
+        return;
+    }
+
+    if (max == 0) {
+        max = INT_MAX;
+    }
+
+    e = history;
+    while (e && max-- > 0) {
+        last = e;
+        e = e->prev;
+    }
+
+    while (last) {
+        fprintf(f, "%s\n", last->line);
+        last = last->next;
+    }
+
+    fclose(f);
+
+}
+
+void UserInput::loadHistory(const char *path) {
+
+    char line[10240];
+    FILE *f = ::fopen(path, "r");
+
+    if (!f) {
+        perror(path);
+        return;
+    }
+
+    memset(line, 0, sizeof(line));
+    while (fgets(line, sizeof(line) - 1, f)) {
+
+        entry *h = (entry *)calloc(sizeof(entry), 1);
+
+        int len = strlen(line);
+        while (len) {
+            if (line[len - 1] == '\n' || line[len - 1] == '\r') {
+                line[len - 1] = 0;
+                len--;
+            } else {
+                break;
+            }
+        }
+
+        if (len) {
+
+            h->len = len + 1 ;
+            h->line = strdup(line);
+            h->prev = history;
+
+            if (history) {
+                history->next = h;
+            }
+
+            history = h;
+        }
+    }
+
+    fclose(f);
+
+}
+
+static void cleanup_history() {
+    entry *h = history;
+    entry *n = NULL;
+
+    char *prev = strdup("");
+
+    while (h) {
+        n = h->prev;
+        if (h->edit) {
+            free(h->edit);
+            h->edit = NULL;
+        }
+
+        if (strlen(h->line) == 0 /*|| strcmp(h->line, prev) == 0*/) {
+            free(h->line);
+
+            if (history == h) {
+                history = h->prev;
+            }
+
+            if (h->next) {
+                h->next->prev = h->prev;
+            }
+
+            if (h->prev) {
+                h->prev->next = h->next;
+            }
+
+            free(h);
+        }
+        else {
+            free(prev);
+            prev = strdup(h->line);
+        }
+
+        h = n;
+    }
+
+    free(prev);
+}
+
+const char *UserInput::getUserInput(const char *prompt, completion_proc completion) {
+
+    bool done = false;
+    context s;
+    entry *h = (entry *)calloc(sizeof(entry), 1);
+    h->len = 80;
+    h->line = (char *)calloc(h->len, 1);
+    h->prev = history;
+
+    if (history) {
+        history->next = h;
+    }
+
+    history = h;
+
+    memset(&s, 0 , sizeof(s));
+    s.prompt = prompt;
+    s.curr = current(h);
+    s.completion = completion;
+
+    enterRaw();
+
+    output(&s);
+
+    while (!done) {
+        char c = nextChar();
+        done = processCode(c, &s);
+        if (!done) {
+            output(&s);
+        }
+    }
+
+    exitRaw();
+
+    if (s.clipboard) {
+        free(s.clipboard);
+    }
+
+    history->len = s.curr->len;
+    free(history->line);
+    history->line = strdup(s.curr->edit);
+
+    if( strlen(history->line) == 0) {
+        cleanup_history();
+        return s.eof ? NULL : "";
+    }
+
+    cleanup_history();
+
+    return s.eof ? NULL : (history ? history->line : "");
+}
+
+} // namespace eckit
diff --git a/eckit/src/eckit/cmd/UserInput.h b/eckit/src/eckit/cmd/UserInput.h
new file mode 100644
index 0000000..932dedf
--- /dev/null
+++ b/eckit/src/eckit/cmd/UserInput.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   UserInput.h
+/// @author Baudouin Raoult
+/// @date   Mar 2016
+
+#ifndef eckit_cmd_UserInput_H
+#define eckit_cmd_UserInput_H
+
+#include <vector>
+#include <string>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class UserInput {
+public:
+
+    typedef bool (*completion_proc)(const char*, int pos, char* insert, int insertmax);
+
+    static const char* getUserInput(const char* prompt, completion_proc callback = 0);
+
+    static void saveHistory(const char* path, int max = 0);
+    static void loadHistory(const char* path);
+
+    static void printHistory(int max = 0);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/cmd/cmdlib.h b/eckit/src/eckit/cmd/cmdlib.h
new file mode 100644
index 0000000..9153213
--- /dev/null
+++ b/eckit/src/eckit/cmd/cmdlib.h
@@ -0,0 +1,18 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file cmdlib.h
+
+#ifndef eckit_cmd_cmdlib_h
+#define eckit_cmd_cmdlib_h
+
+#include "eckit/eckit_config.h"
+
+#endif
diff --git a/eckit/src/eckit/cmd/cmdsl.l b/eckit/src/eckit/cmd/cmdsl.l
new file mode 100644
index 0000000..1d7ed70
--- /dev/null
+++ b/eckit/src/eckit/cmd/cmdsl.l
@@ -0,0 +1,142 @@
+%{
+
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#undef YYLMAX
+#define YYLMAX 2048
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "eckit/cmd/CmdParser.h"
+
+static int paren = 0; // Not thread safe, but are lex and yacc safe anyway?
+
+#ifdef __linux__
+#define YY_INPUT(buf,result,max_size) { int c = CmdParser::input(); result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); }
+#endif
+
+
+#undef input
+#define input()  CmdParser::input()
+
+#undef unput
+#define unput(n) CmdParser::unput(n)
+
+#undef output
+#define output(n) CmdParser::output(n)
+
+%}
+
+IDENT   [_A-Za-z/:]+[_0-9A-Za-z/:\.]*
+WORD    [A-Za-z:]+[_0-9A-Za-z/:\.]*
+NUMB    [0-9]+
+DIG1    [0-9]
+
+%s EXPRESSION
+
+%%
+
+<INITIAL>\|[a-z A-Z0-9/_$:\-\.\'\|\"]*  {
+					yylval.str = (const char *)yytext+1;
+					return PIPE;
+				}
+
+<INITIAL>\%[a-z A-Z0-9/_$:\-\.\'\|\"]*  {
+					yylval.str = (const char *)yytext+1;
+					return SHELL;
+				}
+
+<INITIAL>>[a-z A-Z0-9/_$:\-\.\'\|\"]*  {
+					yylval.str = (const char *)yytext+1;
+					return OUTPUT;
+				}
+
+
+<INITIAL>"("    { paren=1 ; BEGIN EXPRESSION ;     return *yytext ;}
+<EXPRESSION>"(" { paren++ ;                        return *yytext ;}
+<EXPRESSION>")" { if(--paren == 0) BEGIN INITIAL ; return *yytext ;}
+<EXPRESSION>">=" { return GE ; }
+<EXPRESSION>">" { return *yytext ; }
+<EXPRESSION>"/" { return *yytext ; }
+
+\"|\'  	{
+           int c,q = yytext[0];
+
+           yyleng = 0;
+
+           while((c = input()) && c != q && c != '\n')
+           {
+               if(c == '\\') yytext[yyleng++] = input();
+               else yytext[yyleng++] =  c;
+            }
+
+            yytext[yyleng++] = 0;
+            yylval.str = (const char *)yytext;
+            return STRING;
+        }
+
+"and"        { return AND; }
+"&&"         { return AND; }
+"=="         { return '='; }
+"!="         { return NE; }
+">="         { return GE; }
+"<="         { return LE; }
+"<>"         { return NE; }
+"or"         { return OR; }
+"||"         { return OR; }
+"not"        { return NOT; }
+"!"          { return NOT; }
+
+"=~"    	 { return MATCH; }
+"match"      { return MATCH; }
+
+"day"        { return DAY; }
+"days"       { return DAY; }
+"minute"     { return MINUTE; }
+"minutes"    { return MINUTE; }
+"second"     { return SECOND; }
+"seconds"    { return SECOND; }
+"week"       { return WEEK; }
+"weeks"      { return WEEK; }
+"month"      { return MONTH; }
+"months"     { return MONTH; }
+"year"       { return YEAR; }
+"years"      { return YEAR; }
+"hour"       { return HOUR; }
+"hours"      { return HOUR; }
+
+"Hb"         { return HEXABYTE; }
+"Pb"         { return PETABYTE; }
+"Tb"         { return TERABYTE; }
+"Gb"         { return GIGABYTE; }
+"Mb"         { return MEGABYTE; }
+"Kb"         { return KILOBYTE; }
+"byte"       { return BYTE; }
+"bytes"      { return BYTE; }
+
+<INITIAL>"-"{IDENT}	  { yylval.str = (const char *)yytext+1; return OPTION; }
+<INITIAL>{IDENT} 	  { yylval.str = (const char *)yytext; return STRING; }
+<EXPRESSION>{WORD}	  { yylval.str = (const char *)yytext; return STRING; }
+{NUMB} 		          { yylval.num = Translator<std::string,long long>()((const char *)yytext); return NUMB; }
+\#      {
+           int c;
+           while((c = input()) && (c != '\n'))
+                    ;
+		   return '\n';
+        }
+[ \t]*  ;
+\n      { return *yytext; }
+
+.	{ return *yytext; }
+%%
+    /* "loop"       { return LOOP; } */
+    /* "end"        { return END; } */
diff --git a/eckit/src/eckit/cmd/cmdsy.y b/eckit/src/eckit/cmd/cmdsy.y
new file mode 100644
index 0000000..9a7c937
--- /dev/null
+++ b/eckit/src/eckit/cmd/cmdsy.y
@@ -0,0 +1,248 @@
+%{
+
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+
+#ifdef YYBISON
+#define YYSTYPE_IS_DECLARED
+int yylex();
+/* int yydebug;*/ 
+extern "C" int isatty(int);
+#endif
+
+struct YYSTYPE {
+    std::string str;
+    long long num;  // change to long long, and in lex as well..
+    Value val;
+};
+
+inline Value Function(const std::string& op, const Value& a) {
+    return Value::makeList(Value(op)) + Value::makeList(a);
+}
+
+inline Value Function(const std::string& op, const Value& a, const Value& b) {
+    return Value::makeList(Value(op)) + Value::makeList(a) + Value::makeList(b);
+}
+
+%}
+
+%token <str>STRING
+%token <str>OPTION
+%token <str>SHELL
+%token <str>PIPE
+%token <str>OUTPUT
+%token <str>OUTAPPEND
+%token <num>NUMB
+
+%type  <val>value
+%type  <val>list
+
+%type  <val>expression
+%type  <val>condition
+%type  <val>conjonction
+%type  <val>term
+%type  <val>factor
+%type  <val>power
+%type  <val>disjonction
+
+%type  <num>ages
+%type  <num>age
+%type  <num>sizes
+%type  <num>size
+%type  <num>number
+
+%token DAY
+%token MONTH
+%token WEEK
+%token HOUR
+%token MINUTE
+%token SECOND
+%token YEAR
+
+%token AND 
+%token OR 
+%token LE 
+%token GE 
+%token NE 
+%token NOT 
+%token IN 
+%token MATCH 
+
+%token HEXABYTE
+%token PETABYTE
+%token TERABYTE
+%token GIGABYTE
+%token MEGABYTE
+%token KILOBYTE
+%token BYTE
+
+%%
+
+start : lines
+	  | empty
+	  ;
+
+lines : lines line
+      | line
+	  ;
+
+line  : command eol
+      | eol
+	  | error eol
+	  ;
+
+eol   : ';'						{ CmdParser::reset(); }
+	  | empty
+	  ;
+
+command : STRING args           { 
+								  CmdParser::arg(0,$1); 
+								  CmdParser::run(CmdResource::command);
+								} 
+        | '@' STRING args       { 
+								  CmdParser::arg(0,$2); 
+								  CmdParser::run(CmdResource::loop);
+								} 
+        | STRING args PIPE      { 
+								  CmdParser::arg("|",$3); 
+								  CmdParser::arg(0,$1);
+								  CmdParser::run(CmdResource::pipe);
+								}
+        | STRING args OUTPUT    { 
+								  CmdParser::arg(">",$3); 
+								  CmdParser::arg(0,$1);
+								  CmdParser::run(CmdResource::redirect); 
+								}
+        | STRING args OUTAPPEND { 
+								  CmdParser::arg(">>",$3); 
+								  CmdParser::arg(0,$1);
+								  CmdParser::run(CmdResource::append); 
+								}
+		| STRING '=' value       { CmdParser::var($1,$3); }
+		| SHELL                  { CmdParser::shell($1);    }
+		;
+
+args   : empty
+	   | arg args
+	   | arg
+	   | args params
+	   | variable
+	   ;
+
+arg   : OPTION value    { CmdParser::arg($1,$2); }
+      | OPTION          { CmdParser::arg($1,std::string("1")); }
+	  ;
+
+variable : '$' STRING   { CmdParser::var($2); }
+		 ;
+
+params : params param
+       | param
+       | empty
+	   ;
+
+param : value {  CmdParser::arg($1); }
+	  ;
+
+value : '[' list ']'       { $$ = $2; }
+	  | '[' ']'            { $$ = Value::makeList(); }
+	  | number             { $$ = Value($1); }
+	  | '-' number         { $$ = Function("neg",$2); }
+	  | STRING             { $$ = Value($1); }
+	  | '(' expression ')' { $$ = $2; }
+	  | STRING '(' ')'     { $$ = Value($1 + "()"); }
+	  ;
+
+
+
+power  		: value '^' power     { $$ = Function("pow",$1,$3); }
+	   		| value
+	   		;
+
+factor 		: factor '*' power   { $$ = Function("mul",$1,$3); }
+	   		| factor '/' power   { $$ = Function("div",$1,$3); }
+	   		| factor '%' power   { $$ = Function("mod",$1,$3); }
+	   		| power
+	   		;
+
+term   		: term '+' factor    { $$ = Function("add",$1,$3); }
+	   		| term '-' factor    { $$ = Function("sub",$1,$3); }
+	   		| term '&' factor    { $$ = Function("cat",$1,$3); }
+	  	 	| factor
+	  		;
+
+condition 	: condition '>'     term { $$ = Function("gt",$1,$3); }
+	 		| condition '='     term { $$ = Function("eq",$1,$3); }
+	 		| condition '<'     term { $$ = Function("lt",$1,$3); }
+	 		| condition  GE     term { $$ = Function("ge",$1,$3); }
+	 		| condition  LE     term { $$ = Function("le",$1,$3); }
+	 		| condition  NE     term { $$ = Function("ne",$1,$3); }
+	 		| condition  IN     term { $$ = Function("in",$1,$3); }
+	 		| condition  MATCH  term { $$ = Function("match",$1,$3); }
+	 		| NOT condition          { $$ = Function("not",$2); }
+			| term
+	 		;
+
+conjonction : conjonction AND condition { $$ = Function("and",$1,$3); }
+			| condition
+			;
+
+disjonction	: disjonction OR conjonction { $$ = Function("or",$1,$3); }
+			| conjonction
+			;
+
+expression 	: disjonction
+			;
+
+
+list : list ',' value    { $$ = $1 + Value::makeList($3); }
+	 | value             { $$ = Value::makeList($1); }
+	 ;
+
+number    :  ages 
+          |  sizes
+          |  NUMB
+		  ;
+
+ages      : ages age { $$ = $1 + $2; }
+          | age
+		  ;
+
+age       : NUMB SECOND { $$ = $1;}
+		  | NUMB MINUTE { $$ = $1 * 60;}
+		  | NUMB HOUR   { $$ = $1 * 60 * 60;}
+		  | NUMB DAY    { $$ = $1 * 60 * 60 * 24;}
+		  | NUMB WEEK   { $$ = $1 * 60 * 60 * 24 * 7;}
+		  | NUMB MONTH  { $$ = $1 * 60 * 60 * 24 * 31;}
+		  | NUMB YEAR   { $$ = $1 * 60 * 60 * 24 * 365;}
+		  ;
+
+sizes     : sizes size { $$ = $1 + $2; }
+		  | size
+		  ;
+
+size      : NUMB BYTE      { $$ = $1;}
+		  | NUMB KILOBYTE  { $$ = $1 * 1024; }
+		  | NUMB MEGABYTE  { $$ = $1 * 1024 * 1024; }
+		  | NUMB GIGABYTE  { $$ = $1 * 1024 * 1024 * 1024; }
+		  | NUMB TERABYTE  { $$ = $1 * 1024 * 1024 * 1024 * 1024; }
+		  | NUMB PETABYTE  { $$ = $1 * 1024 * 1024 * 1024 * 1024 * 1024; }
+		  | NUMB HEXABYTE  { $$ = $1 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024; }
+		  ;
+
+empty :
+      ;	
+
+%%
+#include "cmdsl.c"
diff --git a/eckit/src/eckit/cmd/term.c b/eckit/src/eckit/cmd/term.c
new file mode 100644
index 0000000..ec5399f
--- /dev/null
+++ b/eckit/src/eckit/cmd/term.c
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <curses.h>
+#include <term.h>
+#include <stdio.h>
+
+/* Term should a class, but curses.h is not C++ compatible */
+
+extern int TermPutChar(int);
+
+void TermInit()
+{
+	setupterm((char*) 0, 1, (int*) 0);
+}
+
+void TermHome()
+{
+	tputs(cursor_home,1,TermPutChar);
+}
+
+void TermClearEOL()
+{
+	tputs(clr_eol,1,TermPutChar);
+}
+
+void TermClearEOS()
+{
+	tputs(clr_eos,1,TermPutChar);
+}
+
+void TermClear()
+{
+	tputs(clear_screen,1,TermPutChar);
+}
diff --git a/eckit/src/eckit/compat/Inited.h b/eckit/src/eckit/compat/Inited.h
new file mode 100644
index 0000000..dbee66e
--- /dev/null
+++ b/eckit/src/eckit/compat/Inited.h
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Inited.h
+// Manuel Fuentes - ECMWF Jan 97
+
+#ifndef eckit_Inited_h
+#define eckit_Inited_h
+
+#include "eckit/types/Types.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Serves to initialise to 0 numerical or pointer values (which
+// should be removed when the compilator supports it)
+// i.e. long() gets the value which is in the stack
+
+template <class T>
+class Inited {
+public:
+
+// -- Contructors
+
+	Inited(): value_(0)           {  }
+	Inited(const T& v): value_(v) {  }
+
+// -- Destructor
+
+	~Inited()                      {  }
+
+// -- Operators
+
+	operator T&()                  { return value_; }
+	operator const T&() const      { return value_; }
+
+	Inited<T>& operator += (const Inited<T>& other);
+	Inited<T>& operator -= (const Inited<T>& other);
+
+private:
+
+// -- Members
+
+	T value_;
+
+};
+
+template<>
+inline
+Inited<Ordinal>& Inited<Ordinal>::operator +=(const Inited<Ordinal>& other)
+{
+	value_ += other.value_;
+	return *this;
+}
+
+template<>
+inline
+Inited<Ordinal>& Inited<Ordinal>::operator -= (const Inited<Ordinal>& other)
+{ 
+	value_ -= other.value_; 
+	return *this;
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/compat/StrStream.h b/eckit/src/eckit/compat/StrStream.h
new file mode 100644
index 0000000..c2dfa84
--- /dev/null
+++ b/eckit/src/eckit/compat/StrStream.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StrStream.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_StrStream_h
+#define eckit_StrStream_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class StrStream : public std::ostringstream {
+public:
+    operator std::string() { return str(); }
+    static std::ostream&  ends(std::ostream& os)  { return os; }
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/config/Configurable.cc b/eckit/src/eckit/config/Configurable.cc
new file mode 100644
index 0000000..a99a961
--- /dev/null
+++ b/eckit/src/eckit/config/Configurable.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/config/Configurable.h"
+#include "eckit/config/Resource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+Configurable::Configurable():
+	ClassExtent<Configurable>(this)
+{
+}
+
+Configurable::~Configurable()
+{
+}
+
+void Configurable::reconfigureAll() 
+{ 
+	callAll(&Configurable::resetResources); // Reset all resources
+	callAll(&Configurable::reconfigure);    // notify clients
+}
+
+void Configurable::dumpAllResources(std::ostream& s) 
+{ 
+	callAll(&Configurable::dumpResources,s);
+}
+
+void Configurable::dumpResources(std::ostream& s) const
+{
+    AutoLock<Mutex> lock(const_cast<Configurable&>(*this).mutex_);
+	for(Set::const_iterator i=resources_.begin(); i != resources_.end();++i)
+		(*i)->dump(s);
+}
+
+// Reset all registered resources
+
+void Configurable::resetResources()
+{
+    AutoLock<Mutex> lock(mutex_);
+	for(Set::iterator i=resources_.begin(); i != resources_.end();++i)
+		(*i)->reset();
+}
+
+void Configurable::add(ResourceBase* res)
+{
+    AutoLock<Mutex> lock(mutex_);
+	resources_.insert(res);
+}
+
+void Configurable::remove(ResourceBase* res)
+{
+    AutoLock<Mutex> lock(mutex_);
+	resources_.erase(res);
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/config/Configurable.h b/eckit/src/eckit/config/Configurable.h
new file mode 100644
index 0000000..f96fe5c
--- /dev/null
+++ b/eckit/src/eckit/config/Configurable.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Configurable.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_Configurable_h
+#define eckit_Configurable_h
+
+#include "eckit/container/ClassExtent.h"
+#include "eckit/thread/Mutex.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Url;
+class ResourceBase;
+
+class Configurable : public ClassExtent<Configurable> {
+public:
+
+// -- Contructors
+
+	Configurable();
+
+// -- Destructor
+
+	virtual ~Configurable();
+// -- Class methods
+
+	static void reconfigureAll();  // Config file as changed, update
+	static void dumpAllResources(std::ostream&); // Dump the configuration to a file
+    static void htmlAllResources(std::ostream&,Url&);
+
+    /// @returns the name of the class
+    virtual std::string kind() const  { return "Configurable"; }
+    /// @returns the name of the instance
+    virtual std::string name() const  { return "Unknown"; }
+
+protected:
+
+// -- Methods
+
+    virtual void reconfigure() = 0;
+
+
+private:
+
+	friend class ResourceBase;
+
+// -- Members
+
+    typedef std::set<ResourceBase*> Set;
+    Mutex mutex_;
+	Set resources_;
+
+// -- Methods
+
+	void add(ResourceBase*);    // Add a resource
+	void remove(ResourceBase*); // Remove a resource
+
+	void resetResources();
+	void dumpResources(std::ostream&) const;       // Dump all resources to a stream
+    void htmlResources(std::ostream&,Url&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/config/Configuration.cc b/eckit/src/eckit/config/Configuration.cc
new file mode 100644
index 0000000..067409d
--- /dev/null
+++ b/eckit/src/eckit/config/Configuration.cc
@@ -0,0 +1,399 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   July 2015
+
+#include "eckit/config/LocalConfiguration.h"
+
+#include "eckit/config/Configuration.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ConfigurationNotFound : public Exception {
+
+  public:
+    ConfigurationNotFound(const std::string& name) {
+        std::ostringstream s;
+        s << "ConfigurationNotFound: [" << name << "]";
+        reason(s.str());
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Configuration::Configuration(const Configuration& other, const std::string& path) :
+    root_(other.root_),
+    separator_(other.separator_) {
+
+    bool found = false;
+    root_ = lookUp(path, found);
+    if(!found) throw ConfigurationNotFound(path);
+}
+
+Configuration::Configuration(const Configuration &other):
+    root_(other.root_),
+    separator_(other.separator_) {
+}
+
+Configuration::Configuration(const eckit::Value& root, char separator) :
+    root_(root),
+    separator_(separator) {
+}
+
+Configuration& Configuration::operator=(const Configuration &other) {
+    root_ = other.root_;
+    separator_ = other.separator_;
+    return *this;
+}
+
+Configuration::~Configuration() {
+}
+
+
+char Configuration::separator() const {
+    return separator_;
+}
+
+eckit::Value Configuration::lookUp(const std::string &s, bool &found) const {
+
+    eckit::Tokenizer parse(separator_);
+    std::vector<std::string> path;
+    parse(s, path);
+
+    eckit::Value result = root_;
+
+    // std::cout << "Configuration::lookUp root=" << result << std::endl;
+    for (size_t i = 0; i < path.size(); i++) {
+        const std::string &key = path[i];
+        if (!result.contains(key)) {
+            found = false;
+            return result;
+        }
+        // std::cout << "Configuration::lookUp key=" << key  << std::endl;
+
+        result = result[key];
+        // std::cout << "Configuration::lookUp key=" << result  << std::endl;
+
+    }
+
+    found = true;
+    return result;
+}
+
+
+eckit::Value Configuration::lookUp(const std::string &name) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if(!found) throw ConfigurationNotFound(name);
+    return v;
+}
+
+
+bool Configuration::has(const std::string &name) const {
+    bool found = false;
+    lookUp(name, found);
+    return found;
+}
+
+bool Configuration::get(const std::string &name, std::string &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        value = std::string(v);
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, bool &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        value = v;
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, long &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        value = long(v);
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, double &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        value = v;
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, LocalConfiguration& value) const {
+    bool found = has(name);
+    if(found) {
+        value = LocalConfiguration(*this, name);
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, std::vector<long> &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        ASSERT(v.isList());
+        value.clear();
+        int i = 0;
+        while (v.contains(i)) {
+            value.push_back(v[i]);
+            i++;
+        }
+    }
+    return found;
+}
+
+
+bool Configuration::get(const std::string &name, std::vector<LocalConfiguration> &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        ASSERT(v.isList());
+        value.clear();
+        int i = 0;
+        while (v.contains(i)) {
+            value.push_back(LocalConfiguration(v[i], separator_));
+            i++;
+        }
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, std::vector<double> &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        ASSERT(v.isList());
+        value.clear();
+        int i = 0;
+        while (v.contains(i)) {
+            value.push_back(v[i]);
+            i++;
+        }
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, std::vector<std::string> &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        ASSERT(v.isList());
+        value.clear();
+        int i = 0;
+        while (v.contains(i)) {
+            value.push_back(v[i]);
+            i++;
+        }
+    }
+    return found;
+}
+
+bool Configuration::get(const std::string &name, size_t &value) const {
+    bool found = false;
+    eckit::Value v = lookUp(name, found);
+    if (found) {
+        value = long(v);
+    }
+    return found;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+void Configuration::_get(const std::string &name, T& value) const {
+    if(!get(name, value)) {
+        throw ConfigurationNotFound(name);
+    }
+}
+
+bool Configuration::getBool(const std::string &name) const {
+    bool result;
+    _get(name, result);
+    return result;
+}
+
+int Configuration::getInt(const std::string &name) const {
+    long result;
+    _get(name, result);
+    ASSERT(int(result) == result);
+    return result;
+}
+
+long Configuration::getLong(const std::string &name) const {
+    long result;
+    _get(name, result);
+    return result;
+}
+
+size_t Configuration::getUnsigned(const std::string &name) const {
+    size_t result;
+    _get(name, result);
+    return result;
+}
+
+float Configuration::getFloat(const std::string &name) const {
+    double result;
+    _get(name, result);
+    ASSERT(float(result) == result);
+    return result;
+}
+
+double Configuration::getDouble(const std::string &name) const {
+    double result;
+    _get(name, result);
+    return result;
+}
+
+std::string Configuration::getString(const std::string &name) const {
+    std::string result;
+    _get(name, result);
+    return result;
+}
+
+
+std::vector<int> Configuration::getIntVector(const std::string &name) const {
+    std::vector<int> result;
+    std::vector<long> tmp;
+    _get(name, tmp);
+    result.reserve(tmp.size());
+    for(size_t i = 0; i < tmp.size(); i++) {
+        ASSERT(int(tmp[i]) == tmp[i]);
+        result.push_back(tmp[i]);
+    }
+    return result;
+}
+
+std::vector<long> Configuration::getLongVector(const std::string &name) const {
+    std::vector<long> result;
+    _get(name, result);
+    return result;
+}
+
+std::vector<size_t> Configuration::getUnsignedVector(const std::string &name) const {
+    std::vector<size_t> result;
+    std::vector<long> tmp;
+    _get(name, tmp);
+    result.reserve(tmp.size());
+    for(size_t i = 0; i < tmp.size(); i++) {
+        ASSERT(tmp[i] >= 0);
+        result.push_back(tmp[i]);
+    }
+    return result;
+}
+
+std::vector<float> Configuration::getFloatVector(const std::string &name) const {
+     std::vector<float> result;
+    std::vector<long> tmp;
+    _get(name, tmp);
+    result.reserve(tmp.size());
+    for(size_t i = 0; i < tmp.size(); i++) {
+        ASSERT(float(tmp[i]) == tmp[i]);
+        result.push_back(tmp[i]);
+    }
+    return result;
+}
+
+std::vector<double> Configuration::getDoubleVector(const std::string &name) const {
+    std::vector<double> result;
+    _get(name, result);
+    return result;
+}
+
+std::vector<std::string> Configuration::getStringVector(const std::string &name) const {
+    std::vector<std::string> result;
+    _get(name, result);
+    return result;
+}
+
+std::vector<LocalConfiguration> Configuration::getSubConfigurations(const std::string &name) const {
+    std::vector<LocalConfiguration> result;
+    _get(name, result);
+    return result;
+}
+
+LocalConfiguration Configuration::getSubConfiguration(const std::string &name) const {
+    LocalConfiguration result;
+    if (has(name)) _get(name, result);
+    return result;
+}
+
+template<class T>
+void Configuration::_getWithDefault(const std::string &name, T& value, const T& defaultVal) const {
+    if(!get(name, value)) {
+        value = defaultVal;
+    }
+}
+
+bool Configuration::getBool(const std::string &name, const bool& defaultVal) const {
+    bool result;
+    _getWithDefault(name, result, defaultVal);
+    return result;
+}
+
+int Configuration::getInt (const std::string &name, const int& defaultVal) const {
+    long result;
+    _getWithDefault(name, result, long(defaultVal));
+    ASSERT(int(result) == result);
+    return result;
+}
+
+long Configuration::getLong(const std::string &name, const long& defaultVal) const {
+    long result;
+    _getWithDefault(name, result, defaultVal);
+    return result;
+}
+
+size_t Configuration::getUnsigned(const std::string &name, const size_t& defaultVal) const {
+    size_t result;
+    _getWithDefault(name, result, defaultVal);
+    return result;
+}
+
+float Configuration::getFloat(const std::string &name, const float& defaultVal) const {
+    double result;
+    _getWithDefault(name, result, double(defaultVal));
+    ASSERT(float(result) == result);
+    return result;
+}
+
+double Configuration::getDouble(const std::string &name, const double& defaultVal) const {
+    double result;
+    _getWithDefault(name, result, defaultVal);
+    return result;
+}
+
+std::string Configuration::getString(const std::string &name, const std::string& defaultVal) const {
+    std::string result;
+    _getWithDefault(name, result, defaultVal);
+    return result;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/config/Configuration.h b/eckit/src/eckit/config/Configuration.h
new file mode 100644
index 0000000..b4848f5
--- /dev/null
+++ b/eckit/src/eckit/config/Configuration.h
@@ -0,0 +1,135 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   July 2015
+
+
+#ifndef eckit_Configuration_H
+#define eckit_Configuration_H
+
+
+#include "eckit/config/Parametrisation.h"
+#include "eckit/value/Value.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LocalConfiguration;
+
+class Configuration : public Parametrisation {
+
+    /// @note Do NOT expose eckit::Value in the interface of configuration
+    ///       eckit::Value should remain an internal detail of configuration objects
+    ///       Clients should use typed configuration parameters
+
+public: // methods
+
+    // Fast access, will throw an exception
+
+    bool getBool(const std::string &name) const;
+    int getInt(const std::string &name) const;
+    long getLong(const std::string &name) const;
+    size_t getUnsigned(const std::string &name) const;
+    float getFloat(const std::string &name) const;
+    double getDouble(const std::string &name) const;
+    std::string getString(const std::string &name) const;
+
+    // Access with default in case of falure
+
+    bool getBool(const std::string &name, const bool& defaultValue) const;
+    int getInt(const std::string &name, const int& defaultValue) const;
+    long getLong(const std::string &name, const long& defaultValue) const;
+    size_t getUnsigned(const std::string &name, const size_t& defaultValue) const;
+    float getFloat(const std::string &name, const float& defaultValue) const;
+    double getDouble(const std::string &name, const double& defaultValue) const;
+    std::string getString(const std::string &name, const std::string& defaultValue) const;
+
+
+    std::vector<int> getIntVector(const std::string &name) const;
+    std::vector<long> getLongVector(const std::string &name) const;
+    std::vector<size_t> getUnsignedVector(const std::string &name) const;
+    std::vector<float> getFloatVector(const std::string &name) const;
+    std::vector<double> getDoubleVector(const std::string &name) const;
+    std::vector<std::string> getStringVector(const std::string &name) const;
+
+    // Access to LocalConfiguration
+
+    std::vector<LocalConfiguration> getSubConfigurations(const std::string &name) const;
+
+    LocalConfiguration getSubConfiguration(const std::string &name) const;
+
+    char separator() const;
+
+    // -- Overridden methods
+
+    virtual bool has(const std::string &name) const;
+
+    virtual bool get(const std::string &name, std::string &value) const;
+    virtual bool get(const std::string &name, bool &value) const;
+    virtual bool get(const std::string &name, long &value) const;
+    virtual bool get(const std::string &name, double &value) const;
+
+    virtual bool get(const std::string &name, std::vector<long> &value) const;
+    virtual bool get(const std::string &name, std::vector<double> &value) const;
+    virtual bool get(const std::string &name, std::vector<std::string> &value) const;
+    virtual bool get(const std::string &name, size_t &value) const;
+
+    bool get(const std::string &name, std::vector<LocalConfiguration>&) const;
+    bool get(const std::string &name, LocalConfiguration&) const;
+
+    /// @todo This method should be protected. As per note above,
+    ///       we don't wnat to expose eckit::Value out of Configuration.
+    const Value& get() const { return root_; }
+
+protected: // methods
+
+    Configuration(const eckit::Value&, char separator = '.');
+
+    Configuration(const Configuration&);
+    Configuration(const Configuration&, const std::string& path); ///< sub-select a subconfiguration
+
+    Configuration &operator=(const Configuration &);
+
+    virtual ~Configuration();
+
+    Value lookUp(const std::string&) const;
+    Value lookUp(const std::string &, bool &) const;
+
+    operator Value() const { return root_; }
+
+protected: // members
+
+    Value root_;
+    char separator_;
+
+private: // methods
+
+    template <class T>
+    void _get(const std::string&, T&) const;
+
+    template <class T>
+    void _getWithDefault(const std::string &name, T& value, const T& defaultVal) const;
+
+    virtual void print(std::ostream &) const = 0;
+
+    friend std::ostream& operator<<(std::ostream& s,const Configuration& p) { p.print(s); return s; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/config/Configured.cc b/eckit/src/eckit/config/Configured.cc
new file mode 100644
index 0000000..dad7335
--- /dev/null
+++ b/eckit/src/eckit/config/Configured.cc
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#include "eckit/config/Configured.h"
+
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+
+Configured::Configured() {
+}
+
+
+Configured::~Configured() {
+}
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/config/Configured.h b/eckit/src/eckit/config/Configured.h
new file mode 100644
index 0000000..bfbea13
--- /dev/null
+++ b/eckit/src/eckit/config/Configured.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef eckit_Configured_H
+#define eckit_Configured_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+
+namespace eckit {
+
+
+class Configured {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    Configured();
+
+// -- Destructor
+
+    virtual ~Configured(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+
+    virtual Configured& set(const std::string &name, const std::string &value) = 0;
+    virtual Configured& set(const std::string &name, const char *value) = 0;
+    virtual Configured& set(const std::string &name, double value) = 0;
+    virtual Configured& set(const std::string &name, long value) = 0;
+    virtual Configured& set(const std::string &name, bool value) = 0;
+    virtual Configured& set(const std::string& name, size_t value) = 0;
+
+    virtual Configured& set(const std::string& name, const std::vector<long>& value) = 0;
+    virtual Configured& set(const std::string& name, const std::vector<double>& value) = 0;
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+// -- Methods
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+
+// -- Members
+    // None
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+
+
+};
+
+} // namespace eckit
+#endif
+
diff --git a/eckit/src/eckit/config/EtcTable.cc b/eckit/src/eckit/config/EtcTable.cc
new file mode 100644
index 0000000..0af908c
--- /dev/null
+++ b/eckit/src/eckit/config/EtcTable.cc
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/config/EtcTable.h"
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static const std::vector<std::string> empty;
+
+EtcTable::EtcTable(const std::string& name, int size, const std::string& dir):
+    last_(0),
+    dir_(dir),
+    name_(name),
+    size_(size)
+{
+}
+
+EtcTable::~EtcTable()
+{
+}
+
+
+const std::vector<std::string>& EtcTable::lookUp(const std::string& name)
+{
+    AutoLock<Mutex> lock(mutex_);
+    if(last_ == 0)
+        load();
+
+    for(std::vector<std::vector<std::string> >::const_iterator j = lines_.begin(); j != lines_.end() ; ++j)
+    {
+        const std::vector<std::string>& line = *j;
+        if(match(name, line))
+            return line;
+    }
+
+    return empty;
+
+}
+
+std::vector<std::string> EtcTable::keys()
+{
+    AutoLock<Mutex> lock(mutex_);
+    if(last_ == 0)
+        load();
+
+    std::vector<std::string> v;
+
+    for(std::vector<std::vector<std::string> >::const_iterator j = lines_.begin(); j != lines_.end() ; ++j)
+    {
+        const std::vector<std::string>& line = *j;
+        v.push_back(line[0]);
+    }
+
+    return v;
+}
+
+void EtcTable::load()
+{
+
+    last_ = 1; // TODP: Check timestamp
+
+	LocalPathName path(std::string("~/") + dir_ + "/" + name_);
+    std::ifstream in(path.localPath());
+
+    Log::info() << "EtcTable::load " << path << std::endl;
+
+    lines_.clear();
+
+	//Log::info() << "Loading table " << path << std::endl;
+
+	if(!in)
+	{
+		Log::error() << path << Log::syserr << std::endl;
+		return;
+	}
+
+	char line[1024];
+	while(in.getline(line,sizeof(line)))
+	{
+		Tokenizer parse(" ");
+		std::vector<std::string> s;
+		parse(line,s);
+
+		size_t i = 0;
+        while( i < s.size() )
+		{
+			if(s[i].length() == 0)
+				s.erase(s.begin()+i);
+			else
+				i++;
+		}
+
+		if(s.size() == 0 || s[0][0] == '#')
+			continue;
+
+        if(size_ && s.size() != size_)
+            Log::warning() << "Ignoring " << line << std::endl;
+
+        lines_.push_back(s);
+	}
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/config/EtcTable.h b/eckit/src/eckit/config/EtcTable.h
new file mode 100644
index 0000000..5e0ff8d
--- /dev/null
+++ b/eckit/src/eckit/config/EtcTable.h
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File EtcTable.h
+// Baudouin Raoult - (c) ECMWF Aug 11
+
+#ifndef eckit_EtcTable_h
+#define eckit_EtcTable_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class EtcTable : private NonCopyable {
+public:
+
+// -- Contructors
+
+    EtcTable(const std::string&, int = 0, const std::string& = "etc");
+
+// -- Destructor
+
+    virtual ~EtcTable();
+
+	const std::vector<std::string>& lookUp(const std::string&);
+    std::vector<std::string> keys();
+
+private: // methods
+
+    void load();
+
+private: // members
+
+    time_t last_;
+    std::string dir_;
+    std::string name_;
+    size_t size_;
+
+    Mutex mutex_;
+
+    std::vector<std::vector<std::string> > lines_;
+
+private: // methods
+
+    virtual bool match(const std::string&, const std::vector<std::string>&) const  = 0;
+
+};
+
+//-----------------------------------------------------------------------------
+
+class EtcKeyTable : public EtcTable {
+    bool match(const std::string& query, const std::vector<std::string>& line) const { return query == line[0]; }
+public:
+    EtcKeyTable(const std::string& name, int size = 0, const std::string& dir= "etc"):
+        EtcTable(name, size, dir) {}
+};
+
+//-----------------------------------------------------------------------------
+
+class EtcStartWithTable : public EtcTable {
+    bool match(const std::string& query, const std::vector<std::string>& line) const { return query.find(line[0]) == 0; }
+public:
+    EtcStartWithTable(const std::string& name, int size = 0, const std::string& dir= "etc"):
+        EtcTable(name, size, dir) {}
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/config/JSONConfiguration.cc b/eckit/src/eckit/config/JSONConfiguration.cc
new file mode 100644
index 0000000..7bb2622
--- /dev/null
+++ b/eckit/src/eckit/config/JSONConfiguration.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Jul 2015
+
+#include "eckit/config/JSONConfiguration.h"
+
+
+// #include <iostream>
+// #include <limits>
+// #include "eckit/filesystem/PathName.h"
+#include "eckit/parser/JSONParser.h"
+// #include "mir/util/Parser.h"
+// #include "eckit/parser/Tokenizer.h"
+
+
+namespace eckit {
+
+static Value root(std::istream &in) {
+    ASSERT(in);
+    eckit::JSONParser parser(in);
+    Value root = parser.parse();
+    return root;
+}
+
+
+static Value root(const std::string &path) {
+    std::ifstream in(path.c_str());
+    if(!in)
+        throw eckit::CantOpenFile(path);
+    return root(in);
+}
+
+
+static Value root(Stream& in) {
+    std::string val;
+    in.next(val);
+    std::istringstream iss(val);
+    return root(iss);
+}
+
+
+JSONConfiguration::JSONConfiguration(const eckit::PathName &path, char separator):
+    Configuration(root(path), separator),
+    path_(path) {
+}
+
+JSONConfiguration::JSONConfiguration(std::istream &in, char separator):
+    Configuration(root(in), separator),
+    path_("<istream>") {
+}
+
+JSONConfiguration::JSONConfiguration(Stream& in, char separator)
+    : Configuration(root(in), separator),
+      path_("<Stream>") {}
+
+JSONConfiguration::~JSONConfiguration() {
+}
+
+void JSONConfiguration::print(std::ostream &out) const {
+    out << "JSONConfiguration[path=" << path_ << ", root=" << root_ << "]";
+}
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/config/JSONConfiguration.h b/eckit/src/eckit/config/JSONConfiguration.h
new file mode 100644
index 0000000..3daef61
--- /dev/null
+++ b/eckit/src/eckit/config/JSONConfiguration.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date JUl 2015
+
+
+#ifndef eckit_JSONConfiguration_H
+#define eckit_JSONConfiguration_H
+
+#include "eckit/config/Configuration.h"
+
+
+namespace eckit {
+
+class PathName;
+class Stream;
+
+class JSONConfiguration : public Configuration {
+  public:
+
+    // -- Exceptions
+    // None
+
+    // -- Contructors
+
+    JSONConfiguration(const eckit::PathName &path, char separator = '.');
+    JSONConfiguration(std::istream &, char separator = '.');
+    JSONConfiguration(Stream&, char separator = '.');
+
+    virtual ~JSONConfiguration(); // Change to virtual if base class
+
+    // -- Convertors
+    // None
+
+    // -- Operators
+    // None
+
+    // -- Methods
+
+
+    // -- Overridden methods
+
+
+    // -- Class members
+    // None
+
+    // -- Class methods
+
+
+  protected:
+
+
+    // -- Destructor
+
+    // -- Members
+    // None
+
+    // -- Methods
+
+    // void print(ostream&) const; // Change to virtual if base class
+
+    // -- Overridden methods
+    // None
+
+    // -- Class members
+    // None
+
+    // -- Class methods
+    // None
+
+  private:
+
+    // No copy allowed
+
+    JSONConfiguration(const JSONConfiguration &);
+    JSONConfiguration &operator=(const JSONConfiguration &);
+
+    // Make private so no one modifies it
+
+    // -- Members
+
+    std::string path_;
+
+
+    // -- Methods
+
+
+    // -- Overridden methods
+
+    // From MIRParametrisation
+    virtual void print(std::ostream &) const;
+
+    // -- Class members
+    // None
+
+    // -- Class methods
+    // None
+
+    // -- Friends
+
+    //friend ostream& operator<<(ostream& s,const JSONConfiguration& p)
+    //  { p.print(s); return s; }
+
+};
+
+
+} // namespace eckit
+#endif
+
diff --git a/eckit/src/eckit/config/LibEcKit.cc b/eckit/src/eckit/config/LibEcKit.cc
new file mode 100644
index 0000000..ff2d189
--- /dev/null
+++ b/eckit/src/eckit/config/LibEcKit.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#include <algorithm>
+#include <string>
+
+#include "eckit/config/LibEcKit.h"
+
+#include "eckit/eckit_version.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static LibEcKit libeckit;
+
+LibEcKit::LibEcKit() : Library("eckit") {}
+
+const LibEcKit& LibEcKit::instance()
+{
+    return libeckit;
+}
+
+const void* LibEcKit::addr() const { return this; }
+
+std::string LibEcKit::version() const { return eckit_version_str(); }
+
+std::string LibEcKit::gitsha1(unsigned int count) const {
+    std::string sha1(eckit_git_sha1());
+    if(sha1.empty()) {
+        return "not available";
+    }
+
+    return sha1.substr(0,std::min(count,40u));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/config/LibEcKit.h b/eckit/src/eckit/config/LibEcKit.h
new file mode 100644
index 0000000..3ecc7d0
--- /dev/null
+++ b/eckit/src/eckit/config/LibEcKit.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_system_LibEcKit_H
+#define eckit_system_LibEcKit_H
+
+#include "eckit/system/Library.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LibEcKit : public eckit::system::Library {
+public:
+
+    LibEcKit();
+
+    static const LibEcKit& instance();
+
+protected:
+
+    const void* addr() const;
+
+    virtual std::string version() const;
+
+    virtual std::string gitsha1(unsigned int count) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/config/LocalConfiguration.cc b/eckit/src/eckit/config/LocalConfiguration.cc
new file mode 100644
index 0000000..f66e474
--- /dev/null
+++ b/eckit/src/eckit/config/LocalConfiguration.cc
@@ -0,0 +1,132 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Jul 2015
+
+#include "eckit/config/LocalConfiguration.h"
+#include "eckit/parser/JSONParser.h"
+#include "eckit/parser/Tokenizer.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+LocalConfiguration::LocalConfiguration(char separator):
+    Configuration(Value::makeMap(), separator) {
+}
+
+LocalConfiguration::LocalConfiguration(const Value& root, char separator):
+    Configuration(root, separator) {
+}
+
+LocalConfiguration::LocalConfiguration(const Configuration &other):
+    Configuration(other) {
+}
+
+LocalConfiguration::LocalConfiguration(const Configuration &other, const std::string &path):
+    Configuration(other, path) {
+}
+
+LocalConfiguration::~LocalConfiguration() {
+}
+
+void LocalConfiguration::print(std::ostream &out) const {
+    out << "LocalConfiguration[root=";
+    out << root_;
+    out << "]";
+}
+
+void LocalConfiguration::set(const std::vector<std::string> &path, size_t i, eckit::Value &root, const eckit::Value &value)  {
+    if (root.shared()) {
+        // std::cout << "Clone " << root << std::endl;
+        root = root.clone();
+    }
+
+    if (i + 1 == path.size()) {
+        // std::cout << i << " SET " << path[i] << " to " << value << std::endl;
+        root[path[i]] = value;
+        return;
+    }
+
+    if (!root.contains(path[i])) {
+        // std::cout << i << " NEW " << path[i]  << std::endl;
+        root[path[i]] = eckit::Value::makeMap();
+    }
+
+    eckit::Value &r = root.element(path[i]);
+    set(path, i + 1, r, value);
+}
+
+void LocalConfiguration::set(const std::string &s, const eckit::Value &value) {
+
+    // std::cout << "---- " << s << " => " << value << std::endl;
+
+    eckit::Tokenizer parse(separator_);
+    std::vector<std::string> path;
+    parse(s, path);
+
+    set(path, 0, root_, value);
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, long value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, const char *value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, const std::string &value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, double value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, bool value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string &s, size_t value)  {
+    set(s, eckit::Value(value));
+    return *this;
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string& s, const std::vector<long>& value) {
+    ValueList values;
+    for (std::vector<long>::const_iterator v = value.begin(); v != value.end(); ++v) {
+        values.push_back(eckit::Value(*v));
+    }
+    set(s, values);
+    return *this;
+}
+
+LocalConfiguration& LocalConfiguration::set(const std::string& s, const std::vector<double>& value) {
+    ValueList values;
+    for (std::vector<double>::const_iterator v = value.begin(); v != value.end(); ++v) {
+        values.push_back(eckit::Value(*v));
+    }
+    set(s, values);
+    return *this;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/config/LocalConfiguration.h b/eckit/src/eckit/config/LocalConfiguration.h
new file mode 100644
index 0000000..95b11db
--- /dev/null
+++ b/eckit/src/eckit/config/LocalConfiguration.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   July 2015
+
+#ifndef eckit_LocalConfiguration_H
+#define eckit_LocalConfiguration_H
+
+#include <vector>
+
+#include "eckit/config/Configuration.h"
+#include "eckit/config/Configured.h"
+
+namespace eckit {
+
+class PathName;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LocalConfiguration : public Configuration, public Configured {
+
+    /// @note Do NOT expose eckit::Value in the interface of configuration
+    ///       eckit::Value should remain an internal detail of configuration objects
+
+public: // methods
+
+    LocalConfiguration(char separator = '.');
+    LocalConfiguration(const Configuration &other);
+    LocalConfiguration(const Configuration &other, const std::string &path);
+
+    virtual ~LocalConfiguration();
+
+    LocalConfiguration& set(const std::string &name, const std::string &value);
+    LocalConfiguration& set(const std::string &name, const char *value);
+    LocalConfiguration& set(const std::string &name, double value);
+    LocalConfiguration& set(const std::string &name, long value);
+    LocalConfiguration& set(const std::string &name, bool value);
+    LocalConfiguration& set(const std::string& name, size_t value);
+
+    LocalConfiguration& set(const std::string& name, const std::vector<long>& value);
+    LocalConfiguration& set(const std::string& name, const std::vector<double>& value);
+
+protected:
+
+    friend class Configuration;
+
+    /// to be used only by class Configuration
+    LocalConfiguration(const eckit::Value&, char separator = '.');
+
+    virtual void print(std::ostream &) const;
+
+private:
+
+    void set(const std::vector<std::string> &path, size_t i, Value &root, const Value &value);
+    void set(const std::string &s, const Value &value);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/config/Parametrisation.h b/eckit/src/eckit/config/Parametrisation.h
new file mode 100644
index 0000000..9ef2535
--- /dev/null
+++ b/eckit/src/eckit/config/Parametrisation.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Apr 2015
+
+#ifndef eckit_config_Parametrisation_H
+#define eckit_config_Parametrisation_H
+
+#include <string>
+#include <vector>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Parametrisation {
+
+public: // methods
+
+    /// Destructor redundant but fixes sanity compiler warnings
+    virtual ~Parametrisation() {}
+
+    virtual bool has(const std::string& name) const = 0;
+
+    virtual bool get(const std::string& name, std::string& value) const = 0;
+    virtual bool get(const std::string& name, bool& value) const = 0;
+    virtual bool get(const std::string& name, long& value) const = 0;
+    virtual bool get(const std::string& name, size_t& value) const = 0;
+    virtual bool get(const std::string& name, double& value) const = 0;
+
+    virtual bool get(const std::string& name, std::vector<long>& value) const = 0;
+    virtual bool get(const std::string& name, std::vector<double>& value) const = 0;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/config/Resource.cc b/eckit/src/eckit/config/Resource.cc
new file mode 100755
index 0000000..4a73987
--- /dev/null
+++ b/eckit/src/eckit/config/Resource.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/config/Resource.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/parser/Tokenizer.h"
+
+namespace eckit {
+template<class T> void Resource<T>::setValue(const std::string& s)
+{
+    value_ = Translator<std::string, T>()(s);
+}
+
+template<class T> std::string Resource<T>::getValue() const
+{
+    return Translator<T, std::string>()(value_);
+}
+
+} // namespace
diff --git a/eckit/src/eckit/config/Resource.h b/eckit/src/eckit/config/Resource.h
new file mode 100644
index 0000000..22440b5
--- /dev/null
+++ b/eckit/src/eckit/config/Resource.h
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Log.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date May 1996
+
+#ifndef eckit_config_Resource_h
+#define eckit_config_Resource_h
+
+#include <string>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/utils/Translator.h"
+
+namespace eckit {
+
+class Configurable;
+class Url;
+
+class ResourceBase : private NonCopyable {
+public:
+
+// -- Contructors
+
+    ResourceBase(Configurable* owner, const std::string& str);
+
+// -- Destructor
+
+    virtual ~ResourceBase();
+
+// -- Methods
+
+    void reset()            { inited_ = false;   }
+    void dump(std::ostream&) const;
+    void html(std::ostream&, Url&);
+
+    std::string name() const;
+
+protected:
+
+// -- Methods
+
+    void init();
+
+private:
+
+// -- Members
+
+    bool           inited_;
+    Configurable*  owner_;
+    std::string         name_;        // In the config file
+    std::string         environment_; // In the environment variables
+    std::string         options_;     // For the command line options
+
+// -- Methods
+
+    virtual void setValue(const std::string&) = 0;
+    virtual std::string getValue() const      = 0;
+
+};
+
+
+
+
+template<class T> class Resource : public ResourceBase {
+public:
+
+// -- Contructors
+
+    // Standalone
+
+    Resource(const std::string& str, const T& value):
+        ResourceBase(0, str),     value_(value) {}
+
+    // Part of a configurable
+
+    Resource(Configurable* owner, const std::string& str, const T& value):
+        ResourceBase(owner, str), value_(value) {}
+
+    Resource(const std::string& str,const std::string& value, bool):
+        ResourceBase(0,str),     value_(eckit::Translator<std::string,T>()(value)) {}
+
+// -- Convertors
+
+    operator const T&()  const { const_cast<Resource<T>*>(this)->init(); return value_;        }
+
+private:
+
+// -- Members
+
+    T value_;
+
+// -- Overridden methods
+
+    // From ResourceBase
+
+    virtual void setValue(const std::string&);
+    virtual std::string getValue() const;
+
+};
+
+template<class T>
+std::ostream& operator<<(std::ostream& os, const Resource<T>& r) {
+    os << static_cast<const T&>(r);
+    return os;
+}
+
+}
+
+#include "eckit/config/Resource.cc"
+
+#endif
diff --git a/eckit/src/eckit/config/ResourceBase.cc b/eckit/src/eckit/config/ResourceBase.cc
new file mode 100644
index 0000000..f86d3d4
--- /dev/null
+++ b/eckit/src/eckit/config/ResourceBase.cc
@@ -0,0 +1,146 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/runtime/Main.h"
+#include "eckit/config/Configurable.h"
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+
+namespace eckit {
+
+ResourceBase::ResourceBase(Configurable* owner, const std::string& str):
+    inited_(false),
+    owner_(owner)
+{
+    if (owner_) owner_->add(this);
+
+    const char *p = str.c_str();
+
+    while (*p)
+    {
+        std::string *s = &name_;
+        char   x  = *p;
+        int len   = 0;
+
+        switch (x)
+        {
+        case '$': s = &environment_; break;
+        case '-': s = &options_;     break;
+        }
+
+        *s = p;
+
+        while (*p && *p != ';')
+        {
+            len++;
+            p++;
+        }
+
+        s->resize(len);
+
+        if (*p) p++;
+
+    }
+}
+
+ResourceBase::~ResourceBase()
+{
+    if (owner_) owner_->remove(this);
+}
+
+void ResourceBase::init()
+{
+    if (inited_) return;
+
+    // First look in config file
+    // First look for an option on the command line
+
+    if (options_ != "")
+    {
+        for (int i = 1; i < Main::instance().argc(); i++)
+            if (options_ == Main::instance().argv(i))
+            {
+                if ( i + 1 == Main::instance().argc() || Main::instance().argv(i + 1)[0] == '-' )
+                    setValue("true");
+                else
+                    setValue(Main::instance().argv(i + 1));
+                inited_ = true;
+                return;
+            }
+    }
+
+    // Then look for an environment variable
+
+    if (environment_ != "")
+    {
+        const char *p = ::getenv(environment_.c_str() + 1);
+        if (p) {
+            setValue(p);
+            inited_ = true;
+            return;
+        }
+    }
+
+    // Otherwise look in the config file
+
+    if (name_ != "")
+    {
+        bool found = false;
+        std::string s;
+
+        if (owner_)
+            found = ResourceMgr::lookUp(owner_->kind(), owner_->name(), name_, s);
+        else
+            found = ResourceMgr::lookUp("", "", name_, s);
+
+        if (found) setValue(s);
+
+        inited_ = true;
+        return;
+    }
+
+    // Else use default. This is done in Resource
+
+    inited_ = true;
+}
+
+std::string ResourceBase::name() const
+{
+    if (owner_)
+        return owner_->kind() + '.' + owner_->name() + '.' + name_;
+    else
+        return name_;
+}
+
+void ResourceBase::dump(std::ostream& s) const
+{
+
+    // need const_cast here
+    ((ResourceBase*)this)->init();
+
+    s << "# " << name_ << ":" << std::endl;
+
+    if (options_ != "")     s << "#   command line option  " << options_     << std::endl;
+    if (environment_ != "")
+    {
+        s << "#   environment variable " << environment_ << " ";
+        const char *p = getenv(environment_.c_str() + 1);
+        if (p) s << "(defined as " << p << ")";
+        else  s << "(undefined)";
+        s << std::endl;
+    }
+
+    s << name() << " : " << getValue();
+
+    s << std::endl;
+}
+
+}
diff --git a/eckit/src/eckit/config/ResourceMgr.cc b/eckit/src/eckit/config/ResourceMgr.cc
new file mode 100644
index 0000000..e3bf42f
--- /dev/null
+++ b/eckit/src/eckit/config/ResourceMgr.cc
@@ -0,0 +1,230 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/ResourceMgr.h"
+
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/StaticMutex.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static StaticMutex smutex;
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static ResourceMgr* mgr = 0;
+
+void ResourceMgr::init() {
+    mgr = new ResourceMgr();
+}
+
+ResourceMgr& ResourceMgr::instance()
+{
+    pthread_once(&once, ResourceMgr::init);
+    return *mgr;
+}
+
+bool ResourceMgr::lookUp(const std::string& s1, const std::string& s2, const std::string& s3, std::string& v)
+{
+    return ResourceMgr::instance().lookUp_(s1, s2, s3, v);
+}
+
+void ResourceMgr::reset()
+{
+    AutoLock<StaticMutex> lock(smutex);
+    resmap_.clear();
+    inited_ = false;
+}
+
+// This has to be redone
+
+static const char* skip_spaces(const char* p) {
+    while (*p && isspace(*p)) p++;
+    return p;
+}
+
+bool ResourceMgr::parse(const char* p)
+{
+    p = skip_spaces(p);
+
+    if (*p == 0 || *p == '#') return true; // skip comments
+
+    std::string s[3];
+    int n = 0;
+
+    for (;;)
+    {
+        const char *q = p;
+
+        p = skip_spaces(p);
+        while (*p && *p != ':' && *p != '.' && !isspace(*p) ) p++;
+
+        int len = p - q;
+        p = skip_spaces(p);
+
+        s[n] = q; s[n].resize(len); n++;
+
+        if (n == 3 || *p != '.') break;
+        p++;
+    }
+    if (*p != ':')
+        return false;
+    else
+    {
+        switch (n) {
+            case 1: s[2] = s[0]; s[0] = ""; break;
+            case 2: s[2] = s[1]; s[1] = s[0]; s[0] = ""; break;
+            case 3: break;
+        }
+
+        p++;
+        p = skip_spaces(p);
+
+        // Remove trailing blanks
+        int l = strlen(p) - 1;
+        while (l >= 0 && isspace(p[l])) l--;
+
+
+        ResourceQualifier x(s[0], s[1], s[2]);
+
+        std::string t = std::string(p, l + 1);
+        resmap_[x] = t;
+    }
+    return true;
+}
+
+void ResourceMgr::readConfigFile(const LocalPathName& file)
+{
+    // Read file ...
+
+    //Log::info() << "ResourceMgr::readConfigFile(" << file << ")" << std::endl;
+
+    int    cnt  = 0;
+
+    std::ifstream in(file.localPath());
+    char line[1024];
+
+    while (in.getline(line, sizeof(line)))
+    {
+        cnt++;
+        if (!parse(line))
+        {
+            Log::warning() << "Invalid line, file " << file << " line " << cnt << " = " << line << std::endl;
+        }
+    }
+}
+
+void ResourceMgr::set(const std::string& name, const std::string& value)
+{
+    AutoLock<StaticMutex> lock(smutex);
+
+    std::string s = name + ": " + value;
+    if (!parse(s.c_str()))
+        Log::warning() << "Failed to parse " << s << std::endl;
+}
+
+bool ResourceMgr::lookUp_(const std::string& kind,
+                          const std::string& owner,
+                          const std::string& name,
+                          std::string& result)
+{
+    AutoLock<StaticMutex> lock(smutex);
+
+    if (!inited_)
+    {
+        inited_ = true;
+        readConfigFile("~/etc/config/general");
+        readConfigFile("~/etc/config/local");
+        readConfigFile(std::string("~/etc/config/")
+                       + Main::instance().name());
+        readConfigFile(std::string("~/etc/config/")
+                       + Main::instance().name() + ".local");
+    }
+
+    ResMap::iterator i = resmap_.find(ResourceQualifier(kind, owner, name));
+
+    if (i != resmap_.end())
+    {
+        result = (*i).second;
+        return true;
+    }
+
+    i = resmap_.find(ResourceQualifier("", owner, name));
+
+    if (i != resmap_.end())
+    {
+        result = (*i).second;
+        return true;
+    }
+
+    i = resmap_.find(ResourceQualifier("", "", name));
+
+    if (i != resmap_.end())
+    {
+        result = (*i).second;
+        return true;
+    }
+
+    return false;
+
+}
+
+bool ResourceMgr::registCmdArgOptions(const std::string&)
+{
+    AutoLock<StaticMutex> lock(smutex);
+
+    NOTIMP;
+}
+
+ResourceMgr::ResourceMgr() :
+    resmap_(),
+    resoptions_(),
+    inited_(false)
+{
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ResourceQualifier::ResourceQualifier(const std::string& kind, const std::string& owner, const std::string& name) :
+    kind_(kind),
+    owner_(owner),
+    name_(name)
+{
+}
+
+ResourceQualifier::ResourceQualifier(const ResourceQualifier& other)
+{
+    kind_  = other.kind_;
+    owner_ = other.owner_;
+    name_  = other.name_;
+}
+
+int ResourceQualifier::operator<(const ResourceQualifier& other) const
+{
+    // to be rewritten
+    char buf1[1024];
+    char buf2[1024];
+
+    sprintf(buf1, "%s.%s.%s", owner_.c_str(), kind_.c_str(), name_.c_str());
+    sprintf(buf2, "%s.%s.%s", other.owner_.c_str(), other.kind_.c_str(),
+            other.name_.c_str());
+
+    return strcmp(buf1, buf2) < 0;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+
diff --git a/eckit/src/eckit/config/ResourceMgr.h b/eckit/src/eckit/config/ResourceMgr.h
new file mode 100644
index 0000000..b6485b2
--- /dev/null
+++ b/eckit/src/eckit/config/ResourceMgr.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 96
+
+#ifndef eckit_config_ResourceMgr_H
+#define eckit_config_ResourceMgr_H
+
+#include <string>
+#include <map>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+
+class LocalPathName;
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// A resource specifier
+
+class ResourceQualifier {
+
+    std::string kind_;  // Kind,  e.g. "Application"
+    std::string owner_; // Owner, e.g. "mars"
+    std::string name_;  // Name,  e.g. "debug"
+
+public:
+
+    ResourceQualifier();
+    ResourceQualifier(const std::string&, const std::string&, const std::string&);
+
+    ResourceQualifier(const ResourceQualifier&);
+    ResourceQualifier& operator=(const ResourceQualifier&);
+
+    int operator<(const ResourceQualifier&) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class ResourceMgr : private eckit::NonCopyable {
+
+public: // class methods
+
+    static ResourceMgr& instance();
+
+    static bool lookUp(const std::string&, const std::string&, const std::string&, std::string&);
+
+    bool registCmdArgOptions(const std::string&);
+
+private:
+
+    static void init();
+
+    ResourceMgr();
+
+    bool lookUp_(const std::string&, const std::string&, const std::string&, std::string&);
+
+    // Only for my friends
+    // You should never call these, resources have a read-only semantics
+    void reset();
+    void set(const std::string&, const std::string&);
+
+    friend class ConfigCmd;
+    friend class ResourceBase;
+
+private: // methods
+
+    void readConfigFile(const LocalPathName&);
+    bool parse(const char*);
+
+private: // members
+
+    typedef std::map<ResourceQualifier, std::string> ResMap;
+
+    ResMap resmap_;
+
+    std::map<std::string, std::string> resoptions_;
+
+    bool inited_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/BSPTree.h b/eckit/src/eckit/container/BSPTree.h
new file mode 100644
index 0000000..3945773
--- /dev/null
+++ b/eckit/src/eckit/container/BSPTree.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef BSPTree_H
+#define BSPTree_H
+
+#include "eckit/container/sptree/SPTree.h"
+#include "eckit/container/bsptree/BSPNode.h"
+
+#include "KDMemory.h"
+#include "KDMapped.h"
+
+namespace eckit {
+
+template<class Traits, class Partition>
+class BSPTreeX : public SPTree<Traits, BSPNode<Traits, Partition> > {
+public:
+    typedef typename Traits::Alloc     Alloc;
+
+private:
+
+    Partition partition_;
+
+public:
+    BSPTreeX(Alloc& alloc): SPTree<Traits, BSPNode<Traits, Partition> >(alloc) {}
+
+
+    /// Container must be a random access
+    /// WARNING: container is changed (sorted)
+    template<typename Container>
+    void build(Container& nodes)
+    {
+        this->root_ = this->alloc_.convert(BSPNode<Traits, Partition>::build(this->alloc_, partition_, nodes, 0.0));
+        this->alloc_.root(this->root_);
+    }
+
+};
+
+template<class Traits, class Partition>
+class BSPTreeMemory : public BSPTreeX< TT<Traits,KDMemory>,Partition> {
+    KDMemory alloc_;
+public:
+    BSPTreeMemory():
+        BSPTreeX< TT<Traits,KDMemory>,Partition>(alloc_) {}
+};
+
+template<class Traits, class Partition>
+class BSPTreeMapped : public BSPTreeX< TT<Traits,KDMapped>,Partition> {
+    KDMapped alloc_;
+public:
+    BSPTreeMapped(const eckit::PathName& path,  size_t itemCount, size_t metadataSize):
+        BSPTreeX< TT<Traits,KDMapped>,Partition>(alloc_),
+        alloc_(path, itemCount, sizeof(BSPNode<TT< Traits,KDMapped>,Partition>), metadataSize)
+    {
+    }
+
+};
+
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/BTree.cc b/eckit/src/eckit/container/BTree.cc
new file mode 100644
index 0000000..55ee7f3
--- /dev/null
+++ b/eckit/src/eckit/container/BTree.cc
@@ -0,0 +1,728 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::Page::print(std::ostream& s) const
+{
+    s << ((this->node_) ? "NODE" : "LEAF" )
+      <<  "_PAGE[id=" << this->id_ << ",count=" << this->count_ << "]";
+
+    // For some strange reason "this" is required...
+    if (this->node_) {
+        this->nodePage().print(s);
+    }
+    else
+    {
+        this->leafPage().print(s);
+    }
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::_LeafPage::print(std::ostream& s) const
+{
+
+    // For some strange reason "this" is required...
+    s << "(";
+    for (unsigned long i = 0; i < this->count_; i++)
+    {
+        s << lentries_[i].key_ << ":" << lentries_[i].value_ << ",";
+    }
+    s << ")";
+    s << "[" << this->left_ << "<=>" << this->right_ << "]";
+
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::_NodePage::print(std::ostream& s) const
+{
+    // For some strange reason "this" is required...
+    s << "{" << this->left_ << "!";
+    for (unsigned long i = 0; i < this->count_; i++)
+    {
+        s << nentries_[i].key_ << "@" << nentries_[i].page_ << ",";
+    }
+    s << "}";
+}
+
+
+template<class K, class V, int S, class L>
+BTree<K,V,S,L>::BTree( const PathName& path, bool readOnly, off_t offset ):
+    path_(path),
+    fd_(-1),
+    cacheReads_(true),
+    cacheWrites_(true),
+    readOnly_(readOnly),
+    offset_(offset)
+{
+    SYSCALL2( fd_ = ::open(path.localPath(), readOnly_ ? O_RDONLY : (O_RDWR|O_CREAT),0777), path );
+
+    AutoLock< BTree<K,V,S,L> > lock(this);
+
+    off_t here = ::lseek(fd_,0,SEEK_END);
+    if(here == off_t(-1))
+        throw FailedSystemCall("lseek");
+
+    if (here <= offset_) {
+
+        here = ::lseek(fd_,offset_,SEEK_SET);
+        if(here == off_t(-1))
+            throw FailedSystemCall("lseek");
+
+        // Add root page
+        Page root;
+        newPage(root);
+        ASSERT(root.id_ == 1);
+    }
+    else
+    {
+        // TODO: Check header
+    }
+
+	eckit::compile_assert< (maxLeafEntries_ > 3) >::check();
+	eckit::compile_assert< (maxNodeEntries_ > 3) >::check();
+
+    compile_assert< (sizeof(Page) == sizeof(LeafPage)) >::check();
+    compile_assert< (sizeof(Page) == sizeof(NodePage)) >::check();
+
+	// std::cout << "::BTree : maxLeafEntries_=" << maxLeafEntries_ << ", maxNodeEntries_=" << maxNodeEntries_ << std::endl;
+}
+
+
+template<class K, class V, int S, class L>
+BTree<K,V,S,L>::~BTree()
+{
+    if (fd_>=0) {
+        flush();
+        SYSCALL(::close(fd_));
+    }
+
+    for(typename Cache::iterator j = cache_.begin(); j != cache_.end(); ++j)
+        delete (*j).second.page_;
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::flush()
+{
+    for(typename Cache::iterator j = cache_.begin(); j != cache_.end(); ++j)
+    {
+        //Log::info() << "BTree<K,V,S,L>::flush() " << path_ << " " << (*j).first << ", " << (*j).second.dirty_ << std::endl;
+        if((*j).second.dirty_)
+        {
+            _savePage(*(*j).second.page_);
+            (*j).second.dirty_ = false;
+        }
+    }
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::dump(std::ostream& s, unsigned long page, int depth) const
+{
+    Page p;
+    loadPage(page, p);
+    for (int i = 0; i < depth; i++)
+        s << "   ";
+    s << p << std::endl;
+    if (p.node_)
+    {
+        dump(s, p.left_, depth+1);
+        for (unsigned long i = 0; i < p.count_ ; i ++ )
+            dump(s, p.nodePage().nentries_[i].page_, depth+1);
+    }
+}
+
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::dump(std::ostream& s) const
+{
+    AutoSharedLock<BTree<K,V,S,L> > lock(const_cast<BTree*>(this));
+    s << "::BTree : maxLeafEntries_=" << maxLeafEntries_ << ", maxNodeEntries_=" << maxNodeEntries_ << std::endl;
+    dump(s,1,0);
+}
+
+
+template<class K, class V, int S, class L>
+bool BTree<K,V,S,L>::set(const K& key, const V& value)
+{
+    AutoLock<BTree<K,V,S,L> > lock(this);
+	// std::cout << "Set " << key << " -> " << value << std::endl;
+    std::vector<unsigned long> path;
+    return insert(1,key,value,path);
+}
+
+
+template<class K, class V, int S, class L>
+unsigned long BTree<K,V,S,L>::next(const K& key, const Page& p) const
+{
+    ASSERT(p.node_);
+
+    const NodeEntry *begin = p.nodePage().nentries_;
+    const NodeEntry *end   = begin + p.count_;
+
+	ASSERT(begin != end);
+
+	if (key < (*begin).key_)
+    {
+		// std::cout << "next " << key << " in " << p << " " <<  p.left_ << " (FIRST)" << std::endl;
+		return p.left_;
+    }
+
+    const NodeEntry* e = std::lower_bound(begin, end, key);
+
+    if (e == end || key < (*e).key_ ) {
+        e--;
+    }
+
+	// std::cout << "next " << key << " -> " << (*e).key_ << ":" << (*e).page_ << std::endl;
+
+    return (*e).page_;
+}
+
+
+template<class K, class V, int S, class L>
+bool BTree<K,V,S,L>::insert(unsigned long page, const K& key, const V& value, std::vector<unsigned long>& path)
+{
+
+    Page p;
+    loadPage(page,p);
+
+	// std::cout << "::VISIT " << p << std::endl;
+
+    if (p.node_) {
+        path.push_back(page);
+        return insert(next(key, p), key, value, path);
+    }
+
+    LeafEntry *begin = p.leafPage().lentries_;
+    LeafEntry *end   = begin + p.count_;
+
+    LeafEntry* e = std::lower_bound(begin, end, key);
+
+	// std::cout << "::store " << key << " in " << p << std::endl;
+	// std::cout << "insert at at " << (e - p.lentries_) << std::endl;
+
+    if ((e != end) && ((*e).key_ == key))
+    {
+		// std::cout << "Page " << p.id_ << " at pos " << e - begin << std::endl;
+		// std::cout << "Replace " << key << std::endl << (*e).value_ << std::endl << value << std::endl;
+        (*e).value_ = value;
+        savePage(p);
+        return true;
+    }
+
+    // Assumes that K and V are PODs....
+    size_t count = p.count_ - (e - begin);
+	// std::cout << "Move count " << count << std::endl;
+    memmove(e+1, e , count  * sizeof(LeafEntry));
+    (*e).key_   = key;
+    (*e).value_ = value;
+    p.count_++;
+    savePage(p);
+
+    while ((p.node_ && (p.count_ == maxNodeEntries_)) || (!p.node_ && (p.count_ == maxLeafEntries_)) )
+    {
+
+		// std::cout << p << " needs spliting" << std::endl;
+
+        if (p.id_ == 1) {
+            splitRoot();
+            return false;
+        }
+        else
+        {
+            int middle = p.count_ / 2;
+            Page n;
+            newPage(n);
+            K k;
+            // Same type
+            n.node_ = p.node_;
+
+            if (p.node_)
+            {
+
+				// std::cout << "SPLIT-NODE " << p << std::endl;
+
+				for (size_t i = middle+1; i < p.count_ ; ++i ) {
+                    n.nodePage().nentries_[n.count_++] = p.nodePage().nentries_[i];
+                }
+
+				ASSERT( n.count_ == p.count_ - middle - 1 );
+
+                k = p.nodePage().nentries_[middle].key_;
+
+                p.count_ -= n.count_;
+				p.count_ --;
+
+                n.left_ = p.nodePage().nentries_[middle].page_;
+
+                // It's a node, Push-up
+
+            }
+            else
+            {
+				// std::cout << "SPLIT-LEAF " << p << std::endl;
+
+				for (size_t i = middle; i < p.count_ ; ++i ) {
+                    n.leafPage().lentries_[n.count_++] = p.leafPage().lentries_[i];
+                }
+
+				ASSERT( n.count_ == p.count_ - middle );
+
+                p.count_ -= n.count_;
+
+                // It's a leaf, Copy-up
+
+                k = n.leafPage().lentries_[0].key_;
+
+
+                if(p.right_) {
+                    // TODO: do an I/O just for the linked list
+                    Page r;
+                    loadPage(p.right_,r);
+                    r.left_ = n.id_;
+                    savePage(r);
+
+                }
+
+                n.right_ = p.right_;
+                n.left_  = p.id_;
+                p.right_ = n.id_;
+            }
+
+            savePage(n);
+            savePage(p);
+
+            page = path.back();
+            path.pop_back();
+            loadPage(page,p);
+
+            ASSERT(p.node_);
+
+            NodeEntry* begin = p.nodePage().nentries_;
+            NodeEntry* end   = p.nodePage().nentries_ + p.count_;
+
+
+            ASSERT(begin != end);
+
+            NodeEntry* e = std::lower_bound(begin, end, k);
+
+            // Node should not be there
+            ASSERT(! ( e != end && (*e).key_ == k ) );
+
+            size_t count = p.count_ - (e - begin);
+			// std::cout << "Move node count " << count << std::endl;
+
+            memmove(e+1, e , count  * sizeof(NodeEntry));
+
+            (*e).key_   = k;
+            (*e).page_  = n.id_;
+            p.count_++;
+
+
+            savePage(p);
+
+
+        }
+    }
+    return false;
+}
+
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::splitRoot()
+{
+    Page p;
+    loadPage(1,p);
+
+    Page pleft;
+    Page pright;
+
+    newPage(pleft);
+    newPage(pright);
+
+	// std::cout << "SPLIT ROOT " << p << std::endl;
+    unsigned long middle = p.count_ / 2;
+
+    K key;
+
+    if (p.node_)
+    {
+        //dump();
+
+        pleft.node_  = true;
+        pright.node_ = true;
+
+
+        pleft.left_ = p.left_;
+        for (unsigned long i = 0; i < middle ; i ++ ) {
+            pleft.nodePage().nentries_[pleft.count_++] = p.nodePage().nentries_[i];
+        }
+
+        pright.left_ = p.nodePage().nentries_[middle].page_;
+        for (size_t i = middle+1; i < p.count_ ; i ++ ) {
+            pright.nodePage().nentries_[pright.count_++] = p.nodePage().nentries_[i];
+        }
+
+        key = p.nodePage().nentries_[middle].key_;
+    }
+	else
+	{
+		pleft.node_  = false;
+		pright.node_ = false;
+
+        for (unsigned long i = 0; i < middle ; ++i )
+		{
+			//		DEBUG_VAR( pleft.count_ );
+			pleft.leafPage().lentries_[pleft.count_++] = p.leafPage().lentries_[i];
+		}
+
+		// Some version of Gcc (e.g. 4.8.1) optimize out the increment of this counter
+		ASSERT( pleft.count_ == middle );
+
+
+		for (size_t i = middle; i < p.count_ ; ++i )
+		{
+			pright.leafPage().lentries_[pright.count_++] = p.leafPage().lentries_[i];
+		}
+
+
+		// Some version of Gcc (e.g. 4.8.1) optimize out the increment of this counter
+		ASSERT( pright.count_ == p.count_ - middle );
+
+		key = pright.leafPage().lentries_[0].key_;
+
+		pleft.right_ = pright.id_;
+		pright.left_ = pleft.id_;
+	}
+
+    zero(p);
+    p.id_    = 1;
+    p.count_ = 1;
+    p.node_  = true;
+
+    p.left_  = pleft.id_;
+
+    p.nodePage().nentries_[0].key_  = key;
+    p.nodePage().nentries_[0].page_ = pright.id_;
+
+    savePage(pright);
+    savePage(pleft);
+    savePage(p);
+
+//	cout << "LEFT\n" << pleft << endl;
+//	cout << "RIGHT\n" << pright << endl;
+}
+
+
+template<class K, class V, int S, class L>
+bool BTree<K,V,S,L>::get(const K& key, V& value)
+{
+    AutoSharedLock<BTree<K,V,S,L> > lock(this);
+
+    V result;
+
+    if (search(1, key, result))
+    {
+		// std::cout << "Found " << result << std::endl;
+        value = result;
+        return true;
+    }
+
+	// std::cout << "Not Found " << std::endl;
+    return false;
+}
+
+
+template<class K, class V, int S, class L>
+bool BTree<K,V,S,L>::search(unsigned long page, const K& key, V& result) const
+{
+    Page p;
+    loadPage(page, p);
+
+	// std::cout << "Search " << key << ", Visit " << p << std::endl;
+
+    if (p.node_) {
+        return search(next(key,p), key, result);
+    }
+
+    const LeafEntry *begin = p.leafPage().lentries_;
+    const LeafEntry *end   = begin + p.count_;
+
+    const LeafEntry* e = std::lower_bound(begin, end, key);
+
+    if ((e != end) && ((*e).key_ == key))
+    {
+        result = (*e).value_;
+        return true;
+    }
+
+    return false;
+}
+
+template<class K, class V, int S, class L>
+template<class T>
+void BTree<K,V,S,L>::range(const K& key1, const K& key2, T& result)
+{
+    AutoSharedLock<BTree<K,V,S,L> > lock(this);
+    result.clear();
+    search(1, key1, key2, result);
+}
+
+template<class K, class V, int S, class L>
+bool BTree<K,V,S,L>::remove(const K &)
+{
+    NOTIMP;
+}
+
+template<class K, class V, int S, class L>
+template<class T>
+void BTree<K,V,S,L>::search(unsigned long page, const K& key1, const K& key2, T& result)
+{
+    Page p;
+    loadPage(page, p);
+
+	// std::cout << "Search " << key << ", Visit " << p << std::endl;
+
+    if (p.node_) {
+        return search(next(key1,p), key1, key2, result);
+    }
+
+    const LeafEntry *begin = p.leafPage().lentries_;
+    const LeafEntry *end   = begin + p.count_;
+
+    const LeafEntry* e = std::lower_bound(begin, end, key1);
+
+    if (e == end)
+        return;
+
+    //while((*e).key_ < key1) { e++; if (e == end) return; }
+
+	//std::cout << "range " << (*e).key_ << " .... " << p.count_ << " " << (e - begin) << std::endl;
+	//std::cout << " key1 " << key1 << std::endl;
+	//std::cout << " key2 " << key2 << std::endl;
+
+	//std::cout << " begin " << (*begin).key_ << std::endl;
+	if( p.count_ )
+	{
+// unused		const LeafEntry *last   = begin + p.count_ -1;
+		//std::cout << " last "   << (*last).key_ << std::endl;
+	}
+
+	while( !(key2 < (*e).key_) )
+    {
+		//std::cout << "match " << p.id_ << " pos " << (e - begin) << " " << (*e).key_ << std::endl;
+        result.push_back( result_type((*e).key_,(*e).value_) );
+
+        ++e;
+        if(e == end)
+        {
+            if(p.right_) {
+                loadPage(p.right_, p);
+                ASSERT(!p.node_);
+                e = p.leafPage().lentries_;
+				end = e + p.count_;
+			}
+            else
+            {
+                return;
+            }
+        }
+    }
+}
+
+
+template<class K, class V, int S, class L>
+off_t BTree<K,V,S,L>::pageOffset(unsigned long page) const
+{
+	ASSERT(page > 0); // Root page is 1. 0 is leaf marker
+    return sizeof(Page) * off_t(page-1) + offset_;
+}
+
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::_loadPage(unsigned long page, Page& p) const
+{
+	// std::cout << "Load " << page << std::endl;
+
+    off_t o = pageOffset(page);
+    off_t here;
+    SYSCALL(here = ::lseek(fd_,o,SEEK_SET));
+    ASSERT(here == o);
+
+    int len;
+    SYSCALL(len = ::read(fd_,&p,sizeof(p)));
+    ASSERT(len == sizeof(p));
+    ASSERT(page == p.id_);
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::loadPage(unsigned long page, Page& p) const
+{
+    BTree<K,V,S,L>* self = const_cast<BTree<K,V,S,L>*>(this);
+
+    typename Cache::iterator j = self->cache_.find(page);
+    if(j != self->cache_.end())
+    {
+        // TODO: find someting better...
+        memcpy(&p, (*j).second.page_, sizeof(Page));
+        return;
+    }
+
+
+    _loadPage(page, p);
+
+    if(cacheReads_)
+    {
+        Page* q = new Page();
+        memcpy(q, &p, sizeof(Page));
+        self->cache_[p.id_] = _PageInfo(q);
+    }
+
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::_savePage(const Page& p)
+{
+    ASSERT(!readOnly_);
+	// std::cout << "Save " << p << std::endl;
+
+    off_t o = pageOffset(p.id_);
+    off_t here;
+    SYSCALL(here = ::lseek(fd_,o,SEEK_SET));
+    ASSERT(here == o);
+
+    int len;
+    SYSCALL(len = ::write(fd_,&p,sizeof(p)));
+    ASSERT(len == sizeof(p));
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::savePage(const Page& p)
+{
+    BTree<K,V,S,L>* self = const_cast<BTree<K,V,S,L>*>(this);
+    typename Cache::iterator j = self->cache_.find(p.id_);
+    if(j != self->cache_.end())
+    {
+        // TODO: find someting better...
+        memcpy((*j).second.page_, &p, sizeof(Page));
+        (*j).second.dirty_ = true;
+        (*j).second.count_++;
+        return;
+    }
+
+    if(cacheWrites_)
+    {
+        Page* q = new Page();
+        memcpy(q, &p, sizeof(Page));
+        self->cache_[p.id_] = _PageInfo(q);
+        j = self->cache_.find(p.id_);
+        (*j).second.dirty_ = true;
+        (*j).second.count_++;
+        return;
+    }
+
+    _savePage(p);
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::_newPage(Page& p)
+{
+    ASSERT(!readOnly_);
+
+    off_t here = ::lseek(fd_,0,SEEK_END);
+    if(here == off_t(-1))
+        throw FailedSystemCall("lseek");
+
+    unsigned long long page = (here - offset_)/sizeof(Page) + 1;
+
+	// std::cout << "NEWPAGE " << page << std::endl;
+
+    zero(p);
+    p.id_ = (unsigned long)page;
+    ASSERT(page == p.id_);
+    ASSERT(pageOffset(page) == here);
+
+    int len;
+    SYSCALL(len = ::write(fd_,&p,sizeof(p))); // TODO: a sparse file....
+    ASSERT(len == sizeof(p));
+
+    //return p.id_;
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::newPage(Page& p)
+{
+    _newPage(p);
+
+    if(cacheReads_ || cacheWrites_)
+    {
+        Page* q = new Page();
+        memcpy(q, &p, sizeof(Page));
+        cache_[p.id_] = _PageInfo(q);
+    }
+}
+
+template<class K, class V, int S, class L>
+size_t BTree<K,V,S,L>::count() const
+{
+	return count(1);
+}
+
+template<class K, class V, int S, class L>
+size_t BTree<K,V,S,L>::count(unsigned long page) const
+{
+	AutoSharedLock<BTree<K,V,S,L> > lock(const_cast<BTree*>(this));
+
+	Page p;
+	loadPage(page, p);
+
+	size_t c = 0;
+
+	if( p.node_ )
+	{
+		c += this->count( p.left_ );
+        for (unsigned long i = 0; i < p.count_ ; i ++ )
+			c += count( p.nodePage().nentries_[i].page_ );
+	}
+	else // leaf
+	{
+		c = p.count_;
+	}
+
+	return c;
+}
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::lockShared()
+{
+    L::lockRange(fd_,0,0,F_SETLKW,F_RDLCK);
+}
+
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::lock()
+{
+    L::lockRange(fd_,0,0,F_SETLKW, readOnly_ ? F_RDLCK : F_WRLCK);
+}
+
+
+template<class K, class V, int S, class L>
+void BTree<K,V,S,L>::unlock()
+{
+    L::lockRange(fd_,0,0,F_SETLK,F_UNLCK);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/container/BTree.h b/eckit/src/eckit/container/BTree.h
new file mode 100644
index 0000000..ed598e1
--- /dev/null
+++ b/eckit/src/eckit/container/BTree.h
@@ -0,0 +1,348 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BTree.h
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef eckit_BTree_h
+#define eckit_BTree_h
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/container/BTree.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/memory/Padded.h"
+#include "eckit/os/Stat.h"
+#include "eckit/thread/AutoLock.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class BTreeLock {
+public:
+    static void lockRange(int fd, off_t start, off_t len, int cmd, int type) {
+
+        struct flock lock;
+
+        lock.l_type   = type;
+        lock.l_whence = SEEK_SET;
+        lock.l_start  = start;
+        lock.l_len    = len;
+
+        SYSCALL(::fcntl(fd, cmd, &lock));
+    }
+};
+
+class BTreeNoLock {
+public:
+    static void lockRange(int fd, off_t start, off_t len, int cmd, int type) {
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// B+Tree index
+///
+/// @todo Deletion
+/// @todo Cache pages
+/// @invariant K and V needs to be PODs
+/// @invariant S is the page size padding
+/// @invariant L implements locking policy
+///
+template<class K, class V, int S, class L = BTreeNoLock>
+class BTree : private NonCopyable {
+public:
+
+    typedef K key_type;
+    typedef V value_type;
+    typedef std::pair<K,V> result_type;
+
+    // -- Contructors
+
+    BTree( const PathName&, bool readOnly = false, off_t offset = 0);
+
+    // -- Destructor
+
+    ~BTree();
+
+    // -- Methods
+
+    bool get(const K&, V&);
+    bool set(const K&, const V&);
+
+    template<class T>
+    void range(const K& key1, const K& key2, T& result);
+
+    bool remove(const K&);
+
+    void dump(std::ostream& s = std::cout) const;
+
+	/// Counts the entries in the whole tree
+	/// This is not an efficient call since it visits the whole tree. Use with care.
+	size_t count() const;
+
+	/// Counts the entries in a page of the tree
+	size_t count(unsigned long page) const;
+
+    void lock();
+    void lockShared();
+    void unlock();
+
+    void flush();
+
+    const PathName& path() const { return path_; }
+
+private: // methods
+
+    void dump(std::ostream&, unsigned long page, int depth) const;
+
+	void print(std::ostream& o) const { dump(o); }
+
+private:
+
+    struct _Header {
+    };
+
+    struct NodeEntry {
+        K    key_;
+        unsigned long page_;
+
+        bool operator<(const K& key) const {
+            return key_ < key;
+        }
+
+        bool operator>(const K& key) const {
+            return key_ > key;
+        }
+
+        bool operator==(const K& key) const {
+            return key_ == key;
+        }
+
+        bool operator!=(const K& key) const {
+            return key_ != key;
+        }
+
+        bool operator>=(const K& key) const {
+            return key_ >= key;
+        }
+
+        bool operator<=(const K& key) const {
+            return key_ <= key;
+        }
+
+        bool operator<(const NodeEntry& e) const {
+            return key_ < e.key_;
+        }
+
+        bool operator>(const NodeEntry& e) const {
+            return key_ > e.key_;
+        }
+
+        bool operator==(const NodeEntry& e) const {
+            return key_ == e.key_;
+        }
+
+        bool operator!=(const NodeEntry& e) const {
+            return key_ != e.key_;
+        }
+
+        bool operator>=(const NodeEntry& e) const {
+            return key_ >= e.key_;
+        }
+
+        bool operator<=(const NodeEntry& e) const {
+            return key_ <= e.key_;
+        }
+    };
+
+    struct LeafEntry {
+        K    key_;
+        V    value_;
+
+        bool operator<(const K& key) const {
+            return key_ < key;
+        }
+
+        bool operator>(const K& key) const {
+            return key_ > key;
+        }
+
+        bool operator==(const K& key) const {
+            return key_ == key;
+        }
+
+        bool operator!=(const K& key) const {
+            return key_ != key;
+        }
+
+        bool operator>=(const K& key) const {
+            return key_ >= key;
+        }
+
+        bool operator<=(const K& key) const {
+            return key_ <= key;
+        }
+
+        bool operator<(const NodeEntry& e) const {
+            return key_ < e.key_;
+        }
+
+        bool operator>(const NodeEntry& e) const {
+            return key_ > e.key_;
+        }
+
+        bool operator==(const NodeEntry& e) const {
+            return key_ == e.key_;
+        }
+
+        bool operator!=(const NodeEntry& e) const {
+            return key_ != e.key_;
+        }
+
+        bool operator>=(const NodeEntry& e) const {
+            return key_ >= e.key_;
+        }
+
+        bool operator<=(const NodeEntry& e) const {
+            return key_ <= e.key_;
+        }
+    };
+
+    struct _Page {
+        unsigned long  id_;
+        unsigned long  count_;
+        unsigned long  node_;
+        unsigned long  left_;
+        unsigned long  right_;
+
+    };
+
+
+
+	struct _LeafPage : public _Page
+	{
+		static const size_t SIZE = (S - sizeof(_Page)) / sizeof(LeafEntry);
+        LeafEntry lentries_[SIZE];
+        void print(std::ostream& s) const ;
+    };
+
+    struct _NodePage : public _Page  {
+	static const size_t SIZE = (S - sizeof(_Page)) / sizeof(NodeEntry);
+        NodeEntry nentries_[SIZE];
+        void print(std::ostream& s) const ;
+    };
+
+
+
+    struct NodePage : Padded<_NodePage, S> {
+    };
+
+    struct LeafPage : Padded<_LeafPage, S> {
+    };
+
+
+    struct Page : Padded<_Page, S> {
+        NodePage& nodePage() {
+            return *(reinterpret_cast<NodePage*>(this));
+        }
+        LeafPage& leafPage() {
+            return *(reinterpret_cast<LeafPage*>(this));
+        }
+        const NodePage& nodePage() const {
+            return *(reinterpret_cast<const NodePage*>(this));
+        }
+        const LeafPage& leafPage() const {
+            return *(reinterpret_cast<const LeafPage*>(this));
+        }
+
+
+        void print(std::ostream& s) const ;
+
+        friend std::ostream& operator<<(std::ostream& s,const Page& p)
+        {
+            p.print(s);
+            return s;
+        }
+    };
+
+	static const size_t maxNodeEntries_ = _NodePage::SIZE; // split at full page -- could be a percentage
+	static const size_t maxLeafEntries_ = _LeafPage::SIZE; // split at full page -- could be a percentage
+
+    PathName path_;
+
+    int fd_;
+
+    bool cacheReads_;
+    bool cacheWrites_;
+    bool readOnly_;
+    off_t offset_;
+
+    struct _PageInfo {
+        Page* page_;
+        unsigned long long count_;
+        time_t last_;
+        bool dirty_;
+
+        _PageInfo(Page* page = 0):
+            page_(page),
+            count_(0),
+            last_(time(0)),
+            dirty_(false) {}
+    };
+
+    typedef std::map<unsigned long,_PageInfo> Cache;
+    Cache cache_;
+
+    void lockRange(off_t start,off_t len,int cmd,int type);
+    bool search(unsigned long page, const K&, V&) const;
+
+    template<class T>
+    void search(unsigned long page, const K& key1, const K& key2, T& result);
+
+
+    void splitRoot();
+
+    off_t pageOffset(unsigned long) const;
+    void savePage(const Page&);
+    void loadPage(unsigned long,Page&) const;
+    void newPage(Page&);
+
+    void _savePage(const Page&);
+    void _loadPage(unsigned long,Page&) const;
+    void _newPage(Page&);
+
+    bool insert(unsigned long page, const K& key, const V& value, std::vector<unsigned long>& path);
+    bool store(unsigned long page, const K& key, const V& value, std::vector<unsigned long>& path);
+
+    unsigned long next(const K&, const Page&) const;
+
+
+    // -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s,const BTree& p)
+    {
+        p.print(s);
+        return s;
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "BTree.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/Cache.h b/eckit/src/eckit/container/Cache.h
new file mode 100644
index 0000000..425969c
--- /dev/null
+++ b/eckit/src/eckit/container/Cache.h
@@ -0,0 +1,302 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date   Oct 2012
+
+#ifndef eckit_container_Cache_h
+#define eckit_container_Cache_h
+
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+/// @todo make an apply() method
+/// @todo implement the expire() and the different policies
+
+template< typename K, typename V >
+class Cache : private NonCopyable {
+
+public: // types
+
+    struct Entry {
+        Entry( const V& v ) :
+            v_(v),
+            expired_(false),
+            hits_(0)
+        {
+            gettimeofday(&age_,0);
+            last_ = age_;
+        }
+
+        void reset( const V& v )
+        {
+            v_ = v;
+            expired_ = false;
+            hits_ = 0;
+            gettimeofday(&age_,0);
+            last_ = age_;
+        }
+
+        V& access()
+        {
+            gettimeofday(&last_,0);
+            ++hits_;
+            return v_;
+        }
+
+        V               v_;
+        bool            expired_;
+        uint64_t        hits_;
+        struct ::timeval  age_;
+        struct ::timeval  last_;
+    };
+
+    typedef K key_type;
+    typedef V value_type;
+    typedef Entry entry_type;
+
+    typedef std::map<key_type,entry_type> store_type;
+
+    class Policy
+    {
+        /// Expires the Least Recently Used (LRU) entries
+        /// @returns true if any entries were expired
+        static bool expireLRU( store_type& c, const size_t& maxSize);
+        /// Expires the Least Frequently Used (LFU) entries
+        /// @returns true if any entries were expired
+        static bool expireLFU( store_type& c, const size_t& maxSize);
+        /// Expires the entries older than a certain age
+        /// @returns true if any entries were expired
+        static bool expireAge( store_type& c, struct ::timeval& maxAge);
+    };
+
+public: // methods
+
+    Cache();
+
+    ~Cache();
+
+    /// inserts an object in the cache
+    /// @returns true if object was correctly inserted,
+    ///          or false if key already existed and cannot be inserted
+    bool insert( const K&, const V& );
+
+    /// updates an object in the cache (or inserts if does not exist)
+    /// @returns true if object existed and was updated,
+    ///          false if the object did not exist and was inserted
+    bool update( const K&, const V& );
+
+    /// accesses an object in the cache
+    /// @param v returns the object
+    bool fetch( const K&, V&);
+
+    /// marks an object as expired
+    /// @returns true if object was present and is marked as expired
+    bool expire( const K& );
+
+    /// evicts entries that are considered expired
+    void purge();
+
+    /// evicts all entries
+    void clear();
+
+    /// @returns true if entry exists and is not expired in cache
+    bool valid( const K& ) const;
+
+    /// @returns the number of entries in the cache, expired or not
+    size_t size() const;
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s,const Cache& p)
+    {
+        p.print(s);
+        return s;
+    }
+
+private: // methods
+
+    /// marks an object as expired
+    /// @returns true if object was present and is marked as expired
+    void expire( typename store_type::iterator i );
+
+private: // members
+
+    store_type      storage_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+template< typename K, typename V>
+Cache<K,V>::Cache() :
+    storage_()
+{
+}
+
+template< typename K, typename V>
+Cache<K,V>::~Cache()
+{
+    clear();
+}
+
+template< typename K, typename V>
+bool Cache<K,V>::insert(const K& k, const V& v)
+{
+    typename store_type::iterator i = storage_.find(k);
+    if( i != storage_.end() )
+    {
+        Entry& e = i->second;
+
+        if( !e.expired_ ) return false;
+
+        e.reset(v);
+    }
+    else
+        storage_.insert( std::make_pair(k,Entry(v)) );
+
+    return true;
+}
+
+template< typename K, typename V>
+bool Cache<K,V>::update(const K& k, const V& v)
+{
+    typename store_type::iterator i = storage_.find(k);
+    if( i != storage_.end() )
+    {
+        Entry& e = i->second;
+        e.reset(v);
+        return true;
+    }
+    else
+    {
+        storage_.insert( std::make_pair(k,Entry(v)) );
+        return false;
+    }
+}
+
+template< typename K, typename V>
+bool Cache<K,V>::fetch(const K& k, V& v)
+{
+    typename store_type::iterator i = storage_.find(k);
+    if( i != storage_.end() )
+    {
+        Entry& e = i->second;
+        if( !e.expired_ )
+        {
+            v = e.access();
+            return true;
+        }
+    }
+    return false;
+}
+
+template< typename K, typename V>
+bool Cache<K,V>::expire(const K& k)
+{
+    typename store_type::iterator i = storage_.find(k);
+    if( i != storage_.end() )
+    {
+        this->expire(i);
+        return true;
+    }
+    return false;
+}
+
+template< typename K, typename V>
+void Cache<K,V>::expire( typename store_type::iterator i )
+{
+    Entry& e = i->second;
+    e.expired_ = true;
+}
+
+template< typename K, typename V>
+void Cache<K,V>::purge()
+{
+    // collect all expired
+    typedef typename store_type::iterator siterator;
+    std::vector< siterator > expired;
+    for( siterator i = storage_.begin(); i != storage_.end(); ++i )
+        if( i->second.expired_ )
+            expired.push_back(i);
+    // remove them
+    for( typename std::vector< siterator >::iterator e = expired.begin(); e != expired.end(); ++e )
+        storage_.erase(*e);
+}
+
+template< typename K, typename V>
+void Cache<K,V>::clear()
+{
+    storage_.clear();
+}
+
+template< typename K, typename V>
+bool Cache<K,V>::valid(const K& k) const
+{
+    typename store_type::const_iterator i = storage_.find(k);
+    if( i != storage_.end() && !i->second.expired_ )
+        return true;
+    return false;
+}
+
+template< typename K, typename V>
+size_t Cache<K,V>::size() const
+{
+    return storage_.size();
+}
+
+template< typename K, typename V>
+void Cache<K,V>::print(std::ostream& out) const
+{
+    typedef typename store_type::const_iterator siterator;
+    for( siterator i = storage_.begin(); i != storage_.end(); ++i )
+        out << i->second.v_ << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+template< typename K, typename V>
+bool Cache<K,V>::Policy::expireLRU( typename Cache<K,V>::store_type& c, const size_t& maxSize )
+{
+//    typedef std::vector< std::pair<
+//    typedef typename store_type::iterator siterator;
+//    for( siterator i = storage_.begin(); i != storage_.end(); ++i )
+
+//    bool found = false;
+//    typedef typename store_type::iterator siterator;
+//    for( siterator i = storage_.begin(); i != storage_.end(); ++i )
+//    {
+//        if( !i->second.expired_ && policy_->toExpire( i->second ) )
+//        {
+//            Entry& e = i->second;
+//            e.expired_ = true;
+//            found = true;
+//        }
+//    }
+//    return found;
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/CacheLRU.cc b/eckit/src/eckit/container/CacheLRU.cc
new file mode 100644
index 0000000..1ecf3d0
--- /dev/null
+++ b/eckit/src/eckit/container/CacheLRU.cc
@@ -0,0 +1,177 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template< typename K, typename V>
+CacheLRU<K,V>::CacheLRU(size_t capacity, purge_handler_type purge) :
+    capacity_( capacity ),
+    purge_(purge)
+{
+}
+
+template< typename K, typename V>
+CacheLRU<K,V>::~CacheLRU()
+{
+    clear();
+}
+
+template< typename K, typename V>
+bool CacheLRU<K,V>::insert(const key_type& key, const value_type& value)
+{
+    bool existed = false;
+
+    typename map_type::iterator itr = map_.find(key);
+    if( itr != map_.end() )
+    {
+        existed = true;
+
+        // erase the key from where it is
+        // we'll reinsert it again so it comes on top
+
+        erase(itr);
+    }
+
+    storage_.push_front( Entry(key,value) );
+    map_[key] = storage_.begin();
+
+    trim();
+
+    return existed;
+}
+
+template< typename K, typename V>
+V CacheLRU<K,V>::access(const key_type& key)
+{
+    // check first the front() since it is the most popular/recent entry
+
+    if( size() && storage_.front().key_ == key )
+        return storage_.front().value_;
+
+    typename map_type::iterator itr = map_.find(key);
+
+    if(itr != map_.end())
+    {
+        // move entry of list to front
+        // this keeps the most popular in front
+
+        moveToFront(itr);
+
+        return valueFrom(itr);
+    }
+    else
+    {
+        throw eckit::OutOfRange( "key not in CacheLRU", Here() );
+    }
+}
+
+template< typename K, typename V>
+V CacheLRU<K,V>::extract(const key_type& key)
+{
+    typename map_type::iterator itr = map_.find(key);
+    if( itr == map_.end() ) {
+        throw OutOfRange("key not in CacheLRU", Here());
+    }
+
+    value_type result = valueFrom(itr);
+    erase(itr);
+
+    return result;
+}
+
+template< typename K, typename V>
+bool CacheLRU<K,V>::remove(const key_type& key)
+{
+    bool existed = false;
+
+    typename map_type::iterator itr = map_.find(key);
+    if( itr != map_.end() )
+    {
+        existed = true;
+        purge(itr->second->key_, valueFrom(itr));
+        erase(itr);
+    }
+
+    return existed;
+}
+
+template< typename K, typename V>
+bool CacheLRU<K,V>::exists(const key_type& key) const
+{
+    return ( map_.find(key) != map_.end() );
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::clear()
+{
+    for(storage_iterator itr = storage_.begin(); itr != storage_.end(); ++itr) {
+        purge(itr->key_,itr->value_);
+    }
+
+    storage_.clear();
+    map_.clear();
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::erase(typename map_type::iterator itr)
+{
+    storage_.erase( itr->second );
+    map_.erase( itr );
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::trim()
+{
+    while(map_.size() > capacity_) {
+        entry_type entry = storage_.back();
+        purge( entry.key_, entry.value_ );
+        map_.erase( entry.key_ );
+        storage_.pop_back();
+    }
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::moveToFront(typename map_type::iterator itr)
+{
+    storage_.splice( storage_.begin(), storage_, itr->second );
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::purge(key_type& key, value_type& value) const
+{
+    if(purge_)
+        purge_(key,value);
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::capacity(size_t size)
+{
+    capacity_ = size;
+    trim();
+}
+
+template< typename K, typename V>
+void CacheLRU<K,V>::print(std::ostream& os) const
+{
+    os << "CacheLRU(capacity=" << capacity_
+        << ",size=" << storage_.size()
+        << ",storage={";
+    for(typename storage_type::const_iterator itr = storage_.begin(); itr != storage_.end(); ++itr) {
+        os << *itr << ",";
+    }
+
+    os << "})";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/container/CacheLRU.h b/eckit/src/eckit/container/CacheLRU.h
new file mode 100644
index 0000000..75a7c6d
--- /dev/null
+++ b/eckit/src/eckit/container/CacheLRU.h
@@ -0,0 +1,132 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date   Dec 2015
+
+#ifndef eckit_container_CacheLRU_h
+#define eckit_container_CacheLRU_h
+
+#include <iosfwd>
+#include <map>
+#include <list>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/CodeLocation.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template< typename K, typename V >
+class CacheLRU : private NonCopyable {
+
+public: // types
+
+    typedef K key_type;
+    typedef V value_type;
+
+    struct Entry {
+
+        key_type   key_;
+        value_type value_;
+
+        Entry(const key_type& k, const value_type& v) : key_(k), value_(v) {}
+
+        friend std::ostream& operator<<(std::ostream& s,const Entry& e) {
+            s << "key=" << e.key_;
+            return s;
+        }
+    };
+
+    typedef Entry entry_type;
+
+    typedef std::list<entry_type> storage_type;
+    typedef typename storage_type::iterator storage_iterator;
+    typedef std::map<key_type,storage_iterator> map_type;
+
+    typedef void (*purge_handler_type)(key_type&, value_type&);
+
+public: // methods
+
+    CacheLRU(size_t capacity, purge_handler_type purge = 0);
+
+    ~CacheLRU();
+
+    /// Inserts an entry into the cache, overwrites if already exists
+    /// @returns true if a key already existed
+    bool insert(const key_type& key, const value_type& value);
+
+    /// Accesses a key that must already exist
+    /// @throws OutOfRange exception is key not in cache
+    value_type access(const key_type& key);
+
+    /// Extracts the key from the cache without purging
+    /// @pre Key must exist in cache
+    /// @throws OutOfRange exception if key not in cache
+    value_type extract(const key_type& key);
+
+    /// Remove a key-value pair from the cache
+    /// No effect if key is not present
+    ///
+    /// @return true if removed
+    bool remove(const key_type& key);
+
+    /// @returns true if the key exists in the cache
+    bool exists(const key_type& key) const;
+
+    /// Clears all entries in the cache
+    void clear();
+
+    /// @returns the maximum size of the cache
+    size_t capacity() const { return capacity_; }
+
+    /// @returns the current (used) size of the cache
+    size_t size() const { return storage_.size(); }
+
+    /// resizes the cache capacity
+    void capacity( size_t size );
+
+    void print(std::ostream& os) const;
+
+    friend std::ostream& operator<<(std::ostream& s,const CacheLRU& p) { p.print(s); return s; }
+
+private: // methods
+
+    void erase(typename map_type::iterator itr);
+
+    void trim();
+
+    void moveToFront(typename map_type::iterator itr);
+
+    value_type& valueFrom(typename map_type::iterator itr) const { return itr->second->value_; }
+
+    void purge(key_type& key, value_type& value) const;
+
+private: // members
+
+    storage_type      storage_;
+
+    map_type        map_;
+
+    size_t          capacity_;
+
+    purge_handler_type purge_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "CacheLRU.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/CacheManager.cc b/eckit/src/eckit/container/CacheManager.cc
new file mode 100644
index 0000000..30542eb
--- /dev/null
+++ b/eckit/src/eckit/container/CacheManager.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/container/CacheManager.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+eckit::CacheManagerBase::CacheManagerBase(const std::string& loaderName) :
+    loaderName_(loaderName)
+{
+}
+
+std::string eckit::CacheManagerBase::loader() const {
+    return loaderName_;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/container/CacheManager.h b/eckit/src/eckit/container/CacheManager.h
new file mode 100644
index 0000000..d0468ff
--- /dev/null
+++ b/eckit/src/eckit/container/CacheManager.h
@@ -0,0 +1,277 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Baudouin Raoult
+/// @date   May 2015
+
+#ifndef eckit_container_CacheManager_h
+#define eckit_container_CacheManager_h
+
+#include <sys/stat.h>
+
+#include <string>
+#include <functional>
+#include <string>
+
+#include "eckit/eckit.h"
+#include "eckit/config/LibEcKit.h"
+#include "eckit/config/Resource.h"
+#include "eckit/io/FileLock.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/os/AutoUmask.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Filesystem Cache Manager
+
+class CacheManagerBase : private NonCopyable {
+
+public: // methods
+
+    CacheManagerBase(const std::string& loaderName);
+
+    std::string loader() const;
+
+private: // members
+
+    std::string loaderName_;
+
+};
+
+template <class Traits>
+class CacheManager : public CacheManagerBase {
+
+public: // methods
+
+    typedef typename Traits::value_type value_type;
+
+    class CacheContentCreator {
+    public:
+        virtual void create(const PathName&, value_type& value) = 0;
+    };
+
+    typedef std::string key_t;
+
+public: // methods
+
+    explicit CacheManager(const std::string& loaderName, const std::string& roots, bool throwOnCacheMiss);
+
+    PathName getOrCreate(const key_t& key,
+                         CacheContentCreator& creator,
+                         value_type& value) const;
+
+private: // methods
+
+
+    bool get(const key_t& key, PathName& path) const;
+
+    PathName stage(const key_t& key, const PathName& root) const;
+
+    bool commit(const key_t& key, const PathName& path, const PathName& root) const;
+
+    PathName entry(const key_t& key, const std::string& root) const;
+
+
+private: // members
+
+    std::vector<PathName> roots_;
+    bool throwOnCacheMiss_;
+};
+
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// NOTE : this should be in the .cc but we have the non-template CacheManagerBase there not to have duplicate symbols
+
+template<class Traits>
+CacheManager<Traits>::CacheManager(const std::string& loaderName, const std::string& roots, bool throwOnCacheMiss) :
+    CacheManagerBase(loaderName),
+    throwOnCacheMiss_(throwOnCacheMiss) {
+    eckit::Tokenizer parse(":");
+    std::vector<std::string> v;
+    parse(roots, v);
+    std::copy(v.begin(), v.end(), std::back_inserter(roots_));
+
+    Log::debug<LibEcKit>() << "CacheManager roots " << roots_ << std::endl;
+
+}
+
+template<class Traits>
+bool CacheManager<Traits>::get(const key_t& key, PathName& v) const {
+
+    for (std::vector<PathName>::const_iterator j = roots_.begin(); j != roots_.end(); ++j) {
+        PathName p = entry(key, *j);
+        if (p.exists()) {
+            v = p;
+            Log::debug<LibEcKit>() << "CacheManager found path " << p << std::endl;
+            return true;
+        }
+    }
+
+    if (throwOnCacheMiss_) {
+        std::ostringstream oss;
+        oss << "CacheManager cache miss: key=" << key << ", tried:";
+
+        const char* sep = " ";
+        for (std::vector<PathName>::const_iterator j = roots_.begin(); j != roots_.end(); ++j) {
+            PathName p = entry(key, *j);
+            oss << sep << p;
+            sep = ", ";
+        }
+
+        throw UserError(oss.str());
+    }
+
+    return false;
+}
+
+template<class Traits>
+PathName CacheManager<Traits>::entry(const key_t &key, const std::string& root) const {
+    std::ostringstream oss;
+    oss <<  root
+        << "/"
+        << Traits::name()
+        << "/"
+        << Traits::version()
+        << "/"
+        << key
+        << Traits::extension();
+    return PathName(oss.str());
+}
+
+template<class Traits>
+PathName CacheManager<Traits>::stage(const key_t& key, const PathName& root) const {
+
+    PathName p = entry(key, root);
+    AutoUmask umask(0);
+    // FIXME: mask does not seem to affect first level directory
+    p.dirName().mkdir(0777);  // ensure directory exists
+    Log::info() << "CacheManager creating file " << p << std::endl;
+    // unique file name avoids race conditions on the file from multiple processes
+    return PathName::unique(p);
+}
+
+template<class Traits>
+bool CacheManager<Traits>::commit(const key_t& key, const PathName& tmpfile, const PathName& root) const
+{
+    PathName file = entry(key, root);
+    try {
+        SYSCALL(::chmod(tmpfile.asString().c_str(), 0444));
+        PathName::rename( tmpfile, file );
+    } catch ( FailedSystemCall& e ) { // ignore failed system call -- another process nay have created the file meanwhile
+        Log::debug() << "Failed rename of cache file -- " << e.what() << std::endl;
+        return false;
+    }
+    return true;
+}
+
+template<class Traits>
+PathName CacheManager<Traits>::getOrCreate(const key_t& key,
+        CacheContentCreator& creator,
+        value_type& value) const {
+
+    PathName path;
+
+    if (get(key, path)) {
+        eckit::Log::debug() << "Loading cache file "
+                           << path
+                           << std::endl;
+
+        Traits::load(*this, value, path);
+        return path;
+    }
+    else {
+
+        for (std::vector<PathName>::const_iterator j = roots_.begin(); j != roots_.end(); ++j) {
+
+            eckit::Log::info() << "Cache file "
+                               << entry(key, *j)
+                               << " does not exist"
+                               << std::endl;
+
+
+            std::ostringstream oss;
+            oss << entry(key, *j) << ".lock";
+
+            try {
+
+                eckit::PathName lockFile(oss.str());
+
+                eckit::FileLock locker(lockFile);
+
+                eckit::AutoLock<eckit::FileLock> lock(locker);
+
+                // Some
+                if (!get(key, path)) {
+                    eckit::Log::info() << "Creating cache file "
+                                       << entry(key, *j)
+                                       << std::endl;
+
+                    eckit::PathName tmp = stage(key, *j);
+                    creator.create(tmp, value);
+                    Traits::save(*this, value, tmp);
+                    ASSERT(commit(key, tmp, *j));
+                }
+                else {
+                    eckit::Log::debug() << "Loading cache file "
+                                        << entry(key, *j)
+                                        << " (created by another process)"
+                                        << std::endl;
+                    Traits::load(*this, value, path);
+                }
+
+                ASSERT(get(key, path));
+
+                if (lockFile.exists()) {
+                    try {
+                        lockFile.unlink();
+                    } catch (...) {
+                    }
+                }
+
+                return path;
+
+            } catch (FailedSystemCall& e) {
+                eckit::Log::error() << "Error creating cache file: "
+                                    << entry(key, *j)
+                                    << " (" << e.what() << ")" << std::endl;
+            }
+
+        }
+
+    }
+
+    std::ostringstream oss;
+    oss << "CacheManager cannot create key=" << key << ", tried:";
+
+    const char* sep = " ";
+    for (std::vector<PathName>::const_iterator j = roots_.begin(); j != roots_.end(); ++j) {
+        PathName p = entry(key, *j);
+        oss << sep << p;
+        sep = ", ";
+    }
+
+    throw UserError(oss.str());
+
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/ClassExtent.h b/eckit/src/eckit/container/ClassExtent.h
new file mode 100644
index 0000000..52fb5e4
--- /dev/null
+++ b/eckit/src/eckit/container/ClassExtent.h
@@ -0,0 +1,254 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClassExtent.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_ClassExtent_h
+#define eckit_ClassExtent_h
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class ClassExtent : private NonCopyable {
+public:
+
+// -- Contructors
+
+	ClassExtent(T*);
+
+// -- Destructor
+
+	~ClassExtent();
+
+// -- Methods
+	
+	static size_t size();
+
+public: // methods
+    
+	static void callAll(void (T::*)());
+	static void callAll(void (T::*)() const);
+
+	template<class P> 
+	static void callAll(void (T::*)(P),P);
+
+	template<class P> 
+	static void callAll(void (T::*)(P) const,P);
+
+	template<class P1,class P2> 
+	static void callAll(void (T::*)(P1,P2),P1,P2);
+
+	template<class P> 
+	static void callAll(void (T::*)(P&) const,P&);
+
+	template<class P> 
+	static void callAll(void (T::*)(P&),P&);
+
+	template<class P1,class P2> 
+	static void callAll(void (T::*)(P1&,P2&),P1&,P2&);
+
+private: // members
+    
+	struct Extent {
+        typedef std::map<ClassExtent<T>*,T*,std::less<ClassExtent<T>*> > Map;
+		Mutex   mutex_;
+		Map     map_;
+		bool    inited_;
+		Extent();
+		~Extent();
+	};
+    
+	static Extent extent_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+// We assume that global-initialisation is single threaded
+
+template<class T>
+typename ClassExtent<T>::Extent ClassExtent<T>::extent_;
+
+template<class T>
+ClassExtent<T>::ClassExtent(T* obj)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+	extent_.map_[this] = obj;
+}
+
+template<class T>
+ClassExtent<T>::~ClassExtent()
+{
+
+	if(extent_.inited_)  // This can be after exit() is called
+						 // I need to find a solution
+	{
+
+	ASSERT(extent_.inited_);
+
+	AutoLock<Mutex> lock(extent_.mutex_);
+	ASSERT(extent_.map_.find(this) != extent_.map_.end());
+	extent_.map_.erase(this);
+	}
+}
+
+template<class T>
+size_t ClassExtent<T>::size()
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+	return extent_.map_.size();
+}
+
+template<class T>         
+void ClassExtent<T>::callAll(void (T::*proc)())
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+    // Make a copy to cater for object that are deleted during the loop
+    typename ClassExtent<T>::Extent::Map map = extent_.map_;
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+    for(i = map.begin(); i != map.end() ; ++i)
+        ((*i).second->*proc)();
+
+}
+
+template<class T>         
+void ClassExtent<T>::callAll(void (T::*proc)() const)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+	for(i = extent_.map_.begin();
+		i != extent_.map_.end() ; ++i)
+		((*i).second->*proc)();
+}
+
+template<class T> template<class P> 
+void ClassExtent<T>::callAll(void (T::*proc)(P),P arg)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+    // Make a copy to cater for object that are deleted during the loop
+    typename ClassExtent<T>::Extent::Map map = extent_.map_;
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+    for(i = map.begin(); i != map.end() ; ++i)
+		((*i).second->*proc)(arg);
+}
+
+template<class T> template<class P> 
+void ClassExtent<T>::callAll(void (T::*proc)(P) const,P arg)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+	for(i = extent_.map_.begin();
+		i != extent_.map_.end() ; ++i)
+		((*i).second->*proc)(arg);
+}
+
+template<class T> template<class P1,class P2> 
+void ClassExtent<T>::callAll(void (T::*proc)(P1,P2),P1 arg1,P2  arg2)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+    // Make a copy to cater for object that are deleted during the loop
+    typename ClassExtent<T>::Extent::Map map = extent_.map_;
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+    for(i = map.begin(); i != map.end() ; ++i)
+		((*i).second->*proc)(arg1,arg2);
+}
+
+template<class T> template<class P> 
+void ClassExtent<T>::callAll(void (T::*proc)(P&),P& arg)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+	for(i = extent_.map_.begin();
+		i != extent_.map_.end() ; ++i)
+		((*i).second->*proc)(arg);
+}
+
+template<class T> template<class P> 
+void ClassExtent<T>::callAll(void (T::*proc)(P&) const,P& arg)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+	for(i = extent_.map_.begin();
+		i != extent_.map_.end() ; ++i)
+		((*i).second->*proc)(arg);
+}
+
+template<class T> template<class P1,class P2> 
+void ClassExtent<T>::callAll(void (T::*proc)(P1&,P2&),P1& arg1,P2&  arg2)
+{
+	ASSERT(extent_.inited_);
+	AutoLock<Mutex> lock(extent_.mutex_);
+
+    // Make a copy to cater for object that are deleted during the loop
+    typename ClassExtent<T>::Extent::Map map = extent_.map_;
+	// for(ClassExtent<T>::Extent::Map::iterator i = extent_.map_.begin();
+	typedef  typename ClassExtent<T>::Extent::Map map_type;
+	typename map_type::iterator i;
+    for(i = map.begin(); i != map.end() ; ++i)
+		((*i).second->*proc)(arg1,arg2);
+}
+
+template<class T>
+ClassExtent<T>::Extent::Extent():
+	inited_(true)
+{
+}
+
+template<class T>
+ClassExtent<T>::Extent::~Extent()
+{
+	inited_ = false;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/DenseMap.h b/eckit/src/eckit/container/DenseMap.h
new file mode 100644
index 0000000..b5a7f2f
--- /dev/null
+++ b/eckit/src/eckit/container/DenseMap.h
@@ -0,0 +1,215 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_DenseMap_h
+#define eckit_DenseMap_h
+
+/// @author Tiago Quintino
+
+#include <vector>
+#include <algorithm>
+#include <utility>
+
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template < typename K, typename V >
+class DenseMap {
+private: // types
+  typedef size_t index_t;   ///< index type
+
+public: // types
+
+	typedef K key_type;     ///< key type
+	typedef V value_type;   ///< value type
+
+	class kidx_t {
+	public:
+
+		/// \brief Key that is used for lookup
+		key_type key() const { return key_; };
+
+		/// \brief Index in values storage
+		index_t idx() const { return idx_; };
+		friend class DenseMap;
+
+	private:
+		kidx_t(key_type k, index_t i) : idx_(i), key_(k)  {}
+		index_t idx_;
+		key_type key_;
+	};
+
+private: // types
+
+	typedef std::vector< kidx_t >      key_store_t;
+	typedef std::vector< value_type >  value_store_t;
+
+public: // methods
+
+	typedef typename key_store_t::iterator iterator;
+	typedef typename key_store_t::const_iterator const_iterator;
+
+	DenseMap( size_t s = 0 ) : sorted_(true)
+	{
+		if(s > 0) reserve(s);
+	}
+
+	~DenseMap() {}
+
+	void reserve( size_t s )
+	{
+		keys_.reserve(s);
+		values_.reserve(s);
+	}
+
+	void insert( const K& k, const V& v )
+	{
+		keys_.push_back( kidx_t(k,values_.size()) );
+		values_.push_back(v);
+		sorted_ = false;
+	}
+
+    /// @TODO shoudl we implement this?
+    //	size_t erase( const key_type& k );
+
+	void replace( const K& k, const V& v )
+	{
+		iterator it = find(k);
+		if( it != end() )
+		{
+			values_[ it->idx() ] = v;
+		}
+		else
+		{
+			insert(k,v);
+		}
+	}
+
+	void clear()
+	{
+		keys_.clear();
+		values_.clear();
+		sorted_ = true;
+	}
+
+	bool sorted() const { return sorted_; }
+
+	size_t size() const { return keys_.size(); }
+	bool empty() const { return keys_.size() == 0; }
+
+	void sort()
+	{
+		if(!sorted_)
+		{
+			std::sort( begin(), end(), LessThan() );
+			sorted_ = true;
+		}
+	}
+
+	iterator begin() { return keys_.begin(); }
+	const_iterator cbegin() const { return keys_.begin(); }
+
+	iterator end() { return keys_.end(); }
+	const_iterator cend() const { return keys_.end(); }
+
+	bool has( const K& k ) const { return find(k) != cend(); }
+
+	const V& get( iterator it ) const { return values_[ it->idx() ]; }
+	V& get( iterator it ) { return values_[ it->idx() ]; }
+
+	const V& get( const_iterator it ) const { return values_[ it->idx() ]; }
+	V& get( const_iterator it ) { return values_[ it->idx() ]; }
+
+	const V& get( const K& k ) const { return values_[ find(k)->idx() ]; }
+	V& get( const K& k ) { return values_[ find(k)->idx() ]; }
+
+	const V& at( const size_t i ) const { ASSERT(i < keys_.size()); return values_[ keys_[i].idx() ]; }
+	V& at( const size_t i ) { ASSERT(i < keys_.size()); return values_[ i ]; }
+
+	const V& operator[] (const K& k ) const { return values_[ find(k)->idx() ]; }
+	V& operator[] (const K& k ) { return values_[ find(k)->idx() ]; }
+
+	const V& operator[] (const size_t& i ) const { ASSERT(i < values_.size()); return values_[ i ]; }
+	V& operator[] (const size_t& i ) { ASSERT(i < keys_.size()); return values_[ i ]; }
+
+	iterator find( const K& k )
+	{
+		if( !empty() )
+		{
+			ASSERT(sorted());
+			iterator it = std::lower_bound( begin(), end(), k, Compare());
+			if( it != end() && it->key() == k )
+				return it;
+		}
+		return end();
+	}
+
+	const_iterator find( const K& k ) const
+	{
+		if( !empty() )
+		{
+			ASSERT(sorted());
+			const_iterator it = std::lower_bound( cbegin(), cend(), k, Compare());
+			if( it != cend() && it->key() == k )
+				return it;
+		}
+		return cend();
+	}
+
+	void print(std::ostream& s) const
+	{
+		const_iterator it = cbegin();
+		for( ; it != cend(); ++it )
+			s << it->key() << " " << values_[ it->idx() ] << std::endl;
+	}
+
+	friend std::ostream& operator<<(std::ostream& s, const DenseMap& m) { m.print(s);  return s; }
+
+private: // types
+
+	class LessThan {
+	public:
+		bool operator() (const kidx_t& e1, const kidx_t& e2) const
+		{
+			return (e1.key() < e2.key()) ? true : false;
+		}
+	};
+
+	class Compare {
+	public:
+		bool operator() (const kidx_t& e, const K& k) const
+		{
+			return (e.key() < k) ? true : false;
+		}
+		bool operator() (const K& k, const kidx_t& e) const
+		{
+			return (e.key() > k) ? true : false;
+		}
+	};
+
+private: // members
+
+	key_store_t   keys_;   ///< storage of the keys
+	value_store_t values_; ///< storage of the values
+
+	bool sorted_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/KDMapped.cc b/eckit/src/eckit/container/KDMapped.cc
new file mode 100644
index 0000000..0d84c7c
--- /dev/null
+++ b/eckit/src/eckit/container/KDMapped.cc
@@ -0,0 +1,155 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "KDMapped.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/os/Stat.h"
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+
+namespace eckit {
+
+
+KDMapped::KDMapped(const PathName& path, size_t itemCount, size_t itemSize, size_t metadataSize):
+    path_(path),
+    header_(itemCount, itemSize, metadataSize),
+    count_(0),
+    size_(0),
+    base_(0),
+    root_(0),
+    addr_(0),
+    fd_(-1)
+{
+
+    int oflags = O_RDWR|O_CREAT;
+    int mflags = PROT_READ|PROT_WRITE;
+    
+    if(itemCount == 0) {
+        oflags = O_RDWR;
+        //mflags = PROT_READ;
+    }
+
+    size_t base;
+
+    SYSCALL(fd_ = ::open(path.localPath(),oflags,  0777));
+    if(itemCount == 0)
+    {
+
+        Stat::Struct s;
+        SYSCALL(Stat::stat(path.localPath(),&s));
+        size_ = s.st_size;
+
+
+        int n;
+        SYSCALL(n = ::read(fd_,&header_, sizeof(header_)));
+        ASSERT(n == sizeof(header_));
+        lseek(fd_, 0, SEEK_SET);
+
+        root_ = 1;
+
+        ASSERT(header_.headerSize_  == sizeof(header_));
+
+        base = ((header_.headerSize_
+                + header_.metadataSize_
+                + header_.itemSize_ - 1) / header_.itemSize_) * header_.itemSize_;
+
+        std::cout << "Here " << root_ << std::endl;
+
+    }
+    else
+    {
+        base = ((header_.headerSize_
+                + header_.metadataSize_
+                + header_.itemSize_ - 1) / header_.itemSize_) * header_.itemSize_;
+
+        char c = 0;
+
+        count_ = 1;
+        size_  = base + (itemCount + 1) * itemSize;
+
+        lseek(fd_, 0, SEEK_SET);
+        SYSCALL(::write(fd_,&header_,sizeof(header_)));
+
+        lseek(fd_, size_ - 1, SEEK_SET);
+        SYSCALL(::write(fd_,&c,1));
+    }
+    
+    addr_ = ::mmap(0, size_, mflags, MAP_SHARED, fd_, 0 );
+    if(addr_ == MAP_FAILED) {
+        Log::error() << "open(" << path << ')'
+                     << Log::syserr << std::endl;
+        throw FailedSystemCall("mmap");
+    }
+
+    base_ = reinterpret_cast<char*>(addr_) + base;
+}
+
+KDMapped::~KDMapped()
+{
+    if(addr_)
+        SYSCALL(munmap(addr_, size_));
+    if(fd_ >= 0)
+        SYSCALL(close(fd_));
+}
+
+// Warning, takes ownership of maps
+KDMapped::KDMapped(const KDMapped& other):
+    path_(other.path_),
+    header_(other.header_),
+    count_(other.count_),
+    size_(other.size_),
+    base_(other.base_),
+    root_(other.root_),
+    addr_(other.addr_),
+    fd_(other.fd_)
+{
+    const_cast<KDMapped&>(other).addr_ = 0;
+    const_cast<KDMapped&>(other).fd_ = -1;
+}
+
+// Warning, takes ownership of maps
+KDMapped& KDMapped::operator=(const KDMapped& other)
+{
+    path_ = other.path_;
+    count_= other.count_;
+    size_= other.size_;
+    addr_= other.addr_;
+    fd_ = other.fd_;
+    root_ = other.root_;
+
+    header_ = other.header_;
+    base_ = other.base_;
+
+    const_cast<KDMapped&>(other).addr_ = 0;
+    const_cast<KDMapped&>(other).fd_ = -1;
+
+    return *this;
+}
+
+void KDMapped::setMetadata(const void *addr, size_t size) {
+    ASSERT(size == header_.metadataSize_);
+    char *start = static_cast<char*>(addr_);
+    ::memcpy(start + sizeof(header_), addr, size);
+}
+
+void KDMapped::getMetadata(void *addr, size_t size) {
+    ASSERT(size == header_.metadataSize_);
+    char *start = static_cast<char*>(addr_);
+    ::memcpy(addr, start + sizeof(header_), size);
+}
+
+
+} //namespace
diff --git a/eckit/src/eckit/container/KDMapped.h b/eckit/src/eckit/container/KDMapped.h
new file mode 100644
index 0000000..e56894e
--- /dev/null
+++ b/eckit/src/eckit/container/KDMapped.h
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KDMapped_H
+#define KDMapped_H
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/container/StatCollector.h"
+
+
+namespace eckit {
+
+struct KDMappedHeader {
+    size_t headerSize_;
+    size_t itemCount_;
+    size_t itemSize_;
+    size_t metadataSize_;
+
+    KDMappedHeader(size_t itemCount, size_t itemSize, size_t metadataSize):
+        headerSize_(sizeof(KDMappedHeader)),
+        itemCount_(itemCount),
+        itemSize_(itemSize),
+        metadataSize_(metadataSize) {}
+};
+
+
+class KDMapped : public StatCollector {
+public:
+
+
+    KDMapped(const PathName&,  size_t itemCount , size_t itemSize, size_t metadataSize);
+    ~KDMapped();
+
+    KDMapped(const KDMapped& other);
+    KDMapped& operator=(const KDMapped& other);
+
+    typedef size_t Ptr;
+
+    template<class Node>
+    Node* base(const Node*) {
+        ASSERT(sizeof(Node) == header_.itemSize_);
+        return reinterpret_cast<Node*>(base_);
+    }
+
+    Ptr root() const {
+        return root_;
+    }
+
+     void root(Ptr r) {
+         ASSERT(r == 1);
+     }
+
+    template<class Node>
+    Ptr convert(Node* p) {
+        return p ? p - base(p) : 0;
+    }
+
+    template<class Node>
+    Node* convert(Ptr p, const Node* dummy) {
+        Node* r = base(dummy);
+        /* ASSERT(p < count_); */
+        return p ? &r[p] : NULL;
+    }
+
+    template<class Node, class A>
+    Node* newNode1(const A& a, const Node* dummy) {
+        Node* r = base(dummy);
+        ASSERT(count_ > 0);
+        //ASSERT(count_ * sizeof(Node) < size_);
+        return new(&r[count_++]) Node(a);
+    }
+
+    template<class Node, class A, class B>
+    Node* newNode2(const A& a, const B& b, const Node* dummy) {
+        Node* r = base(dummy);
+        ASSERT(count_ > 0);
+        //ASSERT(count_ * sizeof(Node) < size_);
+        return new(&r[count_++]) Node(a, b);
+    }
+
+    template<class Node, class A, class B, class C>
+    Node* newNode3(const A& a, const B& b, const C& c, const Node* dummy) {
+        Node* r = base(dummy);
+        ASSERT(count_ > 0);
+        //ASSERT(count_ * sizeof(Node) < size_);
+        return new(&r[count_++]) Node(a, b, c);
+    }
+
+
+    template<class Node>
+    void deleteNode(Ptr p, Node* n) {
+        // Ignore
+        // TODO: recycle space if needed
+    }
+
+    void setMetadata(const void*, size_t);
+    void getMetadata(void*, size_t);
+
+    template<class Metadata>
+    void setMetadata(const Metadata& meta) {
+        setMetadata(&meta, sizeof(meta));
+    }
+
+    template<class Metadata>
+    void getMetadata(Metadata& meta) {
+        getMetadata(&meta, sizeof(meta));
+    }
+
+private:
+
+    PathName path_;
+
+    KDMappedHeader header_;
+
+    size_t count_;
+
+    long long size_;
+    char* base_;
+    Ptr root_;
+
+    void* addr_;
+    int fd_;
+
+};
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/KDMemory.h b/eckit/src/eckit/container/KDMemory.h
new file mode 100644
index 0000000..20216c0
--- /dev/null
+++ b/eckit/src/eckit/container/KDMemory.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KDMemory_H
+#define KDMemory_H
+
+#include "eckit/eckit.h"
+
+#include <limits>
+#include <cmath>
+
+#include "eckit/container/StatCollector.h"
+
+//------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+
+//------------------------------------------------------------------------------------------------------
+
+struct KDMemory : public StatCollector {
+    typedef void* Ptr;
+
+    Ptr root() const { return 0; }
+    void root(Ptr r) {}
+
+    template<class Node>
+    Ptr convert(Node* p) { return p; }
+
+    template<class Node>
+    Node* convert(Ptr p,  const Node*) { return static_cast<Node*>(p); }
+
+    template<class Node,typename A>
+    Node* newNode1(const A& a, const Node*) { return new Node(a); }
+
+    template<class Node,typename A, typename B>
+    Node* newNode2( const A& a,  const B& b, const Node*) { return new Node(a, b); }
+
+    template<class Node,typename A, typename B, typename C>
+    Node* newNode3( const A& a,  const B& b,  const C& c, const Node*) { return new Node(a, b, c); }
+
+    template<class Node>
+    void deleteNode(Ptr p, const Node*) {
+        Node* n = static_cast<Node*>(p);
+        if(n) {
+            deleteNode(n->left(*this),n);
+            deleteNode(n->right(*this),n);
+            delete n;
+        }
+    }
+};
+
+template<class T, class A>
+struct TT : public T {
+    typedef A Alloc;
+};
+
+//------------------------------------------------------------------------------------------------------
+
+} // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/KDTree.h b/eckit/src/eckit/container/KDTree.h
new file mode 100644
index 0000000..0879c61
--- /dev/null
+++ b/eckit/src/eckit/container/KDTree.h
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KDTree_H
+#define KDTree_H
+
+#include "eckit/container/sptree/SPTree.h"
+#include "eckit/container/kdtree/KDNode.h"
+
+#include "KDMemory.h"
+#include "KDMapped.h"
+
+
+namespace eckit {
+
+template<class Traits>
+class KDTreeX : public SPTree<Traits, KDNode<Traits> > {
+public:
+    typedef KDNode<Traits>         Node;
+    typedef SPTree<Traits,Node>    SPTreeType; // cannot redefine as SPTree since some compilers in-class redefinitions
+    typedef typename Traits::Alloc Alloc;
+
+    typedef typename SPTreeType::Value   Value;
+    typedef typename SPTreeType::Point   Point;
+    typedef typename SPTreeType::Payload Payload;
+
+public:
+    KDTreeX(Alloc& alloc): SPTreeType(alloc) {
+
+    }
+
+    /// ITER must be a random access iterator
+    /// WARNING: container is changed (sorted)
+    template<typename ITER>
+    void build(ITER begin, ITER end)
+    {
+        this->root_ = this->alloc_.convert(Node::build(this->alloc_, begin, end));
+        this->alloc_.root(this->root_);
+    }
+
+    /// Container must be a random access
+    /// WARNING: container is changed (sorted)
+    template<typename Container>
+    void build(Container& c)
+    {
+        typename Container::iterator b = c.begin();
+        typename Container::iterator e = c.end();
+        build(b, e);
+    }
+
+};
+
+
+
+template<class Traits>
+class KDTreeMemory : public KDTreeX< TT<Traits,KDMemory>  > {
+    KDMemory alloc_;
+public:
+
+    typedef KDTreeX< TT<Traits,KDMemory>  > KDTree;
+    typedef typename KDTree::Value   Value;
+    typedef typename KDTree::Point   Point;
+    typedef typename KDTree::Payload Payload;
+
+public:
+    KDTreeMemory() : KDTree(alloc_) {}
+};
+
+template<class Traits>
+class KDTreeMapped : public KDTreeX< TT<Traits,KDMapped> > {
+    KDMapped alloc_;
+public:
+
+    typedef KDTreeX< TT<Traits,KDMapped>  > KDTree;
+    typedef typename KDTree::Value   Value;
+    typedef typename KDTree::Point   Point;
+    typedef typename KDTree::Payload Payload;
+    typedef typename KDTree::Node    Node;
+
+public:
+    KDTreeMapped(const eckit::PathName& path,  size_t itemCount, size_t metadataSize):
+        KDTree(alloc_),
+        alloc_(path, itemCount, sizeof(Node), metadataSize)
+    {
+    }
+
+};
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/MappedArray.cc b/eckit/src/eckit/container/MappedArray.cc
new file mode 100644
index 0000000..06b8bb6
--- /dev/null
+++ b/eckit/src/eckit/container/MappedArray.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/container/MappedArray.h"
+#include "eckit/memory/Padded.h"
+
+#include "eckit/os/Stat.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+MappedArray<T>::MappedArray(const PathName& path, unsigned long size):
+	sem_(path),
+    size_(size)    
+{
+
+	AutoLock<Semaphore> lock(sem_);
+
+    typedef Padded<typename MappedArray<T>::Header,4096> PaddedHeader;
+
+	fd_ = ::open(path.localPath(),O_RDWR | O_CREAT, 0777);
+	if(fd_ < 0)
+	{
+        Log::error() << "open(" << path << ')' << Log::syserr << std::endl;
+        throw FailedSystemCall("open",Here());
+	}
+
+    Stat::Struct s;
+    SYSCALL(Stat::stat(path.localPath(),&s));
+
+    bool initHeader = s.st_size < static_cast<long int>( sizeof(PaddedHeader) );
+
+	off_t length = size_ * sizeof(T) + sizeof(PaddedHeader);
+
+	// Resize if needed
+
+	if(s.st_size != length)
+	{
+        SYSCALL(::ftruncate(fd_,length));
+		char buf1[sizeof(PaddedHeader)]; memset(buf1,0,sizeof(buf1));
+		char buf2[sizeof(T)];            memset(buf2,0,sizeof(buf2));
+	    SYSCALL(write(fd_,buf1,sizeof(buf1)));
+        for(size_t i = 0; i < size_ ; i++)
+			SYSCALL(write(fd_,buf2,sizeof(buf2)));
+	}
+
+	map_ = ::mmap(0,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd_,0);
+    if(map_ == MAP_FAILED)
+    {
+        Log::error() << "MappedArray path=" << path << " size=" << size
+                     << " fails to mmap(0,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd_,0)"
+                     << Log::syserr << std::endl;
+        throw FailedSystemCall("mmap",Here());
+    }
+
+	// If first time in, init header
+
+	if(initHeader)
+		new(map_) PaddedHeader();
+	else
+		((PaddedHeader*)map_)->validate();
+
+	array_ = (T*)(((char*)map_) + sizeof(PaddedHeader));
+
+}
+
+template<class T>
+MappedArray<T>::~MappedArray()
+{
+	// Unmap here...
+}
+
+template<class T>
+void MappedArray<T>::sync()
+{
+	int ret = fsync(fd_);
+	while(ret < 0 && errno == EINTR)
+		ret = fsync(fd_);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/container/MappedArray.h b/eckit/src/eckit/container/MappedArray.h
new file mode 100644
index 0000000..d333c5e
--- /dev/null
+++ b/eckit/src/eckit/container/MappedArray.h
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MappedArray.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_MappedArray_h
+#define eckit_MappedArray_h
+
+#include <stdint.h>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/os/Semaphore.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Used to std::map an array to a file 
+
+template<class T>
+class MappedArray : private NonCopyable {
+public:
+
+	// stl compatibility
+
+	typedef T*       iterator;
+	typedef const T* const_iterator;
+
+// -- Contructors
+
+	MappedArray(const PathName&,unsigned long);
+
+// -- Destructor
+
+	~MappedArray(); 
+
+// -- Methods
+
+	void sync();
+	void lock()    { sem_.lock() ; }
+	void unlock()  { sem_.unlock();}
+
+	// stl compatibility
+
+	iterator begin()               { return array_;        }
+	iterator end()                 { return array_ + size_; }
+	const_iterator begin() const   { return array_;        }
+	const_iterator end()   const   { return array_ + size_; }
+
+	unsigned long size()           { return size_;     }
+	T& operator[](unsigned long n) { return array_[n]; }
+
+private: // members
+
+	Semaphore     sem_;
+	void*         map_;
+	int           fd_;
+
+	T*            array_;
+	unsigned long size_;
+
+    static unsigned long mapped_array_version() { return 1; }
+
+    struct Header {
+        uint32_t version_;
+        uint32_t headerSize_;
+        uint32_t elemSize_;
+        Header():
+            version_(mapped_array_version()),
+            headerSize_(sizeof(Header)),
+            elemSize_(sizeof(T))
+        {}
+        void validate()
+        {
+            ASSERT(version_    == mapped_array_version());
+            ASSERT(headerSize_ == sizeof(Header));
+            ASSERT(elemSize_   == sizeof(T));
+        }
+    };
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "MappedArray.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/Recycler.h b/eckit/src/eckit/container/Recycler.h
new file mode 100644
index 0000000..7e47f96
--- /dev/null
+++ b/eckit/src/eckit/container/Recycler.h
@@ -0,0 +1,253 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Recycler.h
+// Baudouin Raoult - ECMWF Apr 97
+
+#ifndef eckit_Recycler_h
+#define eckit_Recycler_h
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/container/Recycler.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class Recycler {
+public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    Recycler(const PathName&);
+
+
+// -- Destructor
+
+    ~Recycler();
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+
+    void lock();
+    void unlock();
+
+
+    void push(const T&);
+
+    template<class Iter>
+    void push(Iter begin, Iter end);
+
+    bool pop(T& value);
+
+
+    template<class Iter>
+    Ordinal pop(Iter begin, Ordinal count);
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+
+protected:
+
+// -- Members
+    // None
+
+// -- Methods
+
+    void print(std::ostream&) const;
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+private:
+
+// No copy allowed
+
+    Recycler(const Recycler<T>&);
+    Recycler<T>& operator=(const Recycler<T>&);
+
+// -- Members
+    // None
+
+
+    PathName path_;
+    int fd_;
+
+// -- Methods
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s,const Recycler<T>& p)
+    {
+        p.print(s);
+        return s;
+    }
+
+};
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+Recycler<T>::Recycler(const PathName& path):
+        path_(path),
+        fd_(-1)
+{
+    path_.dirName().mkdir();
+    fd_ = ::open(path_.localPath(),O_RDWR|O_CREAT,0777);
+    if(fd_ < 0) {
+        throw CantOpenFile(path_);
+    }
+}
+
+template<class T>
+Recycler<T>::~Recycler()
+{
+    if (fd_ >= 0)
+        SYSCALL(::close(fd_));
+}
+
+
+template<class T>
+void Recycler<T>::lock()
+{
+
+    struct flock lock;
+
+    lock.l_type   = F_WRLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start  = 0;
+    lock.l_len    = 0;
+
+    SYSCALL(::fcntl(fd_, F_SETLK, &lock));
+
+}
+
+template<class T>
+void Recycler<T>::unlock()
+{
+
+    struct flock lock;
+
+    lock.l_type   = F_UNLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start  = 0;
+    lock.l_len    = 0;
+
+    SYSCALL(::fcntl(fd_, F_SETLK, &lock));
+
+}
+
+
+template<class T>
+template<class Iter>
+void Recycler<T>::push(Iter begin, Iter end)
+{
+
+    AutoLock<Recycler<T> > lock(this);
+
+    off_t here;
+    SYSCALL(here = ::lseek(fd_,0,SEEK_END));
+    ASSERT((here % sizeof(T)) == 0);
+
+    for (Iter j = begin; j != end; ++j)
+        ASSERT(::write(fd_, &(*j), sizeof(T)) == sizeof(T));
+}
+
+template<class T>
+template<class Iter>
+Ordinal Recycler<T>::pop(Iter begin, Ordinal count)
+{
+
+    AutoLock<Recycler<T> > lock(this);
+
+    off_t here, there;
+    SYSCALL(here = ::lseek(fd_,0,SEEK_END));
+    ASSERT((here % sizeof(T)) == 0);
+
+    Ordinal cnt = std::min(Ordinal(here/sizeof(T)), count);
+
+    here -= cnt * sizeof(T);
+
+    SYSCALL(there = ::lseek(fd_, here ,SEEK_SET));
+    ASSERT(there == here);
+
+
+    for (Ordinal i = 0; i < cnt ; i++)
+    {
+        T value;
+        ASSERT(::read(fd_, &value, sizeof(T)) == sizeof(T));
+        *(begin++) = value;
+    }
+
+    SYSCALL(::ftruncate(fd_, here));
+
+    return cnt;
+}
+
+template<class T>
+bool Recycler<T>::pop(T& value)
+{
+    return pop(&value,1) == 1;
+}
+
+template<class T>
+void Recycler<T>::push(const T& value)
+{
+    const T* p = &value;
+    push(p,p+1);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/container/SharedMemArray.cc b/eckit/src/eckit/container/SharedMemArray.cc
new file mode 100644
index 0000000..fb160b5
--- /dev/null
+++ b/eckit/src/eckit/container/SharedMemArray.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+SharedMemArray<T>::SharedMemArray(const PathName& path, const std::string& shmName, size_t size):
+	sem_(path),
+    size_(size),
+    shmName_(shmName)
+{
+    eckit::Log::debug<LibEcKit>() << "SharedMemArray semaphore path=" << path << ", size=" << size << ", shmName=" << shmName << std::endl;
+
+	AutoLock<Semaphore> lock(sem_);
+
+    typedef Padded<typename SharedMemArray<T>::Header,4096> PaddedHeader;
+
+    fd_ = ::shm_open(shmName_.c_str(), O_RDWR | O_CREAT, 0777);
+	if(fd_ < 0)
+	{
+        Log::error() << "shm_open(" << shmName_ << ')' << Log::syserr << std::endl;
+        throw FailedSystemCall("shm_open",Here());
+	}
+
+    Stat::Struct s;
+    SYSCALL(Stat::fstat(fd_, &s));
+
+	off_t length = size_ * sizeof(T) + sizeof(PaddedHeader);
+
+    eckit::Log::debug<LibEcKit>() << "SharedMemArray fd_=" << fd_ << ", s.st_size=" << s.st_size << ", length=" << length << std::endl;
+
+	// Resize if needed
+
+    bool zero = false;
+    if(length > s.st_size )
+	{
+        SYSCALL(::ftruncate(fd_,length));
+        zero = true;
+	}
+
+    map_ = ::mmap(0, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd_, 0);
+    if(map_ == MAP_FAILED)
+    {
+        Log::error() << "SharedMemArray name=" << shmName_ << " size=" << size
+                     << " fails to mmap(0,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd_,0)"
+                     << Log::syserr << std::endl;
+        throw FailedSystemCall("mmap",Here());
+    }
+
+    if(zero) {
+        ::memset(map_, 0, sizeof(PaddedHeader) + size_*sizeof(T));
+        new(map_) PaddedHeader();
+    }
+    else {
+		((PaddedHeader*)map_)->validate();
+    }
+
+	array_ = (T*)(((char*)map_) + sizeof(PaddedHeader));
+
+}
+
+template<class T>
+SharedMemArray<T>::~SharedMemArray()
+{
+	// Unmap here...
+}
+
+template<class T>
+void SharedMemArray<T>::sync()
+{
+//	int ret = fsync(fd_);
+//	while(ret < 0 && errno == EINTR)
+//		ret = fsync(fd_);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/container/SharedMemArray.h b/eckit/src/eckit/container/SharedMemArray.h
new file mode 100644
index 0000000..16d5639
--- /dev/null
+++ b/eckit/src/eckit/container/SharedMemArray.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   December 2016
+
+#ifndef eckit_SharedMemArray_h
+#define eckit_SharedMemArray_h
+
+#include <stdint.h>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/os/Semaphore.h"
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/memory/Padded.h"
+
+#include "eckit/os/Stat.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/LibEcKit.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Maps an array to shared memory
+
+template<class T>
+class SharedMemArray : private NonCopyable {
+
+public: // types
+
+	typedef T*       iterator;
+	typedef const T* const_iterator;
+
+public: // methods
+
+    SharedMemArray(const PathName&, const std::string& shmName, size_t);
+
+	~SharedMemArray(); 
+
+	void sync();
+	void lock()    { sem_.lock() ; }
+	void unlock()  { sem_.unlock();}
+
+	iterator begin()               { return array_;        }
+	iterator end()                 { return array_ + size_; }
+
+    const_iterator begin() const   { return array_;        }
+	const_iterator end()   const   { return array_ + size_; }
+
+	unsigned long size()           { return size_;     }
+	T& operator[](unsigned long n) { return array_[n]; }
+
+private: // members
+
+	Semaphore     sem_;
+	void*         map_;
+	int           fd_;
+
+	T*            array_;
+    size_t        size_;
+
+    std::string shmName_;
+
+    static unsigned long shared_mem_array_version() { return 1; }
+
+    struct Header {
+        uint32_t version_;
+        uint32_t headerSize_;
+        uint32_t elemSize_;
+        Header():
+            version_(shared_mem_array_version()),
+            headerSize_(sizeof(Header)),
+            elemSize_(sizeof(T))
+        {}
+        void validate()
+        {
+            ASSERT(version_    == shared_mem_array_version());
+            ASSERT(headerSize_ == sizeof(Header));
+            ASSERT(elemSize_   == sizeof(T));
+        }
+    };
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "SharedMemArray.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/StatCollector.h b/eckit/src/eckit/container/StatCollector.h
new file mode 100644
index 0000000..59d3d1f
--- /dev/null
+++ b/eckit/src/eckit/container/StatCollector.h
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StatCollector.h
+// Baudouin Raoult - ECMWF Apr 97
+
+#ifndef eckit_StatCollector_h
+#define eckit_StatCollector_h
+
+
+#include "eckit/eckit.h"
+#include "eckit/log/BigNum.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+struct StatCollector {
+
+    StatCollector() { statsReset(); depth_ = 0; }
+
+
+    // -- Methods
+
+    void statsCall() { calls_++; }
+    void statsVisitNode() { nodes_++; }
+    void statsDepth(size_t d) { if(d>depth_) depth_ = d; }
+
+    void statsNewCandidateOK()   { newCandidateOK_++; }
+    void statsNewCandidateMiss() { newCandidateMiss_++; }
+    void statsCrossOver()        { crossOvers_++; }
+    void statsReset() { crossOvers_ = calls_ = newCandidateOK_ = newCandidateMiss_ = nodes_ = 0; }
+
+    void print(std::ostream& s) const {
+        s << "Stats calls: " << BigNum(calls_)
+          << " avg candidates: " << BigNum(double(newCandidateMiss_ + newCandidateOK_)/double(calls_) + 0.5)
+          << ", avg nodes: " << BigNum(double(nodes_)/double(calls_) + 0.5)
+          <<", depth: " << depth_
+            ;
+    }
+
+    void statsPrint(std::ostream& s, bool fancy) const {
+        if(fancy) {
+            s << *this << std::endl;
+        }
+        else {
+            s << "   calls: " << BigNum(calls_) << std::endl;
+            s << "   miss: "  << BigNum(newCandidateMiss_) << std::endl;
+            s << "   hit: "   << BigNum(newCandidateOK_) << std::endl;
+            s << "   nodes: " << BigNum(nodes_) << std::endl;
+            s << "   depth: " << BigNum(depth_)  << std::endl;
+            s << "   crossovers: " << BigNum(crossOvers_)  << std::endl;
+        }
+    }
+
+    // -- Members
+
+    size_t calls_;
+    size_t nodes_;
+    size_t depth_;
+
+    size_t newCandidateMiss_;
+    size_t newCandidateOK_;
+    size_t crossOvers_;
+
+
+    // -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s,const StatCollector& p)
+    {
+        p.print(s);
+        return s;
+    }
+
+};
+
+//-----------------------------------------------------------------------------
+//
+} // Namespace
+
+#endif
diff --git a/eckit/src/eckit/container/bsptree/BSPHyperPlane.h b/eckit/src/eckit/container/bsptree/BSPHyperPlane.h
new file mode 100644
index 0000000..52996bc
--- /dev/null
+++ b/eckit/src/eckit/container/bsptree/BSPHyperPlane.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef BSPHyperPlane_H
+#define BSPHyperPlane_H
+
+
+namespace eckit {
+
+
+template<class Point>
+class BSPHyperPlane {
+
+    Point normal_;
+    double d_;
+public:
+
+    BSPHyperPlane():
+        normal_(), d_() {}
+
+    BSPHyperPlane(const Point& normal, const Point& point):
+        normal_(Point::normalize(normal)), d_(-Point::dot(normal_, point)) {}
+
+    double position(const Point& p) const {
+        return Point::dot(p, normal_) + d_;
+    }
+
+    const Point& normal() const { return normal_; }
+    double d() const { return d_; }
+};
+
+
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/bsptree/BSPNode.cc b/eckit/src/eckit/container/bsptree/BSPNode.cc
new file mode 100644
index 0000000..da45e11
--- /dev/null
+++ b/eckit/src/eckit/container/bsptree/BSPNode.cc
@@ -0,0 +1,252 @@
+/*
+ * (C) Copythis->right 1996-2013 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef BSPNode_CC
+#define BSPNode_CC
+
+#include <random>
+#include "eckit/eckit.h"
+
+#include <stdio.h>
+#include <limits>
+#include <random>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+
+namespace eckit {
+// The hyperplane is define by the vector (l, r) passing through the middle point
+
+template<class Traits,class Partition>
+BSPNode<Traits,Partition>::BSPNode(const Value& v, const HyperPlane& plane, double dist):
+    SPNodeType(v),
+    plane_(plane),
+    dist_(dist)
+{
+}
+
+template<class Traits,class Partition>
+void BSPNode<Traits,Partition>::nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth)
+{
+    a.statsVisitNode();
+
+    if(this->left_ && this->right_) {
+        // Check in which half the point lies
+
+        double d = plane_.position(p);
+
+        // See if we need to visit both
+
+        double distanceToPlane = fabs(d);
+
+
+        // distanceToPlane = 0;
+
+
+        if(d <= 0) {
+            this->left(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            double dd = this->right(a)->dist_;
+            if(distanceToPlane + dd <= max) {
+                a.statsCrossOver();
+                this->right(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            }
+        }
+        else {
+
+            this->right(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            double dd = this->left(a)->dist_;
+            if(distanceToPlane + dd <= max) {
+                a.statsCrossOver();
+                this->left(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            }
+        }
+
+    }
+    else
+    {
+        if(this->left_) {
+            this->left(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            return;
+        }
+
+        if(this->right_) {
+            this->right(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            return;
+        }
+
+        ASSERT(!this->left_ || !this->right_);
+
+        double d   = Point::distance(p, this->value_.point());
+
+        if(d < max) {
+            max = d;
+            best = this;
+            a.statsNewCandidateOK();
+        }
+        else {
+            a.statsNewCandidateMiss();
+        }
+
+    }
+
+}
+
+//===
+
+template<class Traits,class Partition>
+void BSPNode<Traits,Partition>::kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth)
+{
+    if(this->left_ && this->right_) {
+        // Check in which half the point lies
+
+        double d =  plane_.position(p);
+
+        // See if we need to visit both
+
+        double distanceToPlane = fabs(d);
+        double max = result.largest();
+
+
+        if(d <= 0) {
+            this->left(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+            double dd = this->right(a)->dist_;
+            if(result.incomplete() || distanceToPlane + dd <= max) {
+                a.statsCrossOver();
+                this->right(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+            }
+        }
+        else {
+
+            this->right(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+            double dd = this->left(a)->dist_;
+            if(result.incomplete() || distanceToPlane + dd <= max) {
+                a.statsCrossOver();
+                this->left(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+            }
+        }
+
+        return;
+
+    }
+
+
+    if(this->left_) {
+        this->left(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+        return;
+
+    }
+
+    if(this->right_) {
+        this->right(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+        return;
+
+    }
+
+    // This is a leaf
+    double d   = Point::distance(p, this->value_.point());
+    result.push(this, a.convert(this), d);
+
+}
+
+
+
+template<class Traits,class Partition>
+template<typename Container>
+double BSPNode<Traits,Partition>::distanceToPlane(const Container& in, const HyperPlane& plane)
+{
+    double min = std::numeric_limits<double>::max();
+    for(typename Container::const_iterator j = in.begin(); j != in.end(); ++j)
+    {
+        const Point& p = (*j).point();
+        // Find the closest value to the partitionning plan
+        double dist = fabs(plane.position(p));
+
+        if(dist < min) {
+            min = dist;
+        }
+    }
+
+    return min;
+
+}
+
+
+template<class Traits,class Partition>
+template<typename Container>
+BSPNode<Traits,Partition>* BSPNode<Traits,Partition>::build(Alloc& a, Partition& p, const Container& nodes,
+                                                                      double dist, int depth)
+{
+    HyperPlane plane;
+
+    if(nodes.size() == 0)
+        return 0;
+
+    a.statsDepth(depth);
+
+    if(nodes.size() == 1) {
+        return a.newNode3(nodes[0], plane, dist,(BSPNode*)0);
+    }
+
+
+    Container  left;
+    Container  right;
+
+
+    p(nodes, left, right, plane, depth);
+
+    if(left.size() == 0 || right.size() == 0) {
+        ASSERT(left.size() == 1 || right.size() == 1 );
+        if(left.size() == 1) {
+            return a.newNode3(left[0], plane, dist, (BSPNode*)0);
+        }
+        else
+        {
+            return a.newNode3(right[0], plane, dist, (BSPNode*)0);
+        }
+    }
+    ASSERT(left.size() < nodes.size());
+    ASSERT(right.size() < nodes.size());
+    ASSERT(right.size() + left.size() == nodes.size());
+
+
+    BSPNode* n = a.newNode3(nodes[0], plane, dist, (BSPNode*)0);
+
+
+    double dl = distanceToPlane(left, plane);
+    double dr = distanceToPlane(right, plane);
+
+    //if(depth == 1) {
+        //std::cerr << Partition::name() << " distanceToPlane " << dl << " " << dr << std::endl;
+    //}
+
+    n->left(a, build(a, p, left, dl, depth + 1));
+    n->right(a, build(a, p, right, dr, depth + 1));
+
+    return n;
+
+}
+
+template<class Traits,class Partition>
+void BSPNode<Traits,Partition>::findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth)
+{
+    NOTIMP;
+}
+
+
+
+} //namespace
+
+#endif
diff --git a/eckit/src/eckit/container/bsptree/BSPNode.h b/eckit/src/eckit/container/bsptree/BSPNode.h
new file mode 100644
index 0000000..a6d9c54
--- /dev/null
+++ b/eckit/src/eckit/container/bsptree/BSPNode.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef BSPNode_H
+#define BSPNode_H
+
+#include "BSPHyperPlane.h"
+
+namespace eckit {
+
+template<class Traits, class Partition>
+class BSPNode : public SPNode<Traits, BSPNode<Traits, Partition> > {
+public:
+
+
+    typedef SPNode<Traits, BSPNode<Traits, Partition> > SPNodeType;
+    typedef typename SPNodeType::Value     Value;
+    typedef typename SPNodeType::Alloc     Alloc;
+    typedef typename SPNodeType::Point     Point;
+    typedef typename SPNodeType::NodeList  NodeList;
+    typedef typename SPNodeType::NodeQueue NodeQueue;
+    typedef typename SPNodeType::NodeInfo  NodeInfo;
+
+    typedef BSPHyperPlane<Point>        HyperPlane;
+
+    typedef BSPNode<Traits, Partition> Node;
+
+private:
+
+    HyperPlane plane_;
+
+    double dist_; // Distance to parent's hyperplane
+
+public:
+
+    BSPNode(const Value& v, const HyperPlane& plane, double dist);
+    virtual ~BSPNode() {}
+
+    template<typename Container>
+    static BSPNode* build(Alloc& a, Partition& p, const Container& nodes, double dist, int depth = 0);
+
+
+private:
+    virtual void nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth);
+    virtual void findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth) ;
+    virtual void kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth) ;
+
+    //==========================
+
+    template<typename Container>
+    static double distanceToPlane(const Container& in, const HyperPlane& plane);
+
+};
+
+} // end namespace
+
+#include "BSPNode.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/kdtree/KDNode.cc b/eckit/src/eckit/container/kdtree/KDNode.cc
new file mode 100644
index 0000000..874cc58
--- /dev/null
+++ b/eckit/src/eckit/container/kdtree/KDNode.cc
@@ -0,0 +1,222 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KDNode_CC
+#define KDNode_CC
+#include "eckit/eckit.h"
+
+#include <stdio.h>
+#include <limits>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+#include "KDNode.h"
+
+namespace eckit {
+
+
+
+template<class Traits>
+KDNode<Traits>::KDNode(const Value& value, size_t axis):
+    SPNodeType(value),
+    axis_(axis)
+{
+}
+
+
+template<class Traits>
+void KDNode<Traits>::nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth)
+{
+    a.statsVisitNode();
+
+    bool left_visited = false;
+    bool right_visited = false;
+
+    if(p.x(axis_) < this->value_.point().x(axis_))
+    {
+        if(this->left_) {
+            this->left(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            left_visited = true;
+        }
+    }
+    else
+    {
+        if(this->right_) {
+            this->right(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            right_visited = true;
+        }
+    }
+
+    double d   = Point::distance(p, this->value_.point());
+
+    if(d < max) {
+        max = d;
+        best = this;
+        a.statsNewCandidateOK();
+    }
+    else
+    {
+        a.statsNewCandidateMiss();
+    }
+
+    d = Point::distance(p, this->value_.point(), axis_);
+
+    if(d < max)
+    {
+
+        // Visit other subtree...
+        a.statsCrossOver();
+
+        if(p.x(axis_) < this->value_.point().x(axis_))
+        {
+            if(this->right_ && !right_visited) {
+                this->right(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            }
+
+        }
+        else {
+
+            if(this->left_ && !left_visited) {
+                this->left(a)->nearestNeighbourX(a, p, best, max, depth+1);
+            }
+        }
+    }
+
+}
+
+
+
+//===
+
+template<class Traits>
+void KDNode<Traits>::kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth)
+{
+    if(p.x(axis_) < this->value_.point().x(axis_))
+    {
+        if(this->left_) this->left(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+    }
+    else
+    {
+        if(this->right_) this->right(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+    }
+
+    double d   = Point::distance(p, this->value_.point());
+    Node* self = this;
+    result.push(self, a.convert(self), d);
+
+    if(Point::distance(p, this->value_.point(), axis_) <= result.largest())
+    {
+
+        // Visit other subtree...
+        a.statsCrossOver();
+
+        if(p.x(axis_) < this->value_.point().x(axis_))
+        {
+            if(this->right_) this->right(a)->kNearestNeighboursX(a, p,k, result, depth+1);
+
+        }
+        else {
+
+            if(this->left_) this->left(a)->kNearestNeighboursX(a, p, k, result, depth+1);
+        }
+    }
+}
+
+
+
+template<class Value>
+struct sorter {
+    int axis_;
+    bool operator() (const Value& a,const Value& b)
+    { return (a.point().x(axis_) < b.point().x(axis_)); }
+    sorter(size_t axis) : axis_(axis) {}
+};
+
+
+template<class Traits>
+template<typename ITER>
+KDNode<Traits>* KDNode<Traits>::build(Alloc& a,
+                                      const ITER& begin,
+                                      const ITER& end, int depth)
+{
+    if(end == begin)
+        return 0;
+
+    a.statsDepth(depth);
+
+    //size_t k    = Point::size(*begin);
+    size_t k    = Point::DIMS;
+    size_t axis = depth % k;
+
+    //std::sort(begin, end, sorter<Point>(axis));
+
+    size_t median = (end - begin)/2;
+
+    std::nth_element(begin, begin + median, end, sorter<Value>(axis));
+
+    ITER e2 = begin + median;
+    ITER b2 = begin + median+1;
+
+
+    KDNode* n = a.newNode2(*e2, axis,(KDNode*)0);
+
+    n->left(a,build(a, begin, e2, depth + 1));
+    n->right(a,build(a, b2,   end, depth + 1));
+
+    return n;
+
+}
+
+template<class Traits>
+void KDNode<Traits>::findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth)
+{
+    if(p.x(axis_) < this->value_.point().x(axis_))
+    {
+        if(this->left_) this->left(a)->findInSphereX(a, p, radius, result, depth+1);
+    }
+    else
+    {
+        if(this->right_) this->right(a)->findInSphereX(a, p, radius, result, depth+1);
+    }
+
+    double d   = Point::distance(p, this->value_.point());
+    if(d <= radius) {
+        result.push_back(NodeInfo(this, a.convert(this) ,d));
+    }
+
+
+    if(Point::distance(p, this->value_.point(), axis_) <= radius)
+    {
+
+        // Visit other subtree...
+
+        if(p.x(axis_) < this->value_.point().x(axis_))
+        {
+            if(this->right_) this->right(a)->findInSphereX(a, p,radius, result, depth+1);
+
+        }
+        else {
+
+            if(this->left_) this->left(a)->findInSphereX(a, p, radius, result, depth+1);
+        }
+    }
+}
+
+
+
+
+
+} //nameKDace
+
+#endif
diff --git a/eckit/src/eckit/container/kdtree/KDNode.h b/eckit/src/eckit/container/kdtree/KDNode.h
new file mode 100644
index 0000000..0ed9879
--- /dev/null
+++ b/eckit/src/eckit/container/kdtree/KDNode.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KDNode_H
+#define KDNode_H
+
+#include "eckit/container/sptree/SPNode.h"
+
+namespace eckit {
+
+
+template<class Traits>
+class KDNode : public SPNode<Traits, KDNode<Traits> > {
+public:
+
+    typedef SPNode<Traits, KDNode<Traits> >  SPNodeType; // cannot redefine as SPNode since some compilers in-class redefinitions
+
+    typedef typename SPNodeType::Value     Value;
+    typedef typename SPNodeType::Alloc     Alloc;
+    typedef typename SPNodeType::Point     Point;
+    typedef typename SPNodeType::NodeList  NodeList;
+    typedef typename SPNodeType::NodeQueue NodeQueue;
+    typedef typename SPNodeType::NodeInfo  NodeInfo;
+
+
+    typedef KDNode<Traits> Node;
+
+private:
+
+    size_t    axis_;
+
+public:
+    KDNode(const Value& value, size_t axis);
+    ~KDNode() {}
+
+    template<typename ITER>
+    static KDNode* build(Alloc& a,const ITER& begin, const ITER& end, int depth = 0);
+
+
+public:
+    void nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth);
+    void findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth) ;
+    void kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth) ;
+
+    //==========================
+
+
+};
+
+
+} // end namespace
+
+#include "KDNode.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPIterator.h b/eckit/src/eckit/container/sptree/SPIterator.h
new file mode 100644
index 0000000..51e955d
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPIterator.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPIterator_H
+#define SPIterator_H
+
+
+
+namespace eckit {
+
+template<class Traits,class NodeType>
+class SPIterator {
+
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+    typedef typename Traits::Alloc   Alloc;
+
+    typedef SPValue<Traits>             Value;
+
+    typedef typename Alloc::Ptr Ptr;
+    typedef typename Alloc::Ptr ID;
+    typedef NodeType Node;
+
+    Alloc& alloc_;
+    Ptr   ptr_;
+
+
+public:
+    SPIterator(Alloc& alloc, Ptr ptr):
+        alloc_(alloc), ptr_(ptr) {
+        //std::cout << "SPIterator " << ptr << std::endl;
+        Node* node = alloc_.convert(ptr_,(Node*)0);
+        if(node) {
+            if(!node->next(alloc_)) {
+                Node* prev = 0;
+                node->linkNodes(alloc, prev);
+            }
+        }
+    }
+
+    bool operator !=(const SPIterator& other)
+    { return ptr_ != other.ptr_; }
+
+    operator Value*() {
+        Node* n = alloc_.convert(ptr_,(Node*)0);
+        return &(n->value());
+    }
+
+    Value* operator->() {
+        Node* n = alloc_.convert(ptr_,(Node*)0);
+        return &(n->value());
+    }
+
+    SPIterator& operator++() {
+        ptr_ = alloc_.convert(ptr_,(Node*)0)->next_;
+        return *this;
+    }
+
+    ID nodeID() const {
+        return ptr_;
+    }
+};
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPMetadata.h b/eckit/src/eckit/container/sptree/SPMetadata.h
new file mode 100644
index 0000000..44587a7
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPMetadata.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPMetadata_H
+#define SPMetadata_H
+
+
+namespace eckit {
+
+template<class Traits>
+struct SPMetadata {
+
+    typedef typename Traits::Point   Point;
+
+    Point  offset_;
+    Point  scale_;
+};
+
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPNode.cc b/eckit/src/eckit/container/sptree/SPNode.cc
new file mode 100644
index 0000000..e6b85ec
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPNode.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPNode_CC
+#define SPNode_CC
+
+#include <stdio.h>
+#include <limits>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace eckit {
+
+template<class Traits, class NodeType>
+SPNode<Traits,NodeType>::SPNode(const Value& value):
+    value_(value),
+    left_(0),
+    right_(0),
+    next_(0)
+{
+}
+
+template<class Traits, class NodeType>
+SPNodeInfo<Traits,NodeType> SPNode<Traits,NodeType>::nearestNeighbour(Alloc& a,const Point& p)
+{
+    double max = Point::distance(p,value_.point());
+    Node* best = this->asNode();
+    asNode()->nearestNeighbourX(a, p, best, max, 0);
+    return NodeInfo(best, a.convert(best), max);
+}
+
+
+
+
+template<class Traits, class NodeType>
+SPNodeInfo<Traits,NodeType> SPNode<Traits,NodeType>::nearestNeighbourBruteForce(Alloc& a,const Point& p)
+{
+    double max = Point::distance(p,value_.point());
+    Node* best = this->asNode();
+    asNode()->nearestNeighbourBruteForceX(a, p, best, max, 0);
+    return NodeInfo(best, a.convert(best), max);
+}
+
+
+template<class Traits, class NodeType>
+void SPNode<Traits,NodeType>::nearestNeighbourBruteForceX(Alloc& a,const Point& p, Node*& best, double& max, int depth)
+{
+    double d = Point::distance(p,value_.point());
+    if(d < max)
+    {
+        best = this->asNode();
+        max  = d;
+    }
+
+    if(left_)  left(a)->nearestNeighbourBruteForceX(a, p, best, max, depth+1);
+    if(right_) right(a)->nearestNeighbourBruteForceX(a, p, best, max, depth+1);
+}
+
+//===
+
+
+
+template<class Traits, class NodeType>
+typename SPNode<Traits,NodeType>::NodeList SPNode<Traits,NodeType>::kNearestNeighbours(Alloc& a,const Point& p, size_t k)
+{
+    NodeQueue queue(k);
+    NodeList result;
+    asNode()->kNearestNeighboursX(a,p,k,queue,0);
+    queue.fill(result);
+    return result;
+}
+
+template<class Traits, class NodeType>
+void SPNode<Traits,NodeType>::kNearestNeighboursBruteForceX(Alloc& a,const Point& p, size_t k, NodeQueue& result, int depth)
+{
+    double d = Point::distance(p,value_.point());
+    result.push(this->asNode(), a.convert(this->asNode()), d);
+    if(left_)  left(a)->kNearestNeighboursBruteForceX(a, p, k, result, depth+1);
+    if(right_) right(a)->kNearestNeighboursBruteForceX(a, p, k, result, depth+1);
+}
+
+template<class Traits,class NodeType>
+template<class Visitor>
+void SPNode<Traits,NodeType>::visit(Alloc& a,Visitor& v,int depth)
+{
+    v.enter(value_.point(), !left_ && !right_, depth);
+    if(left_)  left(a)->visit(a, v, depth+1);
+    if(right_) right(a)->visit(a, v, depth+1);
+    v.leave(value_.point(),!left_ && !right_, depth);
+}
+
+
+template<class Traits,class NodeType>
+void SPNode<Traits,NodeType>::linkNodes(Alloc& a, Node*& prev)
+{
+    if(prev) {
+        prev->next(a, this->asNode());
+    }
+    prev = this->asNode();
+    if(left_)  left(a)->linkNodes(a,  prev);
+    if(right_) right(a)->linkNodes(a, prev);
+}
+
+template<class Traits,class NodeType>
+typename SPNode<Traits,NodeType>::NodeList SPNode<Traits,NodeType>::kNearestNeighboursBruteForce(Alloc& a,const Point& p, size_t k)
+{
+    NodeQueue queue(k);
+    NodeList result;
+    asNode()->kNearestNeighboursBruteForceX(a,p,k,queue,0);
+    queue.fill(result);
+    return result;
+}
+//===
+
+template<class Traits,class NodeType>
+typename SPNode<Traits,NodeType>::NodeList SPNode<Traits,NodeType>::findInSphere(Alloc& a,const Point& p, double radius)
+{
+    NodeList result;
+    asNode()->findInSphereX(a,p,radius,result,0);
+    std::sort(result.begin(), result.end());
+    return result;
+}
+
+template<class Traits,class NodeType>
+void SPNode<Traits,NodeType>::findInSphereBruteForceX(Alloc& a,const Point& p, double radius, NodeList& result, int depth)
+{
+    double d = Point::distance(p,value_.point());
+    if(d <= radius) {
+        result.push_back(NodeInfo(this->asNode(), a.convert(this->asNode()), d));
+    }
+    if(left_)  left(a)->findInSphereBruteForceX(a, p, radius, result, depth+1);
+    if(right_) right(a)->findInSphereBruteForceX(a, p, radius, result, depth+1);
+}
+
+template<class Traits,class NodeType>
+typename SPNode<Traits,NodeType>::NodeList SPNode<Traits,NodeType>::findInSphereBruteForce(Alloc& a,const Point& p, double radius)
+{
+    NodeList result;
+    asNode()->findInSphereBruteForceX(a, p, radius, result, 0);
+    std::sort(result.begin(), result.end());
+    return result;
+}
+
+
+} //namespace
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPNode.h b/eckit/src/eckit/container/sptree/SPNode.h
new file mode 100644
index 0000000..6c5107b
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPNode.h
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPNode_H
+#define SPNode_H
+
+#include "eckit/eckit.h"
+
+#include "eckit/container/sptree/SPNodeInfo.h"
+#include "eckit/container/sptree/SPNodeQueue.h"
+#include "eckit/container/sptree/SPValue.h"
+
+namespace eckit {
+
+
+template<class Traits, class NodeType>
+class SPNode {
+public:
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+    typedef typename Traits::Alloc   Alloc;
+
+    typedef SPNodeInfo<Traits, NodeType>          NodeInfo;
+    typedef typename NodeInfo::NodeList NodeList;
+    typedef SPValue<Traits>             Value;
+    typedef SPNodeQueue<Traits, NodeType>         NodeQueue;
+
+    typedef NodeType Node;
+
+protected:
+
+    Value     value_;
+
+    typedef typename Alloc::Ptr Ptr;
+    Ptr left_;
+    Ptr right_;
+    Ptr next_; // For fast transversal
+
+    friend struct SPMemory;
+
+
+public:
+    SPNode(const Value& value);
+    ~SPNode() {}
+
+    NodeInfo nearestNeighbour(Alloc& a,const Point& p);
+    NodeList findInSphere(Alloc& a,const Point& p, double radius);
+    NodeList kNearestNeighbours(Alloc& a,const Point& p,size_t k);
+
+    const Point& point() const     { return value_.point();   }
+    const Payload& payload() const { return value_.payload(); }
+    Value& value()                 { return value_;           }
+    const Value& value() const     { return value_;           }
+
+    template<typename ITER>
+    static SPNode* build(Alloc& a,const ITER& begin, const ITER& end, int depth = 0);
+
+    // For testing only
+
+    NodeInfo nearestNeighbourBruteForce(Alloc& a,const Point& p);
+    NodeList findInSphereBruteForce(Alloc& a,const Point& p, double radius);
+    NodeList kNearestNeighboursBruteForce(Alloc& a,const Point& p,size_t k);
+
+    // -------
+    template<class Visitor>
+    void visit(Alloc& a, Visitor& v, int depth = 0);
+
+    // ---------
+    void linkNodes(Alloc& a, Node*& prev = 0);
+
+    const Node* asNode() const { return static_cast<const Node*>(this); }
+    Node* asNode() { return static_cast<Node*>(this); }
+
+public: // because of a clang bug. Should be protected
+    // void nearestNeighbourX(Alloc& a,const Point& p, Node*& best, double& max, int depth) = 0;
+    void nearestNeighbourBruteForceX(Alloc& a,const Point& p, Node*& best,double& max, int depth);
+    // void findInSphereX(Alloc& a,const Point& p ,double radius, NodeList& result, int depth) = 0;
+    void findInSphereBruteForceX(Alloc& a,const Point& p, double radius, NodeList& result, int depth) ;
+    // void kNearestNeighboursX(Alloc& a,const Point& p ,size_t k, NodeQueue& result, int depth) = 0;
+    void kNearestNeighboursBruteForceX(Alloc& a,const Point& p, size_t k, NodeQueue& result, int depth) ;
+
+    //==========================
+public:
+    Node* left(Alloc& a)  const { return a.convert(left_,  (Node*)0);   }
+    Node* right(Alloc& a) const { return a.convert(right_, (Node*)0);  }
+    Node* next(Alloc& a)  const { return a.convert(next_,  (Node*)0);   }
+
+    void  left(Alloc& a,  Node* n) { left_  = a.convert(n); }
+    void  right(Alloc& a, Node* n) { right_ = a.convert(n); }
+    void  next(Alloc& a,  Node* n) { next_  = a.convert(n); }
+
+    friend class SPIterator<Traits, NodeType>;
+
+};
+
+
+} // end namespace
+
+#include "SPNode.cc"
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPNodeInfo.h b/eckit/src/eckit/container/sptree/SPNodeInfo.h
new file mode 100644
index 0000000..8c4e3a3
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPNodeInfo.h
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPNodeInfo_H
+#define SPNodeInfo_H
+
+
+
+namespace eckit {
+
+
+template<class Traits, class NodeType>
+class SPNode;
+
+template<class Traits>
+class SPTreeIterator;
+
+template<class Traits>
+class SPValue;
+
+template<class Traits, class NodeType>
+struct SPNodeInfo {
+
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+    typedef typename Traits::Alloc   Alloc;
+    typedef SPValue<Traits>             Value;
+    typedef typename Alloc::Ptr   ID;
+
+    typedef NodeType Node;
+
+    const Node* node_;
+    ID id_;
+    double distance_;
+
+public:
+
+    SPNodeInfo():
+        node_(0), id_(0), distance_(0) {}
+
+    SPNodeInfo(const Node* node, ID id, double distance):
+        node_(node), id_(id), distance_(distance) {}
+
+    ID id() const {
+        return id_;
+    }
+
+    bool operator<(const SPNodeInfo& other) const
+    { return distance_ < other.distance_; }
+
+    typedef std::vector<SPNodeInfo> NodeList;
+
+    const Point& point() const { return node_->point(); }
+    const Payload& payload() const { return node_->payload(); }
+    const Value& value() const { return node_->value(); }
+
+    double distance() const    { return distance_; }
+
+    friend std::ostream& operator<<(std::ostream& s,const SPNodeInfo& p)
+    {
+        s << "[value=" << p.value() << ",distance=" << p.distance() << "]";
+        return s;
+    }
+};
+
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPNodeQueue.h b/eckit/src/eckit/container/sptree/SPNodeQueue.h
new file mode 100644
index 0000000..4bc7458
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPNodeQueue.h
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPNodeQueue_H
+#define SPNodeQueue_H
+
+#include <limits>
+
+#include <eckit/eckit.h>
+
+namespace eckit {
+
+
+template<class Traits, class NodeType>
+class SPNode;
+
+template<class Traits, class NodeType>
+class SPIterator;
+
+template<class Traits>
+class SPValue;
+
+template<class Traits, class NodeType>
+struct SPNodeInfo ;
+
+template<class Traits, class NodeType>
+class SPNodeQueue {
+public:
+
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+    typedef typename Traits::Alloc   Alloc;
+
+    typedef typename Alloc::Ptr   ID;
+
+    typedef NodeType              Node;
+    typedef SPNodeInfo<Traits,NodeType>          NodeInfo;
+    typedef typename NodeInfo::NodeList NodeList;
+
+private:
+    size_t k_;
+    std::priority_queue<NodeInfo> queue_;
+
+public:
+
+    SPNodeQueue(size_t k) : k_(k) {}
+
+    void push(Node* n, ID id, double d)
+    {
+        NodeInfo info(n, id, d);
+        queue_.push(info);
+        while(queue_.size() > k_) { queue_.pop();}
+    }
+
+    double largest() const {
+        return queue_.size() ? queue_.top().distance_ : std::numeric_limits<double>::max();
+    }
+
+    void fill(NodeList& v) {
+        v.reserve(k_);
+        while(!queue_.empty()) {
+            v.push_back(queue_.top());
+            queue_.pop();
+        }
+        std::sort(v.begin(),v.end());
+    }
+
+    bool incomplete() const {
+        return queue_.size() < k_;
+    }
+
+};
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPTree.h b/eckit/src/eckit/container/sptree/SPTree.h
new file mode 100644
index 0000000..64dcd25
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPTree.h
@@ -0,0 +1,160 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPTree_H
+#define SPTree_H
+
+#include "eckit/container/sptree/SPNode.h"
+#include "eckit/container/sptree/SPMetadata.h"
+#include "eckit/container/sptree/SPIterator.h"
+
+namespace eckit {
+
+
+template<class Traits, class NodeType>
+class SPTree {
+
+public:
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+    typedef typename Traits::Alloc   Alloc;
+
+    typedef typename Alloc::Ptr Ptr;
+    typedef typename Alloc::Ptr ID;
+
+    typedef NodeType Node;
+    typedef SPMetadata<Traits> Metadata;
+
+    typedef          Point   PointType;
+    typedef          Payload PayloadType;
+    typedef typename Node::NodeList NodeList;
+    typedef          SPNodeInfo<Traits,NodeType>       NodeInfo;
+    typedef typename Node::Value Value;
+
+    Alloc& alloc_;
+    Ptr   root_;
+    Metadata meta_;
+
+    typedef SPIterator<Traits,NodeType> iterator;
+
+    typedef std::pair<Point,Payload> value_type;
+
+public:
+
+    SPTree(Alloc& alloc): alloc_(alloc), root_(0) {}
+
+    ~SPTree()
+    {
+        alloc_.deleteNode(root_,(Node*)0);
+    }
+
+    void setMetadata(const Point& offset, const Point& scale) {
+        meta_.offset_ = offset;
+        meta_.scale_  = scale;
+        alloc_.setMetadata(meta_);
+    }
+
+    NodeInfo nodeByID(ID id) {
+        return SPNodeInfo<Traits, NodeType>(alloc_.convert(id,(Node*)0), id, 0.0);
+    }
+
+    void getMetadata(Point& offset, Point& scale) {
+        alloc_.getMetadata(meta_);
+        offset = meta_.offset_;
+        scale  = meta_.scale_;
+    }
+
+    NodeInfo nearestNeighbour(const Point& p)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        alloc_.statsCall();
+        return alloc_.convert(root_,(Node*)0)->nearestNeighbour(alloc_, p);
+    }
+
+    NodeList findInSphere(const Point& p,double radius)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        alloc_.statsCall();
+        return alloc_.convert(root_,(Node*)0)->findInSphere(alloc_, p, radius);
+    }
+
+    NodeList kNearestNeighbours(const Point& p, size_t k)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        alloc_.statsCall();
+        return alloc_.convert(root_,(Node*)0)->kNearestNeighbours(alloc_, p, k);
+    }
+
+    // For testing only...
+    NodeInfo nearestNeighbourBruteForce(const Point& p)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        alloc_.statsCall();
+        return alloc_.convert(root_,(Node*)0)->nearestNeighbourBruteForce(alloc_, p);
+    }
+
+
+    NodeList findInSphereBruteForce(const Point& p,double radius)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        return alloc_.convert(root_,(Node*)0)->findInSphereBruteForce(alloc_, p, radius);
+    }
+
+    NodeList kNearestNeighboursBruteForce(const Point& p,size_t k)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        return alloc_.convert(root_,(Node*)0)->kNearestNeighboursBruteForce(alloc_, p, k);
+    }
+
+
+    template<class Visitor>
+    void visit(Visitor& v)
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        return alloc_.convert(root_,(Node*)0)->visit(alloc_, v);
+    }
+
+    void statsReset()
+    {
+        alloc_.statsReset();
+    }
+
+    void statsPrint(std::ostream& o, bool fancy) const
+    {
+        if(fancy) o << *this << ": ";
+        alloc_.statsPrint(o, fancy);
+    }
+
+    void print(std::ostream& o) const
+    {
+        o << "SPTree";
+    }
+
+    friend std::ostream& operator<<(std::ostream& o, const SPTree& t)
+    {
+        t.print(o);
+        return o;
+    }
+
+    iterator begin()
+    {
+        if(!root_) { root_ = alloc_.root(); }
+        return iterator(alloc_, root_);
+    }
+
+    iterator end()
+    {
+        return iterator(alloc_, 0);
+    }
+};
+
+} // end namespace
+
+#endif
diff --git a/eckit/src/eckit/container/sptree/SPValue.h b/eckit/src/eckit/container/sptree/SPValue.h
new file mode 100644
index 0000000..30202f3
--- /dev/null
+++ b/eckit/src/eckit/container/sptree/SPValue.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SPValue_H
+#define SPValue_H
+
+
+
+
+namespace eckit {
+
+template<class Traits>
+class SPValue {
+public:
+    typedef typename Traits::Point   Point;
+    typedef typename Traits::Payload Payload;
+
+    Point point_;
+    Payload payload_;
+
+public:
+
+    SPValue(const Point& point, const Payload& payload):
+        point_(point), payload_(payload) {}
+
+    const Point& point() const   { return point_; }
+    const Payload& payload() const { return payload_; }
+
+
+    Point& point()    { return point_; } // FIXME: remove this one
+    Payload& payload()  { return payload_; }
+
+
+    void point(const Point& p) const   {  point_ = p; }
+    void payload(const Payload& p) const {  payload_ = p; }
+
+    void print(std::ostream& o) const {
+        o << "(point=" << point_ << ",payload=" << payload_ << ")";
+    }
+
+    friend std::ostream& operator<<(std::ostream& o, const SPValue& t) {
+        t.print(o);
+        return o;
+    }
+
+    bool operator<(const SPValue& other) const
+        { return point() < other.point(); }
+};
+
+} // end namespace
+
+
+#endif
diff --git a/eckit/src/eckit/eckit.h b/eckit/src/eckit/eckit.h
new file mode 100644
index 0000000..d68798e
--- /dev/null
+++ b/eckit/src/eckit/eckit.h
@@ -0,0 +1,294 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_eckit_h
+#define eckit_eckit_h
+
+#include "eckit/eckit_config.h"
+
+//-----------------------------------------------------------------------------
+
+/* POSIX */
+
+#include <string.h>
+
+//-----------------------------------------------------------------------------
+
+/* STL */
+
+#include <set>
+#include <map>
+#include <list>
+#include <vector>
+#include <stack>
+#include <queue>
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <numeric>
+
+#include <iostream>
+#include <iterator>
+#include <iomanip>
+#include <fstream>
+#include <new>
+#include <sstream>
+
+//-----------------------------------------------------------------------------
+
+// define the nullptr either as macro or as nullptr idiom until C++0x
+//#ifndef HAS_CXX11_NULLPTR
+//#define ECKIT_DEFINE_NULLPTR
+//#ifdef  ECKIT_DEFINE_NULLPTR
+//    const class nullptr_t
+//    {
+//    public:
+//      template<class T> operator T*() const { return 0; }
+//      template<class C, class T> operator T C::*() const { return 0; }
+//    private:
+//      void operator&() const;
+//    } nullptr = {};
+//#else
+//  #define nullptr 0
+//#endif
+//#endif
+
+//-----------------------------------------------------------------------------
+
+#if (!defined EC_HAVE_MAP_ANONYMOUS) && (defined EC_HAVE_MAP_ANON)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#if (!defined EC_HAVE_FDATASYNC) && (defined EC_HAVE_FSYNC)
+#define fdatasync  fsync
+#endif
+
+#ifndef EC_HAVE_FUNCTION_DEF
+#define __FUNCTION__ ""
+#endif
+
+/* Usefull macros */
+
+#ifndef NUMBER
+#define NUMBER(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifdef __GNUC__ /* GCC gets confused about offsetof */
+
+static char _offset_dummy[80];
+static void* _offset = &_offset_dummy;
+
+#define member_offset(Z,z)  size_t( reinterpret_cast<char*>(&reinterpret_cast<Z*>(_offset)->z) - reinterpret_cast<char*>(_offset))
+#define member_size(Z,z)    size_t( sizeof(reinterpret_cast<Z*>(_offset)->z))
+
+namespace eckit  {
+namespace _detail {
+    static void* keep_gcc_quiet_about_offset_2(void* d);
+    static void* keep_gcc_quiet_about_offset_1(void* d) { return keep_gcc_quiet_about_offset_2(_offset); }
+    static void* keep_gcc_quiet_about_offset_2(void* d) { return keep_gcc_quiet_about_offset_1(_offset); }
+    }
+}
+
+#endif /* __GNUC__ */
+
+#ifndef member_size
+#define member_size(a,b)   size_t(sizeof(((a*)0)->b))
+#endif
+
+#ifndef member_offset
+#define member_offset(a,b) size_t(&(((a*)0)->b))
+#endif
+
+//-----------------------------------------------------------------------------
+
+class TypeInfo;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+template<bool b> struct compile_assert {};
+template<>       struct compile_assert<true> { static void check() {} };
+
+
+struct output_iterator {
+  typedef std::output_iterator_tag iterator_category;
+  typedef void                value_type;
+  typedef void                difference_type;
+  typedef void                pointer;
+  typedef void                reference;
+};
+
+template<class T>
+inline void zero(T& p) { ::memset(&p,0,sizeof(T)); }
+class Bless {
+public:
+
+    bool          operator() (bool*  a)          { return *a; }
+    int           operator() (int*   a)          { return *a; }
+    short         operator() (short*   a)          { return *a; }
+    char          operator() (char*  a)          { return *a; }
+    long          operator() (long* a)           { return *a; }
+    long long     operator() (long long* a)      { return *a; }
+
+    unsigned long operator() (unsigned long* a) { return *a; }
+    unsigned int  operator() (unsigned int* a)  { return *a; }
+    unsigned char operator() (unsigned char* a) { return *a; }
+    unsigned short operator() (unsigned short* a) { return *a; }
+    unsigned long long operator() (unsigned long long* a)      { return *a; }
+
+    double        operator() (double* a)        { return *a; }
+
+    template<class T>
+    Bless&        operator() (T*)               { return *this; }
+};
+
+class Exporter;
+class Evolve {
+
+public:
+
+    Evolve(eckit::Exporter&);
+    Evolve(Evolve*,const char*, const char*);
+
+    Evolve operator() (const char*, const char* = 0);
+
+    operator bool();
+    operator double();
+
+    operator int();
+    operator short();
+    operator char();
+    operator long();
+    operator long long();
+
+    operator unsigned int();
+    operator unsigned short();
+    operator unsigned char();
+    operator unsigned long();
+    operator unsigned long long();
+
+    const std::string& path() const { return path_; }
+
+private:
+
+    Exporter&      e_;
+    std::string    path_;
+    Evolve*        parent_;
+
+};
+
+class Isa {
+public:
+    Isa* next_;
+    TypeInfo* type_;
+    Isa(TypeInfo* t,Isa* n) : next_(n), type_(t) {}
+    static void add(TypeInfo* t,const std::string&);
+    static Isa* get(const std::string&);
+
+};
+
+class Schema {
+public:
+    virtual ~Schema(){}
+    virtual void start(const std::string&, size_t size) = 0;
+    virtual void member(const std::string&, size_t size, size_t offset, const std::string& type) = 0;
+    virtual void end(const std::string&) = 0;
+};
+
+template<class T>
+void _describe( std::ostream& s,int depth,const T& what)
+{
+    what.describe(s,depth);
+}
+
+void _describe(std::ostream& s,int depth,int what);
+void _describe(std::ostream& s,int depth,unsigned int what);
+void _describe(std::ostream& s,int depth,short what);
+void _describe(std::ostream& s,int depth,bool what);
+void _describe(std::ostream& s,int depth,unsigned short what);
+void _describe(std::ostream& s,int depth,long what);
+void _describe(std::ostream& s,int depth,long long what);
+void _describe(std::ostream& s,int depth,unsigned long long what);
+void _describe(std::ostream& s,int depth,unsigned long what);
+void _describe(std::ostream& s,int depth,char what);
+void _describe(std::ostream& s,int depth,unsigned char what);
+void _describe(std::ostream& s,int depth,double what);
+
+void _startClass(std::ostream& s,int depth,const std::string& name);
+void _endClass(std::ostream& s,int depth,const std::string& name);
+void _startMember(std::ostream& s,int depth,const std::string& name);
+void _endMember(std::ostream& s,int depth,const std::string& name);
+
+template<class T>
+void _describe(std::ostream& s,int depth,const std::string& name,const T& what)
+{
+    _startMember(s,depth,name);
+    _describe(s,depth,what);
+    _endMember(s,depth,name);
+}
+
+class Exporter;
+
+void _startObject(eckit::Exporter&,unsigned long long type, unsigned long long location, unsigned long long id, size_t count);
+void _endObject(eckit::Exporter&,unsigned long long type, unsigned long long location, unsigned long long id, size_t count);
+void _startSubObject(eckit::Exporter&);
+void _endSubObject(eckit::Exporter&);
+void _nextSubObject(eckit::Exporter&);
+void _startClass(eckit::Exporter&,const std::string& name);
+void _endClass(eckit::Exporter&,const std::string& name);
+void _startClass(eckit::Exporter&,const char* name);
+void _endClass(eckit::Exporter&,const char* name);
+void _startMember(eckit::Exporter&,const char* name);
+void _endMember(eckit::Exporter&,const char* name);
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+void _export(eckit::Exporter& h,const T& what)
+{
+    what._export(h);
+}
+
+void _export(eckit::Exporter&,int what);
+void _export(eckit::Exporter&,unsigned int what);
+void _export(eckit::Exporter&,short what);
+void _export(eckit::Exporter&,bool what);
+void _export(eckit::Exporter&,unsigned short what);
+void _export(eckit::Exporter&,long what);
+void _export(eckit::Exporter&,long long what);
+void _export(eckit::Exporter&,unsigned long long what);
+void _export(eckit::Exporter&,unsigned long what);
+void _export(eckit::Exporter&,char what);
+void _export(eckit::Exporter&,unsigned char what);
+void _export(eckit::Exporter&,double what);
+
+template<class T>
+void _export(eckit::Exporter& s ,const char* name,const T& what)
+{
+    _startMember(s,name);
+    _export(s,what);
+    _endMember(s,name);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+//-----------------------------------------------------------------------------
+
+void* operator new(size_t,void* addr, eckit::Evolve&);
+
+//-----------------------------------------------------------------------------
+
+#endif
+
diff --git a/eckit/src/eckit/eckit_config.h.in b/eckit/src/eckit/eckit_config.h.in
new file mode 100644
index 0000000..34581f8
--- /dev/null
+++ b/eckit/src/eckit/eckit_config.h.in
@@ -0,0 +1,23 @@
+#ifndef eckit_config_h
+#define eckit_config_h
+
+#include "eckit/eckit_ecbuild_config.h"
+
+#include "eckit/eckit_version.h"
+
+// external packages
+
+#cmakedefine ECKIT_HAVE_ARMADILLO
+#cmakedefine ECKIT_HAVE_CUDA
+#cmakedefine ECKIT_HAVE_EIGEN
+#cmakedefine ECKIT_HAVE_LAPACK
+#cmakedefine ECKIT_HAVE_MKL
+#cmakedefine ECKIT_HAVE_MPI
+#cmakedefine ECKIT_HAVE_VIENNACL
+#cmakedefine ECKIT_HAVE_SSL
+#cmakedefine ECKIT_HAVE_XXHASH
+
+#define ECKIT_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
+#define ECKIT_BUILD_DIR      "@CMAKE_BINARY_DIR@"
+
+#endif // eckit_config_h
diff --git a/eckit/src/eckit/eckit_version.cc.in b/eckit/src/eckit/eckit_version.cc.in
new file mode 100644
index 0000000..b21fb00
--- /dev/null
+++ b/eckit/src/eckit/eckit_version.cc.in
@@ -0,0 +1,12 @@
+#include "eckit/eckit_version.h"
+
+const char * eckit_version()     { return ECKIT_VERSION; }
+const char * eckit_version_str() { return ECKIT_VERSION_STR; }
+
+unsigned int eckit_version_int()
+{
+  return 10000*ECKIT_MAJOR_VERSION + 100*ECKIT_MINOR_VERSION + 1*ECKIT_PATCH_VERSION;
+}
+
+const char * eckit_git_sha1() { return "@ECKIT_GIT_SHA1@"; }
+
diff --git a/eckit/src/eckit/eckit_version.h.in b/eckit/src/eckit/eckit_version.h.in
new file mode 100644
index 0000000..b9f03db
--- /dev/null
+++ b/eckit/src/eckit/eckit_version.h.in
@@ -0,0 +1,19 @@
+#ifndef eckit_version_h
+#define eckit_version_h
+
+#define ECKIT_VERSION_STR "@ECKIT_VERSION_STR@"
+#define ECKIT_VERSION     "@ECKIT_VERSION@"
+
+#define ECKIT_MAJOR_VERSION @ECKIT_MAJOR_VERSION@
+#define ECKIT_MINOR_VERSION @ECKIT_MINOR_VERSION@
+#define ECKIT_PATCH_VERSION @ECKIT_PATCH_VERSION@
+
+const char * eckit_version();
+
+unsigned int eckit_version_int();
+
+const char * eckit_version_str();
+
+const char * eckit_git_sha1();
+
+#endif // eckit_version_h
diff --git a/eckit/src/eckit/exception/Exceptions.cc b/eckit/src/eckit/exception/Exceptions.cc
new file mode 100644
index 0000000..bafc655
--- /dev/null
+++ b/eckit/src/eckit/exception/Exceptions.cc
@@ -0,0 +1,491 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/ThreadSingleton.h"
+#include "eckit/runtime/Main.h"
+
+#include "eckit/os/BackTrace.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static Exception*& first()
+{
+    static ThreadSingleton<Exception*> p;
+    return p.instance();
+}
+
+Exception::Exception():  next_(first())
+{
+    first() = this;
+
+    callStack_ = BackTrace::dump();
+
+
+    if(::getenv("LIBECKIT_DEBUG_EXCEPTION_DUMPS_BACKTRACE")) {
+        std::cerr << "Exception dumping backtrace: " << callStack_ << std::endl;
+    }
+}
+
+Exception::~Exception() throw()
+{
+    first() = next_;
+}
+
+void Exception::print(std::ostream& out) const
+{
+    out << what_;
+}
+
+void Exception::exceptionStack(std::ostream& out, bool callStack)
+{
+    out << "Exception stack: " << std::endl;
+    Exception* e =  first();
+    while(e)
+    {
+        out << e->what() << std::endl;
+
+        if(callStack)
+            out << e->callStack() << std::endl << std::endl;
+
+        e = e->next_;
+    }
+    out << "End stack" << std::endl;
+}
+
+Exception::Exception(const std::string& w, const CodeLocation& location):
+    what_(w),
+    next_(first()),
+    location_(location)
+{
+    callStack_ = BackTrace::dump();
+
+    if(::getenv("LIBECKIT_DEBUG_EXCEPTION_DUMPS_BACKTRACE")) {
+        std::cerr << "Exception dumping backtrace: " << callStack_ << std::endl;
+    }
+
+    Log::error() << "Exception: " << w << location_ << std::endl;
+
+#if 0
+    if(next_) {
+        Log::error() << "Exception: stack containts " << next_->what() << std::endl;
+    }
+    else
+    {
+        Log::error() << "Exception: stack is empty" << std::endl;
+    }
+#endif
+
+    first() = this;
+
+    Log::status() << "** " << w << location_ << std::endl;
+}
+
+void Exception::reason(const std::string& w)
+{
+    Log::error() << "Exception: " << w << std::endl;
+    what_ = w;
+}
+
+bool Exception::throwing()
+{
+    return first() != 0;
+}
+
+TooManyRetries::TooManyRetries( const int retries )
+{
+    std::ostringstream s;
+    s << "Too many retries: " << retries;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+TooManyRetries::TooManyRetries( const int retries, const std::string& msg )
+{
+    std::ostringstream s;
+    s << "Too many retries: " << retries << " @ " << msg;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+TimeOut::TimeOut(const std::string& msg, const unsigned long timeout)
+{
+    std::ostringstream s;
+    s  << "Timeout expired: " << timeout << " (" << msg << ")";
+    reason(s.str());
+}
+
+
+FailedSystemCall::FailedSystemCall(const std::string& w)
+{
+    std::ostringstream s;
+    s << "Failed system call: " << w << " " << Log::syserr;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+FailedSystemCall::FailedSystemCall(const std::string& msg, const CodeLocation& loc)
+{
+    std::ostringstream s;
+    s << "Failed system call: " << msg << " " << " in " << loc << " " << Log::syserr;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+FailedSystemCall::FailedSystemCall(const char* msg,const CodeLocation& loc,int err)
+{
+    std::ostringstream s;
+
+    errno = err;
+    s << "Failed system call: " << msg << " in " << loc << " " << Log::syserr;
+
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+FailedSystemCall::FailedSystemCall(const std::string& ctx, const char* msg, const CodeLocation& loc,int err)
+{
+    std::ostringstream s;
+
+    errno = err;
+    s << "Failed system call: " << msg << " in " << loc << " " << Log::syserr << " [" << ctx << "]";
+
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+SeriousBug::SeriousBug(const std::string& w) : Exception(std::string("Serious Bug: ") + w)
+{
+   std::cout << what() << std::endl;
+   std::cout << BackTrace::dump() << std::endl;
+}
+
+SeriousBug::SeriousBug(const std::string& msg, const CodeLocation& loc)
+{
+   std::ostringstream s;
+   s << "SeriousBug: " << msg << " " << " in " << loc;
+   reason(s.str());
+   std::cout << what() << std::endl;
+   std::cout << BackTrace::dump() << std::endl;
+}
+
+SeriousBug::SeriousBug(const char* msg,const CodeLocation& loc)
+{
+   std::ostringstream s;
+   s << "SeriousBug: " << msg << " " << " in " << loc;
+   reason(s.str());
+   std::cout << what() << std::endl;
+   std::cout << BackTrace::dump() << std::endl;
+}
+
+
+AssertionFailed::AssertionFailed(const std::string& w):
+    Exception(std::string("Assertion failed: ") + w)
+{
+    Log::status() << what() << std::endl;
+
+    // if(Main::instance().assertAborts())
+    // {
+        std::cout << what() << std::endl;
+        std::cout << BackTrace::dump() << std::endl;
+    //     Main::instance().abort();
+    // }
+}
+
+AssertionFailed::AssertionFailed(const std::string& msg, const CodeLocation& loc)
+{
+    std::ostringstream s;
+
+    s << "Assertion failed: " << msg << " in " << loc.func()
+      << ", line " << loc.line() << " of " << loc.file();
+
+    reason(s.str());
+    Log::status() << what() << std::endl;
+
+    // if(Main::instance().assertAborts())
+    // {
+        std::cout << what() << std::endl;
+        std::cout << BackTrace::dump() << std::endl;
+    //     Main::instance().abort();
+    // }
+}
+
+AssertionFailed::AssertionFailed(const char* msg, const CodeLocation& loc)
+{
+    std::ostringstream s;
+
+    s << "Assertion failed: " << msg << " in " << loc.func()
+      << ", line " << loc.line() << " of " << loc.file();
+
+    reason(s.str());
+    Log::status() << what() << std::endl;
+
+    //if(Main::instance().assertAborts())
+    //{
+        std::cout << what() << std::endl;
+        std::cout << BackTrace::dump() << std::endl;
+        //Main::instance().abort();
+    //}
+}
+
+BadParameter::BadParameter(const std::string& w):
+    Exception(std::string("Bad parameter: ") + w)
+{
+}
+
+BadParameter::BadParameter(const std::string& w, const CodeLocation& loc):
+    Exception(std::string("Bad parameter: ") + w, loc)
+{
+}
+
+BadCast::BadCast(const std::string& w):
+    Exception(std::string("Bad cast: ") + w)
+{
+}
+
+BadCast::BadCast(const std::string& w, const CodeLocation& loc):
+    Exception(std::string("Bad cast: ") + w, loc)
+{
+}
+
+BadValue::BadValue(const std::string& s)
+    : Exception(std::string("BadValue: ") + s) {}
+
+BadValue::BadValue(const std::string& s, const CodeLocation& loc)
+    : Exception(std::string("BadValue: ") + s, loc) {}
+
+NotImplemented::NotImplemented(const std::string& s, const eckit::CodeLocation& loc)
+{
+    std::ostringstream ss;
+
+    ss << "Not implemented: " << s << loc;
+
+    reason(ss.str());
+	Log::status() << what() << std::endl;
+
+    std::cout << what() << std::endl;
+    std::cout << BackTrace::dump() << std::endl;
+}
+
+NotImplemented::NotImplemented( const CodeLocation& loc )
+{
+    std::ostringstream ss;
+
+    ss << "Not implemented: " << loc.func()
+       << ", line " << loc.line() << " of " << loc.file();
+
+    reason(ss.str());
+    Log::status() << what() << std::endl;
+
+    std::cout << what() << std::endl;
+    std::cout << BackTrace::dump() << std::endl;
+}
+
+UserError::UserError(const std::string& r, const CodeLocation& loc):
+    Exception(std::string("UserError: ") + r, loc)
+{
+}
+
+UserError::UserError(const std::string& r):
+    Exception(std::string("UserError: ") + r)
+{
+}
+
+UserError::UserError(const std::string& r,const std::string& x):
+    Exception(std::string("UserError: ") + r + " : " + x)
+{
+}
+
+Stop::Stop(const std::string& r):
+    Exception(std::string("Stop: ") + r)
+{
+}
+
+Abort::Abort(const std::string& r):
+    Exception(std::string("Abort: ") + r)
+{
+}
+
+Retry::Retry(const std::string& r):
+    Exception(std::string("Retry: ") + r)
+{
+}
+
+Cancel::Cancel(const std::string& r):
+    Exception(std::string("Cancel: ") + r)
+{
+}
+
+OutOfRange::OutOfRange(unsigned long long index, unsigned long long max)
+{
+    std::ostringstream s;
+    s << "Out of range accessing element " << index
+      << ", but maximum is " << max - 1;
+    reason(s.str());
+}
+
+OutOfRange::OutOfRange(unsigned long long index, unsigned long long max,const CodeLocation& loc)
+{
+  std::ostringstream s;
+  s << "Out of range accessing element " << index
+    << ", but maximum is " << max - 1;
+  Exception(s.str(), loc);
+}
+
+OutOfRange::OutOfRange(const std::string& w,const CodeLocation& loc):
+    Exception(std::string("OutOfRange: ")+w,loc)
+{
+}
+
+
+FileError::FileError(const std::string& msg)
+{
+    std::ostringstream s;
+    s << msg <<  Log::syserr;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+FileError::FileError(const std::string& msg, const CodeLocation& here )
+{
+    std::ostringstream s;
+    s << msg << here <<  Log::syserr;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+FileError::FileError()
+{
+}
+
+CantOpenFile::CantOpenFile(const std::string& file, bool retry):
+    retry_(retry)
+{
+    std::ostringstream s;
+    s << "Cannot open " << file << " " << Log::syserr;
+    if(retry) s << " (retry ok)";
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+CantOpenFile::CantOpenFile(const std::string& file, const CodeLocation& loc, bool retry):
+    retry_(retry)
+{
+    std::ostringstream s;
+    s << "Cannot open " << file << " " << Log::syserr;
+    if(retry) s << " (retry ok)";
+    s << loc;
+    reason(s.str());
+    Log::status() << what() << std::endl;
+}
+
+MethodNotYetImplemented::MethodNotYetImplemented(const std::string &msg):
+    Exception(std::string("Method not yet implemented: " + msg))
+{
+}
+
+WriteError::WriteError(const std::string& file, const CodeLocation& loc):
+    FileError(std::string("Write error on ") + file, loc)
+{
+}
+
+WriteError::WriteError(const std::string& file):
+    FileError(std::string("Write error on ") + file)
+{
+}
+
+ReadError::ReadError(const std::string& file, const CodeLocation& loc):
+    FileError(std::string("Read error on ") + file, loc)
+{
+}
+
+ReadError::ReadError(const std::string& file ):
+    FileError(std::string("Read error on ") + file)
+{
+}
+
+ShortFile::ShortFile(const std::string& file):
+    ReadError(std::string("Short file while reading ") + file)
+{
+}
+
+RemoteException::RemoteException(const std::string& msg, const std::string& from):
+    Exception(msg + "(RemoteException from " + from + ")")
+{
+}
+
+UnexpectedState::UnexpectedState(const std::string& msg) : Exception(msg)
+{
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void handle_panic(const char *msg)
+{
+    msg = msg ? msg : "(null message)";
+
+    std::cout << "PANIC: " << msg << std::endl;
+    std::cerr << "PANIC: " << msg << std::endl;
+
+    std::cerr << "----------------------------------------\n"
+              << "BACKTRACE\n"
+              << "----------------------------------------\n"
+              << BackTrace::dump() << std::endl
+              << "----------------------------------------\n"
+              << std::endl;
+
+    if(::getenv("STOP_ON_PANIC"))
+    {
+        pid_t pid = ::getpid();
+
+        std::cout << "Stopped process with PID " << pid
+                  << " - attach a debugger or send a SIGCONT signal to abort" << std::endl;
+
+        std::cerr << "Stopped process with PID " << pid
+                  << " - attach a debugger or send a SIGCONT signal to abort" << std::endl;
+
+        ::kill(pid, SIGSTOP);
+        ::kill(pid, SIGABRT);
+    }
+
+    _exit(1);
+}
+
+void handle_panic(const char* msg, const CodeLocation& location )
+{
+    std::ostringstream s;
+    s << msg << " in " << location;
+    handle_panic(s.str().c_str());
+}
+
+void handle_panic_no_log(const char* msg, const CodeLocation& location )
+{
+    std::cout << "PANIC: " << msg << " in " << location << std::endl;
+    std::cerr << "PANIC: " << msg << " in " << location << std::endl;
+
+    ::kill(::getpid(), SIGABRT);
+    ::pause();
+}
+
+OutOfMemory::OutOfMemory():
+    Exception("out of memory")
+{
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/exception/Exceptions.h b/eckit/src/eckit/exception/Exceptions.h
new file mode 100644
index 0000000..aa1838f
--- /dev/null
+++ b/eckit/src/eckit/exception/Exceptions.h
@@ -0,0 +1,303 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_Exceptions_h
+#define eckit_Exceptions_h
+
+#include <errno.h>
+
+#include "eckit/eckit.h"
+#include "eckit/eckit_version.h"
+
+#include "eckit/log/CodeLocation.h"
+#include "eckit/log/Log.h"
+#include "eckit/log/SavedStatus.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void handle_panic(const char*);
+void handle_panic(const char*, const CodeLocation&);
+void handle_panic_no_log(const char*, const CodeLocation&);
+
+/// @brief General purpose exception
+/// Derive other exceptions from this class and implement then in the class that throws them.
+
+class Exception : public std::exception {
+
+public: // methods
+
+    /// Constructor with message
+    Exception(const std::string& what, const CodeLocation& location = CodeLocation() );
+
+    /// Destructor
+    /// @throws nothing
+    ~Exception() throw();
+
+    virtual const char *what() const throw() {  return what_.c_str(); }
+    virtual bool retryOnServer() const        { return false; }
+    virtual bool retryOnClient() const        { return false; }
+    virtual bool terminateApplication() const { return false; }
+
+    static bool throwing();
+    static void exceptionStack(std::ostream&,bool callStack = false);
+
+    const std::string& callStack() const { return callStack_; }
+
+    const CodeLocation& location() const { return location_; }
+
+protected: // methods
+
+    void reason(const std::string&);
+
+    Exception();
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    std::string       what_;      ///< description
+    std::string       callStack_; ///< call stack
+    SavedStatus       save_;      ///< saved monitor status to recover after destruction
+    Exception*        next_;
+    CodeLocation      location_;  ///< where exception was first thrown
+
+
+    friend std::ostream& operator<<(std::ostream& s,const Exception& p)
+    {
+        p.print(s);
+        return s;
+    }
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class SeriousBug : public Exception {
+public:
+    SeriousBug(const std::string& w);
+    SeriousBug(const std::string&,const CodeLocation&);
+    SeriousBug(const char*,const CodeLocation&);
+};
+
+class TooManyRetries : public Exception {
+public:
+    TooManyRetries(const int);
+    TooManyRetries(const int, const std::string &msg);
+};
+
+class TimeOut : public Exception {
+public:
+    TimeOut(const std::string&, const unsigned long);
+};
+
+class FailedSystemCall : public Exception {
+public:
+    FailedSystemCall(const std::string&);
+    FailedSystemCall(const std::string&,const CodeLocation&);
+    FailedSystemCall(const char*,const CodeLocation&,int);
+    FailedSystemCall(const std::string&,const char*,const CodeLocation&,int);
+};
+
+class AssertionFailed : public Exception {
+public:
+    AssertionFailed(const std::string&);
+    AssertionFailed(const std::string&, const CodeLocation& );
+    AssertionFailed(const char*, const CodeLocation& );
+};
+
+class BadParameter : public Exception {
+public:
+    BadParameter(const std::string& s);
+    BadParameter(const std::string&, const CodeLocation&);
+};
+
+class BadCast : public Exception {
+public:
+    BadCast(const std::string& s);
+    BadCast(const std::string&, const CodeLocation&);
+};
+
+class BadValue: public Exception {
+public:
+    BadValue(const std::string& s);
+    BadValue(const std::string&, const CodeLocation&);
+};
+
+class NotImplemented : public Exception {
+public:
+	NotImplemented( const std::string& s, const CodeLocation& );
+	NotImplemented( const CodeLocation& );
+};
+
+class Stop : public Exception {
+public:
+    Stop(const std::string&);
+};
+
+class Abort : public Exception {
+public:
+    Abort(const std::string&);
+};
+
+class Cancel : public Exception {
+public:
+    Cancel(const std::string&);
+};
+
+class Retry : public Exception {
+public:
+    Retry(const std::string&);
+};
+
+class UserError : public Exception {
+public:
+    UserError(const std::string&,const CodeLocation&);
+    UserError(const std::string&);
+    UserError(const std::string&,const std::string&);
+    UserError(const std::string&,int);
+};
+
+class OutOfRange : public Exception {
+public:
+    OutOfRange(const std::string&,const CodeLocation&);
+    OutOfRange(unsigned long long, unsigned long long,const CodeLocation&);
+    OutOfRange(unsigned long long, unsigned long long);
+};
+
+class MethodNotYetImplemented : public Exception {
+public:
+    MethodNotYetImplemented(const std::string& msg);
+};
+
+// File errors
+
+class FileError : public Exception {
+protected:
+    FileError( const std::string& );
+    FileError( const std::string&, const CodeLocation& );
+    FileError();
+};
+
+class CantOpenFile : public FileError {
+    bool retry_;
+    virtual bool retryOnServer() const { return retry_; }
+public:
+    CantOpenFile(const std::string&,bool retry = false);
+    CantOpenFile(const std::string&, const CodeLocation&, bool retry = false);
+};
+
+class WriteError : public FileError {
+public:
+  WriteError(const std::string& );
+  WriteError(const std::string&, const CodeLocation& );
+};
+
+class ReadError : public FileError {
+public:
+  ReadError(const std::string&);
+  ReadError(const std::string&, const CodeLocation&);
+};
+
+class ShortFile : public ReadError {
+public:
+    ShortFile(const std::string&);
+};
+
+class RemoteException : public Exception {
+public:
+    RemoteException(const std::string& msg, const std::string& from);
+};
+
+class UnexpectedState : public Exception {
+public:
+    UnexpectedState(const std::string& msg);
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+inline T SysCall(T code,const char *msg, const char* file, int line, const char* func)
+{
+    if(code<0)
+        throw FailedSystemCall(msg, CodeLocation(file, line, func), errno);
+    return code;
+}
+
+
+template<class T>
+inline void SysCall(long long code,const char *msg,const T& ctx, const char* file, int line, const char* func)
+{
+    if(code<0)
+    {
+        std::ostringstream os;
+        os << ctx;
+        throw FailedSystemCall(os.str(), msg, CodeLocation(file, line, func), errno);
+    }
+}
+
+
+inline void ThrCall(int code,const char *msg, const char* file, int line, const char* func)
+{
+    if(code != 0) // Threads return errno in return code
+        handle_panic(msg, CodeLocation(file, line, func));
+}
+
+inline void Assert(int code,const char *msg, const char* file, int line, const char* func)
+{
+    if(code != 0) {
+        throw AssertionFailed(msg, CodeLocation(file, line, func));
+    }
+}
+
+inline void Panic(int code,const char *msg, const CodeLocation& loc )
+{
+    if(code != 0) {
+        handle_panic(msg,loc);
+    }
+}
+
+inline void PanicNoLog(int code, const char *msg, const CodeLocation& loc)
+{
+    if(code != 0) {
+        handle_panic_no_log(msg, loc);
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// For compatibility
+class OutOfMemory : public Exception {
+    virtual bool terminateApplication() const { return true; }
+    virtual const char *what() const throw() {  return "OutOfMemory"; }
+public:
+    OutOfMemory();
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#define THRCALL(a)    ::eckit::ThrCall(a,#a, __FILE__, __LINE__, __FUNCTION__)
+#define SYSCALL(a)    ::eckit::SysCall(a,#a, __FILE__, __LINE__, __FUNCTION__)
+#define SYSCALL2(a,b) ::eckit::SysCall(a,#a,b, __FILE__, __LINE__, __FUNCTION__)
+#define ASSERT(a)     ::eckit::Assert(!(a),#a, __FILE__, __LINE__, __FUNCTION__)
+#define PANIC(a)      ::eckit::Panic((a),#a,Here())
+#define NOTIMP  throw ::eckit::NotImplemented(Here())
+
+#define CHECK_CALL_NOLOG(a) ::eckit::PanicNoLog(a,#a,Here())
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/BasePathName.h b/eckit/src/eckit/filesystem/BasePathName.h
new file mode 100644
index 0000000..17705a0
--- /dev/null
+++ b/eckit/src/eckit/filesystem/BasePathName.h
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File filesystem/BasePathName.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_filesystem_BasePathName_h
+#define eckit_filesystem_BasePathName_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/memory/NonCopyable.h"
+
+struct FileSystemSize;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class BasePathName : protected NonCopyable {
+public:
+
+// -- Contructors
+
+	BasePathName() {}
+
+// -- Destructor
+
+	virtual ~BasePathName() {}
+
+// -- Methods
+
+    virtual BasePathName* clone() const = 0;
+    virtual const char* localPath() const = 0;
+    virtual Length size() const = 0;
+    virtual time_t lastAccess() const = 0;
+    virtual time_t lastModified() const = 0;
+    virtual time_t created() const = 0;
+
+    virtual bool isDir() const = 0;
+    virtual bool isLink() const = 0;
+
+    virtual void rename(const BasePathName&) const = 0;
+    virtual void link(const BasePathName&) const = 0;
+    virtual bool sameAs(const BasePathName&) const = 0;
+    virtual BasePathName* mountPoint() const = 0;
+    virtual BasePathName* realName() const = 0;
+
+    virtual bool exists() const = 0;
+    virtual bool available() const = 0;
+    virtual void mkdir(short) const = 0;
+    virtual void unlink() const = 0;
+    virtual void rmdir() const = 0;
+    virtual void touch() const = 0;
+    virtual void children(std::vector<BasePathName*>&,std::vector<BasePathName*>&) const = 0;
+    virtual void match(std::vector<BasePathName*>&,bool) const = 0;
+    virtual void reserve(const Length&) const = 0;
+
+    virtual BasePathName* unique() const = 0;
+    virtual BasePathName* dirName() const = 0;
+    virtual BasePathName* fullName() const = 0;
+    virtual BasePathName* orphanName() const = 0;
+    virtual BasePathName* checkClusterNode() const = 0;
+    virtual BasePathName* baseName(bool) const = 0;
+    virtual std::string extension() const = 0;
+
+    virtual std::string clusterName() const = 0;
+
+    virtual void syncParentDirectory() const = 0;
+
+    virtual std::string asString() const = 0;
+    virtual void fileSystemSize(FileSystemSize&) const = 0;
+    virtual DataHandle* fileHandle(bool overwrite) const = 0;
+    virtual DataHandle* partHandle(const OffsetList&, const LengthList&) const = 0;
+    virtual DataHandle* partHandle(const Offset&, const Length&) const = 0;
+
+    virtual const std::string& node() const = 0;
+    virtual const std::string& path() const = 0;
+
+protected:
+
+    virtual void print(std::ostream&) const = 0;
+
+private:
+
+	friend std::ostream& operator<<(std::ostream& s,const BasePathName& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/BasePathNameT.cc b/eckit/src/eckit/filesystem/BasePathNameT.cc
new file mode 100644
index 0000000..4c0fdcc
--- /dev/null
+++ b/eckit/src/eckit/filesystem/BasePathNameT.cc
@@ -0,0 +1,276 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File filesystem/BasePathNameT.cc
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+
+#include "eckit/filesystem/BasePathNameT.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+void BasePathNameT<T>::print(std::ostream& s) const
+{
+    s << path_;
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::clone() const
+{
+    return new BasePathNameT<T>(path_);
+}
+
+template<class T>
+const char* BasePathNameT<T>::localPath() const
+{
+    return path_.localPath();
+}
+
+template<class T>
+Length BasePathNameT<T>::size() const
+{
+    return path_.size();
+}
+
+template<class T>
+time_t BasePathNameT<T>::lastAccess() const
+{
+    return path_.lastAccess();
+}
+
+template<class T>
+time_t BasePathNameT<T>::lastModified() const
+{
+    return path_.lastModified();
+}
+
+template<class T>
+time_t BasePathNameT<T>::created() const
+{
+    return path_.created();
+}
+
+template<class T>
+bool BasePathNameT<T>::isDir() const
+{
+    return path_.isDir();
+}
+
+template<class T>
+bool BasePathNameT<T>::isLink() const
+{
+    return path_.isLink();
+}
+
+template<class T>
+void BasePathNameT<T>::rename(const BasePathName& other) const
+{
+    const BasePathNameT<T>& o = dynamic_cast<const BasePathNameT<T>&>(other);
+    T::rename(this->path_, o.path_);
+}
+
+template<class T>
+void BasePathNameT<T>::link(const BasePathName& other) const
+{
+    const BasePathNameT<T>& o = dynamic_cast<const BasePathNameT<T>&>(other);
+    T::link(this->path_, o.path_);
+}
+
+template<class T>
+bool BasePathNameT<T>::sameAs(const BasePathName& other) const
+{
+    const BasePathNameT<T>* o = dynamic_cast<const BasePathNameT<T>*>(&other);
+    if(!o) return false;
+    return path_.sameAs(o->path_);
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::mountPoint() const
+{
+    return new BasePathNameT<T>(path_.mountPoint());
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::realName() const
+{
+    return new BasePathNameT<T>(path_.realName());
+}
+
+template<class T>
+bool BasePathNameT<T>::exists() const
+{
+    return path_.exists();
+}
+
+template<class T>
+bool BasePathNameT<T>::available() const
+{
+    return path_.available();
+}
+
+template<class T>
+std::string BasePathNameT<T>::clusterName() const
+{
+    return path_.clusterName();
+}
+
+template<class T>
+void BasePathNameT<T>::mkdir(short mode) const
+{
+    path_.mkdir(mode);
+}
+
+template<class T>
+void BasePathNameT<T>::unlink() const
+{
+    path_.unlink();
+}
+
+template<class T>
+void BasePathNameT<T>::syncParentDirectory() const
+{
+    path_.syncParentDirectory();
+}
+
+template<class T>
+void BasePathNameT<T>::rmdir() const
+{
+    path_.rmdir();
+}
+
+template<class T>
+void BasePathNameT<T>::touch() const
+{
+    path_.touch();
+}
+
+template<class T>
+void BasePathNameT<T>::children(std::vector<BasePathName*>& dirs,std::vector<BasePathName*>& files) const
+{
+	std::vector<T> d;
+	std::vector<T> f;
+	path_.children(d, f);
+
+	for(typename std::vector<T>::iterator j = d.begin(); j != d.end() ; ++j)
+	    	dirs.push_back(new BasePathNameT<T>(*j));
+
+	for(typename std::vector<T>::iterator j = f.begin(); j != f.end() ; ++j)
+	    	files.push_back(new BasePathNameT<T>(*j));
+}
+
+template<class T>
+void BasePathNameT<T>::match(std::vector<BasePathName*>& result,bool rec) const
+{
+    std::vector<T> r;
+    T::match(path_, r, rec);
+
+    for(typename std::vector<T>::iterator j = r.begin(); j != r.end() ; ++j)
+    	result.push_back(new BasePathNameT<T>(*j));
+}
+
+template<class T>
+void BasePathNameT<T>::reserve(const Length& length) const
+{
+    path_.reserve(length);
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::unique() const
+{
+    return new BasePathNameT<T>(T::unique(path_));
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::dirName() const
+{
+    return new BasePathNameT<T>(path_.dirName());
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::orphanName() const
+{
+    return new BasePathNameT<T>(path_.orphanName());
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::checkClusterNode() const
+{
+    return path_.checkClusterNode();
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::fullName() const
+{
+    return new BasePathNameT<T>(path_.fullName());
+}
+
+template<class T>
+BasePathName* BasePathNameT<T>::baseName(bool ext) const
+{
+    return new BasePathNameT<T>(path_.baseName(ext));
+}
+
+template<class T>
+std::string BasePathNameT<T>::extension() const {
+    return path_.extension();
+}
+
+template<class T>
+std::string BasePathNameT<T>::asString() const
+{
+    return std::string(path_);
+}
+
+template<class T>
+void BasePathNameT<T>::fileSystemSize(FileSystemSize& fs) const
+{
+    path_.fileSystemSize(fs);
+}
+
+template<class T>
+DataHandle* BasePathNameT<T>::fileHandle(bool overwrite) const
+{
+    return path_.fileHandle(overwrite);
+}
+
+template<class T>
+DataHandle* BasePathNameT<T>::partHandle(const OffsetList& o, const LengthList& l) const
+{
+    return path_.partHandle(o,l);
+}
+
+template<class T>
+DataHandle* BasePathNameT<T>::partHandle(const Offset& o, const Length& l) const
+{
+    return path_.partHandle(o,l);
+}
+
+
+template<class T>
+const std::string& BasePathNameT<T>::node() const
+{
+    return path_.node();
+}
+
+template<class T>
+const std::string& BasePathNameT<T>::path() const
+{
+    return path_.path();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/BasePathNameT.h b/eckit/src/eckit/filesystem/BasePathNameT.h
new file mode 100644
index 0000000..5cdeb87
--- /dev/null
+++ b/eckit/src/eckit/filesystem/BasePathNameT.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File filesystem/BasePathNameT.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_filesystem_BasePathNameT_h
+#define eckit_filesystem_BasePathNameT_h
+
+#include "eckit/filesystem/BasePathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class BasePathNameT : public BasePathName {
+public:
+
+// -- Contructors
+
+	BasePathNameT(const T& path):
+		path_(path) {}
+
+	BasePathNameT(const char* path):
+		path_(path) {}
+
+	BasePathNameT(const std::string& path):
+		path_(path) {}
+
+protected:
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+private:
+
+// -- Members
+
+	T path_;
+
+// -- Overridden methods
+
+    virtual BasePathName* clone() const;
+    virtual const char* localPath() const;
+    virtual Length size() const;
+    virtual time_t lastAccess() const;
+    virtual time_t lastModified() const;
+    virtual time_t created() const;
+
+    virtual bool isDir() const;
+    virtual bool isLink() const;
+
+    virtual void rename(const BasePathName&) const;
+    virtual void link(const BasePathName&) const;
+    virtual bool sameAs(const BasePathName&) const;
+    virtual BasePathName* mountPoint() const;
+    virtual BasePathName* realName() const;
+    virtual bool exists() const;
+    virtual bool available() const;
+    virtual void mkdir(short) const;
+    virtual void unlink() const;
+    virtual void rmdir() const;
+    virtual void touch() const;
+    virtual void children(std::vector<BasePathName*>&,std::vector<BasePathName*>&) const;
+    virtual void match(std::vector<BasePathName*>&,bool) const;
+    virtual void reserve(const Length&) const;
+
+    virtual BasePathName* unique() const;
+    virtual BasePathName* dirName() const;
+    virtual BasePathName* fullName() const;
+    virtual BasePathName* orphanName() const;
+    virtual BasePathName* checkClusterNode() const;
+    virtual BasePathName* baseName(bool) const;
+    virtual std::string extension() const;
+
+    virtual std::string clusterName() const;
+    virtual void syncParentDirectory() const;
+
+    virtual std::string asString() const;
+    virtual void fileSystemSize(FileSystemSize&) const;
+
+    virtual DataHandle* fileHandle(bool overwrite) const;
+    virtual DataHandle* partHandle(const OffsetList&, const LengthList&) const;
+    virtual DataHandle* partHandle(const Offset&, const Length&) const;
+
+    virtual const std::string& node() const;
+    virtual const std::string& path() const;
+
+// -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s,const BasePathNameT& p)	{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/filesystem/BasePathNameT.cc"
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileManager.cc b/eckit/src/eckit/filesystem/FileManager.cc
new file mode 100644
index 0000000..fa723b2
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileManager.cc
@@ -0,0 +1,207 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/FileName.h"
+#include "eckit/filesystem/FileManager.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Once.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/filesystem/PathName.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if 0
+typedef std::map<std::string,FileManager*>    FileManagerMap;
+typedef std::map<std::string,FileManagerFactory*> FactoryManagerMap;
+
+static Mutex* local_mutex         = 0;
+static FileManagerMap*    m = 0;
+static FactoryManagerMap* f = 0;
+
+// May need local_mutex here
+
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static void init(void)
+{
+    local_mutex = new Mutex;
+    m = new FileManagerMap();
+    f = new FactoryManagerMap();
+}
+
+#endif
+
+static Once<Mutex> local_mutex;
+static std::map<std::string,FileManager*> m;
+
+
+FileManager::FileManager(const std::string& name):
+        name_(name)
+{
+    
+    AutoLock<Mutex> lock(local_mutex);
+
+    ASSERT(m.find(name) == m.end());
+    m[name] = this;
+}
+
+FileManager::~FileManager()
+{
+    AutoLock<Mutex> lock(local_mutex);
+    m.erase(name_);
+   
+}
+
+FileManager& FileManager::lookUp(const std::string& name)
+{
+ 
+    AutoLock<Mutex> lock(local_mutex);
+    std::map<std::string, FileManager*>::const_iterator j = m.find(name);
+    
+    Log::info() << "Looking for FileManager [" << name << "]" << std::endl;
+
+    if (j == m.end())
+    {
+	Log::error() << "No FileManager for [" << name << "]" << std::endl;
+	Log::error() << "Managers are:" << std::endl;
+	for(j = m.begin() ; j != m.end() ; ++j)
+	  Log::error() << "   " << *((*j).second) << std::endl;
+        throw SeriousBug(std::string("No FileManager called ") + name);
+    }
+
+    return *((*j).second);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+#if 0
+
+ManagerFactory::ManagerFactory(const std::string& name):
+        name_(name)
+{
+    pthread_once(&once,init);
+    AutoLock<Mutex> lock(local_mutex);
+
+    ASSERT(f->find(name) == f->end());
+//cout << "Creating FileManager " << name_ << std::endl;
+    (*f)[name] = this;
+}
+
+ManagerFactory::~ManagerFactory()
+{
+    AutoLock<Mutex> lock(local_mutex);
+    ASSERT(f->find(name_) != f->end());
+//cout << "Destroying FileManager " << name_ << std::endl;
+    f->erase(name_);
+}
+
+FileManager* ManagerFactory::build(const std::string& name)
+{
+    pthread_once(&once,init);
+    AutoLock<Mutex> lock(local_mutex);
+
+    PathName config("~/etc/servers");
+    std::ifstream in(config.localPath());
+
+    if (!in) throw CantOpenFile(config);
+
+    std::string s,n;
+
+    while (in >> s >> n)
+        if ( s[0] != '#' && s != "" && n != "")
+        {
+            if (s == name)
+            {
+                if (f->find(n) == f->end())
+                    throw SeriousBug(
+                        "No tape manager called " + n
+                        + " was found, try to lookup server " + s);
+
+                return (*f)[n]->make(name);
+            }
+        }
+
+    // Use our own name
+
+    if (f->find(name) != f->end())
+        return (*f)[name]->make(name);
+
+    throw SeriousBug(std::string("Cannot find a tape manager for server ") + name);
+}
+
+#endif
+
+void FileManager::print(std::ostream& s) const
+{
+    s << "FileManager[" << name_ << "]";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class LocalFileManager : public FileManager {
+      
+    virtual bool exists(const FileName& f) {
+        return PathName(f.name()).exists();
+    }
+    
+    virtual DataHandle*  newWriteHandle(const FileName& f) {
+        return PathName(f.name()).fileHandle();
+    }
+    
+    virtual DataHandle*  newReadHandle(const FileName& f) {
+        return PathName(f.name()).fileHandle();
+    }
+    
+    virtual DataHandle*  newReadHandle(const FileName& f,const OffsetList& ol, const LengthList& ll) {
+        return PathName(f.name()).partHandle(ol,ll);
+    }
+
+public:
+    LocalFileManager(const std::string& name) : FileManager(name) {}
+};
+
+
+
+class MarsFSFileManager : public FileManager {
+  
+    virtual bool exists(const FileName& f) {
+        return PathName(f.scheme() + ":" + f.name()).exists();
+    }
+    
+    virtual DataHandle*  newWriteHandle(const FileName& f) {
+        return PathName(f.scheme() + ":" + f.name()).fileHandle();
+    }
+    
+    virtual DataHandle*  newReadHandle(const FileName& f) {
+        return PathName(f.scheme() + ":" + f.name()).fileHandle();
+    }
+    
+    virtual DataHandle*  newReadHandle(const FileName& f,const OffsetList& ol, const LengthList& ll) {
+        return PathName(f.scheme() + ":" + f.name()).partHandle(ol,ll);
+    }
+
+public:
+    MarsFSFileManager(const std::string& name) : FileManager(name) {}
+};
+
+
+static LocalFileManager  manager1("unix");
+static LocalFileManager  manager3("file");
+static MarsFSFileManager manager2("marsfs");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/FileManager.h b/eckit/src/eckit/filesystem/FileManager.h
new file mode 100644
index 0000000..92af28b
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileManager.h
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Length.h
+// Baudouin Raoult - ECMWF Aug 98
+
+#ifndef eckit_FileManager_h
+#define eckit_FileManager_h
+
+#include "eckit/eckit.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileName;
+class DataHandle;
+
+class FileManager {
+public:
+
+// - Methods
+
+    virtual bool exists(const FileName&) = 0;
+    
+	virtual DataHandle*  newWriteHandle(const FileName&) = 0;
+	virtual DataHandle*  newReadHandle(const FileName&) = 0;
+	virtual DataHandle*  newReadHandle(const FileName&,const OffsetList&, const LengthList&) = 0;
+
+	static FileManager& lookUp(const std::string&);
+
+protected:
+
+	FileManager(const std::string&);
+	virtual ~FileManager();
+
+	virtual void print(std::ostream&) const;
+
+private:
+
+	std::string name_;
+
+	friend std::ostream& operator<<(std::ostream& s,const FileManager& p)
+			{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+class FileManagerFactory {
+	std::string name_;
+	virtual FileManager* make(const std::string&) = 0 ;
+public:
+	static FileManager* build(const std::string&);
+	FileManagerFactory(const std::string&);
+	virtual ~FileManagerFactory();
+};
+
+template<class T>
+class FileManagerBuilder : public FileManagerFactory {
+	virtual FileManager* make(const std::string& name) { return new T(name); }
+public:
+	FileManagerBuilder(const std::string& name) : FileManagerFactory(name) {}
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileName.cc b/eckit/src/eckit/filesystem/FileName.cc
new file mode 100644
index 0000000..eca5b61
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileName.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/filesystem/FileName.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/filesystem/FileManager.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+FileName::FileName(const std::string& path)
+{
+    Tokenizer parse(":");
+    std::vector<std::string> s;
+
+    parse(path,s);
+
+    switch (s.size()) {
+    case 1:
+        scheme_ = "unix";
+        name_   = s[0];
+        break;
+
+    case 2:
+        name_   = s[1];
+        scheme_ = s[0];
+        break;
+
+    default:
+        scheme_ = s[0];
+        name_   = s[1];
+        for (size_t j = 2; j < s.size() ; j++)
+            name_ = name_ + ':' + s[j];
+        break;
+    }
+}
+
+FileName::~FileName()
+{
+}
+
+bool FileName::exists() const
+{
+    return FileManager::lookUp(scheme_).exists(*this);
+}
+
+DataHandle*  FileName::newWriteHandle() const
+{
+    return FileManager::lookUp(scheme_).newWriteHandle(*this);
+}
+
+DataHandle*  FileName::newReadHandle(const OffsetList& ol, const LengthList& ll) const
+{
+    return FileManager::lookUp(scheme_).newReadHandle(*this, ol, ll);
+}
+
+DataHandle*  FileName::newReadHandle() const
+{
+    return FileManager::lookUp(scheme_).newReadHandle(*this);
+}
+
+void FileName::print(std::ostream& s) const 
+{
+    s << "FileName[scheme=" << scheme_ << ",name=" << name_ << "]";
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/FileName.h b/eckit/src/eckit/filesystem/FileName.h
new file mode 100644
index 0000000..b58507d
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileName.h
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_FileName_h
+#define eckit_FileName_h
+
+#include "eckit/eckit.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+class DataHandle;
+
+class FileName {
+public:
+
+// -- Contructors
+
+	FileName(const std::string&);
+//	FileName(const std::string&,const std::string&);
+	
+
+// -- Destructor
+
+	~FileName(); 
+
+// -- Methods
+    
+    bool exists() const;
+
+	DataHandle*  newWriteHandle() const;
+	DataHandle*  newReadHandle(const OffsetList&, const LengthList&) const;
+	DataHandle*  newReadHandle() const;
+	
+	const std::string& name() const { return name_; }
+	const std::string& scheme() const { return scheme_; }
+
+protected: // methods
+
+	void print(std::ostream&) const;
+
+private: // members
+
+	std::string name_;
+	std::string scheme_;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const FileName& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileSpace.cc b/eckit/src/eckit/filesystem/FileSpace.cc
new file mode 100644
index 0000000..9000bd4
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSpace.cc
@@ -0,0 +1,180 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/filesystem/FileSpace.h"
+#include "eckit/filesystem/FileSpaceStrategies.h"
+#include "eckit/io/cluster/ClusterDisks.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/Once.h"
+#include "eckit/types/Types.h"
+#include "eckit/config/Resource.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef std::map<std::string, FileSpace*> Map;
+
+static Once<Mutex> local_mutex;
+static Map space;
+
+FileSpace::FileSpace(const std::string& name) :
+	name_(name), last_(0)
+{
+	AutoLock<Mutex> lock(local_mutex);
+	space[name] = this;
+	load();
+}
+
+FileSpace::~FileSpace()
+{
+	AutoLock<Mutex> lock(local_mutex);
+
+	space.erase(name_);
+}
+
+const PathName& FileSpace::selectFileSystem(const std::string& s) const
+{
+	load();
+
+	if(fileSystems_.size() == 0)
+	{
+		throw Retry(std::string("FileSpace [") + name_ + "] is undefined");
+	}
+
+    return FileSpaceStrategies::selectFileSystem(fileSystems_, s);
+}
+
+const PathName& FileSpace::selectFileSystem() const
+{
+	static Resource<std::string> s("fileSystemSelection", "leastUsed");
+	return selectFileSystem(std::string(s));
+}
+
+bool FileSpace::owns(const PathName& path) const
+{
+	//Log::info() << "FileSpace::owns " <<  name_ << " " << path << std::endl;
+	bool found = false;
+	find(path, found);
+	return found;
+}
+
+const PathName& FileSpace::sameFileSystem(const PathName& path) const
+{
+	//Log::info() << "FileSpace::sameFileSystem " <<  name_ << " " << path << std::endl;
+
+	bool found = false;
+	const PathName& f = find(path, found);
+
+	if(found)
+	{
+		Log::info() << f << " matches " << path << std::endl;
+		return f;
+	}
+	else
+	{
+		Log::warning() << "Cannot find matching file system for " << path << std::endl;
+		return selectFileSystem();
+	}
+}
+
+const PathName& FileSpace::find(const PathName& path, bool& found) const
+{
+	load();
+
+	PathName m(path.mountPoint());
+
+	//TODO: Cache the mount point...
+	for(Ordinal i = 0; i < fileSystems_.size(); i++)
+		if(fileSystems_[i].available() && (fileSystems_[i].mountPoint() == m))
+		{
+			found = true;
+			return fileSystems_[i];
+		}
+
+	found = false;
+	return path;
+}
+
+const FileSpace& FileSpace::lookUp(const std::string& name)
+{
+	AutoLock<Mutex> lock(local_mutex);
+
+	Map::iterator j = space.find(name);
+	if(j == space.end())
+	{
+        std::pair<std::string, FileSpace*> p(name, new FileSpace(name));
+		j = space.insert(p).first;
+	}
+	return *(*j).second;
+}
+
+bool FileSpace::exists(const std::string& name)
+{
+	PathName config(std::string("~/etc/disks/") + name);
+	return config.exists();
+}
+
+
+void FileSpace::load() const
+{
+	FileSpace* self = const_cast<FileSpace*> (this);
+
+	PathName config(std::string("~/etc/disks/") + name_);
+	time_t mod1 = config.lastModified();
+	time_t mod2 = ClusterDisks::lastModified(name_);
+    time_t modified = std::max(mod1, mod2);
+
+	if(last_ == modified)
+		return;
+
+	::srandom(::getpid());
+
+	self->last_ = modified;
+	self->fileSystems_.clear();
+
+	//Log::info() << "Loading FileSpace " << name_ << " modified " << TimeStamp(last_) << std::endl;
+
+    std::ifstream in(config.localPath());
+	if(!in)
+		throw CantOpenFile(config);
+
+	std::vector<std::string> disks;
+
+	char line[1024];
+	while(in >> line)
+		if(line[0] != 0 || line[0] != '#')
+			disks.push_back(line);
+
+	ClusterDisks::load(name_, disks);
+
+	for(Ordinal i = 0; i < disks.size(); i++)
+	{
+		PathName path(disks[i]);
+		try
+		{
+			if(path.available())
+				path.mkdir();
+			self->fileSystems_.push_back(path);
+		}
+		catch(std::exception& e)
+		{
+			Log::error() << "mkdir(" << path << ") failed : " << e.what() << std::endl;
+		}
+	}
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/FileSpace.h b/eckit/src/eckit/filesystem/FileSpace.h
new file mode 100644
index 0000000..ce266b1
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSpace.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   FileSpace.h
+/// @date   Mar 1998
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_FileSpace_h
+#define eckit_FileSpace_h
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FileSpace : private NonCopyable {
+public:
+
+// -- Methods
+
+	const PathName& sameFileSystem(const PathName&) const;
+	const std::vector<PathName>& fileSystems() const { return fileSystems_; }
+
+	bool owns(const PathName&) const;
+	
+	const PathName& selectFileSystem(const std::string&) const;
+	const PathName& selectFileSystem()              const;
+
+    const std::string& name() const { return name_; }
+
+// -- Class methods
+
+	static bool exists(const std::string&);
+	static const FileSpace& lookUp(const std::string&);
+
+private:
+
+	FileSpace(const std::string&);
+	~FileSpace(); 
+
+// -- Methods
+
+	void load() const;
+	const PathName& find(const PathName&,bool&) const;
+	
+
+// -- Members
+	
+	std::string           name_;
+	time_t			 last_;
+	std::vector<PathName> fileSystems_;
+
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileSpaceStrategies.cc b/eckit/src/eckit/filesystem/FileSpaceStrategies.cc
new file mode 100644
index 0000000..914f87f
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSpaceStrategies.cc
@@ -0,0 +1,367 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include <iostream>
+#include <cmath>
+
+#include "eckit/filesystem/FileSpaceStrategies.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/config/Resource.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct Candidate {
+
+    const PathName* path_;
+
+    FileSystemSize size_;
+
+    double probability_;
+
+    Candidate(const PathName* path) : path_(path) {}
+
+    void print(std::ostream& s) const {
+        s << "Candidate(path=" << path_->asString()
+          << ",total=" << total()
+          << ",available=" << available()
+          << ",percent=" << percent()
+          << ",probability=" << probability_
+          << ")";
+    }
+
+    friend std::ostream& operator<<(std::ostream& s, const Candidate& v) { v.print(s);  return s; }
+
+    const PathName& path() const { return *path_; }
+
+    double probability() const { return probability_; }
+    void   probability(double p) { probability_ = p; }
+
+    long percent() const { return long( ( (double(size_.total) - double(size_.available)) / size_.total * 100) + 0.5); }
+
+    unsigned long long total() const { return size_.total; }
+
+    unsigned long long available() const { return size_.available; }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+const PathName& FileSpaceStrategies::selectFileSystem(const std::vector<PathName>& fileSystems, const std::string& s)
+{
+    Log::info() << "FileSpace::selectFileSystem is " << s << std::endl;
+
+    if(s == "roundRobin")
+        return FileSpaceStrategies::roundRobin(fileSystems);
+
+    if(s == "weightedRandom")
+        return FileSpaceStrategies::weightedRandom(fileSystems);
+
+    if(s == "pureRandom")
+        return FileSpaceStrategies::pureRandom(fileSystems);
+
+    if(s == "weightedRandomPercent")
+        return FileSpaceStrategies::weightedRandomPercent(fileSystems);
+
+    if(s == "leastUsedPercent")
+        return FileSpaceStrategies::leastUsedPercent(fileSystems);
+
+    return FileSpaceStrategies::leastUsed(fileSystems);
+}
+
+const PathName& FileSpaceStrategies::leastUsed(const std::vector<PathName>& fileSystems)
+{
+	unsigned long long free = 0;
+	Ordinal best = 0;
+	Ordinal checked = 0;
+
+	ASSERT(fileSystems.size() != 0);
+
+	for(Ordinal i = 0; i < fileSystems.size(); i++)
+	{
+		Log::info() << "leastUsed: " << fileSystems[i] << " " << fileSystems[i].available() << std::endl;
+		if(fileSystems[i].available())
+		{
+			FileSystemSize fs;
+
+			try
+			{
+				fileSystems[i].fileSystemSize(fs);
+			}
+			catch(std::exception& e)
+			{
+				Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+				Log::error() << "** Exception is ignored" << std::endl;
+				Log::error() << "Cannot stat " << fileSystems[i] << Log::syserr << std::endl;
+				continue;
+			}
+
+			if(fs.available >= free || checked == 0)
+			{
+				free = fs.available;
+				best = i;
+				checked++;
+			}
+
+		}
+	}
+
+	if(!checked)
+        throw Retry(std::string("No available filesystem (") + fileSystems[0] + ")");
+
+    Log::info() << "Filespace strategy leastUsed selected " << fileSystems[best] << " " << Bytes(free) << " available" << std::endl;
+
+	return fileSystems[best];
+}
+
+const PathName& FileSpaceStrategies::leastUsedPercent(const std::vector<PathName>& fileSystems)
+{
+    long percent = 0;
+    size_t best = 0;
+
+    ASSERT(fileSystems.size() != 0);
+
+    for(size_t i = 0; i < fileSystems.size(); ++i)
+    {
+        Candidate candidate(&fileSystems[i]);
+
+        Log::info() << "leastUsedPercent: " << fileSystems[i] << " " << fileSystems[i].available() << std::endl;
+        if(fileSystems[i].available())
+        {
+            FileSystemSize fs;
+
+            try
+            {
+                fileSystems[i].fileSystemSize(candidate.size_);
+            }
+            catch(std::exception& e)
+            {
+                Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+                Log::error() << "** Exception is ignored" << std::endl;
+                Log::error() << "Cannot stat " << fileSystems[i] << Log::syserr << std::endl;
+                continue;
+            }
+
+            if(candidate.percent() >= percent)
+            {
+                percent = candidate.percent();
+                best = i;
+            }
+        }
+    }
+
+    Log::info() << "Filespace strategy leastUsedPercent selected " << fileSystems[best] << " " << percent << "% available" << std::endl;
+
+    return fileSystems[best];
+}
+
+typedef void (*compute_probability_t)(Candidate&);
+
+static void computePercent(Candidate& c) {
+    c.probability_ = double(c.percent());
+}
+
+static void computeAvailable(Candidate& c) {
+    c.probability_ = double(c.available());
+}
+
+static void computeIdentity(Candidate& c) {
+    c.probability_ = 1;
+}
+
+static void computeNull(Candidate& c) {
+    c.probability_ = 0;
+}
+
+static std::vector<Candidate> findCandidates(const std::vector<PathName>& fileSystems, compute_probability_t probability) {
+
+    ASSERT(fileSystems.size() != 0);
+
+    static Resource<long> candidateFileSystemPercent("candidateFileSystem", 99);
+
+    std::vector<Candidate> result;
+
+    for(size_t i = 0; i < fileSystems.size(); ++i) {
+
+        Candidate candidate(&fileSystems[i]);
+
+        if(fileSystems[i].available()) {
+
+            try
+            {
+                fileSystems[i].fileSystemSize(candidate.size_);
+            }
+            catch(std::exception& e)
+            {
+                Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+                Log::error() << "** Exception is ignored" << std::endl;
+                Log::error() << "Cannot stat " << fileSystems[i] << Log::syserr << std::endl;
+                continue;
+            }
+
+            if(candidate.total() == 0) {
+                Log::warning() << "Cannot get total size of " << fileSystems[i] << std::endl;
+                return std::vector<Candidate>();
+            }
+
+            if(candidate.percent() <= candidateFileSystemPercent) {
+
+                probability(candidate);
+
+//                Log::info() << candidate << std::endl;
+
+                result.push_back(candidate);
+            }
+        }
+    }
+
+    return result;
+}
+
+const PathName& FileSpaceStrategies::roundRobin(const std::vector<PathName>& fileSystems)
+{
+    std::vector<Candidate> candidates = findCandidates(fileSystems, &computeNull);
+
+    if(candidates.empty())
+        return leastUsed(fileSystems);
+
+    static long value = -1;
+
+	if(value < 0)
+		value = ::getpid();
+
+	value++;
+    value %= candidates.size();
+
+    Log::info() << "Filespace strategy roundRobin selected " << candidates[value].path() << " " << value << " out of " << candidates.size() << std::endl;
+
+    return candidates[value].path();
+}
+
+static void attenuateProbabilities(std::vector<Candidate>& candidates) {
+
+    ASSERT(!candidates.empty());
+
+    static Resource<bool> attenuateFileSpacePeakProbability("attenuateFileSpacePeakProbability", false);
+
+    if(!attenuateFileSpacePeakProbability) return;
+
+    // compute mean
+
+    double mean = 0.;
+    for(std::vector<Candidate>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
+        mean += i->probability();
+    }
+
+    mean /= candidates.size();
+
+    // compute variance
+
+    double variance = 0.;
+    for(std::vector<Candidate>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
+        double diff = (i->probability() - mean);
+        variance += diff*diff;
+    }
+
+    variance /= candidates.size();
+
+    // compute stddev
+
+    double stddev = std::sqrt(variance);
+
+    // attenuate the peaks that exceed the stddev to the stddev value
+
+    double max = mean + stddev;
+    for(std::vector<Candidate>::iterator i = candidates.begin(); i != candidates.end(); ++i) {
+        if(i->probability() > max) {
+            i->probability(max);
+        }
+    }
+}
+
+
+static const PathName& chooseByProbabylity(const char* strategy, const std::vector<Candidate>& candidates) {
+
+    double total = 0;
+    for(std::vector<Candidate>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
+//        Log::info() << "probability " << i->probability() << std::endl;
+        total += i->probability();
+    }
+
+    double choice = (double(random()) / double(RAND_MAX));
+
+//    Log::info() << "choice " << choice << std::endl;
+
+    choice *= total;
+
+    std::vector<Candidate>::const_iterator select = candidates.begin();
+
+    double lower = 0;
+    double upper = 0;
+    for(std::vector<Candidate>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
+
+        upper += i->probability();
+
+//        Log::info() << "Choice " << choice << " total = " << total << " lower = " << lower << " upper = " << upper << std::endl;
+
+        if(choice >= lower && choice < upper) {
+            select = i;
+            break;
+        }
+
+        lower = upper;
+    }
+
+    Log::info() << "Filespace strategy " << strategy << " selected " <<  select->path() << " " << Bytes(select->available()) << " available" << std::endl;
+
+    return select->path();
+}
+
+const PathName& FileSpaceStrategies::pureRandom(const std::vector<PathName>& fileSystems)
+{
+    std::vector<Candidate> candidates = findCandidates(fileSystems, &computeIdentity);
+
+    if(candidates.empty())
+        return leastUsed(fileSystems);
+
+    attenuateProbabilities(candidates); /* has no effect */
+
+    return chooseByProbabylity("pureRandom", candidates);
+}
+
+const PathName& FileSpaceStrategies::weightedRandom(const std::vector<PathName>& fileSystems)
+{
+    std::vector<Candidate> candidates = findCandidates(fileSystems, &computeAvailable);
+
+    if(candidates.empty())
+        return leastUsed(fileSystems);
+
+    attenuateProbabilities(candidates);
+
+    return chooseByProbabylity("weightedRandom",candidates);
+}
+
+const PathName& FileSpaceStrategies::weightedRandomPercent(const std::vector<PathName>& fileSystems)
+{
+    std::vector<Candidate> candidates = findCandidates(fileSystems, &computePercent);
+
+    if(candidates.empty())
+        return leastUsed(fileSystems);
+
+    attenuateProbabilities(candidates);
+
+    return chooseByProbabylity("weightedRandomPercent", candidates);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/FileSpaceStrategies.h b/eckit/src/eckit/filesystem/FileSpaceStrategies.h
new file mode 100644
index 0000000..1ff5c6e
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSpaceStrategies.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   FileSpaceStrategies.h
+/// @date   Mar 1998
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_FileSpaceStrategies_h
+#define eckit_FileSpaceStrategies_h
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FileSpaceStrategies : private NonCopyable {
+public:
+
+    static const PathName& selectFileSystem(const std::vector<PathName>& fileSystems, const std::string& s);
+
+    static const PathName& leastUsed(const std::vector<PathName>& fileSystems);
+    static const PathName& leastUsedPercent(const std::vector<PathName>& fileSystems);
+    static const PathName& roundRobin(const std::vector<PathName>& fileSystems);
+    static const PathName& pureRandom(const std::vector<PathName>& fileSystems);
+    static const PathName& weightedRandom(const std::vector<PathName>& fileSystems);
+    static const PathName& weightedRandomPercent(const std::vector<PathName>& fileSystems);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileSystem.cc b/eckit/src/eckit/filesystem/FileSystem.cc
new file mode 100644
index 0000000..1def445
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSystem.cc
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/FileSystem.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+FileSystem::FileSystem(const PathName& name):
+	name_(name),
+	offLine_(false)
+{
+	update();
+}
+
+FileSystem::~FileSystem()
+{
+}
+
+void FileSystem::update()
+{
+    if(!name_.available()) {
+        offLine_ = true;
+        return;
+    }
+
+	try {
+		name_.fileSystemSize(fs_);
+		offLine_ = false;
+	}
+	catch(std::exception&)
+	{
+		offLine_ = true;
+	}
+
+}
+
+unsigned long long FileSystem::total() const
+{
+	return fs_.total;
+}
+
+unsigned long long FileSystem::avail() const
+{
+	return fs_.available;
+}
+
+long FileSystem::usage() const
+{
+	if(!fs_.total) return 0;
+	return long(((double)(fs_.total-fs_.available)/fs_.total * 100) + 0.5);
+}
+
+void FileSystem::print(std::ostream& s) const
+{
+	s << name_;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/FileSystem.h b/eckit/src/eckit/filesystem/FileSystem.h
new file mode 100644
index 0000000..07e8ee3
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSystem.h
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FileSystem.h
+// MARS (Baudouin Raoult) - ECMWF Nov 01
+
+#ifndef eckit_FileSystem_h
+#define eckit_FileSystem_h
+
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileSystem {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	FileSystem(const PathName&);
+
+// -- Destructor
+
+	~FileSystem(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	void update();
+
+	std::string  name()       const { return name_; }
+	std::string  mountPoint() const;
+
+	unsigned long long  total() const;
+	unsigned long long  avail() const;
+	long usage() const;
+	bool offLine() const { return offLine_; }
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+
+protected:
+
+// -- Members
+	// None
+
+
+
+// -- Methods
+	
+	void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+//	FileSystem(const FileSystem&);
+//	FileSystem& operator=(const FileSystem&);
+
+// -- Members
+	// None
+
+	PathName name_;
+	PathName mount_;
+	FileSystemSize fs_;
+	bool offLine_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const FileSystem& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/FileSystemSize.h b/eckit/src/eckit/filesystem/FileSystemSize.h
new file mode 100644
index 0000000..b0544f8
--- /dev/null
+++ b/eckit/src/eckit/filesystem/FileSystemSize.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#pragma once
+#ifndef eckit_filesystem_FileSystemSize_h
+#define eckit_filesystem_FileSystemSize_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+struct FileSystemSize {
+	unsigned long long available;
+	unsigned long long total;
+	FileSystemSize() : available(0), total(0) {}
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_FileSystemSize_H
diff --git a/eckit/src/eckit/filesystem/LocalPathName.cc b/eckit/src/eckit/filesystem/LocalPathName.cc
new file mode 100644
index 0000000..127ba9e
--- /dev/null
+++ b/eckit/src/eckit/filesystem/LocalPathName.cc
@@ -0,0 +1,842 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include <dirent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include "eckit/filesystem/LocalPathName.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/BasePathNameT.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/StdFile.h"
+#include "eckit/io/cluster/ClusterDisks.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/TimeStamp.h"
+#include "eckit/os/Stat.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/system/SystemInfo.h"
+#include "eckit/system/Library.h"
+#include "eckit/types/Types.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/StaticMutex.h"
+#include "eckit/utils/Regex.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static std::vector<std::pair<std::string, std::string> > pathsTable;
+
+static void readPathsTable() {
+
+    static PathName path = eckit::Resource<PathName>("libraryConfigPaths,$LIBRARY_CONFIG_PATHS", "~/etc/paths");
+
+    std::ifstream in(path.localPath());
+
+    eckit::Log::debug() << "Loading library paths from " << path << std::endl;
+
+    if (!in) {
+        eckit::Log::debug() << "Failed to read " << path << " -- " << eckit::Log::syserr << std::endl;
+        return;
+    }
+
+    eckit::Tokenizer parse(" ");
+
+    char line[1024];
+    while(in.getline(line, sizeof(line))) {
+        std::vector<std::string> s;
+        parse(line, s);
+
+        size_t i = 0;
+        while (i < s.size()) {
+            if (s[i].length() == 0) {
+                s.erase(s.begin() + i);
+            } else {
+                i++;
+            }
+        }
+
+        if (s.size() == 0 || s[0][0] == '#') {
+            continue;
+        }
+
+        switch (s.size()) {
+        case 2:
+            pathsTable.push_back(std::make_pair(s[0] + "/", s[1]));
+            break;
+
+        default:
+            eckit::Log::warning() << "Library Paths: Invalid line ignored: " << line << std::endl;
+            break;
+
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static StaticMutex local_mutex;
+
+// I need to come back here when we have a proper std::string class
+
+LocalPathName LocalPathName::baseName(bool ext) const
+{
+    const char *q = path_.c_str();
+    int n = -1;
+    int i = 0;
+
+    while(*q) {
+        if(*q == '/') n = i;
+        q++;
+        i++;
+    }
+
+    std::string s(path_.c_str() + n + 1);
+    if(!ext)
+    {
+         n = -1;
+         i = 0;
+         q = s.c_str();
+         while(*q) {
+             if(*q == '.') { n = i; break; }
+             q++;
+             i++;
+         }
+         if(n>= 0) s.resize(n);
+    }
+    return s;
+
+}
+
+std::string LocalPathName::extension() const {
+    const std::string base = baseName();
+    const size_t lastDot = base.find_last_of('.');
+    // If no . was found return the empty string
+    if (lastDot == std::string::npos) return "";
+    return base.substr(lastDot);
+}
+
+LocalPathName LocalPathName::dirName() const
+{
+
+    int n = -1;
+    int i = 0;
+    const char *q = path_.c_str();
+    while(*q) {
+        if(*q == '/') n = i;
+        q++;
+        i++;
+    }
+
+    switch(n)
+    {
+        case -1:
+            return std::string(".");
+            break;
+
+        case 0:
+            return std::string("/");
+            break;
+
+    }
+
+    std::string s(path_);
+    s.resize(n);
+    return s;
+
+}
+
+BasePathName* LocalPathName::checkClusterNode() const
+{
+    std::string n = ClusterDisks::node(path_);
+    if(n != "local") {
+//        Log::warning() << *this << " is now on node [" << n << "]" << std::endl;
+        return new BasePathNameT<MarsFSPath>(MarsFSPath(n,path_));
+    }
+    return new BasePathNameT<LocalPathName>(LocalPathName(path_));
+}
+
+LocalPathName LocalPathName::orphanName() const
+{
+
+    std::ostringstream os;
+	os << mountPoint()  << "/orphans/";
+
+    const char *q = path_.c_str();
+    while(*q) {
+        if(*q == '/')  os << '_';
+		else os << *q;
+		q++;
+    }
+
+    return os.str();
+
+}
+
+bool LocalPathName::exists() const
+{
+    return ::access(path_.c_str(), F_OK) == 0;
+}
+
+bool LocalPathName::available() const
+{
+	return true;
+}
+
+LocalPathName LocalPathName::cwd()
+{
+    AutoLock<StaticMutex> lock(local_mutex);
+    char buf [PATH_MAX+1];
+	if(!getcwd(buf, sizeof(buf)))
+		throw FailedSystemCall("getcwd");
+    return LocalPathName( buf );
+}
+
+LocalPathName LocalPathName::unique(const LocalPathName& path)
+{
+    AutoLock<StaticMutex> lock(local_mutex);
+
+    char hostname[256];
+    SYSCALL(::gethostname(hostname, sizeof(hostname)));
+
+    static unsigned long long n = (((unsigned long long)::getpid()) << 32);
+
+    static std::string format = "%Y%m%d.%H%M%S";
+    std::ostringstream os;
+    os << path << '.' << TimeStamp(format) << '.' << hostname << '.' << n++;
+
+    while(::access(os.str().c_str(),F_OK) == 0)
+	{
+        std::ostringstream os;
+        os << path << '.' << TimeStamp(format) << '.' << hostname << '.' << n++;
+	}
+
+    LocalPathName result(os.str());
+	result.dirName().mkdir();
+	return result;
+}
+
+static void mkdir_if_not_exists( const char* path, short mode )
+{
+	Stat::Struct info;
+
+	if( Stat::stat( path, &info) < 0 )
+	{
+		if( errno == ENOENT ) // no such file or dir
+		{
+			if(::mkdir(path,mode) < 0)
+			{
+                /* don't throw error if it was created meanwhile by another process */
+                if( errno != EEXIST ) {
+                    throw FailedSystemCall(std::string("mkdir ") + path);
+                }
+			}
+		}
+		else // stat fails for unknown reason
+		{
+			throw FailedSystemCall( std::string("stat ") + path);
+		}
+	}
+
+}
+
+void LocalPathName::mkdir(short mode) const
+{
+	try
+	{
+		char path[PATH_MAX+1];
+
+        size_t l = path_.length();
+
+		ASSERT( sizeof(path) > l );
+
+		::strcpy( path, path_.c_str()  );
+
+        for(size_t i=1; i < l; i++)
+		{
+			if(path[i] == '/')
+			{
+				path[i] = 0;
+
+				mkdir_if_not_exists(path,mode);
+
+				path[i] = '/'; // put slash back
+			}
+		}
+
+		mkdir_if_not_exists(path,mode);
+	}
+	catch( FailedSystemCall& e )
+	{
+		Log::error() << "Failed to mkdir " << path_ << std::endl;
+		throw;
+	}
+}
+
+void LocalPathName::link(const LocalPathName& from,const LocalPathName& to)
+{
+	if(::link(from.c_str(),to.c_str()) != 0)
+		throw FailedSystemCall(std::string("::link(") + from.path_ + ',' + to.path_ + ')');
+}
+
+void LocalPathName::rename(const LocalPathName& from,const LocalPathName& to)
+{
+	if(::rename(from.c_str(),to.c_str()) != 0)
+		throw FailedSystemCall(std::string("::rename(") + from.path_ + ',' + to.path_ + ')');
+}
+
+void LocalPathName::unlink() const
+{
+	Log::info() << "Unlink " << path_ << std::endl;
+	if(::unlink(path_.c_str()) != 0)
+    {
+        if(errno != ENOENT)
+            throw FailedSystemCall(std::string("unlink ") + path_);
+        else
+            Log::info() << "Unlink failed " << path_ << Log::syserr << std::endl;
+    }
+}
+
+void LocalPathName::rmdir() const
+{
+	Log::info() << "Rmdir " << path_ << std::endl;
+	if(::rmdir(path_.c_str()) != 0)
+    {
+        if(errno != ENOENT)
+            throw FailedSystemCall(std::string("rmdir ") + path_);
+        else
+            Log::info() << "Rmdir failed " << path_ << Log::syserr << std::endl;
+    }
+}
+
+
+void operator<<(Stream& s,const LocalPathName& path)
+{
+	s << path.path_;
+}
+
+void operator>>(Stream& s,LocalPathName& path)
+{
+	s >> path.path_;
+}
+
+LocalPathName LocalPathName::fullName() const
+{
+	if(path_.length()  > 0 && path_[0] != '/')
+    {
+        char buf[PATH_MAX];
+		return LocalPathName(std::string(getcwd(buf,PATH_MAX)) + "/" + path_);
+    }
+
+    return *this;
+}
+
+
+static void expandTilde(std::string& path)
+{
+    if(path[0] == '~')
+    {
+        if(path.length() > 1 && path[1] != '/') {
+
+            std::string s;
+            size_t j = 1;
+            while(j < path.length() && path[j] != '/') {
+                s += path[j];
+                j++;
+            }
+
+            // 1. match against a path defined in application / tool home ~/etc/paths
+            //    or file defined in $LIBRARY_CONFIG_PATHS
+
+            pthread_once(&once, readPathsTable);
+
+            std::vector<std::pair<std::string, std::string> >::const_iterator best = pathsTable.end();
+            size_t match = 0;
+
+            for(std::vector<std::pair<std::string, std::string> >::const_iterator k = pathsTable.begin(); k != pathsTable.end(); ++k) {
+                const std::string& prefix = (*k).first;
+                size_t m = prefix.length();
+
+                if(path.substr(0, m) == prefix) { // longest match is selected
+                    if(m > match) {
+                        match = m;
+                        best = k;
+                    }
+                }
+            }
+
+            if(match) {
+                path = (*best).second + "/" + path.substr(match);
+                return;
+            }
+
+            // 2. if it has the form ~libname/, then delegate to the matching library to expand the path
+
+            if(eckit::system::Library::exists(s)) {
+                path = eckit::system::Library::lookup(s).expandPath(path);
+                return;
+            }
+
+            // in case the library is unknown (not registered), issue error
+
+            std::ostringstream oss;
+            oss << "Could not expand tilde in path " << path << std::endl;
+
+            throw SeriousBug(oss.str());
+        }
+
+        // 3. expand ~/ with registered home
+
+        path =  Main::instance().home() + "/" + path.substr(1);
+    }
+}
+
+LocalPathName& LocalPathName::tidy()
+{
+	if(path_.length() == 0) return *this;
+
+    expandTilde(path_);
+
+	bool more = true;
+	bool last = (path_[path_.length()-1] == '/');
+
+
+	size_t p = 0;
+	size_t q = 0;
+
+	std::vector<std::string> v;
+
+	while( (p = path_.find('/',q)) != std::string::npos )
+	{
+		v.push_back(std::string(path_,q,p-q));
+		q = p+1;
+	}
+
+	v.push_back(std::string(path_,q));
+
+
+	while(more)
+	{
+		more   = false;
+
+		std::vector<std::string>::iterator i = v.begin();
+		std::vector<std::string>::iterator j = i+1;
+
+
+		while( j != v.end() )
+		{
+			if( (*j) == "" || (*j) == ".")
+			{
+				v.erase(j);
+				more = true;
+				break;
+			}
+
+			if( (*j) == ".." && (*i) != "..")
+			{
+				if ((*i) != "")
+					*i = ".";
+				v.erase(j);
+				more = true;
+				break;
+			}
+
+			++i;
+			++j;
+
+		}
+
+	}
+
+	path_ = "";
+
+    for(size_t i = 0; i < v.size() ; i++)
+	{
+		path_ += v[i];
+		if(i != v.size() - 1)
+			path_ += '/';
+	}
+
+	if(last) path_ += '/';
+
+	return *this;
+}
+
+class StdDir {
+	DIR *d_;
+public:
+	StdDir(const LocalPathName& p) { d_ = opendir(p.localPath());}
+	~StdDir()                 { if(d_) closedir(d_);    }
+	operator DIR*()           { return d_;              }
+};
+
+void LocalPathName::match(const LocalPathName& root,std::vector<LocalPathName>& result,bool rec)
+{
+	// Note that pattern matching will only be done
+	// on the base name.
+
+	LocalPathName dir   = root.dirName();
+	std::string   base  = root.baseName();
+
+//    Log::info() << "dir  = " << dir << std::endl;
+//    Log::info() << "base = " << base << std::endl;
+
+	// all those call should be members of LocalPathName
+
+    //long len = base.length() * 2 + 2;
+
+	Regex re(base,true);
+
+	StdDir d(dir);
+
+	if(d == 0)
+	{
+		Log::error() << "opendir(" << dir << ")" << Log::syserr << std::endl;
+		throw FailedSystemCall(std::string("opendir(") + std::string(dir) + ")");
+	}
+
+	struct dirent buf;
+
+
+	for(;;)
+	{
+		struct dirent *e;
+#ifdef EC_HAVE_READDIR_R
+		errno = 0;
+		if(readdir_r(d,&buf,&e) != 0)
+        {
+			if(errno)
+				throw FailedSystemCall("readdir_r");
+			else
+				e = 0;
+        }
+#else
+		e = readdir(d);
+#endif
+
+		if(e == 0)
+			break;
+
+//        Log::info() << "e->d_name = " << e->d_name << std::endl;
+
+		if(re.match(e->d_name))
+		{
+//            Log::info() << "match !!! ---> " << e->d_name << std::endl;
+
+			LocalPathName path = std::string(dir) + std::string("/") + std::string(e->d_name);
+			result.push_back(path);
+		}
+
+		if(rec && e->d_name[0] != '.')
+		{
+			LocalPathName full = dir + "/" + e->d_name;
+            Stat::Struct info;
+            SYSCALL(Stat::lstat(full.c_str(),&info));
+			if(S_ISDIR(info.st_mode))
+				match(full+"/"+base,result,true);
+		}
+	}
+
+}
+
+void LocalPathName::children(std::vector<LocalPathName>& files,std::vector<LocalPathName>& directories)
+	const
+{
+	StdDir d(*this);
+
+	if(d == 0)
+	{
+		Log::error() << "opendir(" << *this << ")" << Log::syserr << std::endl;
+		throw FailedSystemCall("opendir");
+	}
+
+	struct dirent buf;
+
+
+	for(;;)
+	{
+		struct dirent *e;
+#ifdef EC_HAVE_READDIR_R
+		errno = 0;
+		if(readdir_r(d,&buf,&e) != 0)
+        {
+			if(errno)
+				throw FailedSystemCall("readdir_r");
+			else
+				e = 0;
+        }
+#else
+		e = readdir(d);
+#endif
+
+		if(e == 0)
+			break;
+
+		if(e->d_name[0] == '.')
+			if(e->d_name[1] == 0 || (e->d_name[1] =='.' && e->d_name[2] == 0))
+				continue;
+
+		LocalPathName full = *this + "/" + e->d_name;
+        Stat::Struct info;
+        if(Stat::stat(full.c_str(),&info) == 0)
+		{
+			if(S_ISDIR(info.st_mode))
+				directories.push_back(full);
+			else
+				files.push_back(full);
+		}
+		else Log::error() << "Cannot stat " << full << Log::syserr << std::endl;
+
+	}
+}
+
+void LocalPathName::touch() const
+{
+	dirName().mkdir();
+	StdFile f(*this,"a"); // This should touch the file
+}
+
+// This routine is used by TxnLog. It is important that
+// the inode is preserved, otherwise ftok will give different
+// result
+
+void LocalPathName::empty() const
+{
+	StdFile f(*this,"w"); // This should clear the file
+}
+
+void LocalPathName::copy(const LocalPathName& other) const
+{
+	FileHandle in(*this);
+	FileHandle out(other);
+	in.saveInto(out);
+}
+
+void LocalPathName::backup() const
+{
+	copy(unique(*this));
+}
+
+Length LocalPathName::size() const
+{
+    Stat::Struct info;
+
+    if(Stat::stat(path_.c_str(),&info) < 0)
+		throw FailedSystemCall(path_);
+
+	// Should ASSERT(is file)
+
+	return info.st_size;
+}
+
+time_t LocalPathName::created() const
+{
+    Stat::Struct info;
+    if(Stat::stat(path_.c_str(),&info) < 0)
+		throw FailedSystemCall(path_);
+	return info.st_ctime;
+}
+
+time_t LocalPathName::lastModified() const
+{
+    Stat::Struct info;
+    if(Stat::stat(path_.c_str(),&info) < 0)
+		throw FailedSystemCall(path_);
+	return info.st_mtime;
+}
+
+time_t LocalPathName::lastAccess() const
+{
+    Stat::Struct info;
+    SYSCALL(Stat::stat(path_.c_str(),&info));
+	return info.st_atime;
+}
+
+bool LocalPathName::isDir() const
+{
+    Stat::Struct info;
+    SYSCALL(Stat::stat(path_.c_str(),&info));
+	return S_ISDIR(info.st_mode);
+}
+
+bool LocalPathName::isLink() const
+{
+    Stat::Struct info;
+    SYSCALL(Stat::lstat(path_.c_str(),&info));
+    return S_ISLNK(info.st_mode);
+}
+
+bool LocalPathName::sameAs(const LocalPathName& other) const
+{
+	if(!exists() || !other.exists())
+		return false;
+
+    Stat::Struct info1; SYSCALL(Stat::stat(path_.c_str(),&info1));
+    Stat::Struct info2; SYSCALL(Stat::stat(other.path_.c_str(),&info2));
+	return (info1.st_dev == info2.st_dev) && (info1.st_ino == info2.st_ino);
+}
+
+LocalPathName LocalPathName::realName() const
+{
+	char result[PATH_MAX+1];
+	// This code is certainly machine dependant
+	if(!::realpath(c_str(), result))
+		throw FailedSystemCall(std::string("realpath ") + path_);
+	return result;
+#if 0
+	char save[PATH_MAX+1];
+	if(!getcwd(save,sizeof(save)))
+		throw FailedSystemCall("getcwd");
+
+	bool direct = isDir();
+
+	if(direct)
+	 	SYSCALL(::chdir(path_.c_str()));
+	else
+		SYSCALL(::chdir(dirName().path_.c_str()));
+
+	char dir[PATH_MAX+1];
+	if(!getcwd(dir,sizeof(dir)))
+	{
+		PANIC(::chdir(save) != 0); // We must go back
+		throw FailedSystemCall("getcwd");
+	}
+
+	if(direct)
+		return LocalPathName(dir);
+	else
+		return LocalPathName(std::string(dir) + "/" + baseName());
+#endif
+
+}
+
+void LocalPathName::truncate(Length len) const
+{
+	SYSCALL(::truncate(path_.c_str(),len));
+}
+
+void LocalPathName::reserve(const Length& len) const
+{
+	ASSERT(!exists() || size() == Length(0));
+
+	PartFileHandle part("/dev/zero",0,len);
+	FileHandle     file(*this);
+
+	Log::status() << "Reserving " << Bytes(len) << std::endl;
+	part.saveInto(file);
+}
+
+void LocalPathName::fileSystemSize(FileSystemSize& fs) const
+{
+    struct statvfs d;
+    SYSCALL(::statvfs(path_.c_str(),&d));
+	long unavail = (d.f_bfree - d.f_bavail);
+    fs.available = (unsigned long long)d.f_bavail*(unsigned long long)d.f_bsize;
+    fs.total     = (unsigned long long)(d.f_blocks-unavail)*(unsigned long long)d.f_bsize;
+}
+
+
+DataHandle* LocalPathName::fileHandle(bool overwrite) const
+{
+    return new FileHandle(path_, overwrite);
+}
+
+DataHandle* LocalPathName::partHandle(const OffsetList& o, const LengthList& l) const
+{
+    return new PartFileHandle(path_, o, l);
+}
+
+DataHandle* LocalPathName::partHandle(const Offset& o, const Length& l) const
+{
+    return new PartFileHandle(path_, o, l);
+}
+
+LocalPathName LocalPathName::mountPoint() const
+{
+//	dev_t last;
+    Stat::Struct s;
+	LocalPathName p(*this);
+
+	ASSERT(p.path_.length() > 0 && p.path_[0] == '/');
+
+    SYSCALL2(Stat::stat(p.c_str(),&s),p);
+	dev_t dev = s.st_dev;
+
+	while(p != "/") {
+		LocalPathName q(p.dirName());
+        SYSCALL(Stat::stat(q.c_str(),&s));
+		if(s.st_dev != dev)
+			return p;
+		p = q;
+	}
+
+	return p;
+}
+
+void LocalPathName::syncParentDirectory() const
+{
+    PathName directory = dirName();
+#ifdef EC_HAVE_DIRFD
+//    Log::info() << "Syncing directory " << directory << std::endl;
+    DIR *d = opendir(directory.localPath());
+    if (!d) SYSCALL(-1);
+
+    int dir;
+    SYSCALL(dir = dirfd(d));
+    int ret = fsync(dir);
+
+    while (ret < 0 && errno == EINTR)
+        ret = fsync(dir);
+
+    if (ret < 0) {
+        Log::error() << "Cannot fsync directory [" << directory << "]" << Log::syserr << std::endl;
+    }
+
+    ::closedir(d);
+#else
+    Log::info() << "Syncing directory " << directory << " (not supported)" << endl;
+#endif
+}
+
+const std::string& LocalPathName::node() const
+{
+    return NodeInfo::thisNode().node();
+}
+
+const std::string& LocalPathName::path() const
+{
+    return path_;
+}
+
+std::string LocalPathName::clusterName() const
+{
+    std::ostringstream os;
+    os << "marsfs://" << node() << fullName();
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/LocalPathName.h b/eckit/src/eckit/filesystem/LocalPathName.h
new file mode 100644
index 0000000..4dd6a78
--- /dev/null
+++ b/eckit/src/eckit/filesystem/LocalPathName.h
@@ -0,0 +1,285 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date   May 96
+
+#ifndef eckit_filesystem_LocalPathName_h
+#define eckit_filesystem_LocalPathName_h
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Length;
+struct FileSystemSize;
+class DataHandle;
+class BasePathName;
+
+// The class LocalPathName represent a unix path name.
+
+class LocalPathName {
+public:
+
+    friend void operator<<(Stream&,const LocalPathName&);
+    friend void operator>>(Stream&,LocalPathName&);
+
+    friend std::ostream& operator<<(std::ostream& s,const LocalPathName& p)
+    {
+        return s << p.path_;
+    }
+
+// Contructors
+
+    LocalPathName(const char* p = "/")    : path_(p) { tidy(); }
+    LocalPathName(const std::string& p)        : path_(p) { tidy(); }
+    LocalPathName(const LocalPathName& p) : path_(p.path_) {}
+
+// Assignment
+
+    LocalPathName& operator=(const LocalPathName& p)
+    {
+        path_ = p.path_;
+        return *this;
+    }
+
+    LocalPathName& operator=(const std::string& p)
+    {
+        path_ = p      ;
+        return tidy();
+    }
+
+    LocalPathName& operator=(const char* p)
+    {
+        path_ = p      ;
+        return tidy();
+    }
+
+// Convertors
+
+    operator const std::string&() const { return path_; }
+
+    const char* localPath() const  { return path_.c_str(); }
+
+    const char* c_str() const  { return path_.c_str();  }
+
+// Operators
+
+    LocalPathName& operator+=(const std::string& s)  {
+        path_ += s;
+        return tidy();
+    }
+    LocalPathName& operator+=(const char* s)    {
+        path_ += s;
+        return tidy();
+    }
+    LocalPathName& operator+=(char s)           {
+        path_ += s;
+        return tidy();
+    }
+
+    bool operator<(const LocalPathName& other) const
+    {
+        return path_ < other.path_;
+    }
+
+    bool operator>(const LocalPathName& other) const
+    {
+        return path_ > other.path_;
+    }
+
+    bool operator<=(const LocalPathName& other) const
+    {
+        return path_ <= other.path_;
+    }
+
+    bool operator>=(const LocalPathName& other) const
+    {
+        return path_ >= other.path_;
+    }
+
+    bool operator!=(const LocalPathName& other) const
+    {
+        return path_ != other.path_;
+    }
+
+    bool operator==(const LocalPathName& other) const
+    {
+        return path_ == other.path_;
+    }
+
+// Methods
+
+    /// Directory part of the path
+    /// @return directory part of the path
+    LocalPathName dirName() const;
+
+    /// Absolute path
+    /// @return absolute path
+    LocalPathName fullName() const;
+
+    LocalPathName orphanName() const;
+
+    BasePathName* checkClusterNode() const;
+
+    std::string clusterName() const;
+
+    /// Base name of the path
+    /// @param ext if false the extension is stripped
+    /// @return the name part of the path
+    LocalPathName baseName(bool ext = true) const;
+
+    /// File extension
+    /// @return file extension (empty string if there is none)
+    std::string extension() const;
+
+    /// Check if path exists
+    /// @return true if the path exists
+    bool exists() const;
+
+    /// Check if path is available
+    /// @return true (local paths are always available)
+    bool available() const;
+
+    /// Touch the path. The path leading to it is created if it does not exist.
+    void touch() const;
+
+    /// Reserve a given number of Bytes (file must be of length 0 or not exist)
+    /// @param len number of Bytes to reserves
+    void reserve(const Length&) const;
+
+    /// File size
+    /// @return Size in Bytes
+    Length size() const;
+
+    /// Last access time
+    /// @return Time of last access
+    time_t lastAccess()   const;
+
+    /// Last modification time
+    /// @return Time of last modification
+    time_t lastModified() const;
+
+    /// Creation time
+    /// @return Time of creation
+    time_t created()      const;
+
+    /// Check if path is a directory
+    /// @return true if the path is a directory
+    bool isDir() const;
+
+    /// Check if path is a symlink
+    /// @return true if the path is a symlink
+    bool isLink() const;
+
+    /// Clear the file
+    void empty() const;
+
+    /// Truncate the file to given number of Bytes.
+    /// @param len number of Bytes to truncat to
+    void truncate(Length) const;
+
+    /// Create the directory and all directories leading to it with given mode
+    /// @param mode file mode bits
+    void mkdir(short mode = 0777) const;
+
+    /// Change the file mode
+    /// @param mode file mode bits
+    void chmod(short mode) const;
+
+    /// Unlink the path
+    void unlink() const;
+
+    /// Remove the directory
+    void rmdir() const;
+
+    /// Create a copy with a unique path name
+    void backup() const;
+
+    /// Create a copy with the given path name
+    void copy(const LocalPathName&) const;
+
+    /// Check if a given path points to the same inode as this path
+    /// @return true if the path points to the same inode as the given path
+    bool sameAs(const LocalPathName&) const;
+
+    /// Mount point of the path
+    /// @return mount point of path
+    LocalPathName mountPoint() const;
+
+    /// Real path (with symlinks resolved)
+    /// @return the real path (with symlinks resolved)
+    LocalPathName realName() const;
+
+    /// Get child files and directories
+    /// @param files vector to be filled with child files of path
+    /// @param directories vector to be filled with child diretories of path
+    void children(std::vector<LocalPathName>&,std::vector<LocalPathName>&) const;
+
+    const std::string& node() const;
+
+    /// String representation
+    /// @return string representation of path
+    const std::string& path() const;
+
+    void fileSystemSize(FileSystemSize&) const;
+    DataHandle* fileHandle(bool overwrite) const;
+    DataHandle* partHandle(const OffsetList&, const LengthList&) const;
+    DataHandle* partHandle(const Offset&, const Length&) const;
+
+     void syncParentDirectory() const;
+
+// Class methods
+
+    static LocalPathName unique(const LocalPathName&);
+    static void match(const LocalPathName&,std::vector<LocalPathName>&,bool=false);
+    static void link(const LocalPathName& from,const LocalPathName& to);
+    static void rename(const LocalPathName& from,const LocalPathName& to);
+    static void rename(const LocalPathName& from,const std::string& newBase);
+
+    static LocalPathName cwd();
+
+private:
+
+// Members
+
+    std::string path_;
+
+// Methods
+
+    LocalPathName& tidy();
+
+// friend
+
+    friend LocalPathName operator+(const LocalPathName& p,const std::string& s)
+    {
+        return LocalPathName(p.path_ + s);
+    }
+
+    friend LocalPathName operator+(const LocalPathName& p,const char* s)
+    {
+        return LocalPathName(p.path_ + s);
+    }
+
+    friend LocalPathName operator+(const LocalPathName& p,char s)
+    {
+        return LocalPathName(p.path_ + s);
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/PathName.cc b/eckit/src/eckit/filesystem/PathName.cc
new file mode 100644
index 0000000..bad4448
--- /dev/null
+++ b/eckit/src/eckit/filesystem/PathName.cc
@@ -0,0 +1,439 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File filesystem/PathName.cc
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#include "eckit/io/cluster/ClusterDisks.h"
+#include "eckit/io/Length.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+
+#include "eckit/filesystem/BasePathName.h"
+#include "eckit/filesystem/BasePathNameT.h"
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static BasePathName* make(const std::string& p) {
+
+    if(p.find("marsfs:") == 0)
+        return new BasePathNameT<MarsFSPath>(p);
+
+    /*
+    const std::string& node = ClusterDisks::node(p);
+    if(node.length())
+        return new BasePathNameT<MarsFSPath>(std::string("marsfs://") + node +  p , ext);
+    */
+
+    return new BasePathNameT<LocalPathName>(p);
+}
+
+PathName::PathName(const char* p)
+{
+	path_ = make(p);
+}
+
+PathName::PathName(const std::string& p)
+{
+	path_ = make(p);
+}
+
+PathName::PathName(const PathName& other):
+	path_(other.path_->clone())
+{
+}
+
+PathName::PathName(const LocalPathName& other):
+	path_(new BasePathNameT<LocalPathName>(other))
+{
+}
+
+
+PathName::PathName(BasePathName* path):
+	path_(path)
+{
+	ASSERT(path);
+}
+
+PathName& PathName::operator=(const PathName& other)
+{
+	if(this != &other) {
+		delete path_;
+		path_ = other.path_->clone();
+	}
+	return *this;
+}
+
+PathName& PathName::operator=(const std::string& s)
+{
+	// TODO: Review me
+	*this = PathName(s);
+	return *this;
+}
+
+PathName& PathName::operator=(const char* s)
+{
+	// TODO: Review me
+	*this = PathName(s);
+	return *this;
+}
+
+PathName::~PathName()
+{
+	delete path_;
+}
+
+
+void PathName::print(std::ostream& s) const
+{
+	s << *path_;
+}
+
+const char* PathName::localPath() const
+{
+	return path_->localPath();
+}
+
+PathName PathName::clusterName() const
+{
+    return PathName(path_->clusterName());
+}
+
+Length PathName::size() const
+{
+	return path_->size();
+}
+
+time_t PathName::lastAccess() const
+{
+	return path_->lastAccess();
+}
+
+time_t PathName::lastModified() const
+{
+	return path_->lastModified();
+}
+
+time_t PathName::created() const
+{
+    return path_->created();
+}
+
+bool PathName::isDir() const
+{
+    return path_->isDir();
+}
+
+bool PathName::isLink() const
+{
+    return path_->isLink();
+}
+
+bool PathName::exists() const
+{
+	return path_->exists();
+}
+
+bool PathName::available() const
+{
+	return path_->available();
+}
+
+void PathName::unlink() const
+{
+    path_->unlink();
+}
+
+void PathName::syncParentDirectory() const
+{
+    path_->syncParentDirectory();
+}
+
+void PathName::rmdir() const
+{
+	path_->rmdir();
+}
+
+void PathName::touch() const
+{
+	path_->touch();
+}
+
+void PathName::mkdir(short mode) const
+{
+	path_->mkdir(mode);
+}
+
+void PathName::rename(const PathName& from, const PathName& to)
+{
+	from.path_->rename(*to.path_);
+}
+
+void PathName::link(const PathName& from, const PathName& to)
+{
+	from.path_->link(*to.path_);
+}
+
+void PathName::children(std::vector<PathName>& dirs,std::vector<PathName>& files) const
+{
+	std::vector<BasePathName*> d;
+	std::vector<BasePathName*> f;
+
+	path_->children(d,f);
+
+	for(std::vector<BasePathName*>::iterator j = d.begin(); j != d.end(); ++j)
+		dirs.push_back(PathName(*j));
+
+	for(std::vector<BasePathName*>::iterator j = f.begin(); j != f.end(); ++j)
+		files.push_back(PathName(*j));
+}
+
+void PathName::match(const PathName& path, std::vector<PathName>& result, bool rec)
+{
+	std::vector<BasePathName*> v;
+	path.path_->match(v, rec);
+	for(std::vector<BasePathName*>::iterator j = v.begin(); j != v.end(); ++j)
+		result.push_back(PathName(*j));
+}
+
+bool PathName::operator <(const PathName& other) const
+{
+	return this->asString() < other.asString();
+}
+
+bool PathName::operator >(const PathName& other) const
+{
+	return this->asString() > other.asString();
+}
+
+bool PathName::operator !=(const PathName& other) const
+{
+	return this->asString() != other.asString();
+}
+
+bool PathName::operator ==(const PathName& other) const
+{
+	return this->asString() == other.asString();
+}
+
+bool PathName::operator <=(const PathName& other) const
+{
+	return this->asString() <= other.asString();
+}
+
+bool PathName::operator >=(const PathName& other) const
+{
+	return this->asString() >= other.asString();
+}
+
+PathName PathName::unique(const PathName& path)
+{
+	return PathName(path.path_->unique());
+}
+
+PathName PathName::dirName() const
+{
+	return PathName(path_->dirName());
+}
+
+PathName PathName::orphanName() const
+{
+	return PathName(path_->orphanName());
+}
+
+PathName PathName::checkClusterNode() const
+{
+	return PathName(path_->checkClusterNode());
+}
+
+PathName PathName::baseName(bool ext) const
+{
+	return PathName(path_->baseName(ext));
+}
+
+std::string PathName::extension() const
+{
+  return path_->extension();
+}
+
+PathName PathName::fullName() const
+{
+	return PathName(path_->fullName());
+}
+
+void PathName::reserve(const Length& length) const
+{
+	path_->reserve(length);
+}
+
+void PathName::fileSystemSize(FileSystemSize& fs) const
+{
+	path_->fileSystemSize(fs);
+}
+
+PathName PathName::mountPoint() const
+{
+	return PathName(path_->mountPoint());
+}
+
+PathName PathName::realName() const {
+    return PathName(path_->realName());
+}
+
+bool PathName::sameAs(const PathName& other) const
+{
+	return path_->sameAs(*other.path_);
+}
+
+DataHandle* PathName::fileHandle(bool overwrite) const
+{
+    return path_->fileHandle(overwrite);
+}
+
+DataHandle* PathName::partHandle(const OffsetList& o, const LengthList& l) const
+{
+    return path_->partHandle(o,l);
+}
+
+DataHandle* PathName::partHandle(const Offset& o, const Length& l) const
+{
+    return path_->partHandle(o,l);
+}
+
+std::string PathName::asString() const
+{
+	return path_->asString();
+}
+
+PathName operator+(const PathName& p,const std::string& s)
+{
+	// TODO: delegate that to "path_"
+	return PathName(p.asString() + s);
+}
+
+PathName operator+(const PathName& p,const char* s)
+{
+	// TODO: delegate that to "path_"
+	return PathName(p.asString() + s);
+}
+
+PathName operator+(const PathName& p,char s)
+{
+	// TODO: delegate that to "path_"
+	return PathName(p.asString() + s);
+}
+
+PathName operator/(const PathName& p,const std::string& s)
+{
+	// TODO: delegate that to "path_"
+    return PathName(p.asString() + "/" + s);
+}
+
+PathName operator/(const PathName& p,const char* s)
+{
+	// TODO: delegate that to "path_"
+	return PathName(p.asString() + "/" + s);
+}
+
+PathName operator/(const PathName& p,char s)
+{
+	// TODO: delegate that to "path_"
+	return PathName(p.asString() + "/" + s);
+}
+
+void operator<<(Stream& s,const PathName& path)
+{
+	// TODO: delegate that to "path_"
+	s << path.asString();
+}
+
+void operator>>(Stream& s,PathName& path)
+{
+	// TODO: delegate that to "path_"
+	std::string p;
+	s >> p;
+	path = PathName(p);
+}
+
+
+std::string PathName::shorten(const std::string& s) {
+	// TODO: Read from ~etc/disk/...
+
+
+	if(s.find("/locked/") != std::string::npos) return ".../locked/...";
+	if(s.find("/transfer/") != std::string::npos) return ".../transfer/...";
+	if(s.find("/defrag/") != std::string::npos) return ".../defrag/...";
+	if(s.find("/temp/") != std::string::npos) return ".../temp/...";
+	if(s.find("/obstmp/") != std::string::npos) return ".../obstmp/...";
+	if(s.find("/infrequent/") != std::string::npos) return ".../infrequent/...";
+	if(s.find("/prearc/") != std::string::npos) return ".../prearc/...";
+	if(s.find("/cache/") != std::string::npos) return ".../cache/...";
+	return s.substr(0,10) + "...";
+}
+
+const std::string& PathName::node() const
+{
+	return path_->node();
+}
+
+const std::string& PathName::path() const
+{
+	return path_->path();
+}
+
+PathName &PathName::operator /=(const std::string &s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + "/" + s);
+	return *this;
+}
+
+PathName &PathName::operator /=(const char *s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + "/" + s );
+	return *this;
+}
+
+PathName &PathName::operator /=(char s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + "/" + s);
+	return *this;
+}
+PathName &PathName::operator +=(const std::string &s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + s);
+	return *this;
+}
+
+PathName &PathName::operator +=(const char *s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + s );
+	return *this;
+}
+
+PathName &PathName::operator +=(char s)
+{
+    // TODO: Review me
+    *this = PathName(this->asString() + s);
+	return *this;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/filesystem/PathName.h b/eckit/src/eckit/filesystem/PathName.h
new file mode 100644
index 0000000..8a1d43b
--- /dev/null
+++ b/eckit/src/eckit/filesystem/PathName.h
@@ -0,0 +1,250 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File filesystem/PathName.h
+// B.Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_PathName_h
+#define eckit_filesystem_PathName_h
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "eckit/filesystem/FileSystemSize.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Length;
+class BasePathName;
+class DataHandle;
+class LocalPathName;
+class MarsFSPath;
+
+// The class PathName represent a unix path name.
+
+class PathName {
+public:
+
+    friend void operator<<(Stream&,const PathName&);
+    friend void operator>>(Stream&,PathName&);
+
+    friend std::ostream& operator<<(std::ostream& s,const PathName& p)
+    { p.print(s); return s; }
+
+    // Contructors
+
+    PathName(const char* p = "/");
+    PathName(const std::string& p);
+    PathName(const PathName& p);
+    PathName(const LocalPathName&);
+    PathName(const MarsFSPath&);
+
+    // Destructor
+    ~PathName();
+
+    // Assignment
+
+    PathName& operator=(const PathName& p);
+    PathName& operator=(const std::string& p);
+    PathName& operator=(const char* p);
+
+    // Convertors
+
+    std::string asString() const;
+    operator std::string() const { return asString(); }
+    /* const char* c_str() const      { return path_.c_str(); } */
+
+    const char* localPath() const;
+
+    // Operators
+
+    PathName& operator+=(const std::string& s);
+    PathName& operator+=(const char* s);
+    PathName& operator+=(char s);
+
+    PathName& operator/=(const std::string& s);
+    PathName& operator/=(const char* s);
+    PathName& operator/=(char s);
+
+    bool operator<(const PathName& other) const;
+    bool operator>(const PathName& other) const;
+    bool operator<=(const PathName& other) const;
+    bool operator>=(const PathName& other) const;
+    bool operator!=(const PathName& other) const;
+    bool operator==(const PathName& other) const;
+
+    // Methods
+
+    /// Directory part of the path
+    /// @return directory part of the path
+    PathName dirName() const;
+
+    /// Absolute path
+    /// @return absolute path
+    PathName fullName() const;
+
+    PathName clusterName() const;
+
+    /// Base name of the path
+    /// @param ext if false the extension is stripped
+    /// @return the name part of the path
+    PathName baseName(bool ext = true) const;
+
+    /// path extension
+    /// @return file extension (empty string if there is none)
+    std::string extension() const;
+
+    /// Check if path exists
+    /// @return true if the path exists
+    bool exists() const;
+
+    /// Check if path is available
+    /// @return true (local paths are always available)
+    bool available() const;
+
+    /// Touch the path. The path leading to it is created if it does not exist.
+    void touch() const;
+
+    /// Reserve a given number of Bytes (file must be of length 0 or not exist)
+    /// @param len number of Bytes to reserves
+    void reserve(const Length&) const;
+
+    /// File size
+    /// @return Size in Bytes
+    Length size() const;
+
+    /// Last access time
+    /// @return Time of last access
+    time_t lastAccess()   const;
+
+    /// Last modification time
+    /// @return Time of last modification
+    time_t lastModified() const;
+
+    /// Creation time
+    /// @return Time of creation
+    time_t created()      const;
+
+    /// Check if path is a directory
+    /// @return true if the path is a directory
+    bool isDir() const;
+
+    /// Check if path is a symlink
+    /// @return true if the path is a symlink
+    bool isLink() const;
+
+    /// Clear the file
+    void empty() const;
+
+    /// Truncate the file to given number of Bytes.
+    /// @param len number of Bytes to truncat to
+    void truncate(Length) const;
+
+    /// Create the directory and all directories leading to it with given mode
+    /// @param mode file mode bits
+    void mkdir(short mode = 0777) const;
+
+    /// Change the file mode
+    /// @param mode file mode bits
+    void chmod(short mode) const;
+
+    /// Unlink the path
+    void unlink() const;
+
+    /// Remove the directory
+    void rmdir() const;
+
+    void syncParentDirectory() const;
+
+    /// Create a copy with a unique path name
+    void backup() const;
+
+    /// Create a copy with the given path name
+    void copy(const PathName&) const;
+
+    /// Check if a given path points to the same inode as this path
+    /// @return true if the path points to the same inode as the given path
+    bool sameAs(const PathName&) const;
+
+    /// Mount point of the path
+    /// @return mount point of path
+    PathName mountPoint() const;
+
+    /// Real path (with symlinks resolved)
+    /// @return the real path (with symlinks resolved)
+    PathName realName() const;
+
+    PathName orphanName() const;
+    PathName checkClusterNode() const;
+
+    const std::string& node() const;
+
+    /// String representation
+    /// @return string representation of path
+    const std::string& path() const;
+
+    /// Get child files and directories
+    /// @param files vector to be filled with child files of path
+    /// @param directories vector to be filled with child diretories of path
+    void children(std::vector<PathName>&, std::vector<PathName>&) const;
+
+    void fileSystemSize(FileSystemSize&) const;
+
+    DataHandle* fileHandle(bool overwrite = false) const;
+    DataHandle* partHandle(const OffsetList&, const LengthList&) const;
+    DataHandle* partHandle(const Offset&, const Length&) const;
+
+    // Class methods
+
+    static PathName unique(const PathName&);
+    static void match(const PathName&, std::vector<PathName>&,bool=false);
+    static void link(const PathName& from, const PathName& to);
+    static void rename(const PathName& from, const PathName& to);
+    static void rename(const PathName& from, const std::string& newBase);
+
+    static std::string shorten(const std::string&);
+
+private:
+
+    PathName(BasePathName*);
+
+    // Members
+
+
+    BasePathName* path_;
+
+    // Methods
+
+    PathName& tidy();
+    void print(std::ostream&) const;
+
+    // friend
+
+    friend PathName operator+(const PathName& p,const std::string& s);
+    friend PathName operator+(const PathName& p,const char* s);
+    friend PathName operator+(const PathName& p,char s);
+
+    friend PathName operator/(const PathName& p,const std::string& s);
+    friend PathName operator/(const PathName& p,const char* s);
+    friend PathName operator/(const PathName& p,char s);
+};
+
+template <> struct VectorPrintSelector<PathName> { typedef VectorPrintSimple selector; };
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/TempFile.cc b/eckit/src/eckit/filesystem/TempFile.cc
new file mode 100644
index 0000000..9f5954a
--- /dev/null
+++ b/eckit/src/eckit/filesystem/TempFile.cc
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/filesystem/FileSpace.h"
+#include "eckit/filesystem/TempFile.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TempFile::TempFile():
+	PathName(dir("temp"))
+{
+}
+
+TempFile::TempFile(const std::string& name):
+	PathName(dir(name))
+{
+}
+
+TempFile::~TempFile()
+{
+	unlink();
+}
+
+PathName TempFile::dir(const std::string& s)
+{
+	return PathName::unique(FileSpace::lookUp(s).selectFileSystem()+ "/tmp");
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/TempFile.h b/eckit/src/eckit/filesystem/TempFile.h
new file mode 100644
index 0000000..25ae279
--- /dev/null
+++ b/eckit/src/eckit/filesystem/TempFile.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TempFile.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_TempFile_h
+#define eckit_TempFile_h
+
+#include "eckit/filesystem/PathName.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TempFile : public PathName {
+public:
+
+// -- Contructors
+
+	TempFile();
+	TempFile(const std::string&); /* the std::string is the name of a filespace */
+
+// -- Destructor
+
+	~TempFile();
+
+private:
+
+// -- Class member
+
+	static PathName dir(const std::string&);
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/TmpFile.cc b/eckit/src/eckit/filesystem/TmpFile.cc
new file mode 100644
index 0000000..f2f732d
--- /dev/null
+++ b/eckit/src/eckit/filesystem/TmpFile.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/filesystem/TmpFile.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static PathName tmp()
+{
+    const char *tmpdir = ::getenv("TMPDIR");
+	if(!tmpdir)
+	{
+        tmpdir = "/tmp";
+    }
+
+    long max = pathconf(tmpdir, _PC_PATH_MAX);
+    char path[max];
+    sprintf(path, "%s/eckitXXXXXXXXXXX", tmpdir);
+    int fd;
+    SYSCALL(fd = ::mkstemp(path));
+
+    PathName result(path);
+    result.touch();
+
+    SYSCALL(::close(fd));
+
+    return result;
+}
+
+
+TmpFile::TmpFile(): PathName(tmp())
+{
+}
+
+TmpFile::~TmpFile()
+{
+    unlink();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/TmpFile.h b/eckit/src/eckit/filesystem/TmpFile.h
new file mode 100644
index 0000000..4308f4e
--- /dev/null
+++ b/eckit/src/eckit/filesystem/TmpFile.h
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TmpFile.h
+// Baudouin Raoult - ECMWF Sep 01
+
+#ifndef eckit_TmpFile_h
+#define eckit_TmpFile_h
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TmpFile : public  PathName,
+				private NonCopyable {
+public:
+
+	TmpFile();
+
+	~TmpFile(); 
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSClient.cc b/eckit/src/eckit/filesystem/marsfs/MarsFSClient.cc
new file mode 100644
index 0000000..5e17539
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSClient.cc
@@ -0,0 +1,752 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/filesystem/marsfs/MarsFSClient.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/thread/ThreadSingleton.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+//#define X(a) Log::userInfo() << #a << std::endl; Log::info() << #a << std::endl; 
+#define X(a) /**/
+
+// TODO: Return errno
+//
+
+class MarsFSClientSettings {
+	bool forever_;
+public:
+	MarsFSClientSettings() :
+		forever_(true)
+	{
+	}
+
+	bool retry() const
+	{
+		return forever_;
+	}
+
+	void retry(bool on)
+	{
+		forever_ = on;
+	}
+};
+
+static ThreadSingleton<MarsFSClientSettings> settings;
+
+inline static bool retry()
+{
+	return settings.instance().retry();
+}
+
+MarsFSClientRetry::MarsFSClientRetry(bool on) :
+	old_(settings.instance().retry())
+{
+	settings.instance().retry(on);
+}
+
+MarsFSClientRetry::~MarsFSClientRetry()
+{
+	settings.instance().retry(old_);
+}
+
+MarsFSClient::MarsFSClient(const MarsFSPath& path) :
+	connector_(Connector::service("marsfs", path.node()))
+{
+}
+
+MarsFSClient::~MarsFSClient()
+{
+}
+
+Length MarsFSClient::size(const std::string& path)
+{
+	X(MarsFSClient::size);
+
+	for(;;)
+	{
+
+		try
+		{
+			AutoMemoize m(connector_, 1);
+
+			Stream& s = connector_;
+			unsigned long long len;
+			s << "size";
+			s << path;
+			s >> len;
+			return len;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+bool MarsFSClient::exists(const std::string& path)
+{
+	X(MarsFSClient::exit);
+
+	for(;;)
+	{
+
+		try
+		{
+			//AutoMemoize m(connector_, 10);
+
+			bool ok;
+			Stream& s = connector_;
+
+			s << "exists";
+			s << path;
+			s >> ok;
+			return ok;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::string MarsFSClient::mountPoint(const std::string& path)
+{
+	X(MarsFSClient::mountPoint);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 60);
+
+			std::string p;
+			Stream& s = connector_;
+			s << "mountp";
+			s << path;
+			s >> p;
+			return p;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::string MarsFSClient::baseName(const std::string& path, bool ext)
+{
+	X(MarsFSClient::baseName);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 60);
+
+			std::string b;
+			Stream& s = connector_;
+			s << "baseName";
+			s << path;
+			s << ext;
+			s >> b;
+			return b;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::string MarsFSClient::dirName(const std::string& path)
+{
+	X(MarsFSClient::dirName);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 60);
+
+			std::string d;
+			Stream& s = connector_;
+			s << "dirName";
+			s << path;
+			s >> d;
+			return d;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::string MarsFSClient::fullName(const std::string& path)
+{
+	X(MarsFSClient::fullName);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 60);
+
+			std::string f;
+			Stream& s = connector_;
+			s << "fullName";
+			s << path;
+			s >> f;
+			return f;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::string MarsFSClient::unique(const std::string& path)
+{
+	X(MarsFSClient::unique);
+
+	for(;;)
+	{
+		try
+		{
+			std::string u;
+			Stream& s = connector_;
+			s << "unique";
+			s << path;
+			s >> u;
+			return u;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+bool MarsFSClient::sameAs(const std::string& path1, const std::string& path2)
+{
+	X(MarsFSClient::sameAs);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 10);
+
+			bool ok;
+			Stream& s = connector_;
+			s << "sameas";
+			s << path1;
+			s << path2;
+			s >> ok;
+			return ok;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::mkdir(const std::string& path, short mode)
+{
+	X(MarsFSClient::mkdir);
+
+	for(;;)
+	{
+		try
+		{
+			bool ok;
+			Stream& s = connector_;
+			s << "mkdir";
+			s << path;
+			s << mode;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::match(const std::string& path, std::vector<std::string>& result, bool recurse)
+{
+	X(MarsFSClient::match);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 2);
+
+			Stream& s = connector_;
+			s << "match";
+			s << path;
+			s << recurse;
+
+			result.clear();
+
+			long n;
+			s >> n;
+
+			std::string r;
+
+			for(long i = 0; i < n; i++)
+			{
+				s >> r;
+				result.push_back(r);
+			}
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::children(const std::string& path, std::vector<std::string>& dirs, std::vector<std::string>& files)
+{
+	X(MarsFSClient::children);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 2);
+
+			Stream& s = connector_;
+			s << "children";
+			s << path;
+
+			dirs.clear();
+
+			long n;
+			s >> n;
+
+			std::string r;
+
+			for(long i = 0; i < n; i++)
+			{
+				s >> r;
+				dirs.push_back(r);
+			}
+
+			files.clear();
+
+			s >> n;
+
+			for(long i = 0; i < n; i++)
+			{
+				s >> r;
+				files.push_back(r);
+			}
+
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::vector<std::string> MarsFSClient::getFileSpaces()
+{
+	X(MarsFSClient::getFileSpaces);
+	for(;;)
+	{
+		try
+		{
+
+			AutoMemoize m(connector_, 20);
+
+			Stream& s = connector_;
+			s << "getFileSpaces";
+
+			std::vector<std::string> result;
+
+			long n;
+			s >> n;
+
+			std::string r;
+
+			for(long i = 0; i < n; i++)
+			{
+				s >> r;
+				result.push_back(r);
+			}
+
+			return result;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+std::vector<std::string> MarsFSClient::getFileSystems(const std::string& name)
+{
+	X(MarsFSClient::getFileSystems);
+
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 20);
+
+			Stream& s = connector_;
+			s << "getFileSystems";
+			s << name;
+
+			std::vector<std::string> result;
+
+			long n;
+			s >> n;
+
+			std::string r;
+
+			for(long i = 0; i < n; i++)
+			{
+				s >> r;
+				result.push_back(r);
+			}
+
+			return result;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+
+}
+
+void MarsFSClient::rename(const std::string& from, const std::string& to)
+{
+	X(MarsFSClient::rename);
+	for(;;)
+	{
+		try
+		{
+			bool ok;
+			Stream& s = connector_;
+			s << "rename";
+			s << from;
+			s << to;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::link(const std::string& from, const std::string& to)
+{
+	X(MarsFSClient::link);
+	for(;;)
+	{
+		try
+		{
+
+			bool ok;
+			Stream& s = connector_;
+			s << "link";
+			s << from;
+			s << to;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::unlink(const std::string& path)
+{
+	X(MarsFSClient::unlink);
+	for(;;)
+	{
+		try
+		{
+
+			bool ok;
+			Stream& s = connector_;
+			s << "unlink";
+			s << path;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::syncParentDirectory(const std::string& path)
+{
+    X(MarsFSClient::syncParentDirectory);
+    for(;;)
+    {
+        try
+        {
+
+            bool ok;
+            Stream& s = connector_;
+            s << "syncParentDirectory";
+            s << path;
+            s >> ok;
+            return;
+        }
+        catch(ConnectorException& e)
+        {
+            if(!retry())
+                throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is ignored" << std::endl;
+        }
+    }
+}
+
+void MarsFSClient::rmdir(const std::string& path)
+{
+	X(MarsFSClient::rmdir);
+	for(;;)
+	{
+
+		try
+		{
+
+			bool ok;
+			Stream& s = connector_;
+			s << "rmdir";
+			s << path;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::touch(const std::string& path)
+{
+	X(MarsFSClient::touch);
+	for(;;)
+	{
+
+		try
+		{
+
+			bool ok;
+			Stream& s = connector_;
+			s << "touch";
+			s << path;
+			s >> ok;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+void MarsFSClient::fileSystemSize(const std::string& path, FileSystemSize& fs)
+{
+	X(MarsFSClient::fileSystemSize);
+	for(;;)
+	{
+		try
+		{
+			AutoMemoize m(connector_, 10);
+
+			Stream& s = connector_;
+			s << "statfs";
+			s << path;
+
+			s >> fs.available;
+			s >> fs.total;
+			return;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+time_t MarsFSClient::created(const std::string& path)
+{
+	X(MarsFSClient::created);
+	for(;;)
+	{
+
+		try
+		{
+
+			AutoMemoize m(connector_, 60);
+
+			Stream& s = connector_;
+			unsigned long long len;
+			s << "created";
+			s << path;
+			s >> len;
+			return len;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+time_t MarsFSClient::lastAccess(const std::string& path)
+{
+	X(MarsFSClient::lastAccess);
+	for(;;)
+	{
+
+		try
+		{
+
+			AutoMemoize m(connector_, 1);
+
+			Stream& s = connector_;
+			unsigned long long len;
+			s << "lastAccess";
+			s << path;
+			s >> len;
+			return len;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+time_t MarsFSClient::lastModified(const std::string& path)
+{
+	X(MarsFSClient::lastModified);
+	for(;;)
+	{
+
+		try
+		{
+
+			AutoMemoize m(connector_, 1);
+
+			Stream& s = connector_;
+			unsigned long long len;
+			s << "lastModified";
+			s << path;
+			s >> len;
+			return len;
+		}
+		catch(ConnectorException& e)
+		{
+			if(!retry())
+				throw;
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSClient.h b/eckit/src/eckit/filesystem/marsfs/MarsFSClient.h
new file mode 100644
index 0000000..05656cb
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSClient.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsFSClient.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_MarsFSClient_h
+#define eckit_MarsFSClient_h
+
+#include "eckit/net/Connector.h"
+#include "eckit/io/Length.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+struct FileSystemSize;
+class MarsFSPath;
+
+class MarsFSClient : private NonCopyable {
+public:
+
+// -- Contructors
+
+    MarsFSClient(const MarsFSPath&);
+
+// -- Destructor
+
+	~MarsFSClient(); 
+
+// -- Methods
+    
+    void mkdir(const std::string&,short);
+    bool exists(const std::string&);
+    std::string mountPoint(const std::string&);
+    void fileSystemSize(const std::string& path, FileSystemSize& fs);
+    void rename(const std::string&,const std::string&);
+    void link(const std::string&,const std::string&);
+    void unlink(const std::string&);
+    void rmdir(const std::string&);
+    void touch(const std::string&);
+    void syncParentDirectory(const std::string&);
+    std::string unique(const std::string&);
+    Length size(const std::string&);
+    bool sameAs(const std::string& path1, const std::string& path2);
+    void match(const std::string& path,std::vector<std::string>& result,bool recurse);
+    void children(const std::string& path,std::vector<std::string>& dirs, std::vector<std::string>& files);
+    std::string baseName(const std::string& path, bool ext);
+    std::string dirName(const std::string& path);
+    std::string fullName(const std::string& path);
+
+
+    time_t created(const std::string&);
+    time_t lastAccess(const std::string&);
+    time_t lastModified(const std::string&);
+
+
+    std::vector<std::string> getFileSpaces();
+    std::vector<std::string> getFileSystems(const std::string&);
+
+protected:
+
+
+// -- Members
+
+	Connector& connector_;
+
+private:
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const MarsFSClient& p)
+	//	{ p.print(s); return s; }
+    friend class MarsFSClientCache;
+};
+
+class MarsFSClientRetry {
+    bool old_;
+public:
+    MarsFSClientRetry(bool on);
+    ~MarsFSClientRetry();
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSFile.cc b/eckit/src/eckit/filesystem/marsfs/MarsFSFile.cc
new file mode 100644
index 0000000..d3f28c3
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSFile.cc
@@ -0,0 +1,152 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsFSFile.cc
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#include "eckit/eckit_config.h"
+
+#include "eckit/filesystem/marsfs/MarsFSFile.h"
+#include "eckit/config/Resource.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MarsFSFile::MarsFSFile(const MarsFSPath& path):
+    MarsFSClient(path),
+    path_(path),
+	lock_(connector_)
+{
+    static std::string marsFsHashing = eckit::Resource<std::string>("marsFsHashing", "None");
+    hash_.reset( eckit::HashFactory::build(marsFsHashing) );
+}
+
+MarsFSFile::~MarsFSFile()
+{
+}
+
+
+void MarsFSFile::print(std::ostream& s) const
+{
+	s << path_;
+}
+
+Length MarsFSFile::open(const char* mode, bool overwrite)
+{
+//    Log::info() << "MarsFSFile::open " << path_ << " " << mode << std::endl;
+    Stream& s = connector_;
+    int port;
+    unsigned long long length;
+
+    s << "open";
+    s << path_.path();
+    s << mode;
+    s << overwrite;
+    s >> port;
+
+    data_.connect(connector_.host() , port);
+
+    s >> length;
+    return length;
+}
+
+long MarsFSFile::read(void* buffer, long len) {
+
+    Stream& s = connector_;
+    long size;
+
+    //Log::info() << "MarsFSFile::read " << len << std::endl;
+    s << "read";
+    s << len;
+
+    s >> size;
+
+    ASSERT(data_.isConnected());
+    ASSERT(data_.read(buffer, size) == size);
+
+    // hash integrety check
+    {
+        // Timer t("MarsFSFile::read() hashing");
+        std::string remoteHash;
+        s >> remoteHash;
+        ASSERT(hash_->compute(buffer, size) == remoteHash);
+    }
+    return size;
+}
+
+long MarsFSFile::write(const void* buffer, long len) {
+
+    Stream& s = connector_;
+    long size;
+
+    s << "write";
+
+    s << len;
+
+    ASSERT(data_.isConnected());
+    ASSERT(data_.write(buffer, len) == len);
+
+    // hash integrety check
+    {
+        // Timer t("MarsFSFile::write() hashing");
+        s << hash_->compute(buffer, len);
+    }
+
+    s >> size;
+
+    return size;
+}
+
+Offset MarsFSFile::seek(const Offset& pos) {
+    Stream& s = connector_;
+    //Log::info() << "MarsFSFile::seek " << pos << std::endl;
+
+    unsigned long long llpos = pos;
+
+    s << "seek";
+    s << llpos;
+
+    s >> llpos;
+
+    return llpos;
+}
+
+void MarsFSFile::skip(const Length& n)
+{
+    Stream& s = connector_;
+    //Log::info() << "MarsFSFile::skip " << n << std::endl;
+
+    unsigned long long p = n;
+    bool ok;
+
+    s << "skip";
+    s << p;
+    s >> ok;
+
+}
+
+void MarsFSFile::close()
+{
+    Stream& s = connector_;
+    bool ok;
+    s << "close";
+    s >> ok;
+}
+
+Length MarsFSFile::length()
+{
+    return size(path_.path());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSFile.h b/eckit/src/eckit/filesystem/marsfs/MarsFSFile.h
new file mode 100644
index 0000000..37ff6d4
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSFile.h
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsFSFile.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_MarsFSFile_h
+#define eckit_MarsFSFile_h
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/filesystem/marsfs/MarsFSClient.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/utils/Hash.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MarsFSFile : private MarsFSClient {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	MarsFSFile(const MarsFSPath&);
+
+// -- Destructor
+
+	~MarsFSFile(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+//
+    Length open(const char* mode, bool overwrite = false);
+    long read(void*,long);
+    long write(const void*,long);
+    void close();
+    void skip(const Length&);
+    Offset seek(const Offset&);
+    Length length();
+	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	void print(std::ostream&) const; 
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	MarsFSFile(const MarsFSFile&);
+	MarsFSFile& operator=(const MarsFSFile&);
+
+// -- Members
+//
+    TCPClient data_;
+    MarsFSPath path_;
+	AutoLock<Connector> lock_;
+
+    eckit::ScopedPtr<Hash> hash_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const MarsFSFile& p)
+		{ p.print(s); return s; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSPath.cc b/eckit/src/eckit/filesystem/marsfs/MarsFSPath.cc
new file mode 100644
index 0000000..94676cb
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSPath.cc
@@ -0,0 +1,337 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/BasePathNameT.h"
+#include "eckit/io/cluster/ClusterDisks.h"
+#include "eckit/io/cluster/ClusterNodes.h"
+#include "eckit/io/Length.h"
+#include "eckit/filesystem/marsfs/MarsFSClient.h"
+#include "eckit/io/MarsFSHandle.h"
+#include "eckit/io/MarsFSPartHandle.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/io/cluster/NodeInfo.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+MarsFSPath::MarsFSPath(const std::string& path)
+{
+    std::string p = path;
+    //node_ = NodeInfo::thisNode().node();
+
+    ASSERT(p.find("marsfs:") == 0);
+    {
+        p = p.substr(7);
+    }
+
+    ASSERT(p.find("//") == 0);
+    {
+        p        = p.substr(2);
+        size_t n = p.find("/");
+        ASSERT(n != std::string::npos);
+
+        node_ = p.substr(0,n);
+        p     = p.substr(n);
+
+
+    }
+    path_ = p;
+
+    PANIC(path_.find("marsfs:") != std::string::npos);
+    PANIC(node_.find("marsfs:") != std::string::npos);
+
+}
+
+MarsFSPath::MarsFSPath(const std::string& node, const std::string& path):
+    node_(node),
+    path_(path)
+{
+    PANIC(node_.find("marsfs:") != std::string::npos);
+    PANIC(path_.find("marsfs:") != std::string::npos);
+}
+
+MarsFSPath::MarsFSPath(const MarsFSPath& other):
+    node_(other.node_),
+    path_(other.path_)
+{
+    PANIC(node_.find("marsfs:") != std::string::npos);
+    PANIC(path_.find("marsfs:") != std::string::npos);
+}
+
+MarsFSPath& MarsFSPath::operator=(const MarsFSPath& other)
+{
+    node_ = other.node_;
+    path_ = other.path_;
+    PANIC(node_.find("marsfs:") != std::string::npos);
+    PANIC(path_.find("marsfs:") != std::string::npos);
+    return *this;
+}
+
+MarsFSPath::MarsFSPath(Stream& s)
+{
+    s >> node_;
+    s >> path_;
+}
+
+MarsFSPath::~MarsFSPath()
+{
+}
+
+
+void MarsFSPath::print(std::ostream& s) const
+{
+    s << "marsfs://" << node_ << path_;
+}
+
+void operator<<(Stream& s, MarsFSPath const& path)
+{
+    s << path.node_;
+    s << path.path_;
+}
+
+bool MarsFSPath::isLocal() const
+{
+    return node_ == NodeInfo::thisNode().node();
+}
+
+MarsFSPath::operator std::string() const
+{
+    return std::string("marsfs://") + node_ + path_;
+    // return path_;
+}
+
+
+MarsFSPath MarsFSPath::dirName() const
+{
+    return MarsFSPath(node(),MarsFSClient(*this).dirName(path_));
+}
+
+MarsFSPath MarsFSPath::fullName() const
+{
+    return MarsFSPath(node(),MarsFSClient(*this).fullName(path_));
+}
+
+MarsFSPath MarsFSPath::baseName(bool ext) const
+{
+    return MarsFSPath(node(),MarsFSClient(*this).baseName(path_, ext));
+}
+
+void MarsFSPath::touch() const
+{
+    MarsFSClient(*this).touch(path_);
+}
+
+Length MarsFSPath::size() const
+{
+    return MarsFSClient(*this).size(path_);
+}
+
+bool MarsFSPath::sameAs(const MarsFSPath& other) const
+{
+    if(node() != other.node())
+        return false;
+    return MarsFSClient(*this).sameAs(path_, other.path_);
+}
+
+MarsFSPath MarsFSPath::unique(const MarsFSPath& path)
+{
+    return MarsFSPath(path.node(),MarsFSClient(path).unique(path.path_));
+}
+
+void MarsFSPath::reserve(const Length&) const
+{
+    NOTIMP;
+}
+
+void MarsFSPath::mkdir(short mode) const
+{
+    MarsFSClient(*this).mkdir(path_, mode);
+}
+
+void MarsFSPath::fileSystemSize(FileSystemSize& fs) const
+{
+    MarsFSClient(*this).fileSystemSize(path_, fs);
+}
+
+
+time_t MarsFSPath::created() const
+{
+    return MarsFSClient(*this).created(path_);
+}
+
+bool MarsFSPath::isDir() const
+{
+    throw NotImplemented(Here());
+}
+
+bool MarsFSPath::isLink() const
+{
+    throw NotImplemented(Here());
+}
+
+time_t MarsFSPath::lastAccess() const
+{
+    return MarsFSClient(*this).lastAccess(path_);
+}
+
+time_t MarsFSPath::lastModified() const
+{
+    return MarsFSClient(*this).lastModified(path_);
+}
+
+bool MarsFSPath::exists() const
+{
+    return MarsFSClient(*this).exists(path_);
+}
+
+bool MarsFSPath::available() const
+{
+    return ClusterNodes::available("marsfs", node_);
+}
+
+void MarsFSPath::unlink() const
+{
+    MarsFSClient(*this).unlink(path_);
+}
+
+void MarsFSPath::rmdir() const
+{
+    MarsFSClient(*this).rmdir(path_);
+}
+
+void MarsFSPath::syncParentDirectory() const
+{
+    MarsFSClient(*this).syncParentDirectory(path_);
+}
+
+const char* MarsFSPath::localPath() const
+{
+    throw SeriousBug(std::string("Attempting to access ") + std::string(*this) + " locally");
+}
+
+
+void MarsFSPath::children(std::vector<MarsFSPath>& dirs,std::vector<MarsFSPath>& files) const
+{
+    std::vector<std::string> d;
+    std::vector<std::string> f;
+
+    MarsFSClient(*this).children(path_, d, f);
+
+    dirs.clear();
+    for(std::vector<std::string>::iterator j = d.begin(); j != d.end(); ++j)
+        dirs.push_back(MarsFSPath(node(), *j));
+
+    files.clear();
+    for(std::vector<std::string>::iterator j = f.begin(); j != f.end(); ++j)
+        files.push_back(MarsFSPath(node(), *j));
+}
+
+void MarsFSPath::match(const MarsFSPath& path,std::vector<MarsFSPath>& result,bool recurse)
+{
+    std::vector<std::string> r;
+    MarsFSClient(path).match(path.path_, r, recurse);
+    result.clear();
+    for(std::vector<std::string>::iterator j = r.begin(); j != r.end(); ++j)
+        result.push_back(MarsFSPath(path.node(), *j));
+}
+
+void MarsFSPath::link(const MarsFSPath& from,const MarsFSPath& to)
+{
+    ASSERT(from.node_ == to.node_);
+    MarsFSClient(from).link(from.path_, to.path_);
+}
+
+void MarsFSPath::rename(const MarsFSPath& from,const MarsFSPath& to)
+{
+    ASSERT(from.node_ == to.node_);
+    MarsFSClient(from).rename(from.path_, to.path_);
+}
+
+DataHandle* MarsFSPath::fileHandle(bool overwrite) const
+{
+    return new MarsFSHandle(*this, overwrite);
+    //return new MoverHandle(new MarsFSHandle(*this, overwrite));
+}
+
+DataHandle* MarsFSPath::partHandle(const OffsetList& o, const LengthList& l) const
+{
+    return new MarsFSPartHandle(*this, o, l);
+}
+
+DataHandle* MarsFSPath::partHandle(const Offset& o, const Length& l) const
+{
+    return new MarsFSPartHandle(*this, o, l);
+}
+
+MarsFSPath MarsFSPath::mountPoint() const
+{
+    return MarsFSPath(node_, MarsFSClient(*this).mountPoint(path_));
+}
+
+
+MarsFSPath MarsFSPath::realName() const
+{
+    NOTIMP;
+    // return MarsFSPath(node_, MarsFSClient(*this).realName(path_));
+}
+
+MarsFSPath MarsFSPath::orphanName() const
+{
+
+    std::ostringstream os;
+    os << mountPoint()  << "/orphans/";
+
+    const char *q = path_.c_str();
+    while(*q) {
+        if(*q == '/')  os << '_';
+        else os << *q;
+        q++;
+    }
+
+    return os.str();
+
+}
+
+std::string MarsFSPath::clusterName() const
+{
+    return std::string(*this);
+}
+
+std::string MarsFSPath::extension() const {
+    NOTIMP;
+}
+
+BasePathName* MarsFSPath::checkClusterNode() const
+{
+    try
+    {
+        std::string n = ClusterDisks::node(path_);
+        ASSERT(n != NodeInfo::thisNode().node()); // TODO: code mo, if a remote file becomes local
+        if(n != node_) {
+//            Log::warning() << *this << " is now on node [" << n << "]" << std::endl;
+        }
+        return new BasePathNameT<MarsFSPath>(MarsFSPath(n, path_));
+    }
+    catch(std::exception& e)
+    {
+        Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+        Log::error() << "** Exception is handled" << std::endl;
+        return new BasePathNameT<MarsFSPath>(MarsFSPath(node_, path_));
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/filesystem/marsfs/MarsFSPath.h b/eckit/src/eckit/filesystem/marsfs/MarsFSPath.h
new file mode 100644
index 0000000..a8b5d5a
--- /dev/null
+++ b/eckit/src/eckit/filesystem/marsfs/MarsFSPath.h
@@ -0,0 +1,142 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsFSPath.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_MarsFSPath_h
+#define eckit_MarsFSPath_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class BasePathName;
+
+class MarsFSPath {
+public:
+
+// -- Contructors
+
+	MarsFSPath(const std::string&);
+	MarsFSPath(const std::string&, const std::string&);
+	/* MarsFSPath(const PathName&); */
+	MarsFSPath(const MarsFSPath&);
+	MarsFSPath(Stream&);
+
+	MarsFSPath& operator=(const MarsFSPath&);
+
+// -- Destructor
+
+	~MarsFSPath();
+
+// -- Operators
+
+    bool operator==(const MarsFSPath& other) const
+        { return node_ == other.node_ && path_ == other.path_; }
+
+    bool operator!=(const MarsFSPath& other) const
+        { return node_ != other.node_ || path_ != other.path_; }
+
+// -- Methods
+
+    const std::string& node() const { return node_; }
+    const std::string& path() const { return path_; }
+    //const std::string& name() const { return path_; }
+
+    bool isLocal() const;
+    bool isRemote() const { return !isLocal(); }
+
+    // For PathName compatibility
+
+    operator  std::string() const;
+    const char* localPath() const;
+
+    MarsFSPath baseName(bool = true) const;
+    MarsFSPath dirName() const;
+    MarsFSPath fullName() const;
+    MarsFSPath orphanName() const;
+    BasePathName* checkClusterNode() const;
+
+    std::string clusterName() const;
+    std::string extension() const;
+
+    bool exists() const;
+    bool available() const;
+    void touch() const;
+    void reserve(const Length&) const;
+
+    Length size() const;
+    time_t lastAccess()   const;
+    time_t lastModified() const;
+    time_t created()      const;
+
+    bool isDir() const;
+    bool isLink() const;
+
+    void mkdir(short mode = 0777) const;
+    void unlink() const;
+    void rmdir() const;
+
+    void syncParentDirectory() const;
+
+    bool sameAs(const MarsFSPath&) const;
+    MarsFSPath mountPoint() const;
+    MarsFSPath realName() const;
+
+    void children(std::vector<MarsFSPath>&,std::vector<MarsFSPath>&) const;
+
+    static MarsFSPath unique(const MarsFSPath&);
+    static void match(const MarsFSPath&,std::vector<MarsFSPath>&,bool=false);
+    static void link(const MarsFSPath& from,const MarsFSPath& to);
+    static void rename(const MarsFSPath& from,const MarsFSPath& to);
+
+    void fileSystemSize(FileSystemSize&) const;
+
+
+    DataHandle* fileHandle(bool overwrite) const;
+    DataHandle* partHandle(const OffsetList&, const LengthList&) const;
+    DataHandle* partHandle(const Offset&, const Length&) const;
+
+protected:
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+private:
+
+// -- Members
+
+    std::string  node_;
+    std::string  path_;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const MarsFSPath& p)
+		{ p.print(s); return s; }
+
+	friend void operator<<(Stream&,const MarsFSPath&);
+//	friend void operator>>(Stream&,MarsFSPath&);
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/geometry/CMakeLists.txt b/eckit/src/eckit/geometry/CMakeLists.txt
new file mode 100644
index 0000000..cb411bd
--- /dev/null
+++ b/eckit/src/eckit/geometry/CMakeLists.txt
@@ -0,0 +1,17 @@
+list( APPEND eckit_geometry_srcs
+KPoint.h
+Point2.cc
+Point2.h
+Point3.cc
+Point3.h
+)
+
+ecbuild_add_library(TARGET eckit_geometry
+					INSTALL_HEADERS ALL
+					HEADER_DESTINATION
+						${INSTALL_INCLUDE_DIR}/eckit/geometry
+					SOURCES
+						${eckit_geometry_srcs}
+					LIBS
+						eckit  )
+
diff --git a/eckit/src/eckit/geometry/KPoint.h b/eckit/src/eckit/geometry/KPoint.h
new file mode 100644
index 0000000..6c668e6
--- /dev/null
+++ b/eckit/src/eckit/geometry/KPoint.h
@@ -0,0 +1,315 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef KPoint_H
+#define KPoint_H
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <limits>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+//------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+namespace geometry {
+
+//------------------------------------------------------------------------------------------------------
+
+enum XYZCOORDS { XX = 0, YY = 1, ZZ = 2 };
+enum LLCOORDS  { LON = XX, LAT = YY };
+
+/// A generic point in K dimension cartesian space
+
+template<int SIZE = 2>
+class KPoint {
+
+protected:
+
+    double x_[SIZE];
+
+    class NoInit {};
+
+    KPoint( NoInit ) {}
+
+public:
+
+    static const size_t DIMS = SIZE;
+
+    double x(size_t axis) const { return x_[axis]; }
+
+    KPoint()
+    {
+        std::fill(x_, x_+dimensions(), 0);
+    }
+
+    KPoint( const double * x )
+    {
+        std::copy(x, x+dimensions(), x_);
+    }
+
+    template<class Container>
+    explicit KPoint(Container c)
+    {
+        std::copy(c.begin(), c.end(), x_);
+    }
+
+    const KPoint& point() const { return *this; }
+
+    KPoint& point() { return *this; }
+
+    double * data() { return x_; }
+    const double* data() const { return x_; }
+
+    double operator()( const size_t& i ) const { assert( i < SIZE ); return x_[i]; }
+
+    bool operator<(const KPoint& other) const
+    { return std::lexicographical_compare(x_,x_ + SIZE, other.x_, other.x_ + SIZE); }
+
+    static size_t dimensions() { return SIZE; }
+
+    friend std::ostream& operator<<(std::ostream& s,const KPoint& p)
+    {
+        char z = '{';
+        for(size_t i = 0; i < dimensions(); ++i) {
+            s << z << p.x_[i]; z = ',';
+        }
+        s << "}";
+        return s;
+    }
+
+    static double distance(const KPoint& p1, const KPoint& p2)
+    {
+        double d = 0;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            double dx =  p1.x_[i]  - p2.x_[i];
+            d += dx*dx;
+        }
+        return std::sqrt(d);
+    }
+
+    static double distance2(const KPoint& p1, const KPoint& p2)
+    {
+        double d = 0;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            double dx =  p1.x_[i]  - p2.x_[i];
+            d += dx*dx;
+        }
+        return d;
+    }
+
+    static bool equal(const KPoint& p1, const KPoint& p2)
+    {
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            if(p1.x_[i]  != p2.x_[i])
+                return false;
+        }
+        return true;
+    }
+
+    static double norm(const KPoint& p1)
+    {
+        double n = 0.0;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            double dx =  p1.x_[i];
+            n += dx*dx;
+        }
+        return std::sqrt(n);
+    }
+
+    bool operator==(const KPoint& other) const {
+        return equal(*this, other);
+    }
+
+    bool operator!=(const KPoint& other) const {
+        return !equal(*this, other);
+    }
+
+    // Distance along one axis
+    static double distance(const KPoint& p1, const KPoint& p2, int axis)
+    {
+        return std::abs(p1.x_[axis] - p2.x_[axis]);
+    }
+
+    // For projecting a point on a line
+    static double dot(const KPoint& p1, const KPoint& p2)
+    {
+        double m = 0.0;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            m += p1.x_[i] * p2.x_[i];
+        }
+        return m;
+    }
+
+    static KPoint add(const KPoint& p1, const KPoint& p2)
+    {
+        KPoint q(p1);
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] += p2.x_[i];
+        }
+        return q;
+    }
+
+    static KPoint middle(const KPoint& p1, const KPoint& p2)
+    {
+        KPoint q(p1);
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] += p2.x_[i];
+            q.x_[i] /= 2.0;
+        }
+        return q;
+    }
+
+    static KPoint sub(const KPoint& p1, const KPoint& p2)
+    {
+        KPoint q(p1);
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] -= p2.x_[i];
+        }
+        return q;
+    }
+
+    static KPoint mul(const KPoint& p, double m)
+    {
+        KPoint q(p);
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] *= m;
+        }
+        return q;
+    }
+
+    static KPoint div(const KPoint& p, double m)
+    {
+        KPoint q(p);
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] /= m;
+        }
+        return q;
+    }
+
+    static KPoint componentsMin(const KPoint& p1, const KPoint& p2)
+    {
+        KPoint q;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] = std::min(p1.x_[i], p2.x_[i]);
+        }
+        return q;
+    }
+
+    static KPoint componentsMax(const KPoint& p1, const KPoint& p2)
+    {
+        KPoint q;
+        for(size_t i = 0; i < dimensions(); i++)
+        {
+            q.x_[i] = std::max(p1.x_[i], p2.x_[i]);
+        }
+        return q;
+    }
+
+    static KPoint normalize(const KPoint& p)
+    {
+        KPoint zero;
+        return div(p,distance(p, zero));
+    }
+
+    template<class Container>
+    static typename Container::value_type mean(const Container& points) {
+
+        typename Container::const_iterator j = points.begin();
+        typename Container::value_type result(*j);
+
+        ++j;
+
+        for(; j != points.end(); ++j) {
+            for(size_t i = 0; i < dimensions(); i++) {
+                result.point().x_[i] += (*j).point().x_[i];
+            }
+        }
+        for(size_t i = 0; i < dimensions(); i++) {
+            result.point().x_[i] /= points.size();
+        }
+        return result;
+    }
+
+    static KPoint symetrical(const KPoint& w, const KPoint& c) {
+        KPoint result(w);
+        for(size_t i = 0; i < dimensions(); i++) {
+            result.x_[i] -= (c.x_[i] - w.x_[i]);
+        }
+        return result;
+    }
+
+    const double* begin() const { return x_; }
+    const double* end() const { return x_ + dimensions(); }
+
+
+    void normalize(const KPoint& offset, const KPoint& scale)
+    {
+        for(size_t i = 0; i < DIMS; ++i) {
+            x_[i] = (x_[i] - offset.x_[i]) / scale.x_[i];
+        }
+    }
+
+    template<class Container>
+    static void normalizeAll(Container& c, KPoint& offset, KPoint& scale) {
+        std::vector<double> mins(DIMS,  std::numeric_limits<double>::max());
+        std::vector<double> maxs(DIMS, -std::numeric_limits<double>::max());
+
+        for(typename Container::const_iterator j = c.begin(); j != c.end(); ++j) {
+            const typename Container::value_type& v = (*j);
+            for(size_t i = 0; i < DIMS; ++i) {
+                mins[i] = std::min(mins[i], v.point().x_[i]);
+                maxs[i] = std::max(maxs[i], v.point().x_[i]);
+            }
+        }
+
+        for(size_t i = 0; i < DIMS; ++i) {
+            maxs[i] -= mins[i];
+        }
+
+
+        for(typename Container::iterator j = c.begin(); j != c.end(); ++j) {
+            typename Container::value_type& v = (*j);
+            for(size_t i = 0; i < DIMS; ++i) {
+               v.point().x_[i] = (v.point().x_[i] - mins[i]) / maxs[i];
+            }
+        }
+
+        offset = KPoint(mins);
+        scale  = KPoint(maxs);
+
+    }
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template< int SIZE > const size_t KPoint<SIZE>::DIMS;
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace geometry
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/geometry/Point2.cc b/eckit/src/eckit/geometry/Point2.cc
new file mode 100644
index 0000000..3c964d3
--- /dev/null
+++ b/eckit/src/eckit/geometry/Point2.cc
@@ -0,0 +1,57 @@
+#include "eckit/geometry/Point2.h"
+
+#include <vector>
+
+#include "eckit/types/FloatCompare.h"
+
+//------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+namespace geometry {
+
+//------------------------------------------------------------------------------------------------------
+
+void reduceTo2Pi( double& d )
+{
+    // See ECKIT-25 // while( d < 0 )  d += 360;
+    while( d < -360 ) d += 360;
+    while( d >= 360 ) d -= 360;
+}
+
+//------------------------------------------------------------------------------------------------------
+
+std::ostream& operator<<(std::ostream& s,const Point2& p)
+{
+    s << '(' << p.x_[XX] << ","
+             << p.x_[YY] << ")";
+    return s;
+}
+
+//------------------------------------------------------------------------------------------------------
+
+bool points_equal(const Point2 &a, const Point2 &b)
+{
+    return eckit::types::is_approximately_equal<double>( Point2::distance2(a,b), 0.0 );
+}
+
+Point2::operator Value() const
+{
+    std::vector<Value> pts;
+    pts.push_back(x_[XX]);
+    pts.push_back(x_[YY]);
+    return Value::makeList(pts);
+}
+
+LLPoint2::operator Value() const
+{
+    std::vector<Value> pts;
+    pts.push_back(lat());
+    pts.push_back(lon());
+    return Value::makeList(pts);
+}
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace geometry
+} // namespace eckit
+
diff --git a/eckit/src/eckit/geometry/Point2.h b/eckit/src/eckit/geometry/Point2.h
new file mode 100644
index 0000000..f4a5cce
--- /dev/null
+++ b/eckit/src/eckit/geometry/Point2.h
@@ -0,0 +1,96 @@
+#ifndef eckit_Point2_h
+#define eckit_Point2_h
+
+#include "eckit/value/Value.h"
+#include "eckit/geometry/KPoint.h"
+
+//------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+namespace geometry {
+
+//------------------------------------------------------------------------------------------------------
+
+void reduceTo2Pi( double& d );
+
+//------------------------------------------------------------------------------------------------------
+
+class Point2 : public eckit::geometry::KPoint<2> {
+
+    typedef KPoint<2> BasePoint;
+
+public:
+
+    Point2(): BasePoint() {}
+
+    Point2( const BasePoint& p ): BasePoint(p) {}
+
+    double  operator[] (const size_t& i) const { assert(i<2); return x_[i]; }
+    double& operator[] (const size_t& i)       { assert(i<2); return x_[i]; }
+
+    Point2( const double* p ): KPoint<2>(p) {}
+
+    Point2( double x, double y ) : BasePoint( NoInit() )
+    {
+        x_[XX] = x;
+        x_[YY] = y;
+    }
+
+    template < typename T >
+    void assign( const T& p )
+    {
+        x_[XX] = p[XX];
+        x_[YY] = p[YY];
+    }
+
+    operator eckit::Value() const;
+
+    friend std::ostream& operator<<(std::ostream& s,const Point2& p);
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+class LLPoint2 : public Point2 {
+public:
+
+    LLPoint2() : Point2() {}
+
+    LLPoint2( const Point2& p ) : Point2(p) { reduceTo2Pi(x_[LON]); }
+
+    LLPoint2( double lon, double lat ) : Point2(lon,lat) { reduceTo2Pi(x_[LON]); }
+
+    double lat() const { return x_[LAT]; }
+    double lon() const { return x_[LON]; }
+
+    LLPoint2& lon(double lon) { x_[LON] = lon; reduceTo2Pi(x_[LON]); return *this; }
+    LLPoint2& lat(double lat) { x_[LAT] = lat; return *this; }
+
+    void assign( double lon, double lat )
+    {
+        x_[LON] = lon;
+        x_[LAT] = lat;
+        reduceTo2Pi(x_[LON]);
+    }
+
+    bool operator <(const LLPoint2& rhs) const {
+       return lon() < rhs.lon() || lat() < rhs.lat();
+    }
+    bool operator >(const LLPoint2& rhs) const {
+       return lon() > rhs.lon() || lat() > rhs.lat();
+    }
+
+    operator eckit::Value() const;
+};
+
+//------------------------------------------------------------------------------------------------------
+
+bool points_equal( const Point2& a, const Point2& b );
+
+//---------------------------------------------------------------------------------------------------------
+
+} // namespace geometry
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/geometry/Point3.cc b/eckit/src/eckit/geometry/Point3.cc
new file mode 100644
index 0000000..dabb134
--- /dev/null
+++ b/eckit/src/eckit/geometry/Point3.cc
@@ -0,0 +1,96 @@
+#include "eckit/geometry/Point3.h"
+#include "eckit/types/FloatCompare.h"
+
+//------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+namespace geometry {
+
+//------------------------------------------------------------------------------------------------------
+
+void Point3::print(std::ostream &s) const { s << "Point3["
+                                              << x_[XX] << ","
+                                              << x_[YY] << ","
+                                              << x_[ZZ] << "]"; }
+
+std::ostream& operator<<(std::ostream& s,const Point3& p)
+{
+    p.print(s); return s;
+}
+
+void LLPoint3::print(std::ostream &s) const { s << "LLPoint3["
+                                                << "lat=" << lat_ << ","
+                                                << "lon=" << lon_ << "]"; }
+
+std::ostream& operator<<(std::ostream& s,const LLPoint3& p)
+{
+    p.print(s); return s;
+}
+
+const double radius_earth = 6371229; // from ECMWF model ...
+
+    // 6371229  -- IFS
+    // 6367470  -- GRIB1
+    // 6378137  -- WGS84 semi-major axis
+
+void latlon_to_3d(const double lat, const double lon, double xyz[], const double r, const double h )
+{
+    // See http://en.wikipedia.org/wiki/Geodetic_system#From_geodetic_to_ECEF
+
+    double& X = xyz[XX];
+    double& Y = xyz[YY];
+    double& Z = xyz[ZZ];
+
+    const double a = r;
+    const double e2 = 0;  // ignored -- 6.69437999014E-3; // WGS84 first numerical eccentricity squared
+
+    const double phi = lat / 180.0 * M_PI;
+    const double lambda = lon / 180.0 * M_PI;
+
+    const double cos_phi = cos(phi);
+    const double sin_phi = sin(phi);
+    const double cos_lambda = cos(lambda);
+    const double sin_lambda = sin(lambda);
+
+    const double N_phi = a/sqrt(1-e2*sin_phi*sin_phi);
+
+    X = (N_phi + h) * cos_phi * cos_lambda;
+    Y = (N_phi + h) * cos_phi * sin_lambda;
+    Z = (N_phi * (1-e2) + h) * sin_phi;
+}
+
+void latlon_to_3d( const double lat, const double lon, double xyz[] )
+{
+    latlon_to_3d( lat, lon, xyz, radius_earth, 0. );
+}
+
+void lonlat_to_3d( const double lon, const double lat, double xyz[] )
+{
+    latlon_to_3d( lat, lon, xyz, radius_earth, 0. );
+}
+
+void lonlat_to_3d( const double lonlat[], double xyz[] )
+{
+  latlon_to_3d(lonlat[LAT],lonlat[LON],xyz);
+}
+
+void lonlat_to_3d(const double lon, const double lat, double xyz[], const double r, const double h )
+{
+  latlon_to_3d(lat, lon, xyz, r, h);
+}
+
+void lonlat_to_3d( const double lonlat[], double xyz[], const double r, const double h )
+{
+  latlon_to_3d(lonlat[LAT],lonlat[LON],xyz,r,h);
+}
+
+bool points_equal(const Point3 &a, const Point3 &b)
+{
+    return eckit::types::is_approximately_equal<double>( Point3::distance2(a,b), 0.0 );
+}
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace geometry
+} // namespace eckit
+
diff --git a/eckit/src/eckit/geometry/Point3.h b/eckit/src/eckit/geometry/Point3.h
new file mode 100644
index 0000000..da23481
--- /dev/null
+++ b/eckit/src/eckit/geometry/Point3.h
@@ -0,0 +1,103 @@
+#ifndef eckit_Point3_h
+#define eckit_Point3_h
+
+#include "eckit/geometry/KPoint.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace geometry {
+
+//------------------------------------------------------------------------------------------------------
+
+void lonlat_to_3d( const double lonlat[], double xyz[], const double r, const double h );
+
+void lonlat_to_3d( const double lonlat[], double xyz[] );
+
+void lonlat_to_3d( const double lon, const double lat, double xyz[], const double r, const double h );
+
+void lonlat_to_3d( const double lon, const double lat, double xyz[] );
+
+void latlon_to_3d( const double lat, const double lon, double xyz[], const double r, const double h );
+
+void latlon_to_3d( const double lat, const double lon, double xyz[] );
+
+//-----------------------------------------------------------------------------
+
+class Point3 : public eckit::geometry::KPoint<3> {
+
+    typedef KPoint<3> BasePoint;
+
+public:
+
+    Point3(): BasePoint() {}
+
+    Point3( const BasePoint& p ): BasePoint(p) {}
+
+    Point3( double lat, double lon ): KPoint<3>()
+    {
+        eckit::geometry::latlon_to_3d( lat, lon, x_ );
+    }
+
+    Point3( double x, double y, double z ): KPoint<3>( NoInit() )
+    {
+        x_[XX] = x;
+        x_[YY] = y;
+        x_[ZZ] = z;
+    }
+
+    double  operator[] (const size_t& i) const { assert(i<3); return x_[i]; }
+    double& operator[] (const size_t& i)       { assert(i<3); return x_[i]; }
+
+    template < typename T >
+    void assign( const T& p )
+    {
+        x_[XX] = p[XX];
+        x_[YY] = p[YY];
+        x_[ZZ] = p[ZZ];
+    }
+
+    void print(std::ostream& s) const;
+
+    friend std::ostream& operator<<(std::ostream& s,const Point3& p);
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+class LLPoint3 : public KPoint<3> {
+
+    double lat_;
+    double lon_;
+
+public:
+
+    LLPoint3(): KPoint<3>(), lat_(0), lon_(0) {}
+
+    double lat() const { return lat_; }
+    double lon() const { return lon_; }
+
+    LLPoint3(double lat, double lon):
+        KPoint<3>(),
+        lat_(lat), lon_(lon)
+    {
+        latlon_to_3d(lat,lon,this->data());
+    }
+
+    void print(std::ostream& s) const;
+
+    friend std::ostream& operator<<(std::ostream& s,const LLPoint3& p);
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+bool points_equal( const Point3& a, const Point3& b );
+
+//---------------------------------------------------------------------------------------------------------
+
+} // namespace geometry
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/io/AIOHandle.cc b/eckit/src/eckit/io/AIOHandle.cc
new file mode 100644
index 0000000..ddb8ba6
--- /dev/null
+++ b/eckit/src/eckit/io/AIOHandle.cc
@@ -0,0 +1,257 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <algorithm>
+
+#include "eckit/io/AIOHandle.h"
+
+#include "eckit/maths/Functions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+AIOHandle::AIOHandle(const PathName& path, size_t count, size_t size, bool fsync):
+    path_(path),
+    buffers_(count),
+    aiop_(count),
+    aio_(count),
+    len_(count),
+    active_(count),
+    used_(0),
+    count_(count),
+    fd_(-1),
+    pos_(0),
+    fsync_(fsync) {
+
+#ifdef AIO_LISTIO_MAX
+    count_ = std::min<size_t>(count_, AIO_LISTIO_MAX);
+#endif
+
+#ifdef AIO_MAX
+    count_ = std::min<size_t>(count_, AIO_MAX);
+#endif
+
+    for (size_t i = 0; i < count_ ; i++) {
+        buffers_[i] = 0;
+        zero(aio_[i]);
+        aiop_[i] = &aio_[i];
+        active_[i] = false;
+    }
+}
+
+AIOHandle::~AIOHandle() {
+    close();
+    for (size_t i = 0; i < count_ ; i++) {
+        delete buffers_[i];
+    }
+}
+
+Length AIOHandle::openForRead() {
+    NOTIMP;
+}
+
+void AIOHandle::openForWrite(const Length&) {
+    used_ = 0;
+    SYSCALL( fd_ = ::open(path_.localPath(), O_WRONLY | O_CREAT | O_TRUNC, 0777));
+    pos_ = 0;
+}
+
+void AIOHandle::openForAppend(const Length& length) {
+    used_ = 0;
+    SYSCALL( fd_  = ::open(path_.localPath(), O_WRONLY | O_CREAT | O_APPEND, 0777));
+    SYSCALL( pos_ = ::lseek(fd_, 0, SEEK_CUR) );
+}
+
+long AIOHandle::read(void* buffer, long length) {
+    NOTIMP;
+}
+
+long AIOHandle::write(const void* buffer, long length) {
+    if (length == 0)
+        return 0;
+
+    size_t n = 0;
+
+    if (used_ <  count_) {
+        n = used_++;
+    } else {
+        /* wait */
+        while (aio_suspend(&aiop_[0], count_, NULL) < 0) {
+            if (errno != EINTR)
+                throw FailedSystemCall("aio_suspend");
+        }
+
+        bool ok = false;
+        for (n = 0 ; n < count_ ; n++) {
+            int e = aio_error(&aio_[n]);
+            if (e == EINPROGRESS) continue;
+
+            active_[n] = false;
+
+            if (e == 0) {
+                ssize_t len = aio_return(&aio_[n]);
+                if (len != len_[n]) {
+                    // TODO: retry when filesystems are full
+                    std::ostringstream os;
+                    os << "AIOHandle: only " << len << " bytes written instead of " << len_[n];
+                    throw WriteError(os.str());
+                }
+                ok = true;
+                break;
+            } else {
+                throw FailedSystemCall("aio_error");
+            }
+
+        }
+        ASSERT(ok);
+    }
+
+    if ( buffers_[n] == 0 || buffers_[n]->size() < (size_t) length ) {
+        delete buffers_[n];
+        buffers_[n] = new Buffer(eckit::round(length, 64 * 1024));
+
+        ASSERT(buffers_[n]);
+    }
+
+    memcpy( *(buffers_[n]), buffer, length);
+    len_[n] = length;
+
+    struct aiocb  *aio = &aio_[n];
+
+    memset(aio, 0, sizeof(struct aiocb));
+
+    aio->aio_fildes = fd_;
+    aio->aio_offset = pos_;
+    pos_ += length;
+
+    aio->aio_buf = *(buffers_[n]);
+    aio->aio_nbytes = length;
+    aio->aio_sigevent.sigev_notify = SIGEV_NONE;
+
+    SYSCALL(aio_write(aio));
+
+    active_[n] = true;
+
+    return length;
+
+}
+
+void AIOHandle::close() {
+    if (fd_ != -1) {
+        flush(); // this should wait for the async requests to finish
+
+        SYSCALL( ::close(fd_) );
+        fd_ = -1;
+    }
+}
+
+void AIOHandle::flush() {
+
+
+    bool more = true;
+    while (more) {
+        more = false;
+
+        for ( size_t n = 0 ; n < used_ ; ++n ) {
+
+            if (!active_[n]) {
+                continue;
+            }
+
+            /* wait */
+            while (aio_suspend(&aiop_[n], 1, NULL) < 0) {
+                if (errno != EINTR)
+                    throw FailedSystemCall("aio_suspend");
+            }
+
+            int e = aio_error(&aio_[n]);
+            if (e == EINPROGRESS) {
+                more = true;
+                continue;
+            }
+
+            active_[n] = false;
+
+            if (e == 0) {
+                ssize_t len = aio_return(&aio_[n]);
+                if (len != len_[n]) {
+                    // TODO: retry when filesystems are full
+                    std::ostringstream os;
+                    os << "AIOHandle: only " << len << " bytes written instead of " << len_[n];
+                    throw WriteError(os.str());
+                }
+            } else {
+                throw FailedSystemCall("aio_return");
+            }
+
+        }
+    }
+
+    if (fsync_) { // request all current operations to the synchronized I/O completion state
+        struct aiocb aio;
+
+        zero(aio);
+
+        aio.aio_fildes                = fd_;
+        aio.aio_sigevent.sigev_notify = SIGEV_NONE;
+
+        SYSCALL(aio_fsync(O_SYNC, &aio));
+
+        more = true;
+        while (more) {
+            more = false;
+            /* wait */
+            const struct aiocb* aiop = &aio;
+            while (aio_suspend(&aiop, 1, NULL) < 0) {
+                if (errno != EINTR)
+                    throw FailedSystemCall("aio_suspend");
+            }
+
+            int e = aio_error(&aio);
+
+            if (e == EINPROGRESS) {
+                more = 1;
+            } else if (e != 0) {
+                throw FailedSystemCall("aio_error");
+            }
+        }
+    }
+}
+
+void AIOHandle::rewind() {
+    NOTIMP;
+}
+
+void AIOHandle::print(std::ostream& s) const {
+    s << "AIOHandle[";
+    s << path_;
+    s << ']';
+}
+
+Length AIOHandle::estimate() {
+    return 0;
+}
+
+Offset AIOHandle::position() {
+    return pos_;
+}
+
+
+std::string AIOHandle::title() const {
+    return std::string("AIO[") + PathName::shorten(path_) + "]";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/AIOHandle.h b/eckit/src/eckit/io/AIOHandle.h
new file mode 100644
index 0000000..45210ad
--- /dev/null
+++ b/eckit/src/eckit/io/AIOHandle.h
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_AIOHandle_h
+#define eckit_AIOHandle_h
+
+#include <aio.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class AIOHandle : public DataHandle {
+
+public: // methods
+
+
+    /// Contructor
+
+    AIOHandle(const PathName& path, size_t count = 64, size_t = 1024 * 1024, bool fsync = false);
+
+    /// Destructor
+
+    virtual ~AIOHandle();
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*, long);
+    virtual long write(const void*, long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+
+    virtual Length estimate();
+    virtual Offset position();
+
+
+protected: // members
+
+    PathName                   path_;
+
+private: // members
+
+    std::vector<Buffer*>       buffers_;
+    std::vector<const aiocb*>  aiop_;
+    std::vector<aiocb>         aio_;
+    std::vector<long>          len_;
+    std::vector<bool>          active_;
+
+    size_t                     used_;
+    size_t                     count_;
+
+    int                        fd_;
+    off_t                      pos_;
+    bool                       fsync_;
+
+
+    virtual std::string title() const;
+
+// -- Class members
+
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/Buffer.cc b/eckit/src/eckit/io/Buffer.cc
new file mode 100644
index 0000000..6ab139a
--- /dev/null
+++ b/eckit/src/eckit/io/Buffer.cc
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/memory/MemoryPool.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Buffer::Buffer(size_t size):
+    buffer_(0),
+    size_(size),
+    owned_(true)
+{
+	create();
+}
+
+Buffer::Buffer(const char* p, size_t size):
+    buffer_(0),
+    size_(size),
+    owned_(true)
+{
+    create();
+    copy(p,size);
+}
+
+Buffer::Buffer(void* p, size_t size, bool):
+    buffer_(p),
+    size_(size),
+    owned_(false)
+{
+}
+
+Buffer::Buffer(const std::string& s):
+    buffer_(0),
+    size_(s.length()+1),
+    owned_(true)
+{
+    create();
+    copy(s);
+}
+
+
+Buffer::~Buffer()
+{
+    if (owned_) {
+        destroy();
+    }
+}
+
+void Buffer::create()
+{
+    buffer_ = MemoryPool::largeAllocate(size_);
+}
+
+void Buffer::destroy()
+{
+    MemoryPool::largeDeallocate(buffer_);
+}
+
+void Buffer::copy(const std::string &s)
+{
+    ::strcpy((char*)buffer_,s.c_str());
+}
+
+void Buffer::copy(const char *p, size_t size)
+{
+    ::memcpy(buffer_,p,size);
+}
+
+void Buffer::swap(Buffer& rhs) {
+    std::swap(buffer_, rhs.buffer_);
+    std::swap(size_, rhs.size_);
+    std::swap(owned_, rhs.owned_);
+}
+
+void eckit::Buffer::resize(size_t size)
+{
+    ASSERT(owned_);
+    destroy();
+    size_ = size;
+    create();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/Buffer.h b/eckit/src/eckit/io/Buffer.h
new file mode 100644
index 0000000..7f8fa57
--- /dev/null
+++ b/eckit/src/eckit/io/Buffer.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date July 1996
+
+#ifndef eckit_Buffer_h
+#define eckit_Buffer_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// A simple class to implement buffers
+
+class Buffer : private NonCopyable {
+
+public: // methods
+
+    Buffer(size_t size);
+    Buffer(const std::string& s);
+    Buffer(const char*, size_t size);
+
+    /// Constructor that does not take ownership of the data
+    Buffer(void*, size_t size, bool dummy);
+
+    ~Buffer();
+
+    operator char*()                 { return (char*)buffer_; }
+    operator const char*() const     { return (char*)buffer_; }
+
+    operator void*()                 { return buffer_; }
+    operator const void*() const     { return buffer_; }
+
+    size_t size() const		 { return size_; }
+
+    void resize(size_t size);
+
+    void swap(Buffer& rhs);
+
+protected: // methods
+
+    void create();
+    void destroy();
+
+    void copy(const std::string& s);
+    void copy(const char*,size_t size);
+
+private: // members
+
+    void*  buffer_;
+    size_t size_;
+
+    bool   owned_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/BufferCache.cc b/eckit/src/eckit/io/BufferCache.cc
new file mode 100644
index 0000000..cc74727
--- /dev/null
+++ b/eckit/src/eckit/io/BufferCache.cc
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BufferCache.cc
+// Baudouin Raoult - (c) ECMWF Jul 11
+
+
+#include "eckit/io/BufferCache.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+BufferCache::BufferCache(size_t size):
+    count_(0),
+    buffer_(size),
+	updated_(::time(0))
+{
+}
+
+BufferCache::BufferCache(const BufferCache& other):
+    count_(other.count_),
+	buffer_(other.buffer_.size()),
+	updated_(::time(0))
+{
+	::memcpy((char*)buffer_, (const char*)other.buffer_, count_);
+}
+
+BufferCache::~BufferCache()
+{
+}
+
+BufferCache& BufferCache::operator=(const BufferCache& other)
+{
+	if(this != &other) {
+		count_ = other.count_;
+		buffer_.resize(other.buffer_.size());
+		::memcpy((char*)buffer_, (const char*)other.buffer_, count_);
+		updated_ = ::time(0);
+	}
+	return *this;
+}
+
+bool BufferCache::operator<(const BufferCache& other) const
+{
+	return (count_ < other.count_) || 
+		((count_ == other.count_) && (::memcmp(buffer_,other.buffer_,count_) < 0));
+}
+
+void BufferCache::reset()
+{
+	count_ = 0;
+}
+
+void BufferCache::add(const void *buffer, size_t len)
+{
+	if(buffer_.size() < count_ + len) {
+		buffer_.resize(count_ + len + 1024);	
+	}
+	::memcpy(((char*)buffer_) + count_, buffer, len);
+	count_ += len;
+}
+
+
+void BufferCache::print(std::ostream& s) const
+{
+	Stream::dump(s, buffer_, count_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/BufferCache.h b/eckit/src/eckit/io/BufferCache.h
new file mode 100644
index 0000000..4539719
--- /dev/null
+++ b/eckit/src/eckit/io/BufferCache.h
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BufferCache.h
+// Baudouin Raoult - (c) ECMWF Jul 11
+
+#ifndef eckit_BufferCache_h
+#define eckit_BufferCache_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/ResizableBuffer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class BufferCache {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	BufferCache(size_t = 1024);
+	BufferCache(const BufferCache&);
+	BufferCache& operator=(const BufferCache&);
+
+// -- Destructor
+
+	~BufferCache(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+
+    bool operator<(const BufferCache& other) const;
+
+// -- Methods
+	// None
+
+    void add(const void*, size_t);
+	void reset();
+
+	size_t count() const { return count_; }
+
+	const void* buffer() const { return buffer_; }
+
+	time_t updated() const { return updated_; }
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+    void print(std::ostream&) const;
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+
+// -- Members
+    
+    size_t          count_;
+    ResizableBuffer buffer_;
+	time_t          updated_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s,const BufferCache& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/BufferedHandle.cc b/eckit/src/eckit/io/BufferedHandle.cc
new file mode 100644
index 0000000..7e45e38
--- /dev/null
+++ b/eckit/src/eckit/io/BufferedHandle.cc
@@ -0,0 +1,234 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/BufferedHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+#if 0
+ClassSpec BufferedHandle::classSpec_ = {&DataHandle::classSpec(), "BufferedHandle",};
+Reanimator<BufferedHandle> BufferedHandle::reanimator_;
+#endif
+
+
+BufferedHandle::BufferedHandle(DataHandle* h, size_t size):
+    HandleHolder(h),
+    buffer_(size),
+    pos_(0),
+    size_(size),
+    used_(0),
+    eof_(false),
+    read_(false),
+    position_(0)
+{
+}
+
+BufferedHandle::BufferedHandle(DataHandle& h, size_t size):
+    HandleHolder(h),
+    buffer_(size),
+    pos_(0),
+    size_(size),
+    used_(0),
+    eof_(false),
+    read_(false),
+    position_(0)
+{
+}
+
+BufferedHandle::~BufferedHandle()
+{
+}
+
+Length BufferedHandle::openForRead()
+{
+    read_ = true;
+    used_ = pos_ = 0;
+    eof_ = false;
+    position_ = 0;
+    return handle().openForRead();
+}
+
+void BufferedHandle::openForWrite(const Length& length)
+{
+    read_ = false;
+    pos_ = 0;
+    position_ = 0;
+    handle().openForWrite(length);
+}
+
+void BufferedHandle::openForAppend(const Length& )
+{
+    NOTIMP;
+}
+
+void BufferedHandle::skip(const Length& len)
+{
+    ASSERT(read_);
+    unsigned long long left = used_ - pos_;
+    unsigned long long n    = len;
+
+    if (n < left) {
+        position_ += n;
+        pos_ += n;
+        return;
+    }
+
+    seek(position() + len);
+}
+
+long BufferedHandle::read(void* buffer, long length)
+{
+    long len  = 0;
+    long size = length;
+    char *buf = (char*)buffer;
+
+    ASSERT(read_);
+
+    if (eof_)
+        return -1;
+
+    while (len < length && !eof_) {
+        long left = used_ - pos_;
+        ASSERT(left >= 0);
+
+        if (left == 0 && !eof_ )
+        {
+            used_   = handle().read(buffer_, size_);
+            pos_    = 0;
+            if (used_ <= 0)
+            {
+                eof_ = true;
+                len = len ? len : used_;
+                if (len > 0) position_ += len;
+                if (len == 0) return -1;
+                return len;
+            }
+            left = used_;
+        }
+
+        char* p = buffer_;
+        long s = size < left ? size : left;
+        ::memcpy(buf + len, p + pos_, s);
+        len  += s; ASSERT(len <= length);
+        pos_ += s; ASSERT(pos_ <= used_);
+        size -= s; ASSERT(size >= 0);
+    }
+
+    if (len > 0) position_ += len;
+    return len;
+}
+
+long BufferedHandle::write(const void* buffer, long length)
+{
+    long written = 0;
+
+    ASSERT(!read_);
+
+    while (length > 0) {
+        long left = size_ - pos_;
+        ASSERT(left > 0);
+
+        size_t len = std::min(left, length);
+        ASSERT(len > 0);
+
+        char* p = buffer_;
+        const char *q = static_cast<const char*>(buffer);
+        ::memcpy(p + pos_, q + written, len);
+        pos_ += len;
+        written += len;
+        length -= len;
+
+        ASSERT(length >= 0);
+
+        ASSERT(pos_ <= size_);
+        if (pos_ == size_) {
+            bufferFlush();
+        }
+
+    }
+    position_ += written;
+    return written;
+}
+
+void BufferedHandle::close()
+{
+    if (!read_)
+        bufferFlush();
+    handle().close();
+}
+
+void BufferedHandle::flush()
+{
+    bufferFlush();
+    handle().flush();
+}
+
+void BufferedHandle::rewind()
+{
+    position_ = 0;
+    used_ = pos_ = 0;
+    eof_  = false;
+    handle().rewind();
+}
+
+Offset BufferedHandle::seek(const Offset& off)
+{
+    used_ = pos_ = 0;
+    eof_  = false;
+    position_ = handle().seek(off);
+    return position_;
+}
+
+
+void BufferedHandle::print(std::ostream& s) const
+{
+    s << "BufferedHandle[";
+    handle().print(s);
+    s << ']';
+}
+
+Length BufferedHandle::estimate()
+{
+    return handle().estimate();
+}
+
+Offset BufferedHandle::position()
+{
+    // ASSERT(read_);
+    return position_;
+}
+
+void BufferedHandle::bufferFlush()
+{
+    if (pos_)
+    {
+        long len = handle().write(buffer_, pos_);
+        ASSERT( (size_t) len == pos_ );
+        pos_ = 0;
+    }
+}
+
+std::string BufferedHandle::title() const {
+    return std::string("{") + handle().title() + "}";
+}
+
+DataHandle* BufferedHandle::clone() const
+{
+    return new BufferedHandle(handle().clone(), buffer_.size());
+}
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/BufferedHandle.h b/eckit/src/eckit/io/BufferedHandle.h
new file mode 100644
index 0000000..8f3c209
--- /dev/null
+++ b/eckit/src/eckit/io/BufferedHandle.h
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/BufferedHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_BufferedHandle_h
+#define eckit_filesystem_BufferedHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/HandleHolder.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class BufferedHandle : public DataHandle, public HandleHolder {
+public:
+
+
+    /// Contructor, taking ownership
+
+	BufferedHandle(DataHandle*,size_t = 1024*1024);
+
+    /// Contructor, not taking ownership
+
+	BufferedHandle(DataHandle&,size_t = 1024*1024);
+
+    /// Destructor
+
+	virtual ~BufferedHandle();
+
+// -- Operators
+
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+    virtual void skip(const Length&);
+
+    virtual Offset seek(const Offset&);
+
+	virtual Length estimate();
+	virtual Offset position();
+
+    virtual DataHandle* clone() const;
+
+    // From Streamable
+
+#if 0
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+#endif
+
+// -- Class methods
+
+#if 0
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+#endif
+
+private: // methods
+    
+    void bufferFlush();
+
+private: // members
+    
+	Buffer               buffer_;
+	size_t               pos_;
+	size_t               size_;
+	size_t               used_;
+    bool                 eof_;
+    bool                 read_;
+    Offset               position_;
+    
+    virtual std::string title() const;
+
+// -- Class members
+
+#if 0
+	static  ClassSpec                 classSpec_;
+    static  Reanimator<BufferedHandle>  reanimator_;
+#endif
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/io/CommandStream.cc b/eckit/src/eckit/io/CommandStream.cc
new file mode 100644
index 0000000..2e831ef
--- /dev/null
+++ b/eckit/src/eckit/io/CommandStream.cc
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/CommandStream.h"
+#include "eckit/log/Log.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+CommandStream::CommandStream(const std::string& name,const char *mode):
+	file_(popen(name.c_str(),mode))
+{
+    Log::info() << "CommandStream (" << name << ")" << std::endl;
+	if(file_ == 0)
+		throw CantOpenFile(name);
+}
+
+CommandStream::~CommandStream()
+{
+	// Somethings wrong here, throw in a dtor ??
+//	if(pclose(file_))
+//		throw WriteError("CommandStream::~CommandStream()");
+		
+}
+
+long CommandStream::read(void* buf,long length)	     
+{ 
+	return fread(buf,1,length,file_);
+}
+
+long CommandStream::write(const void* buf,long length) 
+{ 
+	return fwrite(buf,1,length,file_);
+}
+
+std::string CommandStream::name() const
+{
+	return "CommandStream";
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/CommandStream.h b/eckit/src/eckit/io/CommandStream.h
new file mode 100644
index 0000000..4e2ee58
--- /dev/null
+++ b/eckit/src/eckit/io/CommandStream.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File CommandStream.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_CommandStream_h
+#define eckit_CommandStream_h
+
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class CommandStream : public Stream {
+public:
+
+// -- Contructors
+	
+    CommandStream(const std::string& name,const char *mode);
+
+// -- Destructor
+
+	~CommandStream();
+
+// -- Overridden methods
+	
+	// From Stream
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+
+private:
+
+// -- Members
+	
+	FILE* file_;
+
+// -- Overridden methods
+
+	// From Stream
+    virtual std::string name() const;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/DataBlob.cc b/eckit/src/eckit/io/DataBlob.cc
new file mode 100644
index 0000000..4471d5f
--- /dev/null
+++ b/eckit/src/eckit/io/DataBlob.cc
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/DataBlob.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/thread/AutoLock.h"
+
+#include <string>
+#include <map>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace {
+    Mutex* local_mutex = 0;
+    std::map<std::string, DataBlobFactory*> *m = 0;
+    pthread_once_t once = PTHREAD_ONCE_INIT;
+    void init() {
+        local_mutex = new Mutex();
+        m = new std::map<std::string, DataBlobFactory*>();
+    }
+}
+
+/// When a concrete instance of a DataBlobFactory is instantiated (in practice
+/// a DataBlobBuilder<>) add it to the list of available factories.
+
+DataBlobFactory::DataBlobFactory(const std::string& name) :
+    name_(name) {
+
+    pthread_once(&once, init);
+
+    AutoLock<Mutex> lock(local_mutex);
+
+    ASSERT(m->find(name) == m->end());
+    (*m)[name] = this;
+}
+
+DataBlobFactory::~DataBlobFactory() {
+    AutoLock<Mutex> lock(local_mutex);
+    m->erase(name_);
+}
+
+void DataBlobFactory::list(std::ostream& out) {
+
+    pthread_once(&once, init);
+
+    AutoLock<Mutex> lock(local_mutex);
+
+    const char* sep = "";
+    for (std::map<std::string, DataBlobFactory*>::const_iterator j = m->begin(); j != m->end(); ++j) {
+        out << sep << (*j).first;
+        sep = ", ";
+    }
+}
+
+
+const DataBlobFactory& DataBlobFactory::findFactory(const std::string& name) {
+
+    pthread_once(&once, init);
+
+    AutoLock<Mutex> lock(local_mutex);
+
+    Log::info() << "Looking for DataBlobFactory [" << name << "]" << std::endl;
+
+    std::map<std::string, DataBlobFactory *>::const_iterator j = m->find(name);
+    if (j == m->end()) {
+        Log::error() << "No DataBlobFactory for [" << name << "]" << std::endl;
+        Log::error() << "DataBlobFactories are:" << std::endl;
+        for (j = m->begin() ; j != m->end() ; ++j)
+            Log::error() << "   " << (*j).first << std::endl;
+        throw SeriousBug(std::string("No DataBlobFactory called ") + name);
+    }
+
+    return *(*j).second;
+}
+
+
+DataBlob* DataBlobFactory::build(const std::string &name, const void* data, size_t length) {
+
+    const DataBlobFactory& factory(findFactory(name));
+
+    return factory.make(data, length);
+}
+
+
+DataBlob* DataBlobFactory::build(const std::string &name, DataHandle& dh, size_t length) {
+
+    const DataBlobFactory& factory(findFactory(name));
+
+    return factory.make(dh, length);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+DataBlob::DataBlob(const void* data, size_t length) :
+    buffer_((const char*)data, length),
+    actualLength_(length)
+{
+}
+
+DataBlob::DataBlob(DataHandle& dh, size_t length) :
+    buffer_(length),
+    actualLength_(length)
+{
+    dh.read(buffer_, length);
+}
+
+DataBlob::~DataBlob() {
+}
+
+const Buffer& DataBlob::buffer() const {
+    return buffer_;
+}
+
+size_t DataBlob::length() const { return actualLength_; }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/DataBlob.h b/eckit/src/eckit/io/DataBlob.h
new file mode 100644
index 0000000..703b59e
--- /dev/null
+++ b/eckit/src/eckit/io/DataBlob.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @author Simon Smart
+/// @date   Jan 2016
+
+#ifndef eckit_DataBlob_h
+#define eckit_DataBlob_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/memory/Owned.h"
+#include "eckit/memory/SharedPtr.h"
+
+namespace eckit {
+
+    class Metadata;
+    class DataHandle;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Blob of data, thread-safe, reference counted, that can have metadata attached
+
+class DataBlob : public eckit::OwnedLock {
+
+public: // methods
+
+    /// Constructor copies the data
+    DataBlob(const void* data, size_t length);
+
+    /// Construct data from a DataHandle
+    DataBlob(DataHandle& dh, size_t length);
+
+    virtual ~DataBlob();
+
+    const Buffer& buffer() const;
+    size_t length() const;
+
+    virtual const eckit::Metadata& metadata() const = 0;
+
+    friend std::ostream& operator<<(std::ostream &s, const DataBlob& p) {
+        p.print(s);
+        return s;
+    }
+
+protected: // methods
+
+    virtual void print(std::ostream&) const = 0;
+
+protected: // members
+
+    eckit::Buffer buffer_;
+    size_t actualLength_;
+
+};
+
+typedef eckit::SharedPtr<eckit::DataBlob> DataBlobPtr;
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// A self-registering factory for producing DataBlob instances.
+
+class DataBlobFactory {
+
+    std::string name_;
+    virtual DataBlob* make(const void* data, size_t length) const = 0 ;
+    virtual DataBlob* make(DataHandle& dh, size_t length) const = 0 ;
+
+protected:
+
+    DataBlobFactory(const std::string&);
+    virtual ~DataBlobFactory();
+
+public:
+
+    static void list(std::ostream &);
+    static DataBlob* build(const std::string&, const void* data, size_t length);
+    static DataBlob* build(const std::string&, DataHandle& dh, size_t length);
+
+private: // methods
+
+    static const DataBlobFactory& findFactory(const std::string&);
+};
+
+/// Templated specialisation of the self-registering factory,
+/// that does the self-registration, and the construction of each object.
+
+template< class T>
+class DataBlobBuilder : public DataBlobFactory {
+
+    virtual DataBlob* make(const void* data, size_t length) const {
+        return new T(data, length);
+    }
+    virtual DataBlob* make(DataHandle& dh, size_t length) const {
+        return new T(dh, length);
+    }
+
+public:
+    DataBlobBuilder(const std::string &name) : DataBlobFactory(name) {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/DataHandle.cc b/eckit/src/eckit/io/DataHandle.cc
new file mode 100644
index 0000000..ce642f8
--- /dev/null
+++ b/eckit/src/eckit/io/DataHandle.cc
@@ -0,0 +1,600 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "eckit/eckit_ecbuild_config.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/DblBuffer.h"
+#include "eckit/io/MoverTransfer.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Progress.h"
+#include "eckit/config/Resource.h"
+#include "eckit/log/Timer.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec DataHandle::classSpec_ = {&Streamable::classSpec(),"DataHandle",};
+Reanimator<DataHandle> DataHandle::reanimator_;
+
+DataHandle::DataHandle()
+{
+}
+
+DataHandle::DataHandle(Stream& s):
+    Streamable(s)
+{
+}
+
+AutoClose::~AutoClose()
+{
+    bool fail = !Exception::throwing();
+
+    try {
+        handle_.close();
+    }
+    catch(std::exception& e)
+    {
+        Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+        if(fail)
+        {
+            Log::error() << "** Exception is re-thrown" << std::endl;
+            throw;
+        }
+        Log::error() << "** An exception is already in progress" << std::endl;
+        Log::error() << "** Exception is ignored" << std::endl;
+    }
+}
+
+static const char *b="kMGPEZY"; // support until Yottabyte :-)
+static const double yotta = 1024.*1024.*1024.*1024.*1024.*1024.*1024.;
+
+static double rate(double x, char& c)
+{
+    if(x > yotta || std::isinf(x)) {
+        c = 'Y';
+        return 1.;
+    }
+
+    c = ' ';
+    const char* p = b;
+    c = ' ';
+    while(x > 100)
+    {
+        x /= 1024;
+        c = *p++;
+    }
+    if(x>=10)
+        return long(x+0.5);
+    else
+        return long(x*10+0.5)/10.0;
+}
+
+void DataHandle::encode(Stream& s) const
+{
+    Streamable::encode(s);
+}
+
+void DataHandle::flush()
+{
+    std::ostringstream os;
+    os << "DataHandle::flush() [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+Length DataHandle::saveInto(DataHandle& other,TransferWatcher& watcher)
+{
+    static const bool moverTransfer = Resource<bool>("-mover;moverTransfer",0);
+
+
+    compress();
+
+    Log::status() << Bytes(estimate()) << " " << title() << " => " << other.title() << std::endl;
+
+    if(moverTransfer && moveable() && other.moveable())
+    {
+        //Log::info() << "Using MoverTransfer" << std::endl;
+        MoverTransfer mover(watcher);
+        return mover.transfer(*this,other);
+    }
+
+
+    static const bool doubleBuffer = Resource<bool>("doubleBuffer",0);
+
+    if(doubleBuffer)
+    {
+        static const long bufsize = Resource<long>("doubleBufferSize",10*1024*1024/20);
+        static const long count   = Resource<long>("doubleBufferCount",20);
+
+        DblBuffer buf(count,bufsize,watcher);
+        return buf.copy(*this,other);
+    }
+    else
+    {
+
+        static const long bufsize = Resource<long>("bufferSize",64*1024*1024);
+
+        Buffer buffer(bufsize);
+        //ResizableBuffer buffer(bufsize);
+
+
+		watcher.watch(0,0);
+
+        Length estimate = openForRead(); AutoClose closer1(*this);
+        other.openForWrite(estimate);    AutoClose closer2(other);
+
+        Progress progress("Moving data",0,estimate);
+
+        Length total = 0;
+        long length = -1;
+        double readTime = 0;
+        double lastRead = 0;
+        double writeTime = 0;
+        double lastWrite = 0;
+        Timer timer("Save into");
+        bool more = true;
+
+        char c1 = ' ';
+        char c2 = ' ';
+
+        while(more)
+        {
+            more = false;
+            try {
+                while( (length = read(buffer,buffer.size())) > 0)
+                {
+                    double r = timer.elapsed() - lastRead;
+                    readTime += r;
+                    lastWrite = timer.elapsed();
+
+                    if(other.write((const char*)buffer,length) != length)
+                        throw WriteError(name() + " into " + other.name());
+
+                    double w = timer.elapsed() - lastWrite;
+                    writeTime += w;
+                    total += length;
+                    progress(total);
+                    watcher.watch(buffer,length);
+                    lastRead = timer.elapsed();
+
+                    double rRate = rate(total/readTime,  c1);
+                    double wRate = rate(total/writeTime, c2);
+
+                    Log::message() << rRate << c1 << " " << wRate << c2 << std::endl;
+                }
+            }
+            catch(RestartTransfer& retry)
+            {
+                Log::warning() << "Retrying transfer from " << retry.from() << " (" << Bytes(retry.from()) << ")" << std::endl;
+                restartReadFrom(retry.from());
+                other.restartWriteFrom(retry.from());
+                Log::warning() << "Total so far " << total << std::endl;
+                total = Length(0) + retry.from();
+                Log::warning() << "New total " << total << std::endl;
+                more = true;
+            }
+        }
+
+        Log::message() <<  "" << std::endl;
+
+
+        Log::info() << "Read  rate: " << Bytes(total/readTime)  << "/s" << std::endl;
+        Log::info() << "Write rate: " << Bytes(total/writeTime) << "/s" << std::endl;
+
+        if(length < 0)
+            throw ReadError(name() + " into " + other.name());
+
+        if(estimate != 0 && estimate != total)
+        {
+            std::ostringstream os;
+            os << "DataHandle::saveInto got " << total << " bytes out of " << estimate;
+            throw ReadError(name() + " into " + other.name() + " " + os.str());
+        }
+
+        return total;
+    }
+}
+
+Length DataHandle::saveInto(const PathName& path,TransferWatcher& w)
+{
+    std::auto_ptr<DataHandle> file(path.fileHandle());
+    return saveInto(*file,w);
+}
+
+Length DataHandle::copyTo(DataHandle& other) {
+    static const long bufsize = Resource<long>("bufferSize",64*1024*1024);
+
+    Buffer buffer(bufsize);
+
+    Length estimate = openForRead(); AutoClose closer1(*this);
+    other.openForWrite(estimate);    AutoClose closer2(other);
+
+    Length total = 0;
+    long length = -1;
+
+    while( (length = read(buffer,buffer.size())) > 0)
+    {
+
+        if(other.write((const char*)buffer,length) != length)
+            throw WriteError(name() + " into " + other.name());
+
+        total += length;
+    }
+
+    if(length < 0)
+        throw ReadError(name() + " into " + other.name());
+
+    if(estimate != 0 && estimate != total)
+    {
+        std::ostringstream os;
+        os << "DataHandle::saveInto got " << total << " bytes out of " << estimate;
+        throw ReadError(name() + " into " + other.name() + " " + os.str());
+    }
+
+    return total;
+}
+
+
+std::string DataHandle::name() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+std::string DataHandle::title() const
+{
+	return className();
+}
+
+#ifndef IBM
+template<>
+Streamable* Reanimator<DataHandle>::ressucitate(Stream& s) const
+{
+    return 0;
+}
+#endif
+
+
+bool DataHandle::compare(DataHandle& other)
+{
+
+    long bufsize = Resource<long>("compareBufferSize",10*1024*1024);
+
+    Buffer buffer1(bufsize);
+    Buffer buffer2(bufsize);
+
+    DataHandle& self = *this;
+
+    Length estimate1 = self.openForRead();  AutoClose closer1(self);
+    Length estimate2 = other.openForRead(); AutoClose closer2(other);
+
+    if(estimate1 != estimate2) {
+        Log::error() << "DataHandle::compare(" << self << "," << other <<") failed: openForRead() returns " <<
+            estimate1 << " and " << estimate2 << std::endl;
+        return false;
+    }
+
+    Log::status() << "Comparing data" << std::endl;
+
+    Progress progress("Comparing data",0,estimate1);
+    Length total = 0;
+
+    for(;;)
+    {
+        long len1 = self.read(buffer1,buffer1.size());
+        long len2 = other.read(buffer2,buffer2.size());
+
+        if(len1 != len2) {
+            Log::error() << "DataHandle::compare(" << self << "," << other <<") failed: read() returns " <<
+                len1 << " and " << len2 << std::endl;
+            return false;
+        }
+
+        if(len1 <= 0 && len2 <= 0)
+        {
+            Log::info() << "DataHandle::compare(" << self << "," << other <<") is successful" << std::endl;
+            return true;
+        }
+
+        total += len1;
+        progress(total);
+
+        if(::memcmp(buffer1,buffer2,len1))
+        {
+            Log::error() << "DataHandle::compare(" << self << "," << other <<") failed: memcmp() returns non-zero value" << std::endl;
+            return false;
+        }
+
+    }
+
+    return false; // keep linter happy
+}
+
+Offset DataHandle::position() {
+    std::ostringstream os;
+    os << "DataHandle::position() [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+void DataHandle::rewind() {
+    std::ostringstream os;
+    os << "DataHandle::rewind() [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+Offset DataHandle::seek(const Offset& from)
+{
+    std::ostringstream os;
+    os << "DataHandle::seek(" << from << ") [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+void DataHandle::skip(const Length& len)
+{
+    seek(position() + len);
+}
+
+
+void DataHandle::restartReadFrom(const Offset& from)
+{
+    std::ostringstream os;
+    os << "DataHandle::restartReadFrom(" << from << ") [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+void DataHandle::restartWriteFrom(const Offset& offset)
+{
+    std::ostringstream os;
+    os << "DataHandle::restartWriteFrom(" << offset << ") [" << *this << "]";
+    throw NotImplemented(os.str(), Here());
+}
+
+void DataHandle::toLocal(Stream& s) const
+{
+    s << *this;
+}
+
+DataHandle* DataHandle::toLocal()
+{
+    return this;
+}
+
+void DataHandle::toRemote(Stream& s) const
+{
+    s << *this;
+}
+
+void DataHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+}
+
+DataHandle* DataHandle::clone() const
+{
+    std::ostringstream os;
+    os << "DataHandle::clone(" << *this << ")";
+    throw NotImplemented(os.str(), Here());
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(EC_HAVE_FOPENCOOKIE) || defined(EC_HAVE_FUNOPEN)
+
+class FOpenDataHandle {
+
+    DataHandle* handle_;
+    const char* mode_;
+    bool delete_on_close_;
+    Offset position_; // Keep track of position to cater for
+
+public:
+
+    FOpenDataHandle(DataHandle* handle, const char* mode, bool delete_on_close);
+    ~FOpenDataHandle() ;
+
+    long read(char *buffer, long length);
+    long write(const char *buffer, long length);
+    long seek(long pos, int whence) ;
+    int close() ;
+};
+
+
+
+FOpenDataHandle::FOpenDataHandle(DataHandle* handle, const char* mode, bool delete_on_close):
+    handle_(handle),
+    mode_(mode),
+    delete_on_close_(delete_on_close),
+    position_(0) {
+
+	bool ok = false;
+
+	if(::strcmp(mode, "r") == 0) { handle_->openForRead(); ok = true;}
+	if(::strcmp(mode, "w") == 0) { handle_->openForWrite(0); ok = true;}
+	if(::strcmp(mode, "a") == 0) { handle_->openForAppend(0); ok = true;}
+
+	ASSERT(ok);
+
+}
+
+FOpenDataHandle::~FOpenDataHandle() {
+
+   handle_->close();
+
+    if(delete_on_close_) {
+        delete handle_;
+    }
+}
+
+
+long FOpenDataHandle::read(char *buffer, long length) {
+    try {
+        long len = handle_->read(buffer, length);
+        if(len > 0) {
+            position_ += len;
+        }
+        return len;
+    }
+    catch(std::exception& e) {
+        return 0;
+    }
+}
+
+long FOpenDataHandle::write(const char *buffer, long length){
+    try{
+        long len = handle_->write(buffer, length);
+        if(len > 0) {
+            position_ += len;
+        }
+        return len;
+    }
+    catch(std::exception& e) {
+        return 0;
+    }
+}
+
+long FOpenDataHandle::seek(long pos, int whence) {
+
+    try {
+        long where = pos;
+        switch(whence) {
+
+            case SEEK_SET:
+                where = pos;
+                break;
+
+            case SEEK_CUR:
+                where = long(position_) + pos;
+                break;
+
+            case SEEK_END:
+                where = long(handle_->estimate()) - pos;
+                break;
+
+            default:
+                NOTIMP;
+                break;
+        }
+
+        if(where == position_) {
+            return where;
+        }
+
+        long w = handle_->seek(where);
+        if(w >= 0) {
+            position_ = w;
+        }
+        return w;
+    }
+    catch(std::exception& e) {
+        return -1;
+    }
+}
+
+int FOpenDataHandle::close() {
+    try {
+        delete this;
+        return 0;
+    }
+    catch(std::exception& e) {
+        return -1;
+    }
+}
+
+
+static long readfn(void *data, char *buffer, long length) {
+    FOpenDataHandle *fd = reinterpret_cast<FOpenDataHandle*>(data);
+    return fd->read(buffer, length);
+}
+
+static long writefn(void *data, const char *buffer, long length){
+    FOpenDataHandle *fd = reinterpret_cast<FOpenDataHandle*>(data);
+    return fd->write(buffer, length);
+}
+
+static long seekfn(void *data, long pos, int whence) {
+    FOpenDataHandle *fd = reinterpret_cast<FOpenDataHandle*>(data);
+    return fd->seek(pos, whence);
+}
+
+static int closefn(void *data) {
+    FOpenDataHandle *fd = reinterpret_cast<FOpenDataHandle*>(data);
+    return fd->close();
+}
+
+#ifdef EC_HAVE_FOPENCOOKIE
+
+static ssize_t _read(void *cookie, char *buf, size_t size) {
+	return readfn(cookie, buf, size);
+}
+
+static ssize_t _write(void *cookie, const char *buf, size_t size) {
+	return writefn(cookie, buf, size);
+}
+
+static int _seek(void *cookie, off64_t *offset, int whence) {
+	*offset =  seekfn(cookie, *offset, whence);
+	return *offset >= 0 ? 0 : -1;
+}
+
+static int _close(void *cookie) {
+	return closefn(cookie);
+}
+
+FILE* DataHandle::openf(const char* mode, bool delete_on_close) {
+        ASSERT(sizeof(long) >= sizeof(size_t));
+        ASSERT(sizeof(long) >= sizeof(ssize_t));
+
+	cookie_io_functions_t f = {&_read, &_write, &_seek, &_close};
+    return  ::fopencookie(new FOpenDataHandle(this, mode, delete_on_close), mode, f);
+
+}
+
+#else
+
+static int _read(void *data, char *buffer, int length) {
+	return readfn(data, buffer, length);
+}
+
+static int _write(void *data, const char *buffer, int length){
+	return writefn(data, buffer, length);
+}
+
+static fpos_t _seek(void *data, fpos_t pos, int whence) {
+	return seekfn(data, pos, whence);
+}
+
+static int _close(void *data) {
+	return closefn(data);
+}
+
+FILE* DataHandle::openf(const char* mode, bool delete_on_close) {
+    ASSERT(sizeof(long) >= sizeof(fpos_t));
+    return ::funopen(new FOpenDataHandle(this, mode, delete_on_close), &_read, &_write, &_seek, &_close);
+}
+
+#endif
+
+#else
+
+FILE* DataHandle::openf(const char* mode, bool delete_on_close) {
+    NOTIMP;
+}
+
+#endif
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/DataHandle.h b/eckit/src/eckit/io/DataHandle.h
new file mode 100644
index 0000000..e112a07
--- /dev/null
+++ b/eckit/src/eckit/io/DataHandle.h
@@ -0,0 +1,163 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DataHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_DataHandle_h
+#define eckit_DataHandle_h
+
+#include <stdio.h>
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/serialisation/Streamable.h"
+#include "eckit/io/TransferWatcher.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class RestartTransfer  {
+	Offset from_;
+public:
+	RestartTransfer(const Offset& from): from_(from) {}
+	const Offset& from() const { return from_; }
+};
+
+//-----------------------------------------------------------------------------
+
+class DataHandle : public Streamable {
+public:
+
+	friend std::ostream& operator<<(std::ostream& s,const DataHandle& handle)
+		{ handle.print(s); return s;}
+
+// -- Contructors
+
+    DataHandle();
+    DataHandle(Stream&);
+
+// -- Destructor
+
+    virtual ~DataHandle() {}
+
+// -- Methods
+
+	virtual void print(std::ostream& s) const = 0;
+
+    virtual Length openForRead()              = 0; // Return estimated length
+    virtual void openForWrite(const Length&)  = 0; // Receive estimated length
+    virtual void openForAppend(const Length&) = 0; // Receive estimated length
+
+    virtual long read(void*,long)        = 0;
+    virtual long write(const void*,long) = 0;
+    virtual void close()                 = 0;
+    virtual void flush();
+
+	virtual Length estimate()            { return 0; }
+	virtual Offset position();
+	virtual Offset seek(const Offset&);
+    virtual void skip(const Length &);
+
+	virtual void rewind();
+	virtual void restartReadFrom(const Offset&);
+	virtual void restartWriteFrom(const Offset&);
+
+    virtual DataHandle* clone() const;
+
+	// Save into an other datahandle
+
+	virtual Length saveInto(DataHandle&, TransferWatcher& = TransferWatcher::dummy());
+
+	Length saveInto(const PathName&, TransferWatcher& = TransferWatcher::dummy()); // Save into a file
+
+    /// Quiet version of saveInto. Does not support progess, restart and double buffering
+    Length copyTo(DataHandle&);
+
+    /// Save into an other datahandle
+	Length appendTo(DataHandle&);
+
+    /// Save into a file
+    Length appendTo(const PathName&);
+
+    virtual std::string name() const;
+
+    /// Create a FILE* from this handle
+    FILE* openf(const char* mode, bool delete_on_close = false);
+
+    /// Compare bytes
+	bool compare(DataHandle&);
+
+	// Merge-in an other datahandle
+
+	virtual bool compress(bool = false) { return false; }
+	virtual bool merge(DataHandle*)     { return false; }
+	virtual bool isEmpty() const        { return false; }
+
+
+    // For remote data movers
+
+    virtual bool moveable() const { return false; }
+    virtual void toLocal(Stream& s) const;
+
+    virtual DataHandle* toLocal();
+
+    virtual void toRemote(Stream& s) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+
+// -- Overridden methods
+
+    // From Streamble
+
+    virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static Reanimator<DataHandle>  reanimator_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+class AutoClose {
+	DataHandle& handle_;
+public:
+	AutoClose(DataHandle& handle) : handle_(handle) {}
+	~AutoClose();
+
+};
+
+template<>
+Streamable* Reanimator<DataHandle>::ressucitate(Stream& s) const
+#ifdef IBM
+{ return 0;}
+#else
+;
+#endif
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/DblBuffer.cc b/eckit/src/eckit/io/DblBuffer.cc
new file mode 100644
index 0000000..50a69c7
--- /dev/null
+++ b/eckit/src/eckit/io/DblBuffer.cc
@@ -0,0 +1,339 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/io/DblBuffer.h"
+#include "eckit/log/Log.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/log/Progress.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/log/Timer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DblBufferError : public Exception {
+    public:
+        DblBufferError(const std::string& what)
+        {
+            reason(std::string("Double buffer error: ") + what);
+        }
+};
+
+
+struct OneBuffer {
+    MutexCond cond_;
+    bool      full_;
+    long      length_;
+    char*     buffer_;
+    OneBuffer():
+        full_(false), length_(0), buffer_(0) {}
+};
+
+class DblBufferTask : public Thread {
+    DblBuffer&         owner_;
+    DataHandle&        out_;
+    Length             estimate_;
+    OneBuffer*         buffers_;
+    public:
+    DblBufferTask(DataHandle&,DblBuffer&,OneBuffer*,const Length&);
+    virtual void run();
+};
+
+DblBuffer::DblBuffer(long count,long size,TransferWatcher& watcher):
+    count_(count),
+    bufSize_(size),
+    error_(false),
+    restart_(false),
+    restartFrom_(0),
+    watcher_(watcher)    
+{
+    Log::info() << "Double buffering: " << 
+        count_ <<  " buffers of " << Bytes(size) << " is " << Bytes(count*size) 
+        << std::endl;
+}
+
+DblBuffer::~DblBuffer()
+{
+}
+
+inline void DblBuffer::error(const std::string& why)
+{
+    AutoLock<Mutex> lock(mutex_);
+    error_ = true;
+    why_   = why;
+}
+
+inline bool DblBuffer::error()
+{
+    AutoLock<Mutex> lock(mutex_);
+    return error_;
+}
+
+void DblBuffer::restart(RestartTransfer& retry)
+{
+    AutoLock<Mutex> lock(mutex_);
+    Log::warning() << "Retrying transfer from " << retry.from() 
+        << " (" << Bytes(retry.from()) << ")" << std::endl;
+    error_       = true;
+    restart_     = true;
+    restartFrom_ = retry.from();
+
+}
+
+Length DblBuffer::copy(DataHandle& in,DataHandle& out)
+{
+
+    Timer timer("Double buffer");
+    in.compress();
+
+
+    Length estimate = in.openForRead(); AutoClose c1(in);
+    out.openForWrite(estimate);         AutoClose c2(out);
+
+    Length total = estimate;
+
+
+    bool more = true;
+    while(more)
+    {
+        more = false;
+        try {
+            Length copied = copy(in,out,estimate);
+            Log::info() << "Copied: " << copied << ", estimate: " << estimate << std::endl;
+            ASSERT(copied == estimate);
+        }
+        catch(RestartTransfer& retry) 
+        { 
+            Log::warning() << "Retrying transfer from " << retry.from() << " (" << Bytes(retry.from()) << ")" << std::endl; 
+            in.restartReadFrom(retry.from()); 
+            out.restartWriteFrom(retry.from());
+            estimate = total - retry.from();
+            more = true;
+        }
+    }
+
+    Log::info() << "Transfer rate " << Bytes(estimate,timer) << std::endl;
+    return total;
+}
+
+Length DblBuffer::copy(DataHandle& in,DataHandle& out,const Length& estimate)
+{
+    Buffer bigbuf(count_ * bufSize_);
+
+    OneBuffer* buffers = new OneBuffer[count_];
+
+    char *addr = bigbuf;
+    for(int j = 0; j < count_; j++)
+    {
+        buffers[j].buffer_ = addr;
+        addr              += bufSize_;
+    }
+
+    Progress progress("Reading data",0,estimate);
+
+    error_   = false;
+    inBytes_ = outBytes_ = 0;
+
+    ThreadControler thread(new DblBufferTask(out,*this,buffers,estimate),false);
+
+    thread.start();
+
+    int    i = 0;
+
+    Timer reader("Double buffer reader");
+    double rate = 0;
+    double first = 0;
+
+	watcher_.watch(0,0);
+
+    while(!error())
+    {
+        Log::message() << "Wait " << i << std::endl;
+        AutoLock<MutexCond> lock(buffers[i].cond_);
+
+        while(buffers[i].full_)
+            buffers[i].cond_.wait();
+
+        if(error())
+            break;
+
+        Log::message() << "Read " << i << std::endl;
+        try {
+            double x = reader.elapsed();
+            buffers[i].length_ = in.read(buffers[i].buffer_,bufSize_);
+            double s = reader.elapsed() - x;
+            Log::status() << Bytes(estimate) << " at " << Bytes(buffers[i].length_/s) << "/s" << std::endl;
+            rate += s;
+            if(first == 0) first = rate;
+
+			watcher_.watch(buffers[i].buffer_, buffers[i].length_);
+        } 
+        catch(RestartTransfer& retry)
+        {
+            Log::warning() << "RestartTransfer: Exiting reader thread" << std::endl;
+            buffers[i].length_ = -1;
+            restart(retry);
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what() 
+                << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+            buffers[i].length_ = -1;
+            error(e.what());
+        }
+        Log::message() << "" << std::endl;
+
+        buffers[i].full_ = true;
+
+        if(buffers[i].length_ == 0)
+        {
+            buffers[i].cond_.signal();
+            break;
+        }
+
+        if(buffers[i].length_ < 0)
+        {
+            ASSERT(error());
+            Log::warning() << "Read error... " << why_ << std::endl;
+            buffers[i].cond_.signal();
+            break;
+        }
+
+        inBytes_ += buffers[i].length_;
+        progress(inBytes_);
+
+        buffers[i].cond_.signal();
+
+        i++;
+        i %= count_;
+
+    }
+
+    Log::info() << "Read done " << Bytes(inBytes_) << std::endl;
+    Log::info() << "Read rate " << Bytes(inBytes_/rate) << "/s" << std::endl;
+    if(first != rate)
+        Log::info()   << "Read rate no mount " << Bytes(inBytes_/(rate-first)) << "/s" << std::endl;
+
+    thread.wait();
+    delete[] buffers;
+
+    if(error_) {
+        if(restart_) throw RestartTransfer(restartFrom_);
+        throw  DblBufferError(why_);
+    }
+
+    PANIC(inBytes_ != outBytes_);
+
+    return inBytes_;
+
+}
+
+DblBufferTask::DblBufferTask(DataHandle& out,DblBuffer& owner,OneBuffer* buffers,
+        const Length& estimate):
+    Thread(false),
+    owner_(owner),
+    out_(out),
+    estimate_(estimate),
+    buffers_(buffers)
+{
+}
+
+void DblBufferTask::run()
+{
+    Log::status() << "Double buffering " << Bytes(estimate_) << std::endl;
+    Progress progress("Writing data",0,estimate_);
+
+    int i    = 0;
+    Timer writer("Double buffer writer");
+    double rate = 0;
+    double first = 0;
+
+    while(!owner_.error())
+    {
+        Log::message() << "Wait " << i << std::endl;
+        AutoLock<MutexCond> lock(buffers_[i].cond_);
+
+        while(!buffers_[i].full_)
+            buffers_[i].cond_.wait();
+
+        if(owner_.error())
+            break;
+
+        if(buffers_[i].length_ == 0)
+            break;
+
+        long length = -1;
+
+        Log::message() << "Write " << i << std::endl;
+        try {
+            double x = writer.elapsed();
+            length = out_.write(buffers_[i].buffer_,buffers_[i].length_);
+            double s = writer.elapsed() - x;
+            Log::status() << Bytes(buffers_[i].length_/s) << "/s" << std::endl;
+            rate += s;
+            if(first == 0) first = rate;
+
+            ASSERT(length == buffers_[i].length_);
+        }
+        catch(RestartTransfer& retry)
+        {
+            Log::warning() << "RestartTransfer: Exiting writer thread" << std::endl;
+            length = -1;
+            owner_.restart(retry);
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " <<
+                Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+            length = -1;
+            owner_.error(e.what());
+        }
+        Log::message() << "" << std::endl;
+
+        buffers_[i].full_ = false;
+
+        if(length < 0)
+        {
+            ASSERT(owner_.error());
+            buffers_[i].cond_.signal();
+            break;
+        }
+
+        ASSERT(length == buffers_[i].length_);
+
+        owner_.outBytes_ += length;
+        progress(owner_.outBytes_);
+
+        buffers_[i].cond_.signal();
+
+        i++;
+        i %= owner_.count_;
+    }
+
+    Log::info() << "Write done " << Bytes(owner_.outBytes_) << std::endl;
+    Log::info() << "Write rate " << Bytes(owner_.outBytes_/rate) << "/s" << std::endl;
+    if(rate != first)
+        Log::info() << "Write rate no mount " << Bytes(owner_.outBytes_/(rate-first)) << "/s" << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/DblBuffer.h b/eckit/src/eckit/io/DblBuffer.h
new file mode 100644
index 0000000..246a22a
--- /dev/null
+++ b/eckit/src/eckit/io/DblBuffer.h
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DblBuffer.h
+// Baudouin Raoult - ECMWF Feb 97
+
+#ifndef eckit_DblBuffer_h
+#define eckit_DblBuffer_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/io/TransferWatcher.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DblBuffer : private NonCopyable {
+public:
+
+// -- Contructors
+
+	DblBuffer(long count = 5, long size = 1024*1024, TransferWatcher& = TransferWatcher::dummy());
+
+// -- Destructor
+
+	~DblBuffer();
+
+// -- Methods
+
+	Length copy(DataHandle&,DataHandle&);
+
+	bool   error();
+    void   error(const std::string&);
+	void   restart(RestartTransfer&);
+
+private: // methods
+
+	Length copy(DataHandle&,DataHandle&,const Length&);
+
+private: // members
+
+	Mutex  mutex_;
+
+    long   count_;
+	long   bufSize_;
+
+	Length inBytes_;
+	Length outBytes_;
+
+	bool error_;
+    std::string why_;
+
+    bool restart_;
+
+	Offset restartFrom_;
+	TransferWatcher& watcher_;
+
+// -- Friends
+
+	friend class DblBufferTask;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/EmptyHandle.cc b/eckit/src/eckit/io/EmptyHandle.cc
new file mode 100644
index 0000000..d0d36b3
--- /dev/null
+++ b/eckit/src/eckit/io/EmptyHandle.cc
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/EmptyHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec EmptyHandle::classSpec_ = {&DataHandle::classSpec(),"EmptyHandle",};
+Reanimator<EmptyHandle> EmptyHandle::reanimator_;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
diff --git a/eckit/src/eckit/io/EmptyHandle.h b/eckit/src/eckit/io/EmptyHandle.h
new file mode 100644
index 0000000..37dfc8f
--- /dev/null
+++ b/eckit/src/eckit/io/EmptyHandle.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/EmptyHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_EmptyHandle_h
+#define eckit_filesystem_EmptyHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class EmptyHandle :  public DataHandle {
+public:
+
+// -- Contructors
+	EmptyHandle()							{ }
+	EmptyHandle(Stream& s): DataHandle(s) 	{ }
+
+// -- Destructor
+
+	~EmptyHandle() 							{ }
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead()              { return 0;}
+    virtual void openForWrite(const Length&)  {}
+    virtual void openForAppend(const Length&) {}
+
+    virtual long   read(void*,long)			{ return 0; }
+    virtual long   write(const void*,long n){ return n; }
+    virtual void   close()					{ }
+    virtual void   flush()					{ }
+    virtual void   rewind()					{ }
+    virtual void   print(std::ostream& s) const	{ s << "Empty Handle"; }
+	virtual bool   isEmpty() const          { return true; }
+
+    // From Streamable
+
+    virtual void encode(Stream& s) const		 { DataHandle::encode(s); }
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Class members
+
+    static  ClassSpec                 classSpec_;
+    static  Reanimator<EmptyHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FTPHandle.cc b/eckit/src/eckit/io/FTPHandle.cc
new file mode 100644
index 0000000..6fe1b44
--- /dev/null
+++ b/eckit/src/eckit/io/FTPHandle.cc
@@ -0,0 +1,158 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <arpa/inet.h>
+
+#include "eckit/io/FTPHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/net/TCPServer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec FTPHandle::classSpec_ = {&DataHandle::classSpec(),"FTPHandle",};
+Reanimator<FTPHandle> FTPHandle::reanimator_;
+
+const char* FTPHandle::FTPError::what() const throw()
+{
+	return "FTP Error";
+}
+
+void FTPHandle::print(std::ostream&s) const
+{
+	s << "FTPHandle[file=" << remote_ << ",host=" << host_ << ",port=" << port_ << ']';
+}
+
+void FTPHandle::encode(Stream& s) const
+{
+	DataHandle::encode(s);
+	s << remote_;
+	s << host_;
+	s << port_;
+}
+
+FTPHandle::FTPHandle(Stream& s):
+	DataHandle(s),
+	port_(0)
+{
+	s >> remote_;
+	s >> host_;
+	s >> port_;
+}
+
+std::string FTPHandle::readLine()
+{
+	std::string s;
+	char c;
+	while(cmds_.read(&c,1)==1 && c!='\n')
+		s += c;
+	Log::info() << "receive " << s << std::endl;
+	return s;
+}
+
+void FTPHandle::ftpCommand(const std::string& s)
+{
+	Log::info() << "send " << s << std::endl;
+	cmds_.write(s.c_str(),s.length());
+	cmds_.write("\r\n",2);
+	std::string out = readLine();	
+
+	if(atoi(out.c_str()) / 100 == 5)
+		throw FTPError();
+}
+
+FTPHandle::FTPHandle(const std::string& host,const std::string& remote,int port):
+    remote_(remote),
+	host_(host),
+	port_(port)
+{
+}
+
+void FTPHandle::open(const std::string& cmd)
+{
+	
+	cmds_.connect(host_,port_);
+
+	readLine();
+	ftpCommand("USER mab");
+	ftpCommand("PASS 04clave");
+	ftpCommand("TYPE I");
+
+	TCPServer server(0); // Any port
+
+	int port    = htons(server.localPort());
+	std::string addr = ::inet_ntoa(server.localAddr());
+
+	char p[1024];
+	snprintf(p,1024,"PORT %s,%d,%d",addr.c_str(),port/256,port%256);
+	char *q = p;
+	while(*q)
+	{
+		if(*q=='.')
+			*q=',';
+		q++;
+	}
+	ftpCommand(p);
+
+	snprintf(p,1024,"%s %s",cmd.c_str(),remote_.c_str());
+
+	ftpCommand(p);
+
+	data_ = server.accept();
+}
+
+Length FTPHandle::openForRead()
+{
+	open("RETR");
+	return 0;
+}
+
+void FTPHandle::openForWrite(const Length&)
+{
+	open("STOR");
+}
+
+void FTPHandle::openForAppend(const Length&)
+{
+	NOTIMP;
+}
+
+long FTPHandle::read(void* buffer,long length)
+{
+	return data_.read(buffer,length);
+}
+
+long FTPHandle::write(const void* buffer,long length)
+{
+	return data_.write(buffer,length);
+}
+
+void FTPHandle::close()
+{
+	data_.close();
+
+	readLine();
+	ftpCommand("QUIT");
+	cmds_.close();
+}
+
+void FTPHandle::rewind()
+{
+	NOTIMP;
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/FTPHandle.h b/eckit/src/eckit/io/FTPHandle.h
new file mode 100644
index 0000000..04f0191
--- /dev/null
+++ b/eckit/src/eckit/io/FTPHandle.h
@@ -0,0 +1,94 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/FTPHandle.h
+// Manuel Fuentes - ECMWF May 96
+
+#ifndef eckit_filesystem_FTPHandle_h
+#define eckit_filesystem_FTPHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FTPHandle : public DataHandle {
+public:
+
+// -- Exceptions
+
+    class FTPError : public std::exception { virtual const char *what() const throw(); };
+
+// -- Contructors
+
+	FTPHandle(const std::string&,const std::string&,int port = 21);
+	FTPHandle(Stream&);
+
+// -- Destructor
+
+	~FTPHandle() {}
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void rewind();
+	virtual void print(std::ostream&) const;
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	std::string        remote_;
+	std::string        host_;
+	int           port_;
+	TCPClient     cmds_;
+	TCPSocket     data_;
+
+// -- Methods
+	
+	void   ftpCommand(const std::string&);
+	std::string readLine();
+	void   open(const std::string&);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<FTPHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FileBase.cc b/eckit/src/eckit/io/FileBase.cc
new file mode 100644
index 0000000..3347627
--- /dev/null
+++ b/eckit/src/eckit/io/FileBase.cc
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+FileBase<T>::FileBase(const PathName& path) : fd_(-1), path_(path), pos_(0)
+{
+	SYSCALL(fd_ = ::open(path.localPath(),O_RDWR|O_CREAT,0777));
+}
+
+template<class T>
+FileBase<T>::~FileBase()
+{
+	if(fd_>=0)
+		SYSCALL(::close(fd_));
+}
+
+template<class T>
+bool FileBase<T>::read(long rec,T& data)
+{
+	off_t pos = rec * sizeof(Record);
+	if(pos != pos_)
+		SYSCALL(pos_ = ::lseek(fd_,pos,SEEK_SET));
+
+	long size = 0;
+
+	//struct flock lock   = { F_RDLCK, SEEK_SET, pos_, sizeof(Record), };
+	//struct flock unlock = { F_UNLCK, SEEK_SET, pos_, sizeof(Record), };
+
+	// SYSCALL(::fcntl(fd_,F_SETLK,&lock));
+	SYSCALL(size = ::read(fd_,&buffer_,sizeof(Record)));	
+	// SYSCALL(::fcntl(fd_,F_SETLK,&unlock));
+
+	pos_ += size;
+
+	if(size != sizeof(Record)) // EOF reached
+		return false;
+
+	::memcpy(&data,&buffer_,sizeof(T));
+	return buffer_.valid_;
+}
+
+template<class T>
+void FileBase<T>::write(long rec,const T& data)
+{
+	off_t pos = rec * sizeof(Record);
+	if(pos != pos_)
+		SYSCALL(pos_ = ::lseek(fd_,pos,SEEK_SET));
+
+	long size = 0;
+
+	::memcpy(&buffer_,&data,sizeof(T));
+	buffer_.valid_ = true;
+
+	//struct flock lock   = { F_WRLCK, SEEK_SET, pos_, sizeof(Record), };
+	//struct flock unlock = { F_UNLCK, SEEK_SET, pos_, sizeof(Record), };
+	// SYSCALL(::fcntl(fd_,F_SETLK,&lock));
+	SYSCALL(size = ::write(fd_,&buffer_,sizeof(Record)));	
+	// SYSCALL(::fcntl(fd_,F_SETLK,&unlock));
+
+	pos_ += size;
+	ASSERT(size == sizeof(Record));
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/FileBase.h b/eckit/src/eckit/io/FileBase.h
new file mode 100644
index 0000000..ec4c205
--- /dev/null
+++ b/eckit/src/eckit/io/FileBase.h
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FileBase.h
+// Baudouin Raoult - ECMWF Jun 97
+
+#ifndef eckit_FileBase_h
+#define eckit_FileBase_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class FileBase {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	FileBase(const PathName&);
+
+// -- Destructor
+
+	~FileBase(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	bool read(long,T&);
+	void write(long,const T&);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+
+	// void print(std::ostream&) const; 
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	FileBase(const FileBase<T>&);
+	FileBase<T>& operator=(const FileBase<T>&);
+
+// -- Members
+
+	struct Record {
+		T    data_;
+		bool valid_;
+	};
+
+	int      fd_;
+	PathName path_;
+	Record   buffer_;
+	off_t  pos_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const FileBase& p)
+	//	{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/io/FileBase.cc"
+
+#endif
diff --git a/eckit/src/eckit/io/FileDescHandle.cc b/eckit/src/eckit/io/FileDescHandle.cc
new file mode 100644
index 0000000..e0c8e72
--- /dev/null
+++ b/eckit/src/eckit/io/FileDescHandle.cc
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/io/FileDescHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void FileDescHandle::print(std::ostream& s) const
+{
+    s << "FileDescHandle[fd=" << fd_ << ']';
+}
+
+void FileDescHandle::encode(Stream& s) const
+{
+    NOTIMP;
+}
+
+FileDescHandle::FileDescHandle(int fd):
+    fd_(fd)
+{
+}
+
+FileDescHandle::~FileDescHandle()
+{
+}
+
+Length FileDescHandle::openForRead()
+{
+    return 0;
+}
+
+void FileDescHandle::openForWrite(const Length&)
+{
+}
+
+void FileDescHandle::openForAppend(const Length&)
+{
+}
+
+long FileDescHandle::read(void* buffer,long length)
+{
+    return ::read(fd_,buffer,length);
+}
+
+long FileDescHandle::write(const void* buffer,long length)
+{
+    return ::write(fd_,buffer,length);
+}
+
+void FileDescHandle::close()
+{
+    // May be we should close fd_ here ?
+}
+
+Offset FileDescHandle::position()
+{
+    return ::lseek(fd_, 0, SEEK_CUR);
+}
+
+Offset FileDescHandle::seek(const Offset& o)
+{
+    return ::lseek(fd_, o, SEEK_SET);
+}
+
+void FileDescHandle::skip(const Length& l)
+{
+    ::lseek(fd_, l, SEEK_CUR);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/FileDescHandle.h b/eckit/src/eckit/io/FileDescHandle.h
new file mode 100644
index 0000000..fa1e103
--- /dev/null
+++ b/eckit/src/eckit/io/FileDescHandle.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/FileDescHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_FileDescHandle_h
+#define eckit_filesystem_FileDescHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileDescHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	FileDescHandle(int);
+
+// -- Destructor
+
+	~FileDescHandle();
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long   read(void*,long);
+	virtual long   write(const void*,long);
+	virtual void   close();
+	virtual void   print(std::ostream&) const;
+
+    virtual Offset position();
+    virtual Offset seek(const Offset&);
+    virtual void skip(const Length &);
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+
+// -- Class methods
+
+
+private:
+
+// -- Members
+
+	int fd_;
+
+// -- Class members
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FileHandle.cc b/eckit/src/eckit/io/FileHandle.cc
new file mode 100644
index 0000000..346c743
--- /dev/null
+++ b/eckit/src/eckit/io/FileHandle.cc
@@ -0,0 +1,349 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/MarsFSHandle.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/os/Stat.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec FileHandle::classSpec_ = {&DataHandle::classSpec(),"FileHandle",};
+Reanimator<FileHandle> FileHandle::reanimator_;
+
+void FileHandle::print(std::ostream& s) const
+{
+    s << "FileHandle[file=" << name_ << ']';
+}
+
+void FileHandle::encode(Stream& s) const
+{
+    DataHandle::encode(s);
+    s << name_;
+    s << overwrite_;
+}
+
+FileHandle::FileHandle(Stream& s):
+        DataHandle(s),
+        overwrite_(false),
+        file_(0),
+        read_(false)
+{
+    s >> name_;
+    s >> overwrite_;
+}
+
+FileHandle::FileHandle(const std::string& name,bool overwrite):
+    name_(name),
+    overwrite_(overwrite),
+    file_(0),
+    read_(false)
+{
+}
+
+FileHandle::~FileHandle()
+{
+}
+
+void FileHandle::open(const char* mode)
+{
+    file_ = ::fopen(name_.c_str(),mode);
+    if (file_ == 0)
+        throw CantOpenFile(name_);
+
+    // Don't buffer writes, so we know when the filesystems
+    // are full at fwrite time, and not at fclose time.
+    // There should not be any performances issues as
+    // this class is used with large buffers anyway
+
+    if (!(::strcmp(mode,"r") == 0))
+        setbuf(file_,0);
+	else
+	{
+	   static long bufSize  = Resource<long>("FileHandleIOBufferSize;$FILEHANDLE_IO_BUFFERSIZE;-FileHandleIOBufferSize",0);
+		long size = bufSize;
+		if(size)
+		{
+			Log::debug() << "FileHandle using " << Bytes(size) << std::endl;
+			buffer_.reset(new Buffer(size));
+			Buffer& b = *(buffer_.get());
+			::setvbuf(file_,b,_IOFBF,size);
+		}
+	}
+
+    //Log::info() << "FileHandle::open " << name_ << " " << mode << " " << fileno(file_) << std::endl;
+
+}
+
+Length FileHandle::openForRead()
+{
+    read_ = true;
+    open("r");
+    return estimate();
+}
+
+void FileHandle::openForWrite(const Length& length)
+{
+    read_ = false;
+    PathName path(name_);
+    // This is for preallocated files
+    if (overwrite_ && path.exists())
+    {
+        ASSERT(length == path.size());
+        open("r+");
+    }
+    else
+        open("w");
+}
+
+void FileHandle::openForAppend(const Length&)
+{
+    open("a");
+}
+
+long FileHandle::read(void* buffer,long length)
+{
+    return ::fread(buffer,1,length,file_);
+}
+
+long FileHandle::write(const void* buffer,long length)
+{
+	ASSERT( buffer );
+
+	long written = ::fwrite(buffer,1,length,file_);
+
+    if (written != length && errno == ENOSPC)
+    {
+        long len = written;
+
+        do {
+
+            clearerr(file_);
+
+            Log::status() << "Disk is full, waiting..." << std::endl;
+            ::sleep(60);
+
+            errno   = 0;
+            buffer  = ((char*)buffer) + len;
+            length -= len;
+
+            len      = ::fwrite(buffer,1,length,file_);
+            written += len;
+
+        } while (len != length && errno == ENOSPC);
+    }
+
+    return written;
+}
+
+void FileHandle::flush()
+{
+    if( file_ == 0 ) return;
+    
+    if (file_) 
+    {
+        if (!read_)
+        {
+            if (::fflush(file_))
+                throw WriteError(std::string("fflush(") + name_ + ")", Here());
+
+            int ret = fsync(fileno(file_));
+
+            while (ret < 0 && errno == EINTR)
+                ret = fsync(fileno(file_));
+            if (ret < 0) {
+                Log::error() << "Cannot fsync(" << name_ << ") " <<fileno(file_) <<  Log::syserr << std::endl;
+            }
+            
+            // On Linux, you must also flush the directory
+			static bool fileHandleSyncsParentDir = eckit::Resource<bool>("fileHandleSyncsParentDir", true);
+			if( fileHandleSyncsParentDir )
+				PathName(name_).syncParentDirectory();
+
+        }
+    }
+}
+
+
+void FileHandle::close()
+{
+    if( file_ == 0 ) return;
+    
+    if (file_) 
+    {
+        // Because AIX has large system buffers, 
+        // the close may be successful without the
+        // data being physicaly on disk. If there is
+        // a power failure, we lose some data. So we
+        // need to fsync
+
+        flush();
+        
+        if (::fclose(file_) != 0)
+        {
+            throw WriteError(std::string("fclose ") + name());
+        }
+    }
+    else
+    {
+        Log::warning() << "Closing FileHandle " << name_ << ", file is not opened" << std::endl;
+    }
+    buffer_.reset(0);
+}
+
+void FileHandle::rewind()
+{
+    ::rewind(file_);
+}
+
+Length FileHandle::estimate()
+{
+    Stat::Struct info;
+    SYSCALL(Stat::stat(name_.c_str(),&info));
+    return info.st_size;
+}
+
+bool FileHandle::isEmpty() const
+{
+    Stat::Struct info;
+    if( Stat::stat(name_.c_str(),&info) == -1 )
+        return false; // File does not exists
+    return info.st_size == 0;
+}
+
+// Try to be clever ....
+
+Length FileHandle::saveInto(DataHandle& other,TransferWatcher& w)
+{
+    static bool fileHandleSaveIntoOptimisationUsingHardLinks = eckit::Resource<bool>("fileHandleSaveIntoOptimisationUsingHardLinks", false);
+    if(!fileHandleSaveIntoOptimisationUsingHardLinks)
+        return DataHandle::saveInto(other,w);
+
+    // Poor man's RTTI,
+    // Does not support inheritance
+
+    if ( !sameClass(other) )
+        return DataHandle::saveInto(other,w);
+
+    // We should be safe to cast now....
+
+    FileHandle* handle = dynamic_cast<FileHandle*>(&other);
+
+    if (::link(name_.c_str(),handle->name_.c_str()) == 0)
+        Log::debug() << "Saved ourselves a file to file copy!!!" << std::endl;
+    else {
+        Log::debug() << "Failed to link " <<  name_
+        << " to " << handle->name_ << Log::syserr << std::endl;
+        Log::debug() << "Using defaut method" << std::endl;
+        return DataHandle::saveInto(other,w);
+    }
+
+    return estimate();
+}
+
+Offset FileHandle::position()
+{
+    ASSERT(file_);
+    return ::ftello(file_);
+}
+
+void FileHandle::advance(const Length& len)
+{
+    off_t l = len;
+    if (::fseeko(file_,l,SEEK_CUR) < 0)
+        throw ReadError(name_);
+}
+
+void FileHandle::restartReadFrom(const Offset& from)
+{
+    ASSERT(read_);
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    off_t l = from;
+    if (::fseeko(file_,l,SEEK_SET) < 0)
+        throw ReadError(name_);
+
+    ASSERT(::ftello(file_) == l);
+}
+
+void FileHandle::restartWriteFrom(const Offset& from)
+{
+    ASSERT(!read_);
+    Log::warning() << *this << " restart write from " << from << std::endl;
+    off_t l = from;
+    if (::fseeko(file_,l,SEEK_SET) < 0)
+        throw ReadError(name_);
+
+    ASSERT(::ftello(file_) == l);
+}
+
+Offset FileHandle::seek(const Offset& from)
+{
+    off_t l = from;
+    if (::fseeko(file_,l,SEEK_SET) < 0)
+        throw ReadError(name_);
+    off_t w = ::ftello(file_);
+    ASSERT(w == l);
+    return w;
+}
+
+void FileHandle::skip(const Length &n)
+{
+    off_t l = n;
+    if (::fseeko(file_,l,SEEK_CUR) < 0)
+        throw ReadError(name_);
+}
+
+void FileHandle::toRemote(Stream& s) const
+{
+    MarsFSPath p(PathName(name_).clusterName());
+    MarsFSHandle remote(p);
+    s << remote;
+}
+
+void FileHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+    if (read)
+    {
+        c[NodeInfo::thisNode().node()] += const_cast<FileHandle*>(this)->estimate();
+    }
+    else
+    {
+        // Just mark the node as being a candidate
+        c[NodeInfo::thisNode().node()] += 0;
+    }
+}
+
+std::string FileHandle::title() const
+{
+    return PathName::shorten(name_);
+}
+
+
+DataHandle* FileHandle::clone() const
+{
+    return new FileHandle(name_, overwrite_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/FileHandle.h b/eckit/src/eckit/io/FileHandle.h
new file mode 100644
index 0000000..f804c5b
--- /dev/null
+++ b/eckit/src/eckit/io/FileHandle.h
@@ -0,0 +1,107 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/FileHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_FileHandle_h
+#define eckit_filesystem_FileHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Buffer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	FileHandle(const std::string&,bool = false);
+	FileHandle(Stream&);
+
+// -- Destructor
+
+	~FileHandle();
+
+// --  Methods
+
+	void advance(const Length&); 
+	const std::string& path() const { return name_; }
+
+// -- Overridden methods
+
+	// From DataHandle
+
+	virtual Length openForRead();
+	virtual void   openForWrite(const Length&);
+	virtual void   openForAppend(const Length&);
+
+	virtual long   read(void*,long);
+	virtual long   write(const void*,long);
+    virtual void   close();
+    virtual void   flush();
+	virtual void   rewind();
+	virtual void   print(std::ostream&) const;
+	virtual Length estimate();
+	virtual Length saveInto(DataHandle&,TransferWatcher& = TransferWatcher::dummy());
+	virtual Offset position();
+	virtual bool isEmpty() const;
+	virtual void restartReadFrom(const Offset& from);
+	virtual void restartWriteFrom(const Offset& from);
+    virtual void toRemote(Stream&) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+    virtual Offset seek(const Offset&);
+    virtual void skip(const Length&);
+
+    virtual DataHandle* clone() const;
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	std::string              name_;
+	bool                overwrite_;
+	FILE*               file_;
+	bool                read_;
+    std::auto_ptr<Buffer>    buffer_;
+
+// -- Methods
+
+	void open(const char*);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<FileHandle>  reanimator_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FileLock.cc b/eckit/src/eckit/io/FileLock.cc
new file mode 100644
index 0000000..b24da44
--- /dev/null
+++ b/eckit/src/eckit/io/FileLock.cc
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/FileLock.h"
+#include "eckit/os/AutoUmask.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static int openLock(const PathName& lockFile) {
+    AutoUmask mask(0);
+    int fd;
+    lockFile.dirName().mkdir(0777);
+    SYSCALL2(fd = ::open(lockFile.asString().c_str(), O_CREAT|O_RDWR, 0777), lockFile);
+    return fd;
+}
+
+
+FileLock::FileLock(const PathName& lockFile):
+    fd_(openLock(lockFile)),
+    locker_(fd_)
+{
+}
+
+FileLock::~FileLock()
+{
+    ::close(fd_);
+}
+
+void FileLock::lock()
+{
+    locker_.lockExclusive();
+}
+
+
+void FileLock::unlock()
+{
+    locker_.unlock();
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/FileLock.h b/eckit/src/eckit/io/FileLock.h
new file mode 100644
index 0000000..bfeaeea
--- /dev/null
+++ b/eckit/src/eckit/io/FileLock.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+///
+/// @date   Oct 2016
+
+#ifndef eckit_io_FileLock_h
+#define eckit_io_FileLock_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/io/FileLocker.h"
+
+namespace eckit {
+
+class PathName;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FileLock : public NonCopyable {
+
+public:
+
+    /// Constructor
+    /// creates the lock file if needed
+	FileLock(const PathName& lockFile);
+
+	~FileLock();
+
+	void lock();
+	void unlock();
+
+private:
+
+	int fd_;
+
+    FileLocker locker_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FileLocker.cc b/eckit/src/eckit/io/FileLocker.cc
new file mode 100644
index 0000000..c1dc01c
--- /dev/null
+++ b/eckit/src/eckit/io/FileLocker.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <fcntl.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/FileLocker.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+FileLocker::FileLocker(int fd):
+	fd_(fd)
+{
+}
+
+FileLocker::~FileLocker()
+{
+}
+
+void FileLocker::lockExclusive(off_t off,off_t len)
+{
+	lockRange(off,len,F_SETLKW,F_WRLCK);
+}
+
+void FileLocker::lockShared(off_t off,off_t len)
+{
+	lockRange(off,len,F_SETLKW,F_RDLCK);
+}
+
+void FileLocker::unlock(off_t off,off_t len)
+{
+	lockRange(off,len,F_SETLK,F_UNLCK);
+}
+
+void FileLocker::lockRange(off_t start,off_t len,int cmd,int type)
+{
+	struct flock lock;
+
+    lock.l_type   = type;
+    lock.l_whence = SEEK_SET;
+    lock.l_start  = start;
+    lock.l_len    = len;
+
+    SYSCALL(::fcntl(fd_, cmd, &lock));
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/FileLocker.h b/eckit/src/eckit/io/FileLocker.h
new file mode 100644
index 0000000..b3a8f0e
--- /dev/null
+++ b/eckit/src/eckit/io/FileLocker.h
@@ -0,0 +1,113 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Feb 2002
+
+#ifndef eckit_FileLocker_h
+#define eckit_FileLocker_h
+
+#include "eckit/eckit.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FileLocker {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	FileLocker(int fd);
+
+// -- Destructor
+
+	~FileLocker();
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	void lockShared(off_t off = 0, off_t len = 0);
+	void lockExclusive(off_t off = 0, off_t len = 0);
+	void unlock(off_t off = 0, off_t len = 0);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+
+	// void print(std::ostream&) const;
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	FileLocker(const FileLocker&);
+	FileLocker& operator=(const FileLocker&);
+
+// -- Members
+
+	int fd_;
+
+// -- Methods
+	// None
+
+	void lockRange(off_t,off_t,int cmd,int type);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const FileLocker& p)
+	//	{ p.print(s); return s; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/FilePool.cc b/eckit/src/eckit/io/FilePool.cc
new file mode 100644
index 0000000..21aa3cb
--- /dev/null
+++ b/eckit/src/eckit/io/FilePool.cc
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+#include "eckit/io/FilePool.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/types/Types.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static void closeDataHandle(PathName&, DataHandle*& handle) {
+    if(handle) {
+        handle->close();
+        delete handle;
+        handle = 0;
+    }
+}
+
+static bool inUse(const std::map<PathName,DataHandle*>& store, const PathName& path) {
+    return store.find(path) != store.end();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+FilePool::FilePool(size_t size) :
+    cache_(size, &closeDataHandle) {
+}
+
+FilePool::~FilePool() {
+}
+
+DataHandle* FilePool::checkout(const PathName& path) {
+    AutoLock<MutexCond> lock(cond_);
+
+    while(inUse(inUse_, path)) {
+        cond_.wait();
+    }
+
+    DataHandle* dh;
+
+    if( cache_.exists(path) ) {
+        dh = cache_.extract(path);
+    }
+    else {
+        dh = path.fileHandle(false); // append mode (no overwrite)
+        dh->openForAppend(0);
+    }
+
+    ASSERT(dh);
+
+    inUse_[path] = dh;
+
+    return dh;
+}
+
+void FilePool::checkin(DataHandle* handle) {
+    AutoLock<MutexCond> lock(cond_);
+
+    typedef std::map<PathName,DataHandle*>::iterator iterator_type;
+    for(iterator_type itr = inUse_.begin(); itr != inUse_.end(); ++itr) {
+        if( itr->second == handle ) {
+            cache_.insert(itr->first, itr->second);
+            inUse_.erase(itr);
+            cond_.signal();
+            return;
+        }
+    }
+    throw eckit::SeriousBug("Should have found a DataHandle in pool use", Here());
+
+}
+
+bool FilePool::remove(const PathName& path) {
+    AutoLock<MutexCond> lock(cond_);
+
+    while(inUse(inUse_, path)) {
+        cond_.wait();
+    }
+
+    return cache_.remove(path);
+}
+
+void FilePool::print(std::ostream& os) const {
+    AutoLock<MutexCond> lock(const_cast<FilePool&>(*this).cond_);
+    os << "FilePool("
+       << "inUse=" << inUse_ << ", "
+       << "cache=" << cache_ << ")";
+}
+
+size_t FilePool::size() const {
+    AutoLock<MutexCond> lock(cond_);
+    return cache_.size();
+}
+
+void FilePool::capacity( size_t size ) {
+    AutoLock<MutexCond> lock(cond_);
+    cache_.capacity(size);
+}
+
+size_t FilePool::capacity() const {
+    AutoLock<MutexCond> lock(cond_);
+    return cache_.capacity();
+}
+
+size_t FilePool::usage() const {
+    AutoLock<MutexCond> lock(cond_);
+    return inUse_.size();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/FilePool.h b/eckit/src/eckit/io/FilePool.h
new file mode 100644
index 0000000..430f895
--- /dev/null
+++ b/eckit/src/eckit/io/FilePool.h
@@ -0,0 +1,94 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+
+#ifndef eckit_io_FilePool_h
+#define eckit_io_FilePool_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/container/CacheLRU.h"
+
+namespace eckit {
+    class DataHandle;
+    class PathName;
+}
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Pool of a maximum number of open files.
+/// Open files manipulated via DataHandle interface.
+/// Handles are kept open as long as possible following LRU algorithm
+/// Handles must be checked-out before usage, should be checked back in once writing finished.
+/// No limit to number of checked out handles.
+/// Handles are closed when purged from pool due to LRU or when pool is destroyed.
+///
+/// @note this class is thread-safe
+///
+class FilePool : private eckit::NonCopyable {
+
+public:
+
+    FilePool(size_t capacity);
+
+    ~FilePool();
+
+    /// Checkout a DataHandle for use
+    /// Ownership is passed from FilePool to the client
+    /// @post DataHandle is marked in use and should not be closed by client
+    /// @invariant If handle is in use, it assumes is from another thread and it will wait() for a checkin()
+    DataHandle * checkout(const PathName& path);
+
+    /// Return a DataHandle after use
+    /// Ownership is passed back from the client to FilePool
+    /// @post DataHandle is marked out of use and may now be closed by FilePool
+    void checkin(DataHandle* handle);
+
+    /// Remove a DataHandle from the pool
+    /// @invariant If handle is in use, it assumes is from another thread and it will wait() for a checkin()
+    bool remove(const PathName& path);
+
+    /// Current size of pool
+    size_t size() const;
+
+    /// Resize pool capacity
+    void capacity( size_t size );
+
+    /// Returns max size of pool
+    size_t capacity() const;
+
+    /// Returns number of checked-out DataHandles
+    size_t usage() const;
+
+    /// Lists the contents of the Pool both in use and cached DataHandle's
+    void print(std::ostream& os) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const FilePool& p) { p.print(s); return s; }
+
+private:
+
+    std::map< PathName, DataHandle* > inUse_;
+
+    eckit::CacheLRU< PathName, DataHandle* > cache_;
+
+    mutable eckit::MutexCond cond_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/io/HandleBuf.cc b/eckit/src/eckit/io/HandleBuf.cc
new file mode 100644
index 0000000..92e0124
--- /dev/null
+++ b/eckit/src/eckit/io/HandleBuf.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/HandleBuf.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+HandleBuf::HandleBuf(DataHandle& handle):
+	handle_(handle)
+{ 
+#ifndef OLD_STREAMBUF
+	/* setg(in_,  in_,  in_  + sizeof(in_) );  */
+    setg(in_, in_, in_);
+    setp(out_, out_ + sizeof(out_));
+#else
+    setb(in_, in_ + sizeof(in_), 0);
+    setg(in_, in_, in_);
+    setp(out_, out_ + sizeof(out_));
+#endif
+}
+
+HandleBuf::~HandleBuf()
+{
+	sync();
+}
+
+int HandleBuf::sync()
+{
+	if(handle_.write(pbase(),pptr() - pbase()) < 0)	
+		return EOF;	
+
+	setp(pbase(), epptr());
+
+	return 0;
+}
+
+int HandleBuf::overflow(int c) 
+{
+	if(sync())
+		return EOF;
+
+	if(c == EOF)
+		return 0;
+
+	sputc(c);
+	return 0;
+}
+
+int HandleBuf::underflow()
+{
+	if (gptr () < egptr ()) 
+		return *(unsigned char*)gptr ();
+
+#ifndef OLD_STREAMBUF
+	int n = handle_.read(in_,sizeof(in_));
+#else
+	int n = handle_.read(base(),sizeof(in_));
+#endif
+
+	if(n == EOF || n == 0)
+		return EOF;
+
+#ifndef OLD_STREAMBUF
+	setg(in_,  in_,  in_  + n ); 
+#else
+	setg (eback (), base (), base () + n);
+#endif
+
+	return *(unsigned char*)gptr ();
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/HandleBuf.h b/eckit/src/eckit/io/HandleBuf.h
new file mode 100644
index 0000000..e614013
--- /dev/null
+++ b/eckit/src/eckit/io/HandleBuf.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HandleBuf.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_HandleBuf_h
+#define eckit_HandleBuf_h
+
+#include "eckit/io/DataHandle.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class HandleBuf : public std::streambuf  {
+public:
+
+// -- Contructors
+
+	HandleBuf(DataHandle& handle);
+
+// -- Destructor
+
+	~HandleBuf();
+
+private:
+
+// No copy allowed
+
+	HandleBuf(const HandleBuf&);
+	HandleBuf& operator=(const HandleBuf&);
+
+// -- Members
+
+	char in_[1];
+	char out_[80];
+	DataHandle& handle_;
+
+// -- Overridden methods
+
+	// From streambuf
+
+	virtual int overflow(int c);
+	virtual int underflow();
+	virtual int sync();
+//	virtual int uflow();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/HandleHolder.cc b/eckit/src/eckit/io/HandleHolder.cc
new file mode 100644
index 0000000..9cd3fb9
--- /dev/null
+++ b/eckit/src/eckit/io/HandleHolder.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/io/HandleHolder.h"
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+HandleHolder::HandleHolder(DataHandle &handle):
+    handle_(&handle),
+    owned_(false)
+{
+}
+
+HandleHolder::HandleHolder(DataHandle *handle):
+    handle_(handle),
+    owned_(true)
+{
+}
+
+HandleHolder::~HandleHolder()
+{
+    if( owned_ )
+        delete handle_;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/HandleHolder.h b/eckit/src/eckit/io/HandleHolder.h
new file mode 100644
index 0000000..6210d06
--- /dev/null
+++ b/eckit/src/eckit/io/HandleHolder.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Partio/FileHandle.h
+// Baudouin Raoult - ECMWF Dec 2013
+
+#ifndef eckit_filesystem_WrapperHandle_h
+#define eckit_filesystem_WrapperHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+#include "eckit/log/Timer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class HandleHolder {
+public:
+
+    // -- Contructors
+
+    HandleHolder(DataHandle& handle);
+    HandleHolder(DataHandle* handle);
+
+    // -- Destructor
+
+    ~HandleHolder();
+
+    // -- Methods
+
+    // -- Overridden methods
+
+
+protected:
+
+    DataHandle& handle() { return *handle_; }
+    const DataHandle& handle() const { return *handle_; }
+
+private:
+
+    // -- Members
+
+    DataHandle* handle_;
+    bool owned_;
+
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/Length.cc b/eckit/src/eckit/io/Length.cc
new file mode 100644
index 0000000..53716aa
--- /dev/null
+++ b/eckit/src/eckit/io/Length.cc
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/io/Length.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void Length::dump(DumpLoad& a) const
+{
+	a.dump(value_);
+}
+
+void Length::load(DumpLoad& a)
+{
+	a.load(value_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/Length.h b/eckit/src/eckit/io/Length.h
new file mode 100644
index 0000000..df2b253
--- /dev/null
+++ b/eckit/src/eckit/io/Length.h
@@ -0,0 +1,103 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Length.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Length_h
+#define eckit_Length_h
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Bless;
+class DumpLoad;
+
+// But because the compiler aligns long longs
+// on 64bits boundaries and longs on 32 bits boundaries,
+// we need the help of a little pragma here, to make ObjectStore happy
+
+#ifdef _AIX
+#pragma options align=twobyte
+#endif
+
+class Length {
+
+public: // types
+
+    typedef long long value_t;
+
+public: // methods
+	
+	friend std::ostream& operator<<(std::ostream& s,const Length& x)
+		{ return s << x.value_; }
+
+	friend Stream& operator<<(Stream& s,const Length& x)
+		{ return s << x.value_; }
+
+	friend Stream& operator>>(Stream& s,Length& x)
+		{ return s >> x.value_; }
+
+// -- Contructors
+
+	Length(long long l = 0) : value_(l) {}
+	Length(const Length& other) : value_(other.value_) {}
+
+#include "eckit/io/Length.b"
+
+public: // operators
+
+	Length& operator=(const Length& other) 
+		{ value_ = other.value_; return *this;}
+
+	Length operator+(const Length& other) const
+		{ return Length(value_ + other.value_);}
+
+	Length& operator+=(const Length& other) 
+		{ value_ += other.value_; return *this;}
+
+	bool operator==(const Length& other) const
+		{ return value_ == other.value_; }
+
+	bool operator<(const Length& other) const
+		{ return value_ < other.value_; }
+
+	operator long long() const { return value_; }
+
+	long long operator-(const Length& other) const
+		{ return value_ - other.value_;}
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+private: // members
+
+    value_t value_;
+	
+	friend class Offset;
+};
+
+typedef std::vector<Length> LengthList;
+
+
+#ifdef _AIX
+#pragma options align=reset
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MarsFSHandle.cc b/eckit/src/eckit/io/MarsFSHandle.cc
new file mode 100644
index 0000000..5a56361
--- /dev/null
+++ b/eckit/src/eckit/io/MarsFSHandle.cc
@@ -0,0 +1,198 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/io/MarsFSHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec MarsFSHandle::classSpec_ = {&DataHandle::classSpec(),"MarsFSHandle",};
+Reanimator<MarsFSHandle> MarsFSHandle::reanimator_;
+
+void MarsFSHandle::print(std::ostream& s) const
+{
+	s << "MarsFSHandle[file=" << path_ << ']';
+}
+
+void MarsFSHandle::encode(Stream& s) const
+{
+	DataHandle::encode(s);
+	s << path_;
+	s << overwrite_;
+}
+
+MarsFSHandle::MarsFSHandle(Stream& s):
+	DataHandle(s),
+    path_(s),
+    read_(false),
+    position_(0),
+    overwrite_(false)
+{
+	s >> overwrite_;
+}
+
+MarsFSHandle::MarsFSHandle(const MarsFSPath& path, bool overwrite):
+	path_(path),
+	read_(false),
+   position_(0),
+   overwrite_(overwrite)
+{
+	//Log::info() << "CREATE " << *this << std::endl;
+}
+
+MarsFSHandle::~MarsFSHandle()
+{
+	//Log::info() << "DESTROY " << *this << std::endl;
+}
+
+
+Length MarsFSHandle::openForRead()
+{
+	read_   = true;
+    position_ = 0;
+    if(!file_.get())
+        file_ = std::auto_ptr<MarsFSFile>(new MarsFSFile(path_));
+    length_ = file_->open("r");
+    return length_;
+}
+
+void MarsFSHandle::openForWrite(const Length& length)
+{
+	read_   = false;
+    length_ = length;
+    position_ = 0;
+    if(!file_.get())
+        file_ = std::auto_ptr<MarsFSFile>(new MarsFSFile(path_));
+    file_->open("w", overwrite_);
+}
+
+void MarsFSHandle::openForAppend(const Length& length)
+{
+    NOTIMP;
+}
+
+long MarsFSHandle::read(void* buffer,long length)
+{
+	ASSERT(file_.get());
+    long len = file_->read(buffer, length);
+    if(len > 0) position_ += len;
+    return len;
+}
+
+long MarsFSHandle::write(const void* buffer,long length)
+{
+	ASSERT(file_.get());
+    long len = file_->write(buffer, length);
+    if(len > 0) position_ += len;
+    return len;
+}
+
+Offset MarsFSHandle::position() 
+{
+    return position_;
+}
+
+void MarsFSHandle::close()
+{
+	if(file_.get()) {
+		file_->close();
+        file_ = std::auto_ptr<MarsFSFile>(0);
+	}
+}
+
+void MarsFSHandle::rewind()
+{
+	ASSERT(file_.get());
+	file_->seek(0);
+}
+
+void MarsFSHandle::skip(const Length &n)
+{
+	ASSERT(file_.get());
+	file_->skip(n);
+    position_ += n;
+}
+
+Length MarsFSHandle::estimate()
+{
+	return MarsFSClient(path_).size(path_.path());
+}
+
+bool MarsFSHandle::isEmpty() const
+{
+	if(!MarsFSClient(path_).exists(path_.path()))
+		return false;
+    return  MarsFSClient(path_).size(path_.path()) == Length(0);
+}
+
+void MarsFSHandle::restartReadFrom(const Offset& from)
+{
+    ASSERT(read_);
+    ASSERT(file_.get());
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    ASSERT(file_->seek(from) == from);
+}
+
+void MarsFSHandle::restartWriteFrom(const Offset& from)
+{
+    ASSERT(!read_);
+    ASSERT(file_.get());
+    Log::warning() << *this << " restart write from " << from << std::endl;
+    ASSERT(file_->seek(from) == from);
+}
+
+void MarsFSHandle::toLocal(Stream& s) const
+{
+    // TODO: Return FileHandle if local
+    if(path_.isLocal()) {
+        FileHandle fh(path_.path());
+        s << fh;
+        return;
+    }
+    s << *this;
+}
+
+DataHandle* MarsFSHandle::toLocal()
+{
+    // TODO: Return FileHandle if local
+    if(path_.isLocal()) {
+        return new FileHandle(path_.path());
+    }
+    return this;
+}
+
+void MarsFSHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+    if(read) {
+        c[path_.node()] += const_cast<MarsFSHandle*>(this)->estimate();
+    }
+    else
+    {
+        // Just mark the node as being a candidate
+        c[path_.node()] += 0;
+    }
+}
+
+std::string MarsFSHandle::title() const
+{
+    std::ostringstream os;
+    os << "marsfs:/" << path_.node() << "/" << PathName::shorten(path_.path());
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/MarsFSHandle.h b/eckit/src/eckit/io/MarsFSHandle.h
new file mode 100644
index 0000000..dd7e774
--- /dev/null
+++ b/eckit/src/eckit/io/MarsFSHandle.h
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MarsFSHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_MarsFSHandle_h
+#define eckit_filesystem_MarsFSHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/marsfs/MarsFSFile.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MarsFSHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	MarsFSHandle(const MarsFSPath&, bool overwrite = false);
+	MarsFSHandle(Stream&);
+
+// -- Destructor
+
+	~MarsFSHandle();
+
+// --  Methods
+
+	void advance(const Length&); 
+	const MarsFSPath& path() const { return path_; }
+
+// -- Overridden methods
+
+	// From DataHandle
+
+	virtual Length openForRead();
+	virtual void   openForWrite(const Length&);
+	virtual void   openForAppend(const Length&);
+
+	virtual long   read(void*,long);
+	virtual long   write(const void*,long);
+	virtual void   close();
+	virtual void   rewind();
+    virtual void   skip(const Length& len);
+	virtual void   print(std::ostream&) const;
+	virtual Length estimate();
+	virtual Offset position();
+	virtual bool isEmpty() const;
+	virtual void restartReadFrom(const Offset& from);
+	virtual void restartWriteFrom(const Offset& from);
+
+    virtual DataHandle* toLocal();
+	virtual void toLocal(Stream&) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+
+    virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	MarsFSPath    path_;
+	bool          read_;
+    Length        length_;
+    Offset        position_;
+    bool          overwrite_;
+    
+    std::auto_ptr<MarsFSFile>  file_;
+
+// -- Methods
+
+	void open(const char*);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<MarsFSHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MarsFSPartHandle.cc b/eckit/src/eckit/io/MarsFSPartHandle.cc
new file mode 100644
index 0000000..080fb95
--- /dev/null
+++ b/eckit/src/eckit/io/MarsFSPartHandle.cc
@@ -0,0 +1,306 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/Log.h"
+
+#include "eckit/io/MarsFSPartHandle.h"
+#include "eckit/io/PartFileHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+ClassSpec MarsFSPartHandle::classSpec_ = {&DataHandle::classSpec(),"MarsFSPartHandle",};
+Reanimator<MarsFSPartHandle> MarsFSPartHandle::reanimator_;
+
+void MarsFSPartHandle::print(std::ostream& s) const
+{
+    if(format(s) == Log::compactFormat)
+        s << "MarsFSPartHandle";
+    else
+        s << "MarsFSPartHandle[path=" << path_
+           << ",offset=" << offset_
+           << ",length=" << length_ << ']';
+}
+
+void MarsFSPartHandle::encode(Stream& s) const
+{
+    DataHandle::encode(s);
+    s << path_;
+    s << offset_;
+    s << length_;
+
+}
+
+MarsFSPartHandle::MarsFSPartHandle(Stream& s):
+    DataHandle(s),
+    path_(s),
+    pos_(0),
+    index_(0)
+{
+    s >> offset_;
+    s >> length_;
+
+    ASSERT(offset_.size() == length_.size());
+}
+
+MarsFSPartHandle::MarsFSPartHandle(const MarsFSPath& path,
+    const OffsetList& offset,const LengthList& length):
+    path_(path),
+    pos_(0),
+    index_(0),
+    offset_(offset),
+    length_(length)
+{
+    ASSERT(offset_.size() == length_.size());
+    compress(false);
+}
+
+MarsFSPartHandle::MarsFSPartHandle(const MarsFSPath& path,
+    const Offset& offset,const Length& length):
+    path_(path),
+    pos_(0),
+    index_(0),
+    offset_(1,offset),
+    length_(1,length)
+{
+}
+
+bool MarsFSPartHandle::compress(bool sorted)
+{
+    if(sorted)
+        eckit::sort(offset_,length_);
+    return eckit::compress(offset_,length_);
+}
+
+MarsFSPartHandle::~MarsFSPartHandle()
+{
+}
+
+Length MarsFSPartHandle::openForRead()
+{
+    ASSERT(!file_.get());
+	file_.reset( new MarsFSFile(path_) );
+    file_->open("r");
+    rewind();
+    return estimate();
+}
+
+void MarsFSPartHandle::openForWrite(const Length&)
+{
+    NOTIMP;
+}
+
+void MarsFSPartHandle::openForAppend(const Length&)
+{
+    NOTIMP;
+}
+
+long MarsFSPartHandle::read1(char *buffer,long length)
+{
+    ASSERT(file_.get());
+    // skip empty entries if any
+    while (index_ < offset_.size() && length_[index_] == Length(0))
+        index_++;
+
+    if(index_ == offset_.size()) return 0;
+
+
+    Offset pos  = (long long)offset_[index_] + Length(pos_);
+    ASSERT(file_->seek(pos) == pos);
+
+    Length ll    = length_[index_] - Length(pos_);
+    Length lsize = std::min(Length(length),ll);
+    long size  = lsize;
+
+    ASSERT(Length(size) == lsize);
+
+    long n = file_->read(buffer,size);
+
+    if(n != size)
+    {
+        std::ostringstream s;
+        s << path_ << ": cannot read " << size << ", got only " << n;
+        throw ReadError(s.str());
+    }
+
+    pos_ += n;
+    if(pos_ >= length_[index_])
+    {
+        index_++;
+        pos_ = 0;
+    }
+
+    return n;
+}
+
+
+long MarsFSPartHandle::read(void* buffer,long length)
+{
+    char *p = (char*)buffer;
+
+    long n = 0;
+    long total = 0;
+
+    //Log::info() << "MarsFSPartHandle::read " << length << std::endl;
+
+    while( length > 0 && (n = read1(p,length)) >0)
+    {
+        length -= n;
+        total  += n;
+        p      += n;
+    }
+
+    return total>0?total:n;
+
+}
+
+long MarsFSPartHandle::write(const void* buffer,long length)
+{
+    return -1;
+}
+
+void MarsFSPartHandle::close()
+{
+    if(file_.get()) {
+        file_->close();
+		file_.reset(0) ;
+    }
+}
+
+void MarsFSPartHandle::rewind()
+{
+    pos_   = 0;
+    index_ = 0;
+}
+
+void MarsFSPartHandle::restartReadFrom(const Offset& from)
+{
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    rewind();
+    long long len = from;
+    long long pos = 0;
+
+    for(index_ = 0; index_ < length_.size(); index_++)
+    {
+        long long e = length_[index_];
+        if(len >= pos && len < pos + e)
+        {
+            Log::warning() << *this << " restart read from " << from << ", index=" << index_ << ", pos=" << pos_ << std::endl;
+            pos_ = len - pos;
+            return;
+        }
+        pos += e;
+    }
+    ASSERT(from  == Offset(0) && estimate() == Length(0));
+}
+
+Offset MarsFSPartHandle::seek(const Offset& from)
+{
+    rewind();
+    long long len = from;
+    long long pos = 0;
+
+    for(index_ = 0; index_ < length_.size(); index_++)
+    {
+        long long e = length_[index_];
+        if(len >= pos && len < pos + e)
+        {
+            pos_ = len - pos;
+            return from;
+        }
+        pos += e;
+    }
+    return pos;
+}
+
+bool MarsFSPartHandle::merge(DataHandle* other)
+{
+    if(other->isEmpty())
+        return true;
+
+    // Poor man's RTTI,
+    // Does not support inheritance
+
+    if( !sameClass(*other) )
+        return false;
+
+    // We should be safe to cast now....
+
+    MarsFSPartHandle* handle = dynamic_cast<MarsFSPartHandle*>(other);
+
+    if(path_ != handle->path_)
+        return false;
+
+    ASSERT(handle->offset_.size() == handle->length_.size());
+
+    offset_.reserve(offset_.size() + handle->offset_.size());
+    length_.reserve(length_.size() + handle->length_.size());
+
+    for(Ordinal i = 0; i < handle->offset_.size() ; ++i)
+    {
+        offset_.push_back(handle->offset_[i]);
+        length_.push_back(handle->length_[i]);
+    }
+
+    compress(false);
+    return true;
+}
+
+Length MarsFSPartHandle::estimate()
+{
+    return accumulate(length_.begin(),length_.end(),Length(0));
+}
+
+void MarsFSPartHandle::toLocal(Stream& s) const
+{
+    // TODO: Return PathFileHandle if local
+    if(path_.isLocal())
+    {
+        PartFileHandle ph(path_.path(), offset_, length_);
+        s << ph;
+        return;
+    }
+    s << *this;
+}
+
+DataHandle* MarsFSPartHandle::toLocal()
+{
+    // TODO: Return PathFileHandle if local
+    if(path_.isLocal())
+    {
+        return new PartFileHandle(path_.path(), offset_, length_);
+    }
+    return this;
+}
+
+void MarsFSPartHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+    if(read) {
+        c[path_.node()] += const_cast<MarsFSPartHandle*>(this)->estimate();
+    }
+}
+
+std::string MarsFSPartHandle::title() const
+{
+    std::ostringstream os;
+    os << "marsfs:/" << path_.node() << "/" << PathName::shorten(path_.path())
+       << " (" << length_.size() << ")";
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/MarsFSPartHandle.h b/eckit/src/eckit/io/MarsFSPartHandle.h
new file mode 100644
index 0000000..c5840f6
--- /dev/null
+++ b/eckit/src/eckit/io/MarsFSPartHandle.h
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MarsFSPartHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_MarsFSPartHandle_h
+#define eckit_filesystem_MarsFSPartHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/marsfs/MarsFSFile.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/memory/ScopedPtr.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MarsFSPartHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	MarsFSPartHandle(const MarsFSPath&,const OffsetList&,const LengthList&);
+	MarsFSPartHandle(const MarsFSPath&,const Offset&,const Length&);
+	MarsFSPartHandle(Stream&);
+
+// -- Destructor
+
+	~MarsFSPartHandle();
+
+// -- Methods
+
+	const MarsFSPath& path() const { return path_; }
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void rewind();
+
+
+	virtual void print(std::ostream&) const;
+	virtual bool merge(DataHandle*);
+	virtual bool compress(bool = false);
+	virtual Length estimate();
+
+	virtual void restartReadFrom(const Offset& from);
+    virtual Offset seek(const Offset&);
+
+    virtual DataHandle* toLocal();
+    virtual void toLocal(Stream&) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	MarsFSPath         path_;
+	long long          pos_;
+	Ordinal            index_;
+	OffsetList         offset_;
+	LengthList         length_;
+
+	ScopedPtr<MarsFSFile>  file_;
+
+// -- Methods
+
+	long read1(char*,long);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<MarsFSPartHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MemoryHandle.cc b/eckit/src/eckit/io/MemoryHandle.cc
new file mode 100644
index 0000000..347b872
--- /dev/null
+++ b/eckit/src/eckit/io/MemoryHandle.cc
@@ -0,0 +1,178 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/MemoryHandle.h"
+#include "eckit/io/Buffer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+#if 0
+ClassSpec MemoryHandle::classSpec_ = {&DataHandle::classSpec(),"MemoryHandle",};
+Reanimator<MemoryHandle> MemoryHandle::reanimator_;
+#endif
+
+
+MemoryHandle::MemoryHandle(const Buffer& buffer):
+	address_(const_cast<Buffer&>(buffer)),
+    size_(buffer.size()),
+    opened_(false),
+    readOnly_(true),
+    read_(false),
+    position_(0)
+{
+}
+
+
+MemoryHandle::MemoryHandle(Buffer& buffer):
+    address_(buffer),
+    size_(buffer.size()),
+    opened_(false),
+    readOnly_(false),
+    read_(false),
+    position_(0)
+{
+}
+
+MemoryHandle::MemoryHandle(const void* address,size_t size):
+	address_(const_cast<char*>(reinterpret_cast<const char*>(address))),
+    size_(size),
+    opened_(false),
+    readOnly_(true),
+    read_(false),
+    position_(0)
+{
+}
+
+
+MemoryHandle::MemoryHandle(void* address,size_t size):
+    address_(reinterpret_cast<char*>(address)),
+    size_(size),
+    opened_(false),
+    readOnly_(false),
+    read_(false),
+    position_(0)
+{
+}
+
+MemoryHandle::~MemoryHandle()
+{
+}
+
+Length MemoryHandle::openForRead()
+{
+    read_ = true;
+    position_ = 0;
+    opened_ = true;
+    return size_;
+}
+
+void MemoryHandle::openForWrite(const Length& length)
+{
+    ASSERT(!readOnly_);
+    read_ = false;
+    position_ = 0;
+    opened_ = true;
+}
+
+void MemoryHandle::openForAppend(const Length& )
+{
+	NOTIMP;
+}
+
+void MemoryHandle::skip(const Length& len)
+{
+    ASSERT(read_);
+    seek(position() + len);
+}
+
+long MemoryHandle::read(void* buffer,long length)
+{
+    ASSERT(opened_);
+    ASSERT(read_);
+
+    size_t left = size_ - position_;
+    size_t size = std::min(left, size_t(length));
+    ::memcpy(buffer, address_ + position_, size);
+    position_ += size;
+
+	return size;
+}
+
+long MemoryHandle::write(const void* buffer,long length)
+{
+    ASSERT(opened_);
+    ASSERT(!read_);
+
+    size_t left = size_ - position_;
+    size_t size = std::min(left, size_t(length));
+    ::memcpy(address_ + position_, buffer, size);
+    position_ += size;
+
+    return size;
+}
+
+void MemoryHandle::close()
+{
+    opened_ = false;
+}
+
+void MemoryHandle::flush()
+{
+
+}
+
+void MemoryHandle::rewind()
+{
+    ASSERT(opened_);
+	position_ = 0;
+}
+
+Offset MemoryHandle::seek(const Offset& off)
+{
+    ASSERT(opened_);
+    ASSERT(size_t(off) < size_);
+    position_ = off;
+    return position_;
+}
+
+
+void MemoryHandle::print(std::ostream& s) const
+{
+	s << "MemoryHandle[size=" << size_ << ']';
+}
+
+Length MemoryHandle::estimate()
+{
+    return size_;
+}
+
+Offset MemoryHandle::position()
+{
+    ASSERT(opened_);
+    return position_;
+}
+
+std::string MemoryHandle::title() const {
+    return "<mem>";
+}
+
+DataHandle* MemoryHandle::clone() const
+{
+    return new MemoryHandle(address_, size_);
+}
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/MemoryHandle.h b/eckit/src/eckit/io/MemoryHandle.h
new file mode 100644
index 0000000..3c09ba5
--- /dev/null
+++ b/eckit/src/eckit/io/MemoryHandle.h
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MemoryHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_MemoryHandle_h
+#define eckit_filesystem_MemoryHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+class Buffer;
+
+//-----------------------------------------------------------------------------
+
+class MemoryHandle : public DataHandle {
+public:
+
+
+
+	MemoryHandle(const Buffer&);
+    MemoryHandle(Buffer&);
+
+	MemoryHandle(const void* address,size_t size);
+    MemoryHandle(void* address,size_t size);
+
+    /// Destructor
+
+	virtual ~MemoryHandle();
+
+// -- Operators
+
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+    virtual void skip(const Length&);
+
+    virtual Offset seek(const Offset&);
+
+	virtual Length estimate();
+	virtual Offset position();
+
+    virtual DataHandle* clone() const;
+
+    // From Streamable
+
+#if 0
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+#endif
+
+// -- Class methods
+
+#if 0
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+#endif
+
+private: // methods
+
+
+private: // members
+
+	char*          address_;
+    const size_t   size_;
+
+    bool           opened_;
+    bool           readOnly_;
+    bool           read_;
+    Offset         position_;
+
+    virtual std::string title() const;
+
+// -- Class members
+
+#if 0
+	static  ClassSpec                 classSpec_;
+    static  Reanimator<MemoryHandle>  reanimator_;
+#endif
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/io/MoverHandle.cc b/eckit/src/eckit/io/MoverHandle.cc
new file mode 100644
index 0000000..dc9195d
--- /dev/null
+++ b/eckit/src/eckit/io/MoverHandle.cc
@@ -0,0 +1,203 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MoverHandle.cc
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/io/MoverHandle.h"
+#include "eckit/io/MoverTransfer.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/io/TCPHandle.h"
+#include "eckit/net/TCPServer.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MoverHandleThread : public Thread {
+
+    MoverHandle& owner_;
+	TCPServer server_;
+	TCPHandle local_;
+    bool      read_;
+    bool      fail_;
+	Mutex     mutex_;
+
+	public:
+
+	MoverHandleThread(MoverHandle& owner, bool read):
+        owner_(owner),
+		local_(NodeInfo::thisNode().host(), server_.localPort()),
+		read_(read),
+		fail_(false)
+	{
+	}
+
+	void run() 
+	{
+		{
+			AutoLock<Mutex> lock(mutex_);
+			if(fail_)
+				return;
+		}
+
+		try
+		{
+			DataHandle& remote = owner_.handle();
+			Monitor::instance().name("mover");
+			Log::status() << "Using mover" << std::endl;
+			MoverTransfer mover;
+			if(read_) {
+				mover.transfer(remote, local_);
+			}
+			else
+			{
+				mover.transfer(local_, remote);
+			}
+		}
+		catch(TimeOut& e)
+		{
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is handled" << std::endl;
+			owner_.fail(Retry(e.what()).what());
+		}
+		catch(std::exception& e)
+		{
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is handled" << std::endl;
+			owner_.fail(e.what());
+		}
+
+	}
+
+	TCPSocket& accept() {
+
+		try {
+			return server_.accept("MoverHandle waiting for connection", 60);
+		}
+		catch(TimeOut& e)
+		{
+			AutoLock<Mutex> lock(mutex_);
+			fail_ = true;
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+			Log::error() << "** Exception is handled" << std::endl;
+			throw Retry(e.what());
+		}
+		catch(...)
+		{
+			AutoLock<Mutex> lock(mutex_);
+			fail_ = true;
+			throw;
+		}
+	}
+};
+
+//-----------------------------------------------------------------------------
+
+MoverHandle::MoverHandle(DataHandle* handle):
+	handle_(handle),
+	fail_(false)
+{
+}
+
+MoverHandle::~MoverHandle()
+{
+}
+
+void MoverHandle::fail(const std::string& error)
+{
+	AutoLock<Mutex> lock(mutex_);
+	error_ = error;
+	fail_  = true;
+}
+
+Length MoverHandle::openForRead() 
+{
+
+	MoverHandleThread* t = new MoverHandleThread(*this, true);
+	ThreadControler tc(t);
+	tc.start();
+	data_ = t->accept();
+	Log::info() << "Connected" << std::endl;
+
+	return handle_->estimate();
+}
+
+void MoverHandle::openForWrite(const Length&)
+{
+	MoverHandleThread* t = new MoverHandleThread(*this, false);
+	ThreadControler tc(t);
+	tc.start();
+
+	try {
+		data_ = t->accept();
+	}
+	catch(TimeOut& e)
+	{
+		// TODO: Check if the thread disapear
+		Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+		Log::error() << "** Exception is handled" << std::endl;
+		throw Retry(e.what());
+	}
+
+	Log::info() << "Connected" << std::endl;
+}
+
+
+void MoverHandle::openForAppend(const Length&)
+{
+	NOTIMP;
+}
+
+void MoverHandle::close()
+{
+	AutoLock<Mutex> lock(mutex_);
+	data_.close();
+	if(fail_) throw Exception(std::string("MoverHandle::close: ") + error_);
+}
+
+long MoverHandle::read(void* buf, long len)
+{
+	AutoLock<Mutex> lock(mutex_);
+	if(fail_) throw Exception(std::string("MoverHandle::read: ") + error_);
+	return data_.read(buf, len);
+}
+
+long MoverHandle::write(const void* buf, long len)
+{
+	AutoLock<Mutex> lock(mutex_);
+	if(fail_) throw Exception(std::string("MoverHandle::write: ") + error_);
+	return data_.write(buf, len);
+}
+
+void MoverHandle::print(std::ostream& s) const
+{
+	s << "MoverHandle[";
+	handle_->print(s);
+	s << "]";
+}
+
+
+std::string MoverHandle::title() const
+{
+	return std::string("->") + handle_->title();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/MoverHandle.h b/eckit/src/eckit/io/MoverHandle.h
new file mode 100644
index 0000000..15eed1e
--- /dev/null
+++ b/eckit/src/eckit/io/MoverHandle.h
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MoverHandle.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_filesystem_MoverHandle_h
+#define eckit_filesystem_MoverHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/net/TCPSocket.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MoverHandle : public DataHandle {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	MoverHandle(DataHandle*);
+
+// -- Destructor
+
+	virtual ~MoverHandle(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	DataHandle& handle() { return *handle_; }
+	void fail(const std::string&);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+
+// -- Members
+
+    std::auto_ptr<DataHandle> handle_;
+    TCPSocket data_;
+    bool fail_;
+    std::string error_;
+    Mutex mutex_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+    virtual Length openForRead() ;
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+    virtual long read(void*,long); 
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual std::string title() const;
+
+	void print(std::ostream&) const; 	
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const MoverHandle& p)
+	//	{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MoverTransfer.cc b/eckit/src/eckit/io/MoverTransfer.cc
new file mode 100644
index 0000000..673d799
--- /dev/null
+++ b/eckit/src/eckit/io/MoverTransfer.cc
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/net/Connector.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/io/MoverTransfer.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/log/Progress.h"
+#include "eckit/config/Resource.h"
+#include "eckit/net/TCPServer.h"
+#include "eckit/net/TCPStream.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/io/cluster/ClusterNodes.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+MoverTransfer::MoverTransfer(TransferWatcher& watcher):
+        watcher_(watcher)
+{
+}
+
+MoverTransfer::~MoverTransfer()
+{
+}
+
+Length MoverTransfer::transfer(DataHandle& from, DataHandle& to)
+{
+
+    if (!from.moveable()) throw SeriousBug(from.title() + " is not moveable");
+    if (!to.moveable()) throw SeriousBug(to.title() + " is not moveable");
+
+    std::map<std::string,Length> cost;
+    from.cost(cost, true);
+    to.cost(cost, false);
+
+    if (cost.empty()) {
+        NodeInfo info = ClusterNodes::any("mover");
+        cost[info.node()] = 0;
+//        throw SeriousBug(std::string("No cost for ") + from.title() + " => " + to.title());
+    }
+
+    Log::info() << "MoverTransfer::transfer(" << from << "," << to << ")" << std::endl;;
+
+    Log::info() << "MoverTransfer::transfer cost:" << std::endl;
+    for (std::map<std::string,Length> ::iterator j = cost.begin() ; j != cost.end() ; ++j)
+        Log::info() << "   " << (*j).first << " => " << Bytes((*j).second) << std::endl;
+
+    Connector& c(Connector::service("mover", cost));
+
+    Log::message() << c.host() << std::endl;
+    Stream& s = c;
+
+	s << bool(false); // New batch
+
+    //NodeInfo::sendLogin(s);
+    //NodeInfo remote = NodeInfo::acceptLogin(s);
+
+    Log::status() << "Sending input handle " << from.title() << std::endl;
+    from.toRemote(s);
+
+    Log::status() << "Sending output handle " << to.title() << std::endl;
+    to.toRemote(s);
+
+    Length estimate = from.estimate();
+
+    AutoState state('M');
+    if(estimate)
+        Log::status() << Bytes(estimate) << " " ;
+    Log::status() << from.title() << " => " << to.title() << std::endl;
+
+    Progress progress("mover", 0, estimate);
+    unsigned long long total = 0;
+    bool more;
+    s >> more;
+    while(more) 
+    {
+        long pos;
+        std::string msg;
+        s >> pos;
+        s >> msg;
+        total += pos;
+        progress(total);
+        s >> more;
+    }
+
+    unsigned long long len;
+    s >> len;
+
+//    ASSERT(len == total);
+
+    Log::message() << " " << std::endl;
+
+
+    return len;
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/MoverTransfer.h b/eckit/src/eckit/io/MoverTransfer.h
new file mode 100644
index 0000000..1b9302d
--- /dev/null
+++ b/eckit/src/eckit/io/MoverTransfer.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MoverTransfer.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_MoverTransfer_h
+#define eckit_MoverTransfer_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TransferWatcher;
+
+class MoverTransfer {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	MoverTransfer(TransferWatcher& = TransferWatcher::dummy());
+
+// -- Destructor
+
+	~MoverTransfer(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+    Length transfer(DataHandle&, DataHandle&);
+    TransferWatcher& watcher() const { return watcher_; }
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	// void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	MoverTransfer(const MoverTransfer&);
+	MoverTransfer& operator=(const MoverTransfer&);
+
+// -- Members
+	// None
+    TransferWatcher& watcher_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const MoverTransfer& p)
+	//	{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MultiHandle.cc b/eckit/src/eckit/io/MultiHandle.cc
new file mode 100644
index 0000000..f5180af
--- /dev/null
+++ b/eckit/src/eckit/io/MultiHandle.cc
@@ -0,0 +1,478 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/types/Types.h"
+#include "eckit/io/MultiPartHandle.h"
+#include "eckit/log/Timer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec MultiHandle::classSpec_ = {&DataHandle::classSpec(), "MultiHandle",};
+Reanimator<MultiHandle> MultiHandle::reanimator_;
+
+MultiHandle::MultiHandle():
+    current_(datahandles_.end()),
+    read_(false)
+{
+}
+
+MultiHandle::MultiHandle(const std::vector<DataHandle*>& v):
+    datahandles_(v),
+    current_(datahandles_.end()),
+    read_(false)
+{
+}
+
+MultiHandle::MultiHandle(Stream& s):
+    DataHandle(s),
+    read_(false)
+{
+    unsigned long size;
+    s >> size;
+
+    datahandles_.reserve(size);
+
+    for (size_t i = 0; i < size; i++)
+    {
+        DataHandle* dh = Reanimator<DataHandle>::reanimate(s); ASSERT(dh);
+        datahandles_.push_back(dh);
+    }
+    s >> length_;
+    current_ = datahandles_.end();
+}
+
+void MultiHandle::encode(Stream& s) const
+{
+    DataHandle::encode(s);
+    s << datahandles_.size();
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        s << *(datahandles_[i]);
+    s << length_;
+}
+
+MultiHandle::~MultiHandle()
+{
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        delete datahandles_[i];
+}
+
+void MultiHandle::operator+=(DataHandle* dh)
+{
+    ASSERT(dh != 0);
+
+    // Try to merge with self
+
+    if (merge(dh))
+    {
+        delete dh;
+        return;
+    }
+
+    // Try to merge with last
+
+    if (datahandles_.size() > 0)
+    {
+        if ( datahandles_.back()->merge(dh) )
+        {
+            delete dh;
+            return;
+        }
+    }
+
+
+    // Add to end
+    datahandles_.push_back(dh);
+}
+
+void MultiHandle::operator+=(const Length& length)
+{
+    length_.push_back(length);
+}
+
+Length MultiHandle::openForRead()
+{
+
+    read_   = true;
+
+    current_ = datahandles_.begin();
+    openCurrent();
+
+    // compress();
+
+    return estimate();
+}
+
+void MultiHandle::openForWrite(const Length& length)
+{
+    ASSERT(length == accumulate(length_.begin(), length_.end(), Length(0)));
+    ASSERT(datahandles_.size() == length_.size());
+
+    read_ = false;
+
+    Log::info() << "MultiHandle::openForWrite " << length << std::endl;
+    Log::info() << "MultiHandle::openForWrite " << datahandles_.size() << std::endl;
+    Log::info() << "MultiHandle::openForWrite " << length_.size() << std::endl;
+    current_ = datahandles_.begin();
+    curlen_  = length_.begin();
+    openCurrent();
+
+
+    written_ = 0;
+
+    Log::info() << "MultiHandle::openForWrite " << length_.size() << std::endl;
+
+
+    if (current_ != datahandles_.end())
+        Log::info() << "MultiHandle::openForWrite " << (*curlen_) << std::endl;
+    else
+        Log::warning() << "MultiHandle::openForWrite is empty" << std::endl;
+}
+
+void MultiHandle::openForAppend(const Length& )
+{
+    NOTIMP;
+}
+
+void MultiHandle::openCurrent()
+{
+    if (current_ != datahandles_.end())
+    {
+        if (read_)
+        {
+            Log::debug() << *(*current_) << std::endl;
+            Log::debug() << "Multi handle: open " << (*current_)->openForRead() << std::endl;
+        }
+        else {
+            (*current_)->openForWrite(*curlen_);
+        }
+    }
+}
+
+long MultiHandle::read1(char* buffer, long length)
+{
+    if (current_ == datahandles_.end()) return 0;
+
+    long n = (*current_)->read(buffer, length);
+    if (n <= 0)
+    {
+        (*current_)->close();
+        current_++;
+        openCurrent();
+        return read1(buffer, length);
+    }
+    return n;
+}
+
+long MultiHandle::read(void* buffer, long length)
+{
+    char *p    = (char*)buffer;
+    long n     = 0;
+    long total = 0;
+
+    while (length > 0  && (n = read1(p, length)) > 0)
+    {
+        length -= n;
+        total  += n;
+        p      += n;
+    }
+
+    Log::debug() << "MultiHandle::read " << (total > 0 ? total : n) << std::endl;
+
+    return total > 0 ? total : n;
+}
+
+long MultiHandle::write(const void* buffer, long length)
+{
+    Length len = std::min( Length(*curlen_ - written_), Length(length));
+    long   l   = (long)len;
+
+    ASSERT(len == Length(l));
+    ASSERT((*current_));
+
+    long n = (*current_)->write(buffer, l);
+
+    Log::debug() << "MultiHandle::write " << *(*current_) << " " <<
+                 length << ' ' << *curlen_ << ' ' << len << ' ' << written_ << std::endl;
+
+    if (n <= 0) return n;
+
+    written_ += n;
+
+
+    if (written_ == (*curlen_))
+    {
+        (*current_)->close();
+        current_++;
+        curlen_++;
+        openCurrent();
+        written_ = 0;
+
+        // Some more ?
+        if (length > l)
+        {
+
+            if (current_ == datahandles_.end())
+            {
+                Log::debug() << length  << " " << l << std::endl;
+            }
+
+            ASSERT(current_ != datahandles_.end());
+
+            char *p = (char*)buffer;
+            long m = write((void*)(p + l), length - l);
+            return (m > 0 ? n + m : n);
+        }
+    }
+
+    return n;
+}
+
+void MultiHandle::close()
+{
+    if (current_ != datahandles_.end())
+        (*current_)->close();
+    current_ = datahandles_.end();
+}
+
+void MultiHandle::flush()
+{
+    for (size_t i = 0; i < datahandles_.size(); ++i)
+    {
+        datahandles_[i]->flush();
+    }
+}
+
+void MultiHandle::rewind()
+{
+    ASSERT(read_);
+    if (current_ != datahandles_.end())
+        (*current_)->close();
+    current_ = datahandles_.begin();
+    openCurrent();
+}
+
+void MultiHandle::print(std::ostream& s) const
+{
+
+    if (format(s) == Log::compactFormat)
+        s << "MultiHandle";
+    else
+    {
+        s << "MultiHandle[";
+        for (size_t i = 0; i < datahandles_.size(); i++)
+        {
+            if (i != 0)
+                s << ",(";
+            datahandles_[i]->print(s);
+            s << ")";
+        }
+        s << ']';
+    }
+}
+
+bool MultiHandle::merge(DataHandle* other)
+{
+
+    if (other->isEmpty())
+        return true;
+
+    // Poor man's RTTI,
+    // Does not support inheritance
+
+    if (!sameClass(*other))
+        return false;
+
+    // An other MultiHandle
+
+    // We should be safe to cast now....
+    MultiHandle* handle = dynamic_cast<MultiHandle*>(other);
+
+    // Merge in all datahandles
+
+    for (size_t i = 0; i < handle->datahandles_.size() ; i++)
+        (*this) += handle->datahandles_[i];
+
+    handle->datahandles_.clear();
+
+    return true;
+}
+
+Length MultiHandle::estimate()
+{
+    Length total = 0;
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        total += datahandles_[i]->estimate();
+    return total;
+}
+
+void MultiHandle::restartReadFrom(const Offset& from)
+{
+    Log::warning() << *this << " restart read from " << from << std::endl;
+
+    ASSERT(read_);
+    if (current_ != datahandles_.end()) (*current_)->close();
+
+    long long len = from;
+    long long pos = 0;
+    for (current_ = datahandles_.begin(); current_ != datahandles_.end() ; ++current_)
+    {
+        long long e = (*current_)->estimate();
+        if (len >= pos && len < pos + e)
+        {
+            Log::warning() << *this << " restart read from " << from << ", current=" << (current_ - datahandles_.begin() ) << std::endl;
+
+            openCurrent();
+            (*current_)->restartReadFrom(len - pos);
+            return;
+        }
+        pos += e;
+    }
+    ASSERT(from  == Offset(0) && estimate() == Length(0));
+}
+
+void MultiHandle::toRemote(Stream &s) const
+{
+    s.startObject();
+    s << className();
+    DataHandle::encode(s);
+    s << datahandles_.size();
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        datahandles_[i]->toRemote(s);
+    s << length_;
+    s.endObject();
+}
+
+void MultiHandle::toLocal(Stream &s) const
+{
+    s.startObject();
+    s << className();
+    DataHandle::encode(s);
+    s << datahandles_.size();
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        datahandles_[i]->toLocal(s);
+    s << length_;
+    s.endObject();
+}
+
+DataHandle* MultiHandle::toLocal()
+{
+    for (size_t i = 0; i < datahandles_.size(); i++)
+    {
+        DataHandle* loc = datahandles_[i]->toLocal();
+        if (loc != datahandles_[i])
+        {
+            delete datahandles_[i];
+            datahandles_[i] = loc;
+        }
+    }
+    return this;
+}
+
+void MultiHandle::cost(std::map<std::string, Length>& c, bool read) const
+{
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        datahandles_[i]->cost(c, read);
+}
+
+bool MultiHandle::moveable() const
+{
+    for (size_t i = 0; i < datahandles_.size(); i++)
+        if (!datahandles_[i]->moveable())
+            return false;
+    return datahandles_.size() > 0;
+}
+
+std::string MultiHandle::title() const
+{
+    std::ostringstream os;
+    os << "[";
+    if (datahandles_.size() > 0) os << datahandles_[0]->title();
+    if (datahandles_.size() > 1) os << ",...{" << datahandles_.size() << "}";
+    os << "]";
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+bool MultiHandle::compress(bool sorted) {
+
+    Timer timer("Compress handle");
+
+    if(datahandles_.empty()) {
+
+        return false;
+    }
+    // return false;
+
+    if(sorted) {
+        NOTIMP;
+    }
+
+    bool changed = false;
+    std::vector<bool> skip(datahandles_.size(), false);
+    std::vector<Length> sizes(datahandles_.size());
+
+    for (size_t i = 0; i < datahandles_.size() ; i++) { 
+        sizes[i] = datahandles_[i]->estimate();
+    }
+
+    for (size_t i = 0; i < datahandles_.size() - 1; i++) {
+
+        if(skip[i]) {
+            continue;
+        }
+
+        DataHandle* h1 = datahandles_[i];
+
+        MultiPartHandle* prev = 0;
+        Length len1 = sizes[i];
+
+        for (size_t j = i + 1; j < datahandles_.size(); j++) {
+
+            if(skip[j]) {
+                continue;
+            }
+
+            DataHandle* h2 = datahandles_[j];
+            Length len2 = sizes[j];
+
+            if (h1->merge(h2)) {
+
+                if(prev == 0) {
+                    prev = new MultiPartHandle(h1, len1, 0);
+                    datahandles_[i] = prev;
+                    skip[i] = true;
+                }
+
+                prev = new MultiPartHandle(h1, len2, prev);
+                datahandles_[j] = prev;
+                skip[j] = true;
+                delete h2;
+            }
+        }
+
+        if(h1->compress(sorted)) {
+            changed = true;
+        }
+
+    }
+
+    return changed;
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/MultiHandle.h b/eckit/src/eckit/io/MultiHandle.h
new file mode 100644
index 0000000..bcbb5f8
--- /dev/null
+++ b/eckit/src/eckit/io/MultiHandle.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MultiHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_MultiHandle_h
+#define eckit_filesystem_MultiHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MultiHandle : public DataHandle {
+public:
+
+	typedef std::vector<DataHandle*> HandleList;
+
+// -- Contructors
+
+	MultiHandle();
+	MultiHandle(const LengthList&);
+	MultiHandle(const HandleList&);
+	MultiHandle(const HandleList&,const LengthList&);
+	MultiHandle(Stream&);
+
+// -- Destructor
+
+	~MultiHandle();
+
+// -- Operators
+
+	virtual void operator+=(DataHandle*);
+	virtual void operator+=(const Length&);
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+	void restartReadFrom(const Offset& from);
+
+
+	virtual bool merge(DataHandle*);
+	virtual bool compress(bool = false);
+
+	virtual Length estimate();
+    virtual void toRemote(Stream&) const;
+    virtual void toLocal(Stream&) const;
+    virtual DataHandle* toLocal();
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+    virtual bool moveable() const;
+
+    // From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	HandleList			    datahandles_;
+	HandleList::iterator 	current_;
+	LengthList::iterator    curlen_;
+	LengthList              length_;
+	Length                  written_;
+	bool                    read_;
+
+// -- Methods
+
+	void openCurrent();
+	void open();
+	long read1(char*,long);
+
+// -- Class members
+
+	static  ClassSpec                 classSpec_;
+    static  Reanimator<MultiHandle>   reanimator_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/MultiPartHandle.cc b/eckit/src/eckit/io/MultiPartHandle.cc
new file mode 100644
index 0000000..5c989e2
--- /dev/null
+++ b/eckit/src/eckit/io/MultiPartHandle.cc
@@ -0,0 +1,233 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/MultiPartHandle.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+MultiPartHandle::MultiPartHandle(DataHandle* handle, const Length& size, MultiPartHandle* prev):
+    handle_(handle),
+    size_(size),
+    prev_(prev),
+    next_(0),
+    position_(0),
+    start_(0),
+    opened_(false),
+    opens_(0)
+{
+    if (prev_) {
+        ASSERT(prev_->next_ == 0);
+        prev_->next_ = this;
+        start_ = prev_->start_ + prev_->size_;
+    }
+}
+
+MultiPartHandle::~MultiPartHandle()
+{
+    if (next_ == 0) {
+        delete handle_;
+    }
+}
+
+Length MultiPartHandle::openForRead()
+{
+    ASSERT(!opened_);
+    opened_ = true;
+
+    first().openHandle();
+
+    rewind();
+    return estimate();
+}
+
+void MultiPartHandle::openForWrite(const Length& length)
+{
+    NOTIMP;
+}
+
+void MultiPartHandle::openForAppend(const Length& )
+{
+    NOTIMP;
+}
+
+
+long MultiPartHandle::read(void* buffer, long length)
+{
+    ASSERT(opened_);
+    size_t left = size_ - position_;
+    size_t size = std::min(left, size_t(length));
+
+    size = handle_->read(buffer, size);
+    position_ += size;
+
+    return size;
+}
+
+long MultiPartHandle::write(const void* buffer, long length)
+{
+    NOTIMP;
+}
+
+void MultiPartHandle::close()
+{
+    ASSERT(opened_);
+    opened_ = false;
+    first().closeHandle();
+}
+
+void MultiPartHandle::flush()
+{
+    NOTIMP;
+}
+
+void MultiPartHandle::rewind()
+{
+    seek(0);
+}
+
+void MultiPartHandle::print(std::ostream& s) const
+{
+
+    if (format(s) == Log::compactFormat)
+        s << "MultiPartHandle";
+    else
+    {
+        s << "MultiPartHandle[";
+        // for (size_t i = 0; i < datahandles_.size(); i++)
+        // {
+        //     if (i != 0)
+        //         s << ",(";
+        // handle_->print(s);
+        //     s << ")";
+        // }
+        s << handle_;
+        s << ']';
+    }
+}
+
+bool MultiPartHandle::merge(DataHandle* other)
+{
+    return false;
+}
+
+Length MultiPartHandle::estimate()
+{
+    return size_;
+}
+
+void MultiPartHandle::restartReadFrom(const Offset& from)
+{
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    ASSERT(opened_);
+    seek(from);
+}
+
+void MultiPartHandle::toRemote(Stream &s) const
+{
+    NOTIMP;
+}
+
+void MultiPartHandle::toLocal(Stream &s) const
+{
+    NOTIMP;
+}
+
+DataHandle* MultiPartHandle::toLocal()
+{
+    NOTIMP;
+}
+
+void MultiPartHandle::cost(std::map<std::string, Length>& c, bool read) const
+{
+    NOTIMP;
+}
+
+bool MultiPartHandle::moveable() const
+{
+    return false;
+}
+
+Offset MultiPartHandle::position() {
+    return position_;
+}
+
+Offset MultiPartHandle::seek(const Offset& offset) {
+    position_ = offset;
+    if(position_ > size_) {
+        position_ = (unsigned long long)size_;
+    }
+
+    ASSERT(handle_->seek(start_ + position_) == Offset(start_ + position_));
+    return position_;
+
+}
+
+void MultiPartHandle::skip(const Length &length) {
+    seek(position_ + length);
+}
+
+
+std::string MultiPartHandle::title() const
+{
+    std::ostringstream os;
+    os << "[";
+    os << handle_->title();
+    os << '|';
+    os << start_;
+    os << ':';
+    os << size_;
+    // if (datahandles_.size() > 0) os << datahandles_[0]->title();
+    // if (datahandles_.size() > 1) os << ",...{" << datahandles_.size() << "}";
+    os << "]";
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+bool MultiPartHandle::compress(bool sorted) {
+    return false;
+}
+
+
+//-----------------------------------------------------------------------------
+
+MultiPartHandle& MultiPartHandle::first() {
+    MultiPartHandle* h = this;
+    while(h->prev_) {
+        h = h->prev_;
+    }
+    return *h;
+}
+
+void MultiPartHandle::openHandle() {
+    ASSERT(prev_ == 0);
+    if(opens_ == 0) {
+        handle_->openForRead();
+    }
+    opens_++;
+}
+
+void MultiPartHandle::closeHandle() {
+    ASSERT(prev_ == 0);
+    ASSERT(opens_ > 0);
+    opens_--;
+    if(opens_ == 0) {
+        handle_->close();
+    }
+}
+
+
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/MultiPartHandle.h b/eckit/src/eckit/io/MultiPartHandle.h
new file mode 100644
index 0000000..f43c37c
--- /dev/null
+++ b/eckit/src/eckit/io/MultiPartHandle.h
@@ -0,0 +1,103 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/MultiPartHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_MultiPartHandle_h
+#define eckit_filesystem_MultiPartHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MultiPartHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	MultiPartHandle(DataHandle* handle, const Length& length, MultiPartHandle* prev);
+
+
+// -- Destructor
+
+	~MultiPartHandle();
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+
+	void restartReadFrom(const Offset& from);
+
+    virtual Offset seek(const Offset&);
+    virtual Offset position();
+    virtual void skip(const Length &);
+
+	virtual bool merge(DataHandle*);
+	virtual bool compress(bool = false);
+
+	virtual Length estimate();
+    virtual void toRemote(Stream&) const;
+    virtual void toLocal(Stream&) const;
+    virtual DataHandle* toLocal();
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+    virtual bool moveable() const;
+
+    // From Streamable
+
+
+// -- Class methods
+
+
+private:
+
+// -- Members
+
+    DataHandle*      handle_;
+    Length           size_;
+    MultiPartHandle* prev_;
+    MultiPartHandle* next_;
+    Offset           position_;
+    Offset           start_;
+    bool             opened_;
+    size_t           opens_;
+
+// -- Methods
+
+    MultiPartHandle& first();
+    void openHandle();
+    void closeHandle();
+
+// -- Class members
+
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/Offset.cc b/eckit/src/eckit/io/Offset.cc
new file mode 100644
index 0000000..a9c3af4
--- /dev/null
+++ b/eckit/src/eckit/io/Offset.cc
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/io/Offset.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void sort(OffsetList& offset,LengthList& length)
+{
+	ASSERT(offset.size() == length.size());
+	size_t i = 0;
+
+    typedef std::map<Offset,Length> OffsetLengthMap;
+
+	OffsetLengthMap ol;
+	for(i = 0; i < offset.size() ; i++)
+		ol[offset[i]] = length[i];
+	
+	i = 0;
+	for(OffsetLengthMap::iterator j = ol.begin(); j != ol.end(); ++j, ++i)
+	{
+		offset[i] = (*j).first;
+		length[i] = (*j).second;
+	}
+}
+
+bool compress(OffsetList& offset,LengthList& length)
+{
+	ASSERT(offset.size() == length.size());
+
+	size_t j = 0;
+	for(size_t i = 1; i < offset.size() ; i++)
+		if( (offset[j] + length[j]) == offset[i])
+			length[j] += length[i];
+		else
+		{
+			ASSERT(++j < offset.size());
+			offset[j] = offset[i];
+			length[j] = length[i];
+		}
+
+	long save  = offset.size() - j - 1;
+	if(save > 0)
+	{
+		offset.erase(offset.begin() + j + 1, offset.end());
+		length.erase(length.begin() + j + 1, length.end());
+	}
+
+	return save > 0;
+}
+
+void accumulate(const LengthList& length,OffsetList& offset,const Offset& from)
+{
+	offset.clear(); offset.reserve(length.size());
+
+	Offset o(from);
+	for( size_t i = 0; i < length.size(); i++)
+	{
+		offset.push_back(o);
+		o += length[i];
+	}
+}
+
+void Offset::load(DumpLoad& a)
+{
+	a.load(value_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/Offset.h b/eckit/src/eckit/io/Offset.h
new file mode 100644
index 0000000..965cc18
--- /dev/null
+++ b/eckit/src/eckit/io/Offset.h
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Offset.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Offset_h
+#define eckit_Offset_h
+
+#include "eckit/io/Length.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Forwarded declarations
+
+class Bless;
+
+// But because the compiler aligns long longs
+// on 64bits boundaries and longs on 32 bits boundaries,
+// we need the help of a little pragma here, to make ObjectStore happy
+
+#ifdef _AIX
+#pragma options align=twobyte
+#endif
+
+class Offset {
+
+public: // types
+
+    typedef long long value_t;
+
+public:
+
+	friend std::ostream& operator<<(std::ostream& s,const Offset& x)
+		{ return s << x.value_; }
+
+	friend Stream& operator<<(Stream& s,const Offset& x)
+		{ return s << x.value_; }
+
+	friend Stream& operator>>(Stream& s,Offset& x)
+		{ s >> x.value_; return s;}
+
+	//Offset(fpos_t); <- To implement
+    Offset(value_t l = 0) : value_(l) {}
+	Offset(const Offset& other) : value_(other.value_) {}
+
+#include "eckit/io/Offset.b"
+
+	Offset& operator=(const Offset& other) 
+		{ value_ = other.value_; return *this;}
+
+	bool operator==(const Offset& other) const
+		{ return value_ == other.value_; }
+
+	bool operator!=(const Offset& other) const
+		{ return value_ != other.value_; }
+
+	bool operator<(const Offset& other) const
+		{ return value_ < other.value_; }
+
+	bool operator>(const Offset& other) const
+		{ return value_ > other.value_; }
+
+	bool operator<=(const Offset& other) const
+		{ return value_ <= other.value_; }
+
+	bool operator>=(const Offset& other) const
+		{ return value_ >= other.value_; }
+
+	Offset operator+(const Length& other) const
+		{ return Offset(value_ + other.value_);}
+
+	void operator+=(const Length& other)
+		{ value_ += other.value_;}
+
+	void operator-=(const Length& other)
+		{ value_ -= other.value_;}
+
+	Length operator-(const Offset& other) const
+		{ return value_ - other.value_;}
+
+	
+    operator value_t() const { return value_; }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+private:
+    
+// -- Members
+
+    value_t value_;
+
+	friend class Length;
+};
+
+typedef std::vector<Offset> OffsetList;
+
+//-----------------------------------------------------------------------------
+
+// Global routines
+
+// Sort both std::vector according to offset
+void sort(OffsetList&, LengthList&);
+bool compress(OffsetList&, LengthList&);
+void accumulate(const LengthList&, OffsetList&, const Offset& = 0);
+
+#ifdef _AIX
+#pragma options align=reset
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/PartFileHandle.cc b/eckit/src/eckit/io/PartFileHandle.cc
new file mode 100644
index 0000000..ddfc623
--- /dev/null
+++ b/eckit/src/eckit/io/PartFileHandle.cc
@@ -0,0 +1,391 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/io/MarsFSPartHandle.h"
+#include "eckit/io/PartFileHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec PartFileHandle::classSpec_ = {&DataHandle::classSpec(),"PartFileHandle",};
+Reanimator<PartFileHandle> PartFileHandle::reanimator_;
+
+void PartFileHandle::print(std::ostream& s) const
+{
+    if(format(s) == Log::compactFormat)
+        s << "PartFileHandle";
+    else
+        s << "PartFileHandle[path=" << name_
+           << ",offset=" << offset_
+           << ",length=" << length_ << ']';
+}
+
+void PartFileHandle::encode(Stream& s) const
+{
+    DataHandle::encode(s);
+    s << name_;
+    s << offset_;
+    s << length_;
+
+}
+
+PartFileHandle::PartFileHandle(Stream& s):
+    DataHandle(s),
+    file_(0),
+    pos_(0),
+    index_(0)
+{
+    s >> name_;
+    s >> offset_;
+    s >> length_;
+
+    ASSERT(offset_.size() == length_.size());
+}
+
+#ifdef USE_LINKS
+static std::string linkName(const std::string& name)
+{
+    static int n = 1;
+    std::string path = name + ".part.0";
+    while(::link(name.c_str(),path.c_str()) < 0)
+    {
+        if(errno != EEXIST)
+            throw FailedSystemCall(std::string("link ") + name + " " + path);
+        std::ostringstream os;
+        os << name + ".part." << n++;
+        path = std::string(os);
+    }
+    return path;
+}
+#endif
+
+PartFileHandle::PartFileHandle(const PathName& name,
+    const OffsetList& offset,const LengthList& length):
+    name_(name),
+    file_(0),
+    pos_(0),
+    index_(0),
+#ifdef USE_LINKS
+    link_(linkName(name)),
+#endif
+    offset_(offset),
+    length_(length)
+{
+//    Log::info() << "PartFileHandle::PartFileHandle " << name << std::endl;
+    ASSERT(offset_.size() == length_.size());
+    compress(false);
+}
+
+PartFileHandle::PartFileHandle(const PathName& name,
+    const Offset& offset,const Length& length):
+    name_(name),
+    file_(0),
+    pos_(0),
+    index_(0),
+#ifdef USE_LINKS
+    link_(linkName(name)),
+#endif
+    offset_(1,offset),
+    length_(1,length)
+{
+}
+
+bool PartFileHandle::compress(bool sorted)
+{
+    if(sorted)
+        eckit::sort(offset_,length_);
+    return eckit::compress(offset_,length_);
+}
+
+PartFileHandle::~PartFileHandle()
+{
+    if(file_)
+    {
+        Log::warning() << "Closing PartFileHandle " << name_ << std::endl;
+        ::fclose(file_);
+        file_ = 0;
+    }
+#ifdef USE_LINKS
+    link_.unlink();
+#endif
+}
+
+Length PartFileHandle::openForRead()
+{
+    static long bufSize  = Resource<long>("FileHandleIOBufferSize;$FILEHANDLE_IO_BUFFERSIZE;-FileHandleIOBufferSize",0);
+    static bool best     = Resource<bool>("bestPartFileHandleBufferSize",false);
+
+//    Log::info() << "PartFileHandle::openForRead " << name_ << std::endl;
+
+#ifdef USE_LINKS
+    file_ = ::fopen(link_.localPath(),"r");
+#else
+    file_ = ::fopen(name_.localPath(),"r");
+#endif
+
+    if(file_ == 0)
+        throw CantOpenFile(name_,errno == ENOENT);
+
+    long size = bufSize;
+
+    if(best && size) {
+
+        long fourK = 4096;
+
+        // TODO: find a best algorithm
+
+        // Now, use the size of the smallest block
+        for(Ordinal i = 0; i < length_.size(); i++)
+        {
+            if(length_[i] < Length(size)) {
+                size = length_[i];
+            }
+        }
+
+        size = ((size + fourK - 1)/fourK)*fourK;
+        if(size < fourK)    { size = fourK;    }
+
+    }
+
+    if(size)
+    {
+        Log::debug() << "PartFileHandle using " << Bytes(size) << std::endl;
+        buffer_.reset(new Buffer(size));
+        Buffer& b = *(buffer_.get());
+        ::setvbuf(file_,b,_IOFBF,size);
+    }
+    rewind();
+
+    return estimate();
+}
+
+void PartFileHandle::openForWrite(const Length&)
+{
+    NOTIMP;
+}
+
+void PartFileHandle::openForAppend(const Length&)
+{
+    NOTIMP;
+}
+
+long PartFileHandle::read1(char *buffer,long length)
+{
+    
+    ASSERT(file_);
+    
+    // skip empty entries if any
+    while (index_ < offset_.size() && length_[index_] == Length(0))
+        index_++;
+
+    if(index_ == offset_.size()) return 0;
+
+
+    Length ll  = (long long)offset_[index_] + Length(pos_);
+    off_t pos = ll;
+
+    //ASSERT( Length(pos) == ll);
+
+    // try llseek()
+
+    if(fseeko(file_,pos ,SEEK_SET) != 0)
+    {
+        std::ostringstream s;
+        s << name_ << ": cannot seek to " << pos << " (file=" << fileno(file_) << ")";
+        throw ReadError(s.str());
+    }
+
+    ASSERT(::ftello(file_) == pos);
+
+    ll        = length_[index_] - Length(pos_);
+    Length lsize = std::min(Length(length),ll);
+    long size  = lsize;
+
+    ASSERT(Length(size) == lsize);
+
+    long n = ::fread(buffer,1,size,file_);
+
+    if(n != size)
+    {
+        std::ostringstream s;
+        s << name_ << ": cannot read " << size << ", got only " << n;
+        throw ReadError(s.str());
+    }
+
+    pos_ += n;
+    if(pos_ >= length_[index_])
+    {
+        index_++;
+        pos_ = 0;
+    }
+
+    return n;
+}
+
+
+long PartFileHandle::read(void* buffer,long length)
+{
+    char *p = (char*)buffer;
+
+    long n = 0;
+    long total = 0;
+
+    while( length > 0 && (n = read1(p,length)) >0)
+    {
+        length -= n;
+        total  += n;
+        p      += n;
+    }
+
+    return total>0?total:n;
+
+}
+
+long PartFileHandle::write(const void* buffer,long length)
+{
+    return -1;
+}
+
+void PartFileHandle::close()
+{
+    if(file_)
+    {
+        ::fclose(file_);
+        file_ = 0;
+    }
+    else
+    {
+        Log::warning() << "Closing PartFileHandle " << name_ << ", file is not opened" << std::endl;
+    }
+    buffer_.reset(0);
+}
+
+void PartFileHandle::rewind()
+{
+    pos_   = 0;
+    index_ = 0;
+}
+
+void PartFileHandle::restartReadFrom(const Offset& from)
+{
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    rewind();
+    long long len = from;
+    long long pos = 0;
+
+    for(index_ = 0; index_ < length_.size(); index_++)
+    {
+        long long e = length_[index_];
+        if(len >= pos && len < pos + e)
+        {
+            Log::warning() << *this << " restart read from " << from << ", index=" << index_ << ", pos=" << pos_ << std::endl;
+            pos_ = len - pos;
+            return;
+        }
+        pos += e;
+    }
+    ASSERT(from  == Offset(0) && estimate() == Length(0));
+}
+
+Offset PartFileHandle::seek(const Offset& from)
+{
+    rewind();
+    long long len = from;
+    long long pos = 0;
+
+    for(index_ = 0; index_ < length_.size(); index_++)
+    {
+        long long e = length_[index_];
+        if(len >= pos && len < pos + e)
+        {
+            pos_ = len - pos;
+            return from;
+        }
+        pos += e;
+    }
+    return pos;
+}
+
+
+bool PartFileHandle::merge(DataHandle* other)
+{
+    if(other->isEmpty())
+        return true;
+
+    // Poor man's RTTI,
+    // Does not support inheritance
+
+    if( !sameClass(*other) )
+        return false;
+
+    // We should be safe to cast now....
+
+    PartFileHandle* handle = dynamic_cast<PartFileHandle*>(other);
+
+    if(name_ != handle->name_)
+        return false;
+
+    ASSERT(handle->offset_.size() == handle->length_.size());
+
+    offset_.reserve(offset_.size() + handle->offset_.size());
+    length_.reserve(length_.size() + handle->length_.size());
+
+    for(Ordinal i = 0; i < handle->offset_.size() ; ++i)
+    {
+        offset_.push_back(handle->offset_[i]);
+        length_.push_back(handle->length_[i]);
+    }
+
+    compress(false);
+    return true;
+}
+
+Length PartFileHandle::estimate()
+{
+    return accumulate(length_.begin(),length_.end(),Length(0));
+}
+
+void PartFileHandle::toRemote(Stream& s) const
+{
+    MarsFSPath p(PathName(name_).clusterName());
+    MarsFSPartHandle remote(p,  offset_, length_);
+    s << remote;
+}
+
+void PartFileHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+    if(read) {
+        c[NodeInfo::thisNode().node()] += const_cast<PartFileHandle*>(this)->estimate();
+    }
+}
+
+
+std::string PartFileHandle::title() const
+{
+    std::ostringstream os;
+    os << PathName::shorten(name_) << " (" << length_.size() << ")";
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/PartFileHandle.h b/eckit/src/eckit/io/PartFileHandle.h
new file mode 100644
index 0000000..6f28075
--- /dev/null
+++ b/eckit/src/eckit/io/PartFileHandle.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Partio/FileHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_PartFileHandle_h
+#define eckit_filesystem_PartFileHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+#include "eckit/memory/ScopedPtr.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class PartFileHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	PartFileHandle(const PathName&,const OffsetList&,const LengthList&);
+	PartFileHandle(const PathName&,const Offset&,const Length&);
+	PartFileHandle(Stream&);
+
+// -- Destructor
+
+	~PartFileHandle();
+
+// -- Methods
+
+	const PathName& path() const { return name_; }
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void rewind();
+
+	virtual void print(std::ostream&) const;
+	virtual bool merge(DataHandle*);
+	virtual bool compress(bool = false);
+	virtual Length estimate();
+
+	virtual void restartReadFrom(const Offset& from);
+    virtual Offset seek(const Offset&);
+
+    virtual void toRemote(Stream&) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	PathName           name_;
+#ifdef USE_LINKS
+	PathName           link_;
+#endif
+	FILE*              file_;
+	long long          pos_;
+	Ordinal            index_;
+	OffsetList         offset_;
+	LengthList         length_;
+	ScopedPtr<Buffer>  buffer_;
+
+// -- Methods
+
+	long read1(char*,long);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<PartFileHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/PartHandle.cc b/eckit/src/eckit/io/PartHandle.cc
new file mode 100644
index 0000000..ba7e34b
--- /dev/null
+++ b/eckit/src/eckit/io/PartHandle.cc
@@ -0,0 +1,239 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/io/MarsFSPartHandle.h"
+#include "eckit/io/PartHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec PartHandle::classSpec_ = {&DataHandle::classSpec(),"PartHandle",};
+Reanimator<PartHandle> PartHandle::reanimator_;
+
+void PartHandle::print(std::ostream& s) const
+{
+    /*
+    if(format(s) == Log::compactFormat)
+        s << "PartHandle";
+    else*/
+    s << "PartHandle[handle=" << handle()
+      << ",offset=" << offset_
+      << ",length=" << length_ << ']';
+}
+
+void PartHandle::encode(Stream& s) const
+{
+    DataHandle::encode(s);
+    s << handle();
+    s << offset_;
+    s << length_;
+
+}
+
+PartHandle::PartHandle(Stream& s):
+    DataHandle(s),
+    HandleHolder(Reanimator<DataHandle>::reanimate(s)),
+    pos_ (0),
+    index_ (0)
+{
+    s >> offset_;
+    s >> length_;
+
+    ASSERT(offset_.size() == length_.size());
+}
+
+
+PartHandle::PartHandle(DataHandle& handle,
+                       const OffsetList& offset,const LengthList& length):
+    HandleHolder(handle),
+    pos_ (0),
+    index_ (0),
+    offset_(offset),
+    length_(length)
+{
+    ASSERT(offset_.size() == length_.size());
+}
+
+PartHandle::PartHandle(DataHandle* handle,
+                       const OffsetList& offset,const LengthList& length):
+    HandleHolder(handle),
+    pos_ (0),
+    index_ (0),
+    offset_(offset),
+    length_(length)
+{
+    ASSERT(offset_.size() == length_.size());
+}
+
+PartHandle::PartHandle(DataHandle& handle,
+                       const Offset& offset,const Length& length):
+    HandleHolder(handle),
+    pos_ (0),
+    index_ (0),
+    offset_(1,offset),
+    length_(1,length)
+{
+    ASSERT(offset_.size() == length_.size());
+}
+
+PartHandle::PartHandle(DataHandle* handle,
+                       const Offset& offset,const Length& length):
+    HandleHolder(handle),
+    pos_ (0),
+    index_ (0),
+    offset_(1,offset),
+    length_(1,length)
+{
+    ASSERT(offset_.size() == length_.size());
+}
+
+PartHandle::~PartHandle()
+{
+}
+
+Length PartHandle::openForRead()
+{
+    handle().openForRead();
+    rewind();
+    return estimate();
+}
+
+void PartHandle::openForWrite(const Length&)
+{
+    NOTIMP;
+}
+
+void PartHandle::openForAppend(const Length&)
+{
+    NOTIMP;
+}
+
+long PartHandle::read1(char *buffer,long length)
+{
+    // skip empty entries if any
+    while (index_ < offset_.size() && length_[index_] == Length(0))
+        index_++;
+
+    ASSERT(index_ <= offset_.size());
+
+    if(index_ == offset_.size()) return 0;
+
+
+    Length ll  = (long long)offset_[index_] + Length(pos_);
+    off_t pos = ll;
+
+
+    ASSERT(handle().seek(pos) == Offset(pos));
+
+
+    ll        = length_[index_] - Length(pos_);
+    Length lsize = std::min(Length(length),ll);
+    long size  = lsize;
+
+    ASSERT(Length(size) == lsize);
+
+    long n = handle().read(buffer,size);
+
+    if(n != size)
+    {
+        std::ostringstream s;
+        s << handle() << ": cannot read " << size << ", got only " << n;
+        throw ReadError(s.str());
+    }
+
+    pos_ += n;
+    if(pos_ >= length_[index_])
+    {
+        index_++;
+        pos_ = 0;
+    }
+
+    return n;
+}
+
+
+long PartHandle::read(void* buffer,long length)
+{
+    char *p = (char*)buffer;
+
+    long n = 0;
+    long total = 0;
+
+    while( length > 0 && (n = read1(p,length)) >0)
+    {
+        length -= n;
+        total  += n;
+        p      += n;
+    }
+
+    return total>0?total:n;
+
+}
+
+long PartHandle::write(const void* buffer,long length)
+{
+    return -1;
+}
+
+void PartHandle::close()
+{
+    handle().close();
+}
+
+void PartHandle::rewind()
+{
+    pos_   = 0;
+    index_ = 0;
+}
+
+void PartHandle::restartReadFrom(const Offset& from)
+{
+    Log::warning() << *this << " restart read from " << from << std::endl;
+    rewind();
+    long long len = from;
+    long long pos = 0;
+
+    for(index_ = 0; index_ < length_.size(); index_++)
+    {
+        long long e = length_[index_];
+        if(len >= pos && len < pos + e)
+        {
+            Log::warning() << *this << " restart read from " << from << ", index=" << index_ << ", pos=" << pos_ << std::endl;
+            pos_ = len - pos;
+            return;
+        }
+        pos += e;
+    }
+    ASSERT(from  == Offset(0) && estimate() == Length(0));
+}
+
+
+Length PartHandle::estimate()
+{
+    return accumulate(length_.begin(),length_.end(),Length(0));
+}
+
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/PartHandle.h b/eckit/src/eckit/io/PartHandle.h
new file mode 100644
index 0000000..d1c97e3
--- /dev/null
+++ b/eckit/src/eckit/io/PartHandle.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Partio/FileHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_PartHandle_h
+#define eckit_filesystem_PartHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/HandleHolder.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class PartHandle : public DataHandle, public HandleHolder {
+public:
+
+    // -- Contructors
+
+    PartHandle(DataHandle& handle,const OffsetList&,const LengthList&);
+    PartHandle(DataHandle& handle,const Offset&,const Length&);
+
+    PartHandle(DataHandle* handle,const OffsetList&,const LengthList&);
+    PartHandle(DataHandle* handle,const Offset&,const Length&);
+
+    PartHandle(Stream&);
+
+    // -- Destructor
+
+    ~PartHandle();
+
+    // -- Methods
+
+    // -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+
+
+    virtual Length estimate();
+    virtual void restartReadFrom(const Offset& from);
+
+    // From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+    // -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+    // -- Members
+
+    long long          pos_;
+    Ordinal            index_;
+    OffsetList         offset_;
+    LengthList         length_;
+
+    // -- Methods
+
+    long read1(char*,long);
+
+    // -- Class members
+
+    static  ClassSpec               classSpec_;
+    static  Reanimator<PartHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/PipeHandle.cc b/eckit/src/eckit/io/PipeHandle.cc
new file mode 100644
index 0000000..11b811d
--- /dev/null
+++ b/eckit/src/eckit/io/PipeHandle.cc
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/io/PipeHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec PipeHandle::classSpec_ = {&DataHandle::classSpec(),"PipeHandle",};
+Reanimator<PipeHandle> PipeHandle::reanimator_;
+
+void PipeHandle::print(std::ostream& s) const
+{
+	s << "PipeHandle[file=" << name_ << ']';
+}
+
+void PipeHandle::encode(Stream& s) const
+{
+	DataHandle::encode(s);
+	s << name_;
+}
+
+PipeHandle::PipeHandle(Stream& s):
+	DataHandle(s),
+   file_(0),
+   read_(false)
+{
+	s >> name_;
+}
+
+PipeHandle::PipeHandle(const std::string& name):
+    name_(name),
+	file_(0),
+	read_(false)
+{
+}
+
+PipeHandle::~PipeHandle()
+{
+}
+
+void PipeHandle::open(const char* mode)
+{
+	file_ = ::popen(name_.c_str(),mode);
+	if(file_ == 0)
+		throw CantOpenFile(name_);
+
+}
+
+Length PipeHandle::openForRead()
+{
+	read_ = true;
+	open("r");
+	return estimate();
+}
+
+void PipeHandle::openForWrite(const Length& length)
+{
+    open("w");
+}
+
+void PipeHandle::openForAppend(const Length&)
+{
+	open("a");
+}
+
+long PipeHandle::read(void* buffer,long length)
+{
+	return ::fread(buffer,1,length,file_);
+}
+
+long PipeHandle::write(const void* buffer,long length)
+{
+	return ::fwrite(buffer,1,length,file_);
+}
+
+void PipeHandle::close()
+{
+	if( file_ == 0 ) return;
+    
+    if( ::pclose(file_) != 0)
+	{
+		Log::error() << "pclose(" << name_ << ')' << Log::syserr << std::endl;
+		throw WriteError(name());
+	}
+}
+
+void PipeHandle::rewind()
+{
+	NOTIMP;
+}
+
+void PipeHandle::advance(const Length& len)
+{
+    NOTIMP;
+}
+/*
+
+void PipeHandle::restartReadFrom(const Offset& from)
+{
+    NOTIMP;
+}
+
+void PipeHandle::restartWriteFrom(const Offset& from)
+{
+    NOTIMP;
+}
+*/
+
+Offset PipeHandle::seek(const Offset& from)
+{
+    NOTIMP;
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/PipeHandle.h b/eckit/src/eckit/io/PipeHandle.h
new file mode 100644
index 0000000..41a11b5
--- /dev/null
+++ b/eckit/src/eckit/io/PipeHandle.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/PipeHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_PipeHandle_h
+#define eckit_filesystem_PipeHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class PipeHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	PipeHandle(const std::string&);
+	PipeHandle(Stream&);
+
+// -- Destructor
+
+	~PipeHandle();
+
+// --  Methods
+
+	void advance(const Length&); 
+
+// -- Overridden methods
+
+	// From DataHandle
+
+	virtual Length openForRead();
+	virtual void   openForWrite(const Length&);
+	virtual void   openForAppend(const Length&);
+
+	virtual long   read(void*,long);
+	virtual long   write(const void*,long);
+	virtual void   close();
+	virtual void   rewind();
+	virtual void   print(std::ostream&) const;
+    /*
+	virtual void restartReadFrom(const Offset& from);
+	virtual void restartWriteFrom(const Offset& from);
+    */
+    /* virtual void toRemote(Stream&) const; */
+    /* virtual void cost(std::map<std::string,Length>&, bool) const; */
+    /* virtual std::string title() const; */
+    virtual bool moveable() const { return false; }
+
+    virtual Offset seek(const Offset&);
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	std::string        name_;
+	FILE*         file_;
+	bool          read_;
+
+// -- Methods
+
+	void open(const char*);
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<PipeHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/RawFileHandle.cc b/eckit/src/eckit/io/RawFileHandle.cc
new file mode 100644
index 0000000..877f5c9
--- /dev/null
+++ b/eckit/src/eckit/io/RawFileHandle.cc
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/io/RawFileHandle.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void RawFileHandle::print(std::ostream& s) const
+{
+    s << "RawFileHandle[" << path_ << ']';
+}
+
+void RawFileHandle::encode(Stream& s) const
+{
+    NOTIMP;
+}
+
+RawFileHandle::RawFileHandle(const std::string& path,bool overwrite):
+    path_(path),
+    overwrite_(overwrite),
+    fd_(-1)
+{
+}
+
+
+RawFileHandle::~RawFileHandle()
+{
+    if(fd_ != -1)
+        close();
+}
+
+Length RawFileHandle::openForRead()
+{
+    SYSCALL( fd_ = ::open(std::string(path_).c_str(), O_RDONLY) );
+    SYSCALL( ::fcntl( fd_,F_SETFD,FD_CLOEXEC ) );
+    struct stat st;
+    SYSCALL(::fstat(fd_, &st)); ASSERT(sizeof(st.st_size) == sizeof(long long));
+    return st.st_size;
+}
+
+void RawFileHandle::openForWrite(const Length&)
+{
+    if(overwrite_) {
+        fd_ = SYSCALL( ::open(std::string(path_).c_str(), O_WRONLY,0777) );
+    }
+    else {
+        fd_ = SYSCALL( ::open(std::string(path_).c_str(), O_WRONLY|O_CREAT,0777) );
+    }
+    SYSCALL( ::fcntl( fd_,F_SETFD,FD_CLOEXEC ) );
+}
+
+void RawFileHandle::openForAppend(const Length&)
+{
+    NOTIMP;
+}
+
+long RawFileHandle::read(void* buffer,long length)
+{
+    long n;
+    SYSCALL(n = ::read(fd_,buffer,length));
+    return n;
+}
+
+long RawFileHandle::write(const void* buffer,long length)
+{
+    long n;
+    SYSCALL(n = ::write(fd_,buffer,length));
+    return n;
+}
+
+void RawFileHandle::close()
+{
+    //TODO: fsync
+    SYSCALL(::close(fd_));
+    fd_ = -1;
+
+}
+
+Offset RawFileHandle::position()
+{
+    return ::lseek(fd_, 0, SEEK_CUR);
+}
+
+Offset RawFileHandle::seek(const Offset& o)
+{
+    return ::lseek(fd_, o, SEEK_SET);
+}
+
+void RawFileHandle::skip(const Length& l)
+{
+    ::lseek(fd_, l, SEEK_CUR);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/io/RawFileHandle.h b/eckit/src/eckit/io/RawFileHandle.h
new file mode 100644
index 0000000..be781e5
--- /dev/null
+++ b/eckit/src/eckit/io/RawFileHandle.h
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/RawFileHandle.h
+// Baudouin Raoult - ECMWF Dec 2013
+
+#ifndef eckit_filesystem_RawFileHandle_h
+#define eckit_filesystem_RawFileHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class RawFileHandle : public DataHandle {
+public:
+
+    // -- Contructors
+
+    RawFileHandle(const std::string&,bool = false);
+
+
+    // -- Destructor
+
+    ~RawFileHandle();
+
+    // -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long   read(void*,long);
+    virtual long   write(const void*,long);
+    virtual void   close();
+    virtual void   print(std::ostream&) const;
+
+    virtual Offset position();
+    virtual Offset seek(const Offset&);
+    virtual void skip(const Length &);
+
+    // From Streamable
+
+    virtual void encode(Stream&) const;
+
+    // -- Class methods
+
+
+private:
+
+    // -- Members
+
+    std::string path_;
+    bool overwrite_;
+    int fd_;
+
+    // -- Class members
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/ResizableBuffer.cc b/eckit/src/eckit/io/ResizableBuffer.cc
new file mode 100644
index 0000000..a5d36ee
--- /dev/null
+++ b/eckit/src/eckit/io/ResizableBuffer.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/ResizableBuffer.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ResizableBuffer::ResizableBuffer(size_t size):
+    buffer_(0),
+    size_(size),
+    fd_(-1)
+{
+        create();
+}
+
+ResizableBuffer::ResizableBuffer(const char* p,size_t size):
+    buffer_(0),
+    size_(size)
+{
+        create();
+        ::memcpy(buffer_,p,size);
+}
+
+ResizableBuffer::ResizableBuffer(const std::string& s):
+    buffer_(0),
+    size_(s.length()+1)
+{
+        create();
+        ::strcpy((char*)buffer_,s.c_str());
+}
+
+ResizableBuffer::~ResizableBuffer()
+{
+        destroy();
+}
+
+void ResizableBuffer::create()
+{
+//	Log::info() << "ResizableBuffer::create " << size_ << std::endl;
+        if(size_)
+        {
+                fd_ = -1;
+                buffer_ = ::mmap(0,size_,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,fd_,0);
+                if(buffer_ == MAP_FAILED)
+                    throw FailedSystemCall("mmap",Here());
+        }
+}
+
+void ResizableBuffer::destroy()
+{
+//	Log::info() << "ResizableBuffer::destroy " << size_ << std::endl;
+        if(size_)
+        {
+                munmap(buffer_,size_);
+                if(fd_ >= 0) close(fd_);
+                fd_ = -1;
+        }
+        buffer_ = 0;
+}
+
+void ResizableBuffer::resize(size_t size)
+{
+        destroy();
+        size_ = size;
+        create();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/ResizableBuffer.h b/eckit/src/eckit/io/ResizableBuffer.h
new file mode 100644
index 0000000..f353f88
--- /dev/null
+++ b/eckit/src/eckit/io/ResizableBuffer.h
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ResizableBuffer.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_ResizableBuffer_h
+#define eckit_ResizableBuffer_h
+
+#include "eckit/eckit.h"
+
+// A simple class to implement buffers
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ResizableBuffer {
+public:
+
+// -- Contructors
+
+	ResizableBuffer(size_t size);
+	ResizableBuffer(const std::string& s);
+	ResizableBuffer(const char*,size_t size);
+
+// -- Destructor
+
+	~ResizableBuffer();
+
+// -- Operators
+
+	operator char*()                 { return (char*)buffer_; }
+	operator const char*() const     { return (char*)buffer_; }
+
+	operator void*()                 { return buffer_; }
+	operator const void*() const     { return buffer_; }
+
+// -- Methods
+
+	size_t size() const		 { return size_; }
+	void resize(size_t);
+
+private:
+
+// No copy allowed
+
+	ResizableBuffer(const ResizableBuffer&);
+	ResizableBuffer& operator=(const ResizableBuffer&);
+
+// -- Methods
+
+	void create();
+	void destroy();
+
+// -- Members
+
+	void*  buffer_;
+	size_t size_;
+	int    fd_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/Select.cc b/eckit/src/eckit/io/Select.cc
new file mode 100644
index 0000000..58229de
--- /dev/null
+++ b/eckit/src/eckit/io/Select.cc
@@ -0,0 +1,152 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "eckit/io/Select.h"
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Select::Select():
+	last_(-1)
+{
+	FD_ZERO(&files_);	
+}
+
+Select::Select(TCPSocket& p):
+	last_(-1)
+{
+	FD_ZERO(&files_);	
+	add(p);
+}
+
+Select::Select(int fd):
+	last_(-1)
+{
+	FD_ZERO(&files_);	
+	add(fd);
+}
+
+Select::~Select()
+{
+}
+
+void Select::add(int fd)
+{
+	ASSERT(fd >=0 && fd < getdtablesize());
+	FD_SET(fd,&files_);
+	if(fd > last_) last_ = fd;
+}
+
+void Select::add(TCPSocket& p)
+{
+	add(p.socket());
+}
+
+void Select::remove(int fd)
+{
+	ASSERT(fd >=0 && fd < getdtablesize());
+	FD_CLR(fd,&files_);
+}
+
+void Select::remove(TCPSocket& p)
+{
+	remove(p.socket());
+}
+
+bool Select::set(int fd)
+{
+	ASSERT(fd >=0 && fd < getdtablesize());
+	return FD_ISSET(fd,&set_);
+}
+
+bool Select::set(TCPSocket& p)
+{
+	return set(p.socket());
+}
+
+bool Select::ready(long sec)
+{
+
+	int size = last_ + 1;
+
+	::timeval timeout;
+	timeout.tv_sec  = sec;
+	timeout.tv_usec = 0;
+
+	for(;;)
+	{
+
+		// First check with ioctl, as select is not always trustworthy
+
+		bool some = false;
+		FD_ZERO(&set_);
+
+		for(int i  = 0 ; i < size ; i++)
+			if(FD_ISSET(i,&files_))
+			{
+				int nbytes = 0;
+
+                //cout << "ioctl(i,FIONREAD,&nbytes) " << i << " .... " << ioctl(i,FIONREAD,&nbytes) << " " << nbytes << std::endl;
+                nbytes = 0;
+
+
+				//SYSCALL(ioctl(i,FIONREAD,&nbytes));
+                //
+
+                // On Linux, a socket in acceoct() mode will return "Invalid argument"
+                // so we simply ignor ethe error here....
+				if((ioctl(i,FIONREAD,&nbytes) == 0) && (nbytes > 0))
+				{
+					FD_SET(i,&set_);
+					some = true;
+				}
+			}
+
+		if(some)
+			return true;
+
+		for(;;)
+		{
+
+			set_         = files_;
+			fd_set excep = files_;
+
+			switch(::select(size,&set_,0,&excep,&timeout) )
+			{
+				case -1: 
+					if(errno != EINTR) 
+						throw FailedSystemCall("select");
+					break; 
+
+				case 0:  
+					return false;
+					break; 
+
+				default:
+					return true;
+					break;
+			}
+		}
+	}
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/Select.h b/eckit/src/eckit/io/Select.h
new file mode 100644
index 0000000..d39641b
--- /dev/null
+++ b/eckit/src/eckit/io/Select.h
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Select.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_Select_h
+#define eckit_Select_h
+
+#if 0
+#ifdef __hpux
+#include <sys/time.h>
+#else
+#include <sys/select.h>
+#endif
+#endif
+
+#include <sys/select.h>
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TCPSocket;
+
+/// Wraps calls to select
+class Select : private NonCopyable {
+
+public:
+
+// -- Contructors
+
+	Select();
+	Select(int);
+	Select(TCPSocket&);
+
+// -- Destructor
+
+	~Select(); 
+
+// -- Methods
+
+	bool ready(long sec = 20);
+
+	void add(TCPSocket&);
+	void add(int);
+
+	void remove(TCPSocket&);
+	void remove(int);
+
+	bool set(TCPSocket&);
+	bool set(int);
+
+private:
+
+// -- Members
+
+	fd_set files_;
+	fd_set set_;
+	int    last_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/SharedHandle.cc b/eckit/src/eckit/io/SharedHandle.cc
new file mode 100644
index 0000000..83abdfe
--- /dev/null
+++ b/eckit/src/eckit/io/SharedHandle.cc
@@ -0,0 +1,185 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/io/SharedHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+SharedHandle::SharedHandle(DataHandle &handle):
+    handle_(handle)
+{
+
+}
+
+SharedHandle::~SharedHandle()
+{
+
+}
+
+void SharedHandle::print(std::ostream& s) const
+{
+    if(format(s) == Log::compactFormat)
+        s << "SharedHandle";
+    else
+        s << "SharedHandle[handle=" << handle_ << ']';
+}
+
+Length SharedHandle::openForRead()
+{
+    // Just ignore, assumes handle already opened
+    rewind();
+    return estimate();
+}
+
+void SharedHandle::openForWrite(const Length&)
+{
+    // Just ignore, assumes handle already opened
+    rewind();
+
+}
+void SharedHandle::openForAppend(const Length&)
+{
+    // Just ignore, assumes handle already opened
+    rewind();
+}
+
+long SharedHandle::read(void* data,long len)
+{
+    return handle_.read(data, len);
+}
+
+long SharedHandle::write(const void* data,long len)
+{
+    return handle_.write(data, len);
+}
+
+void SharedHandle::close()
+{
+    // Just ignore, assumes handle closed somewhere else
+}
+
+void SharedHandle::flush()
+{
+    return handle_.flush();
+}
+
+Length SharedHandle::estimate()
+{
+    return handle_.estimate();
+}
+
+Offset SharedHandle::position()
+{
+    return handle_.position();
+}
+
+Offset SharedHandle::seek(const Offset& o)
+{
+    return handle_.seek(o);
+}
+
+void SharedHandle::skip(const Length &n)
+{
+    return handle_.skip(n);
+}
+
+void SharedHandle::rewind()
+{
+    return handle_.rewind();
+}
+
+void SharedHandle::restartReadFrom(const Offset& o)
+{
+    return handle_.restartReadFrom(o);
+}
+
+void SharedHandle::restartWriteFrom(const Offset& o)
+{
+    return handle_.restartWriteFrom(o);
+}
+
+DataHandle* SharedHandle::clone() const
+{
+    NOTIMP;
+}
+
+std::string SharedHandle::name() const
+{
+    return handle_.name();
+}
+
+
+bool SharedHandle::compress(bool b)
+{
+    return handle_.compress(b);
+}
+
+bool SharedHandle::merge(DataHandle*)
+{
+    NOTIMP;
+}
+
+bool SharedHandle::isEmpty() const
+{
+    return handle_.isEmpty();
+}
+
+
+bool SharedHandle::moveable() const
+{
+    return false;
+}
+
+void SharedHandle::toLocal(Stream& s) const
+{
+    NOTIMP;
+}
+
+DataHandle* SharedHandle::toLocal()
+{
+    NOTIMP;
+}
+
+void SharedHandle::toRemote(Stream& s) const
+{
+    NOTIMP;
+}
+
+void SharedHandle::cost(std::map<std::string,Length>&, bool) const
+{
+    NOTIMP;
+}
+
+std::string SharedHandle::title() const
+{
+    return handle_.title();
+}
+
+Length SharedHandle::saveInto(DataHandle& other, TransferWatcher& watcher)
+{
+    return handle_.saveInto(other, watcher);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/SharedHandle.h b/eckit/src/eckit/io/SharedHandle.h
new file mode 100644
index 0000000..af031bb
--- /dev/null
+++ b/eckit/src/eckit/io/SharedHandle.h
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Partio/FileHandle.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_filesystem_SharedHandle_h
+#define eckit_filesystem_SharedHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class SharedHandle : public DataHandle {
+public:
+
+    // -- Contructors
+
+    SharedHandle(DataHandle& handle);
+
+    // -- Destructor
+
+    ~SharedHandle();
+
+    // -- Methods
+
+    // -- Overridden methods
+
+    // From DataHandle
+
+
+    virtual void print(std::ostream& s) const;
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+
+    virtual Length estimate();
+    virtual Offset position();
+    virtual Offset seek(const Offset&);
+    virtual void skip(const Length &);
+
+    virtual void rewind();
+    virtual void restartReadFrom(const Offset&);
+    virtual void restartWriteFrom(const Offset&);
+
+    virtual DataHandle* clone() const;
+
+    virtual Length saveInto(DataHandle& other, TransferWatcher& watcher);
+
+    virtual std::string name() const;
+
+
+    virtual bool compress(bool);
+    virtual bool merge(DataHandle*);
+    virtual bool isEmpty() const;
+
+
+    virtual bool moveable() const;
+    virtual void toLocal(Stream& s) const;
+
+    virtual DataHandle* toLocal();
+
+    virtual void toRemote(Stream& s) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+
+
+
+private:
+
+    // -- Members
+
+    DataHandle&        handle_;
+
+    // -- Methods
+
+
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/SockBuf.cc b/eckit/src/eckit/io/SockBuf.cc
new file mode 100644
index 0000000..f0c9c9f
--- /dev/null
+++ b/eckit/src/eckit/io/SockBuf.cc
@@ -0,0 +1,105 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/SockBuf.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+SockBuf::SockBuf(TCPSocket& proto):
+	protocol_(proto)
+{ 
+#ifndef OLD_STREAMBUF
+	/* setg(in_,  in_,  in_  + sizeof(in_) );  */
+	setg(in_,  in_,  in_  ); 
+	setp(out_, out_ + sizeof(out_)); 
+#else
+	setb(in_, in_ + sizeof(in_), 0); 
+	setg(in_, in_, in_); 
+	setp(out_, out_ + sizeof(out_)); 
+#endif
+}
+
+SockBuf::~SockBuf()
+{
+	sync();
+}
+
+int SockBuf::sync()
+{
+
+	if(!protocol_.isConnected()) 
+	{
+		setp(pbase(), epptr());
+		return EOF;
+	}
+
+	if(protocol_.write(pbase(),pptr() - pbase()) < 0)	
+	{
+		protocol_.close();
+		return EOF;	
+	}
+
+	setp(pbase(), epptr());
+
+	return 0;
+}
+
+int SockBuf::overflow(int c) 
+{
+	if(sync())
+		return EOF;
+
+	if(c == EOF)
+		return 0;
+
+	sputc(c);
+	return 0;
+}
+
+int SockBuf::underflow()
+{
+	if (gptr () < egptr ()) 
+		return *(unsigned char*)gptr ();
+
+	if(!protocol_.isConnected()) 
+		return EOF;
+
+#ifndef OLD_STREAMBUF
+	int n = protocol_.read(in_,sizeof(in_));
+#else
+	int n = protocol_.read(base(),sizeof(in_));
+#endif
+
+	if(n == EOF || n == 0)
+	{
+		protocol_.close();
+		return EOF;
+	}
+
+#ifndef OLD_STREAMBUF
+	setg(in_,  in_,  in_  + n ); 
+#else
+	setg (eback (), base (), base () + n);
+#endif
+	return *(unsigned char*)gptr ();
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/SockBuf.h b/eckit/src/eckit/io/SockBuf.h
new file mode 100644
index 0000000..9c75ac0
--- /dev/null
+++ b/eckit/src/eckit/io/SockBuf.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SockBuf.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_SockBuf_h
+#define eckit_SockBuf_h
+
+#include "eckit/net/TCPSocket.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class SockBuf : public std::streambuf  {
+public:
+
+// -- Contructors
+
+	SockBuf(TCPSocket& proto);
+
+// -- Destructor
+
+	~SockBuf();
+
+private:
+
+// No copy allowed
+
+	SockBuf(const SockBuf&);
+	SockBuf& operator=(const SockBuf&);
+
+// -- Members
+
+	char in_[1];
+	char out_[80];
+	TCPSocket& protocol_;
+
+// -- Overridden methods
+
+	// From streambuf
+
+	virtual int overflow(int c);
+	virtual int underflow();
+	virtual int sync();
+//	virtual int uflow();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/StatsHandle.cc b/eckit/src/eckit/io/StatsHandle.cc
new file mode 100644
index 0000000..9df6fc5
--- /dev/null
+++ b/eckit/src/eckit/io/StatsHandle.cc
@@ -0,0 +1,256 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/marsfs/MarsFSPath.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+#include "eckit/io/StatsHandle.h"
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StatsHandle::StatsHandle(DataHandle &handle):
+    HandleHolder(handle),
+    reads_(0),
+    seeks_(0),
+    writes_(0),
+//    positions_(0),
+    bytesRead_(0),
+    bytesWritten_(0),
+    timer_(),
+    readTime_(0),
+    writeTime_(0),
+    seekTime_(0)
+{
+
+}
+
+StatsHandle::StatsHandle(DataHandle *handle):
+    HandleHolder(handle),
+    reads_(0),
+    seeks_(0),
+    writes_(0),
+//    positions_(0),
+    bytesRead_(0),
+    bytesWritten_(0),
+    timer_(),
+    readTime_(0),
+    writeTime_(0),
+    seekTime_(0)
+{
+
+}
+
+StatsHandle::~StatsHandle()
+{
+    std::cout << "StatsHandle for " << handle() << std::endl;
+    std::cout << "       Elapsed: " << eckit::Seconds(timer_.elapsed()) << std::endl;
+
+    if(reads_) {
+        std::cout << "  No. of reads: " << eckit::BigNum(reads_) << std::endl;
+        std::cout << "    Bytes read: " << eckit::Bytes(bytesRead_)<< std::endl;
+        std::cout << "  Average read: " << eckit::Bytes(bytesRead_/reads_)<< std::endl;
+        std::cout << "     Read time: " << eckit::Seconds(readTime_)<< std::endl;
+        std::cout << "     Read rate: " << eckit::Bytes(bytesRead_, readTime_)<< std::endl;
+    }
+
+    if(writes_) {
+        std::cout << " No. of writes: " << eckit::BigNum(writes_) << std::endl;
+        std::cout << " Bytes written: " << eckit::Bytes(bytesWritten_)<< std::endl;
+        std::cout << " Average write: " << eckit::Bytes(bytesWritten_/writes_)<< std::endl;
+        std::cout << "    Write time: " << eckit::Seconds(writeTime_)<< std::endl;
+        std::cout << "    Write rate: " << eckit::Bytes(bytesWritten_, writeTime_)<< std::endl;
+    }
+
+    if(seeks_) {
+        std::cout << "  No. of seeks: " << eckit::BigNum(seeks_) << std::endl;
+        std::cout << "     Seek time: " << eckit::Seconds(seekTime_)<< std::endl;
+    }
+
+}
+
+void StatsHandle::print(std::ostream& s) const
+{
+    /*
+    if(format(s) == Log::compactFormat)
+        s << "StatsHandle";
+    else*/
+    s << "StatsHandle[handle=" << handle() << ']';
+}
+
+Length StatsHandle::openForRead()
+{
+    return handle().openForRead();
+}
+
+void StatsHandle::openForWrite(const Length& l)
+{
+    handle().openForWrite(l);
+}
+
+void StatsHandle::openForAppend(const Length& l)
+{
+    handle().openForAppend(l);
+}
+
+long StatsHandle::read(void* data,long len)
+{
+    double x = timer_.elapsed();
+    reads_++;
+    bytesRead_ += len;
+    long ret = handle().read(data, len);
+    readTime_ += timer_.elapsed() - x;
+    return ret;
+}
+
+long StatsHandle::write(const void* data,long len)
+{
+    double x = timer_.elapsed();
+    writes_++;
+    bytesWritten_ += len;
+    long ret = handle().write(data, len);
+    writeTime_ += timer_.elapsed() - x;
+    return ret;
+}
+
+void StatsHandle::close()
+{
+    handle().close();
+}
+
+void StatsHandle::flush()
+{
+    return handle().flush();
+}
+
+Length StatsHandle::estimate()
+{
+    return handle().estimate();
+}
+
+Offset StatsHandle::position()
+{
+    return handle().position();
+}
+
+Offset StatsHandle::seek(const Offset& o)
+{
+    double x = timer_.elapsed();
+    seeks_++;
+    Offset ret = handle().seek(o);
+    seekTime_ += timer_.elapsed() - x;
+    return ret;
+}
+
+void StatsHandle::skip(const Length &n)
+{
+    double x = timer_.elapsed();
+    seeks_++;
+    handle().skip(n);
+    seekTime_ += timer_.elapsed() - x;
+}
+
+void StatsHandle::rewind()
+{
+    double x = timer_.elapsed();
+    seeks_++;
+    handle().rewind();
+    seekTime_ += timer_.elapsed() - x;
+}
+
+void StatsHandle::restartReadFrom(const Offset& o)
+{
+    return handle().restartReadFrom(o);
+}
+
+void StatsHandle::restartWriteFrom(const Offset& o)
+{
+    return handle().restartWriteFrom(o);
+}
+
+DataHandle* StatsHandle::clone() const
+{
+    NOTIMP;
+}
+
+std::string StatsHandle::name() const
+{
+    return handle().name();
+}
+
+
+bool StatsHandle::compress(bool b)
+{
+    return handle().compress(b);
+}
+
+bool StatsHandle::merge(DataHandle*)
+{
+    NOTIMP;
+}
+
+bool StatsHandle::isEmpty() const
+{
+    return handle().isEmpty();
+}
+
+
+bool StatsHandle::moveable() const
+{
+    return false;
+}
+
+void StatsHandle::toLocal(Stream& s) const
+{
+    NOTIMP;
+}
+
+DataHandle* StatsHandle::toLocal()
+{
+    NOTIMP;
+}
+
+void StatsHandle::toRemote(Stream& s) const
+{
+    NOTIMP;
+}
+
+void StatsHandle::cost(std::map<std::string,Length>&, bool) const
+{
+    NOTIMP;
+}
+
+std::string StatsHandle::title() const
+{
+    return handle().title();
+}
+
+Length StatsHandle::saveInto(DataHandle& other, TransferWatcher& watcher)
+{
+    return handle().saveInto(other, watcher);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/StatsHandle.h b/eckit/src/eckit/io/StatsHandle.h
new file mode 100644
index 0000000..627b364
--- /dev/null
+++ b/eckit/src/eckit/io/StatsHandle.h
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Partio/FileHandle.h
+// Baudouin Raoult - ECMWF Decembre 2013
+
+#ifndef eckit_filesystem_StatsHandle_h
+#define eckit_filesystem_StatsHandle_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/HandleHolder.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/types/Types.h"
+#include "eckit/log/Timer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StatsHandle : public DataHandle, public HandleHolder {
+public:
+
+    // -- Contructors
+
+    StatsHandle(DataHandle& handle);
+    StatsHandle(DataHandle* handle);
+
+    // -- Destructor
+
+    ~StatsHandle();
+
+    // -- Methods
+
+    // -- Overridden methods
+
+    // From DataHandle
+
+
+    virtual void print(std::ostream& s) const;
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+
+    virtual Length estimate();
+    virtual Offset position();
+    virtual Offset seek(const Offset&);
+    virtual void skip(const Length &);
+
+    virtual void rewind();
+    virtual void restartReadFrom(const Offset&);
+    virtual void restartWriteFrom(const Offset&);
+
+    virtual DataHandle* clone() const;
+
+    virtual Length saveInto(DataHandle& other, TransferWatcher& watcher);
+
+    virtual std::string name() const;
+
+
+    virtual bool compress(bool);
+    virtual bool merge(DataHandle*);
+    virtual bool isEmpty() const;
+
+
+    virtual bool moveable() const;
+    virtual void toLocal(Stream& s) const;
+
+    virtual DataHandle* toLocal();
+
+    virtual void toRemote(Stream& s) const;
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+    virtual std::string title() const;
+
+
+
+private:
+
+    // -- Members
+
+    size_t reads_;
+    size_t seeks_;
+//    size_t positions_; // unused
+    size_t writes_;
+
+    unsigned long long bytesRead_;
+    unsigned long long bytesWritten_;
+
+    Timer timer_;
+
+    double readTime_;
+    double writeTime_;
+    double seekTime_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/StdFile.cc b/eckit/src/eckit/io/StdFile.cc
new file mode 100644
index 0000000..397af02
--- /dev/null
+++ b/eckit/src/eckit/io/StdFile.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/StdFile.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StdFile::StdFile(const PathName& name,const std::string& mode)
+{
+	file_ = ::fopen(name.localPath(),mode.c_str());
+
+	if(file_ == 0)
+		throw CantOpenFile(name);
+}
+
+StdFile::~StdFile()
+{
+	if(file_)
+		if(fclose(file_))
+			throw FailedSystemCall("fclose");
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/StdFile.h b/eckit/src/eckit/io/StdFile.h
new file mode 100644
index 0000000..88ec304
--- /dev/null
+++ b/eckit/src/eckit/io/StdFile.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StdFile.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_StdFile_h
+#define eckit_StdFile_h
+
+#include <stdio.h>
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Simple wrapper around a stdio file
+
+class PathName;
+
+class StdFile : private NonCopyable {
+public:
+
+	StdFile(const PathName& name,const std::string& mode = "r");
+
+	~StdFile();
+
+	operator FILE*() { return file_; } // don't call fclose !!!
+
+private: // members
+
+	FILE *file_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/StdFileHandle.cc b/eckit/src/eckit/io/StdFileHandle.cc
new file mode 100644
index 0000000..195d64d
--- /dev/null
+++ b/eckit/src/eckit/io/StdFileHandle.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/StdFileHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+void StdFileHandle::print(std::ostream& s) const
+{
+	s << "StdFileHandle[" << "TODO" << ']';
+	//s << "StdFileHandle[fd=" << fd_ << ']';
+}
+
+void StdFileHandle::encode(Stream& s) const
+{
+	NOTIMP;
+}
+
+StdFileHandle::StdFileHandle(FILE* f):
+	f_(f)
+{
+}
+
+StdFileHandle::~StdFileHandle()
+{
+}
+
+Length StdFileHandle::openForRead()
+{
+	return 0;
+}
+
+void StdFileHandle::openForWrite(const Length&)
+{
+}
+
+void StdFileHandle::openForAppend(const Length&)
+{
+}
+
+long StdFileHandle::read(void* buffer, long length)
+{
+	size_t n = ::fread(buffer, 1, length, f_);
+	return n;
+}
+
+long StdFileHandle::write(const void* buffer, long length)
+{
+	return ::fwrite(buffer, 1, length, f_);
+}
+
+void StdFileHandle::close()
+{
+	// May be we should close fd_ here ?
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/StdFileHandle.h b/eckit/src/eckit/io/StdFileHandle.h
new file mode 100644
index 0000000..1b2ba26
--- /dev/null
+++ b/eckit/src/eckit/io/StdFileHandle.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Stdio/FileHandle.h
+// Piotr Kuchta - ECMWF April 09
+
+#ifndef eckit_filesystem_StdFileHandle_h
+#define eckit_filesystem_StdFileHandle_h
+
+
+#include "eckit/io/DataHandle.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StdFileHandle : public DataHandle {
+public:
+
+// -- Contructors
+
+	StdFileHandle(FILE *);
+
+// -- Destructor
+
+	~StdFileHandle();
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long   read(void*,long);
+	virtual long   write(const void*,long);
+	virtual void   close();
+	virtual void   print(std::ostream&) const;
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+
+// -- Class methods
+
+
+private:
+
+// -- Members
+
+	FILE* f_;
+
+// -- Class members
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/StdPipe.cc b/eckit/src/eckit/io/StdPipe.cc
new file mode 100644
index 0000000..9cc0d0e
--- /dev/null
+++ b/eckit/src/eckit/io/StdPipe.cc
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/StdPipe.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+StdPipe::StdPipe(const std::string& name,const std::string& mode)
+{
+	file_ = popen(name.c_str(),mode.c_str());
+
+	if(file_ == 0)
+		throw CantOpenFile(name);
+}
+
+StdPipe::~StdPipe()
+{
+	if(file_)
+		if(pclose(file_))
+			throw FailedSystemCall("pclose");
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/StdPipe.h b/eckit/src/eckit/io/StdPipe.h
new file mode 100644
index 0000000..d4731b8
--- /dev/null
+++ b/eckit/src/eckit/io/StdPipe.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StdPipe.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_StdPipe_h
+#define eckit_StdPipe_h
+
+#include <stdio.h>
+#include <string>
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+// Simple wrapper around a stdio file
+
+class StdPipe {
+public:
+
+// -- Contructors
+
+	StdPipe(const std::string& name,const std::string& mode = "r");
+
+// -- Destructor
+
+	~StdPipe();
+
+// -- Convertors
+
+	operator FILE*() { return file_; } // don't call fclose !!!
+
+private:
+
+// No copy allowed
+
+	StdPipe(const StdPipe&);
+	StdPipe& operator=(const StdPipe&);
+
+// -- Members
+
+	FILE *file_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/StdioBuf.cc b/eckit/src/eckit/io/StdioBuf.cc
new file mode 100644
index 0000000..0422bb6
--- /dev/null
+++ b/eckit/src/eckit/io/StdioBuf.cc
@@ -0,0 +1,92 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/StdioBuf.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+
+StdioBuf::StdioBuf(FILE* file):
+	file_(file)
+{ 
+#ifndef OLD_STREAMBUF
+	/* setg(in_,  in_,  in_  + sizeof(in_) );  */
+	setg(in_,  in_,  in_  ); 
+	setp(out_, out_ + sizeof(out_)); 
+#else
+	setb(in_, in_ + sizeof(in_), 0); 
+	setg(in_, in_, in_); 
+	setp(out_, out_ + sizeof(out_)); 
+#endif
+}
+
+StdioBuf::~StdioBuf()
+{
+	sync();
+}
+
+int StdioBuf::sync()
+{
+    size_t s = pptr() - pbase();
+    if(s)
+        if(::fwrite(pbase(),1,s,file_) != s)
+            return EOF;
+
+	setp(pbase(), epptr());
+
+	return 0;
+}
+
+int StdioBuf::overflow(int c) 
+{
+	if(sync())
+		return EOF;
+
+	if(c == EOF)
+		return 0;
+
+	sputc(c);
+	return 0;
+}
+
+int StdioBuf::underflow()
+{
+	if (gptr () < egptr ()) 
+		return *(unsigned char*)gptr ();
+
+#ifndef OLD_STREAMBUF
+	int n = ::fread(in_,1,sizeof(in_),file_);
+#else
+	int n = ::fread(base(),1,sizeof(in_),file_);
+#endif
+
+	if(n == EOF || n == 0)
+		return EOF;
+
+#ifndef OLD_STREAMBUF
+	setg(in_,  in_,  in_  + n ); 
+#else
+	setg (eback (), base (), base () + n);
+#endif
+	return *(unsigned char*)gptr ();
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/StdioBuf.h b/eckit/src/eckit/io/StdioBuf.h
new file mode 100644
index 0000000..1d2265b
--- /dev/null
+++ b/eckit/src/eckit/io/StdioBuf.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StdioBuf.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_StdioBuf_h
+#define eckit_StdioBuf_h
+
+#include "eckit/eckit.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class StdioBuf : public std::streambuf  {
+public:
+
+// -- Contructors
+
+	StdioBuf(FILE*);
+
+// -- Destructor
+
+	~StdioBuf();
+
+private:
+
+// No copy allowed
+
+	StdioBuf(const StdioBuf&);
+	StdioBuf& operator=(const StdioBuf&);
+
+// -- Members
+
+	char in_[1];
+	char out_[80];
+	FILE* file_;
+
+// -- Overridden methods
+
+	// From streambuf
+
+	virtual int overflow(int c);
+	virtual int underflow();
+	virtual int sync();
+//	virtual int uflow();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/TCPHandle.cc b/eckit/src/eckit/io/TCPHandle.cc
new file mode 100644
index 0000000..74eacfb
--- /dev/null
+++ b/eckit/src/eckit/io/TCPHandle.cc
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/TCPHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec TCPHandle::classSpec_ = {&DataHandle::classSpec(),"TCPHandle",};
+Reanimator<TCPHandle> TCPHandle::reanimator_;
+
+
+void TCPHandle::print(std::ostream& s) const
+{
+	s << "TCPHandle[host=" << host_ << ",port=" << port_ << ']';
+}
+
+void TCPHandle::encode(Stream& s) const
+{
+	DataHandle::encode(s);
+	s << host_;
+	s << port_;
+}
+
+TCPHandle::TCPHandle(Stream& s):
+	DataHandle(s),port_(0)
+{
+	s >> host_;
+	s >> port_;
+}
+
+
+TCPHandle::TCPHandle(const std::string& host,int port):
+	host_(host),
+	port_(port)
+{
+}
+
+TCPHandle::~TCPHandle()
+{
+}
+
+Length TCPHandle::openForRead()
+{
+	connection_.connect(host_,port_);
+	return 0;
+}
+
+void TCPHandle::openForWrite(const Length&)
+{
+	connection_.connect(host_,port_);
+}
+
+void TCPHandle::openForAppend(const Length&)
+{
+	NOTIMP;
+}
+
+long TCPHandle::read(void* buffer,long length)
+{
+	return connection_.read(buffer,length);
+}
+
+long TCPHandle::write(const void* buffer,long length)
+{
+	return connection_.write(buffer,length);
+}
+
+void TCPHandle::close()
+{
+	connection_.close();
+}
+
+void TCPHandle::rewind()
+{
+	NOTIMP;
+}
+
+std::string TCPHandle::title() const
+{
+    std::ostringstream os;
+    os << "TCP[" << host_ << ":" << port_ << "]";
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/TCPHandle.h b/eckit/src/eckit/io/TCPHandle.h
new file mode 100644
index 0000000..aad89e1
--- /dev/null
+++ b/eckit/src/eckit/io/TCPHandle.h
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/TCPHandle.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_filesystem_TCPHandle_h
+#define eckit_filesystem_TCPHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/net/TCPClient.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TCPHandle : public DataHandle {
+public:
+
+// -- Contructors
+	
+	TCPHandle(Stream&);
+	TCPHandle(const std::string& host,int port);
+
+// -- Destructor
+
+	~TCPHandle();
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void rewind();
+	virtual void print(std::ostream&) const;
+	virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+	// From Streamable
+
+	virtual void encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const ClassSpec&  classSpec()         { return classSpec_;}
+
+protected:
+
+// -- Members
+
+	std::string      host_;
+	int         port_;
+	TCPClient   connection_;
+
+private:
+
+
+// -- Class members
+
+    static  ClassSpec               classSpec_;
+	static  Reanimator<TCPHandle>  reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/TCPSocketHandle.cc b/eckit/src/eckit/io/TCPSocketHandle.cc
new file mode 100644
index 0000000..7fdf383
--- /dev/null
+++ b/eckit/src/eckit/io/TCPSocketHandle.cc
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/TCPSocketHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void TCPSocketHandle::print(std::ostream& s) const
+{
+	s << "TCPSocketHandle[]";
+}
+
+
+TCPSocketHandle::TCPSocketHandle(TCPSocket& s):
+    connection_(s)
+{
+}
+
+TCPSocketHandle::~TCPSocketHandle()
+{
+}
+
+Length TCPSocketHandle::openForRead()
+{
+	return 0;
+}
+
+void TCPSocketHandle::openForWrite(const Length&)
+{
+}
+
+void TCPSocketHandle::openForAppend(const Length&)
+{
+	NOTIMP;
+}
+
+long TCPSocketHandle::read(void* buffer,long length)
+{
+	return connection_.read(buffer,length);
+}
+
+long TCPSocketHandle::write(const void* buffer,long length)
+{
+	return connection_.write(buffer,length);
+}
+
+void TCPSocketHandle::close()
+{
+	connection_.close();
+}
+
+void TCPSocketHandle::rewind()
+{
+	NOTIMP;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/TCPSocketHandle.h b/eckit/src/eckit/io/TCPSocketHandle.h
new file mode 100644
index 0000000..d601204
--- /dev/null
+++ b/eckit/src/eckit/io/TCPSocketHandle.h
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/TCPSocketHandle.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_filesystem_TCPSocketHandle_h
+#define eckit_filesystem_TCPSocketHandle_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TCPSocketHandle : public DataHandle {
+public:
+
+// -- Contructors
+	
+	TCPSocketHandle(TCPSocket&);
+
+// -- Destructor
+
+	~TCPSocketHandle();
+
+// -- Overridden methods
+
+	// From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void rewind();
+	virtual void print(std::ostream&) const;
+
+	// From Streamable
+
+
+// -- Class methods
+
+protected:
+
+// -- Members
+
+	TCPSocket   connection_;
+
+private:
+
+// No copy allowed
+
+	TCPSocketHandle(const TCPSocketHandle&);
+	TCPSocketHandle& operator=(const TCPSocketHandle&);
+
+
+// -- Class members
+
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/TeeHandle.cc b/eckit/src/eckit/io/TeeHandle.cc
new file mode 100644
index 0000000..5f20152
--- /dev/null
+++ b/eckit/src/eckit/io/TeeHandle.cc
@@ -0,0 +1,186 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/TeeHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec TeeHandle::classSpec_ = {&DataHandle::classSpec(),"TeeHandle",};
+Reanimator<TeeHandle> TeeHandle::reanimator_;
+
+TeeHandle::TeeHandle()
+{
+}
+
+TeeHandle::TeeHandle(const std::vector<DataHandle*>& v):
+	datahandles_(v)
+{
+}
+
+TeeHandle::TeeHandle(DataHandle* a, DataHandle* b)
+{
+    datahandles_.push_back(a);
+    datahandles_.push_back(b);
+}
+
+
+TeeHandle::TeeHandle(Stream& s):
+	DataHandle(s)
+{
+	unsigned long size;
+	s >> size;
+
+	datahandles_.reserve(size);
+
+    for(size_t i=0; i < size; i++)
+	{
+		DataHandle* dh = Reanimator<DataHandle>::reanimate(s); ASSERT(dh);
+		datahandles_.push_back(dh);
+	}
+}
+
+void TeeHandle::encode(Stream& s) const
+{
+	DataHandle::encode(s);
+	s << datahandles_.size();
+    for(size_t i=0; i < datahandles_.size(); i++)
+		s << *(datahandles_[i]);
+}
+
+TeeHandle::~TeeHandle() 
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+		delete datahandles_[i];
+}
+
+void TeeHandle::operator+=(DataHandle* dh)
+{
+	ASSERT(dh != 0);
+	datahandles_.push_back(dh);
+}
+
+Length TeeHandle::openForRead()
+{
+    NOTIMP;
+}
+
+void TeeHandle::openForWrite(const Length& length)
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+        datahandles_[i]->openForWrite(length);
+}
+
+void TeeHandle::openForAppend(const Length& )
+{
+	NOTIMP;
+}
+
+long TeeHandle::read(void* buffer,long length)
+{
+	NOTIMP;
+}
+
+long TeeHandle::write(const void* buffer,long length)
+{
+    long len = 0;
+    for(size_t i=0; i < datahandles_.size(); i++)
+    {
+        long l = datahandles_[i]->write(buffer, length);
+        if(i) ASSERT(len == l);
+        len = l;
+    }
+    return len;
+}
+
+void TeeHandle::close()
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+        datahandles_[i]->close();
+}
+
+void TeeHandle::flush()
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+        datahandles_[i]->flush();
+}
+
+void TeeHandle::rewind()
+{
+    NOTIMP;
+}
+
+void TeeHandle::print(std::ostream& s) const
+{
+	if(format(s) == Log::compactFormat)
+		s << "TeeHandle";
+	else 
+    {
+		s << "TeeHandle[";
+        for(size_t i=0;i<datahandles_.size();i++)
+		{
+			if(i != 0) 
+				s << ",(";
+			datahandles_[i]->print(s);	
+			s << ")";
+		}
+		s << ']';
+	}
+}
+
+void TeeHandle::toRemote(Stream &s) const
+{
+    s.startObject();
+    s << className();
+	DataHandle::encode(s);
+	s << datahandles_.size();
+    for(size_t i=0; i < datahandles_.size(); i++)
+		datahandles_[i]->toRemote(s);
+    s.endObject();
+}
+
+void TeeHandle::toLocal(Stream &s) const
+{
+    s.startObject();
+    s << className();
+	DataHandle::encode(s);
+	s << datahandles_.size();
+    for(size_t i=0; i < datahandles_.size(); i++)
+		datahandles_[i]->toLocal(s);
+    s.endObject();
+}
+
+DataHandle* TeeHandle::toLocal()
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+    {
+        DataHandle* loc = datahandles_[i]->toLocal();
+        if(loc != datahandles_[i])
+        {
+            delete datahandles_[i];
+            datahandles_[i] = loc;
+        }
+    }
+    return this;
+}
+
+void TeeHandle::cost(std::map<std::string,Length>& c, bool read) const
+{
+    for(size_t i=0; i < datahandles_.size(); i++)
+        datahandles_[i]->cost(c, read);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/TeeHandle.h b/eckit/src/eckit/io/TeeHandle.h
new file mode 100644
index 0000000..b04d978
--- /dev/null
+++ b/eckit/src/eckit/io/TeeHandle.h
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File io/TeeHandle.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_filesystem_TeeHandle_h
+#define eckit_filesystem_TeeHandle_h
+
+#include "eckit/io/DataHandle.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TeeHandle : public DataHandle {
+public:
+
+	typedef std::vector<DataHandle*> HandleList;
+
+// -- Contructors
+
+	TeeHandle();
+	TeeHandle(DataHandle*, DataHandle*);
+	TeeHandle(const HandleList&);
+	TeeHandle(Stream&);
+
+// -- Destructor
+
+	~TeeHandle();
+
+// -- Operators
+
+	virtual void operator+=(DataHandle*);
+
+// -- Overridden methods
+
+    // From DataHandle
+
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+    virtual long read(void*,long);
+    virtual long write(const void*,long);
+    virtual void close();
+    virtual void flush();
+    virtual void rewind();
+    virtual void print(std::ostream&) const;
+
+
+    virtual void toRemote(Stream&) const;
+    virtual void toLocal(Stream&) const;
+    virtual DataHandle* toLocal();
+    virtual void cost(std::map<std::string,Length>&, bool) const;
+
+    // From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_;}
+
+private:
+
+// -- Members
+
+	HandleList			    datahandles_;
+
+// -- Methods
+
+// -- Class members
+
+	static  ClassSpec                 classSpec_;
+    static  Reanimator<TeeHandle>  reanimator_;
+
+};
+
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/TransferWatcher.cc b/eckit/src/eckit/io/TransferWatcher.cc
new file mode 100644
index 0000000..f3ad181
--- /dev/null
+++ b/eckit/src/eckit/io/TransferWatcher.cc
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/TransferWatcher.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+struct DummyTransferWatcher : public TransferWatcher {
+	void watch(const void*,long) {}
+};
+
+TransferWatcher& TransferWatcher::dummy()
+{
+	static DummyTransferWatcher x;
+	return x;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/TransferWatcher.h b/eckit/src/eckit/io/TransferWatcher.h
new file mode 100644
index 0000000..0e54640
--- /dev/null
+++ b/eckit/src/eckit/io/TransferWatcher.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TransferWatcher.h
+// Baudouin Raoult - ECMWF Jun 98
+
+#ifndef eckit_TransferWatcher_h
+#define eckit_TransferWatcher_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TransferWatcher {
+public:
+
+// -- Methods
+
+	virtual void watch(const void*,long) = 0;
+	virtual ~TransferWatcher() {}
+
+// -- Class methods
+
+	static TransferWatcher& dummy();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/io/cluster/ClusterDisks.cc b/eckit/src/eckit/io/cluster/ClusterDisks.cc
new file mode 100644
index 0000000..8d81fb6
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterDisks.cc
@@ -0,0 +1,467 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @date   Jun 2011
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+
+#include "eckit/config/Resource.h"
+#include "eckit/container/MappedArray.h"
+#include "eckit/container/SharedMemArray.h"
+#include "eckit/filesystem/FileSpace.h"
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/io/cluster/ClusterDisks.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/thread/AutoLock.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ClusterDisk {
+	bool active_;
+	bool offLine_;
+	time_t lastSeen_;
+	char node_[256];
+	char type_[256];
+	char path_[2048];
+
+public:
+	ClusterDisk(const std::string& node, const std::string& type, const std::string& path) :
+		active_(true), offLine_(false), lastSeen_(::time(0))
+	{
+		zero(node_);
+		strncpy(node_, node.c_str(), sizeof(node_) - 1);
+		zero(type_);
+		strncpy(type_, type.c_str(), sizeof(type_) - 1);
+		zero(path_);
+		strncpy(path_, path.c_str(), sizeof(path_) - 1);
+	}
+
+	bool operator<(const ClusterDisk& other) const
+	{
+		if(strcmp(path_, other.path_) < 0)
+			return true;
+		return false;
+	}
+
+	void active(bool on)
+	{
+		active_ = on;
+	}
+	void offLine(bool on)
+	{
+		offLine_ = on;
+	}
+
+	bool active() const
+	{
+		return active_;
+	}
+	bool offLine() const
+	{
+		return offLine_;
+	}
+	time_t lastSeen() const
+	{
+		return lastSeen_;
+	}
+	void lastSeen(time_t n)
+	{
+		lastSeen_ = n;
+	}
+
+	const char* node() const
+	{
+		return node_;
+	}
+	const char* type() const
+	{
+		return type_;
+	}
+	const char* path() const
+	{
+		return path_;
+	}
+
+	friend std::ostream& operator<<(std::ostream&, const ClusterDisk&);
+
+	void json(JSON& s) const
+	{
+		s.startObject();
+		s << "lastSeen" << lastSeen_;
+		s << "offLine" << offLine_;
+		s << "node" << node_;
+		s << "type" << type_;
+		s << "path" << path_;
+		s.endObject();
+	}
+
+};
+std::ostream& operator<<(std::ostream& s, const ClusterDisk& d)
+{
+	s << "ClusterDisk[" << d.node_ << "," << d.type_ << "," << d.path_ << "," << (::time(0) - d.lastSeen_) << 
+        "," << (d.offLine_ ? "off" : "on") << "-line" <<
+        "]";
+	return s;
+}
+
+inline unsigned long version(ClusterDisk*)
+{
+	return 1;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class DiskArray : private eckit::NonCopyable {
+
+public:
+
+    typedef ClusterDisk*       iterator;
+    typedef const ClusterDisk* const_iterator;
+
+    virtual ~DiskArray() {}
+
+    virtual void sync() = 0;
+    virtual void lock() = 0;
+    virtual void unlock() = 0;
+
+    virtual iterator begin() = 0;
+    virtual iterator end() = 0;
+    virtual const_iterator begin() const = 0;
+    virtual const_iterator end()   const = 0;
+
+    virtual unsigned long size() = 0;
+    virtual ClusterDisk& operator[](unsigned long n) = 0;
+};
+
+class MemoryMappedDiskArray : public DiskArray {
+
+    virtual void sync() { map_.sync(); }
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual iterator begin()  { return map_.begin(); }
+    virtual iterator end()    { return map_.end(); }
+
+    virtual const_iterator begin() const   { return map_.begin(); }
+    virtual const_iterator end()   const   { return map_.end(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual ClusterDisk& operator[](unsigned long n) { return map_[n]; }
+
+    MappedArray<ClusterDisk> map_;
+
+public:
+
+    MemoryMappedDiskArray(const PathName& path, unsigned long size) :
+        DiskArray(),
+        map_(path, size)
+    {}
+};
+
+class SharedMemoryDiskArray : public DiskArray {
+
+    virtual void sync() { map_.sync(); }
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual iterator begin()  { return map_.begin(); }
+    virtual iterator end()    { return map_.end(); }
+
+    virtual const_iterator begin() const   { return map_.begin(); }
+    virtual const_iterator end()   const   { return map_.end(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual ClusterDisk& operator[](unsigned long n) { return map_[n]; }
+
+    SharedMemArray<ClusterDisk> map_;
+
+public:
+
+    SharedMemoryDiskArray(const PathName& path, const std::string& name, unsigned long size) :
+        DiskArray(),
+        map_(path, name, size)
+    {}
+};
+
+static DiskArray* clusterDisks = 0;
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void diskarray_init()
+{
+    LocalPathName path("~/etc/cluster/disks"); // Avoid recursion...
+    size_t disksArraySize  = Resource<size_t>("disksArraySize", 10240);
+
+    std::string diskArrayType = Resource<std::string>("disksArrayType","MemoryMapped");
+
+    if(diskArrayType == "MemoryMapped")
+        clusterDisks = new MemoryMappedDiskArray(path, disksArraySize);
+    else if(diskArrayType == "SharedMemory")
+        clusterDisks = new SharedMemoryDiskArray(path, "/etc-cluster-disks", disksArraySize);
+    else {
+        std::ostringstream oss;
+        oss << "Invalid diskArrayType : " << diskArrayType << ", valid types are 'MemoryMapped' and 'SharedMemory'" << std::endl;
+        throw eckit::BadParameter(oss.str(), Here());
+    }
+}
+
+
+void ClusterDisks::reset()
+{
+    pthread_once(&once, diskarray_init);
+	AutoLock<DiskArray> lock(*clusterDisks);
+
+	for(DiskArray::iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+		(*k).active(false);
+}
+
+void ClusterDisks::cleanup()
+{
+    /*
+    pthread_once(&once, diskarray_init);
+    AutoLock<DiskArray> lock(*clusterDisks);
+
+	for(DiskArray::iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+        if((*k).active() && (*k).offline())
+        {
+            Log::info() << "Forget " << (*k) << std::endl;
+            (*k).active(false);
+        }
+    */
+    reset();
+}
+
+void ClusterDisks::forget(const NodeInfo& info)
+{
+	if(info.name() == "marsfs")
+	{
+		time_t now = ::time(0);
+        pthread_once(&once, diskarray_init);
+        AutoLock<DiskArray> lock(*clusterDisks);
+
+		for(DiskArray::iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+		{
+			if(info.node() == (*k).node())
+			{
+				(*k).active(false);
+			}
+
+			(*k).lastSeen(now);
+		}
+	}
+}
+
+void ClusterDisks::offLine(const NodeInfo& info)
+{
+	if(info.name() == "marsfs")
+	{
+		time_t now = ::time(0);
+        pthread_once(&once, diskarray_init);
+        AutoLock<DiskArray> lock(*clusterDisks);
+
+		//cout << "=========== ClusterDisks::forget "  << info << std::endl;
+
+		for(DiskArray::iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+		{
+			if(info.node() == (*k).node())
+			{
+				(*k).offLine(true);
+			}
+			(*k).lastSeen(now);
+		}
+	}
+}
+
+void ClusterDisks::update(const std::string& node, const std::string& type, const std::vector<std::string>& disks)
+{
+
+    pthread_once(&once, diskarray_init);
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+
+	for(DiskArray::iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if(type == (*k).type() && node == (*k).node())
+		{
+			(*k).active(false);
+		}
+	}
+
+	for(std::vector<std::string>::const_iterator j = disks.begin(); j != disks.end(); ++j)
+	{
+		ClusterDisk c(node, type, *j);
+        DiskArray::iterator k = std::lower_bound(clusterDisks->begin(), clusterDisks->end(), c);
+		if(k != clusterDisks->end() && !(c < *k))
+		{
+			// Log::info() << "=========== Update " << node << "-" << type << " " << *j << std::endl;
+			// TODO check for change of node or type
+			*k = c;
+
+		}
+		else
+		{
+			ASSERT(!(*clusterDisks)[0].active());
+			//	Log::info() << "================ New " << node << "-" << type << " " << * j << std::endl;
+			(*clusterDisks)[0] = c;
+			std::sort(clusterDisks->begin(), clusterDisks->end());
+		}
+	}
+
+}
+
+void ClusterDisks::list(std::ostream& out)
+{
+    pthread_once(&once, diskarray_init);
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+	for(DiskArray::const_iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if((*k).active())
+			out << *k << std::endl;
+	}
+}
+
+void ClusterDisks::json(JSON& j)
+{
+    pthread_once(&once, diskarray_init);
+
+	j.startList();
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+	for(DiskArray::const_iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if((*k).active())
+		{
+			(*k).json(j);
+		}
+	}
+
+	j.endList();
+
+}
+
+time_t ClusterDisks::lastModified(const std::string& type)
+{
+
+    pthread_once(&once, diskarray_init);
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+
+	time_t last = 0;
+
+	for(DiskArray::const_iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if((*k).active() && ((*k).type() == type))
+            last = std::max(last, (*k).lastSeen());
+	}
+
+	return last;
+}
+
+void ClusterDisks::load(const std::string& type, std::vector<std::string>& disks)
+{
+
+    pthread_once(&once, diskarray_init);
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+	for(DiskArray::const_iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if((*k).active() && ((*k).type() == type))
+		{
+			disks.push_back(std::string("marsfs://") + (*k).node() + (*k).path());
+		}
+	}
+
+}
+
+std::string ClusterDisks::node(const std::string& path)
+{
+
+    pthread_once(&once, diskarray_init);
+
+	DiskArray::const_iterator j = clusterDisks->end();
+
+	AutoLock<DiskArray> lock(*clusterDisks);
+	for(DiskArray::const_iterator k = clusterDisks->begin(); k != clusterDisks->end(); ++k)
+	{
+		if((*k).active() && (path.find((*k).path()) == 0))
+		{
+			if(j != clusterDisks->end())
+			{
+                std::ostringstream os;
+				os << "Two nodes found for [" << path << "] "
+                   << "marsfs://" << (*j).node() << "/" << (*j).path() << "and "
+                   << "marsfs://" << (*k).node() << "/" << (*k).path();
+
+                throw SeriousBug(os.str());
+			}
+			j = k;
+		}
+	}
+
+
+
+
+
+	if(j == clusterDisks->end())
+	{
+        // Look for local names
+       
+        // This is ineficent, but is should be called very rarely
+
+        if(LocalPathName(path).exists())
+            return NodeInfo::thisNode().node();
+
+        
+        LocalPathName df("~/etc/disks/df");
+        std::ifstream in(df.localPath());
+        char line[1024];
+        while(in.getline(line,sizeof(line)))
+        {
+            if(line[0] != 0  || line[0] != '#')
+            {
+                Tokenizer tokenize(", \t");
+                std::vector<std::string> tokens;
+                tokenize(line, tokens);
+                if(tokens.size() == 2)
+                {
+                    const FileSpace &fs = FileSpace::lookUp(tokens[0]);
+                    const std::vector<PathName>& v = fs.fileSystems();
+                    for(size_t j = 0; j<v.size(); ++j)
+                    {
+                        if(path.find(v[j].asString()) == 0)
+                        {
+                            Log::info() << "ClusterDisks::node [" << path << "] is on " << v[j] << std::endl;
+                            return "local";
+                        }
+                    }
+                }
+            }
+        }
+
+    
+        std::ostringstream os;
+        os << "No node found for [" << path << "]";
+        throw SeriousBug(os.str());
+	}
+
+	return (*j).node();
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/cluster/ClusterDisks.h b/eckit/src/eckit/io/cluster/ClusterDisks.h
new file mode 100644
index 0000000..cc5b629
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterDisks.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @date   Jun 2011
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_ClusterDisks_h
+#define eckit_ClusterDisks_h
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/parser/JSON.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class NodeInfo;
+class JSON;
+
+class ClusterDisks {
+public:
+
+
+
+	static void reset();
+	static void cleanup();
+
+	static void offLine(const NodeInfo&);
+	static void forget(const NodeInfo&);
+	static void update(const std::string&,const std::string&, const std::vector<std::string>&);
+
+    static void list(std::ostream& out);
+    static void json(JSON& out);
+
+
+    static time_t lastModified(const std::string&);
+    static void   load(const std::string&, std::vector<std::string>&);
+    static std::string node(const std::string& path);
+
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/cluster/ClusterNode.cc b/eckit/src/eckit/io/cluster/ClusterNode.cc
new file mode 100644
index 0000000..599bd8e
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterNode.cc
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/io/cluster/ClusterNode.h"
+#include "eckit/io/cluster/ClusterNodes.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/net/Port.h"
+#include "eckit/config/Resource.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPStream.h"
+#include "eckit/net/TCPStream.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ClusterHeartBeat : public Thread {
+    ClusterNode& owner_;
+    virtual void run();
+public:
+    ClusterHeartBeat(ClusterNode& owner):
+        owner_(owner) {}
+};
+
+void ClusterHeartBeat::run()
+{
+    Monitor::instance().name("heartbeat");
+    std::string host   = Resource<std::string>("clusterHost","localhost");
+    int port = Port("cluster", 9555);
+    std::string reply;
+    NodeInfo remote;
+
+	for(;;)
+	{
+        TCPClient client;
+
+        try {
+            Log::status() <<"Connecting to " << host << ":" << port << std::endl;
+            TCPStream s(client.connect(host, port));
+            Log::status() <<"Connected to " << host << ":" << port << std::endl;
+
+            NodeInfo::thisNode().port(owner_.port());
+
+            remote = NodeInfo::sendLogin(s);
+
+            owner_.initialise(s);
+
+            //s << owner_.port();
+
+            char x[] = ".:";
+            int n = 0;
+
+            for(;;) {
+                Monitor::instance().state(x[n]); n = 1 - n;
+                s << "heartbeat";
+                s >> reply;
+
+
+
+                if(reply == "sync") {
+                    ClusterNodes::receive(s);
+                }
+
+                if(reply == "exit") {
+                    ::exit(0);
+                }
+
+                ::sleep(20);
+                owner_.refresh(s);
+            }
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+        }
+    }
+}
+
+
+
+ClusterNode::ClusterNode()
+{
+}
+
+ClusterNode::~ClusterNode()
+{
+}
+
+
+void ClusterNode::heartbeat()
+{
+    ThreadControler t(new ClusterHeartBeat(*this));
+    t.start();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/cluster/ClusterNode.h b/eckit/src/eckit/io/cluster/ClusterNode.h
new file mode 100644
index 0000000..b03692e
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterNode.h
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClusterNode.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_ClusterNode_h
+#define eckit_ClusterNode_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+class NodeInfo;
+
+class ClusterNode {
+public:
+
+// -- Contructors
+
+	ClusterNode();
+
+// -- Destructor
+
+	virtual ~ClusterNode();
+
+// -- Methods
+
+	void heartbeat();
+
+// -- For cluster MARS
+
+	virtual int port() const = 0;
+	virtual void initialise(Stream&)  = 0;
+	virtual void refresh(Stream&)  = 0;
+
+// -- Overridden methods
+
+
+// -- Class methods
+
+	//static Stream& connector(const std::string& name, const std::string& node);
+
+
+//	static bool         exited()    { return instance_ != 0; }
+
+protected:
+
+
+// -- Overridden methods
+
+	// From Configurable
+
+// -- Class methods
+
+private:
+
+// No copy allowed
+
+	ClusterNode(const ClusterNode&);
+	ClusterNode& operator=(const ClusterNode&);
+
+// -- Members
+
+
+// -- Overridden methods
+
+	// From Configurable
+
+
+// -- Class members
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/cluster/ClusterNodes.cc b/eckit/src/eckit/io/cluster/ClusterNodes.cc
new file mode 100644
index 0000000..a82115c
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterNodes.cc
@@ -0,0 +1,469 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClusterNodes.cc
+// Baudouin Raoult - (c) ECMWF Jul 11
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/io/cluster/ClusterNodes.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/container/MappedArray.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ClusterNodeEntry {
+	bool active_;
+	time_t lastSeen_;
+	bool offLine_;
+	char node_[256];
+	char type_[256];
+	char host_[256];
+	int port_;
+
+public:
+	ClusterNodeEntry(const std::string& node, const std::string& type, const std::string& host, int port) :
+		active_(true), lastSeen_(::time(0)), offLine_(false), port_(port)
+	{
+		zero(node_);
+		strncpy(node_, node.c_str(), sizeof(node_) - 1);
+		zero(type_);
+		strncpy(type_, type.c_str(), sizeof(type_) - 1);
+		zero(host_);
+		strncpy(host_, host.c_str(), sizeof(host_) - 1);
+	}
+
+	bool operator<(const ClusterNodeEntry& other) const
+	{
+		if (strcmp(node_, other.node_) < 0)
+			return true;
+		if (strcmp(type_, other.type_) < 0)
+			return true;
+		return false;
+	}
+
+	void send(Stream& s) const
+	{
+		unsigned long long t = lastSeen_;
+		s << t;
+		s << offLine_;
+		s << node_;
+		s << type_;
+		s << host_;
+		s << port_;
+	}
+
+	void json(JSON& s) const
+	{
+        s.startObject();
+		s << "lastSeen"  << lastSeen_ ;
+		s << "offLine"   << offLine_ ;
+		s << "available" << available();
+		s << "node"      << node_;
+		s << "type"      << type_;
+		s << "host"      << host_;
+		s << "port"      << port_;
+        s.endObject();
+	}
+
+	void receive(Stream& s)
+	{
+		unsigned long long t;
+		std::string x;
+
+		s >> t;
+		lastSeen_ = t;
+
+		s >> offLine_;
+
+		s >> x;
+		zero(node_);
+		strncpy(node_, x.c_str(), sizeof(node_) - 1);
+
+		s >> x;
+		zero(type_);
+		strncpy(type_, x.c_str(), sizeof(type_) - 1);
+
+		s >> x;
+		zero(host_);
+		strncpy(host_, x.c_str(), sizeof(host_) - 1);
+
+		s >> port_;
+
+		active_ = true;
+	}
+
+	bool available() const
+	{
+        static long maxNodeLastSeen = Resource<long>("maxNodeLastSeen", 60);
+		return ((::time(0) - lastSeen_) <= maxNodeLastSeen) && !offLine_;
+	}
+
+	void active(bool on)
+	{
+
+		active_ = on;
+	}
+	void offLine(bool on)
+	{
+		offLine_ = on;
+	}
+
+	bool active() const
+	{
+		return active_;
+	}
+
+	bool offLine() const
+	{
+		return offLine_;
+	}
+
+	time_t lastSeen() const
+	{
+		return lastSeen_;
+	}
+
+	void lastSeen(time_t n)
+	{
+		lastSeen_ = n;
+	}
+
+	const char* node() const
+	{
+		return node_;
+	}
+
+	const char* type() const
+	{
+		return type_;
+	}
+
+	const char* host() const
+	{
+		return host_;
+	}
+
+	void host(const std::string& h)
+	{
+		zero(host_);
+		strncpy(host_, h.c_str(), sizeof(host_) - 1);
+	}
+
+	void port(int p)
+	{
+		port_ = p;
+	}
+
+	int port() const
+	{
+		return port_;
+	}
+
+	friend std::ostream& operator<<(std::ostream&, const ClusterNodeEntry&);
+
+};
+std::ostream& operator<<(std::ostream& s, const ClusterNodeEntry& d)
+{
+	s << "ClusterNodeEntry[" << d.node_ << "," << d.type_ << "," << d.host_ << ":" << d.port_ << "," << (::time(0) - d.lastSeen_)
+        << "," << (d.available() ? "available" : "not-available")
+        << "," << (d.offLine_ ? "off" : "on") << "-line" 
+        << "]";
+	return s;
+}
+
+inline unsigned long version(ClusterNodeEntry*)
+{
+	return 1;
+}
+
+typedef MappedArray<ClusterNodeEntry> NodeArray;
+static NodeArray* nodeArray = 0;
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void init()
+{
+	nodeArray = new NodeArray("~/etc/cluster/nodes", 1024);
+}
+
+void ClusterNodes::reset()
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+        (*k).offLine(true);
+}
+
+void ClusterNodes::cleanup()
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+        if((*k).active() && !(*k).available())
+        {
+            Log::info() << "Forget " << (*k) << std::endl;
+            (*k).active(false);
+        }
+}
+
+
+void ClusterNodes::forget(const NodeInfo& info)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if (info.node() == (*k).node())
+		{
+			(*k).active(false);
+		}
+	}
+}
+
+void ClusterNodes::refresh(const NodeInfo& info)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	time_t now = ::time(0);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+        if((*k).active()) 
+        {
+		if (info.node() == (*k).node() && info.name() == (*k).type())
+		{
+			(*k).lastSeen(now);
+			(*k).host(info.host());
+			(*k).port(info.port());
+			(*k).offLine(false);
+			return;
+		}
+        }
+	}
+
+	std::sort(nodeArray->begin(), nodeArray->end());
+	ClusterNodeEntry c(info.node(), info.name(), info.host(), info.port());
+	ASSERT(!(*nodeArray)[0].active());
+	(*nodeArray)[0] = c;
+	std::sort(nodeArray->begin(), nodeArray->end());
+}
+
+NodeInfo ClusterNodes::lookUp(const std::string& type, const std::string& node)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && type == (*k).type() && node == (*k).node())
+		{
+			NodeInfo info;
+			info.name((*k).type());
+			info.node((*k).node());
+			info.host((*k).host());
+			info.port((*k).port());
+			info.active(!(*k).offLine());
+			return info;
+		}
+	}
+
+	throw SeriousBug(std::string("Cannot find info for ") + type + "@" + node);
+}
+
+NodeInfo ClusterNodes::any(const std::string& type)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && (*k).available() && type == (*k).type())
+		{
+			NodeInfo info;
+			info.name((*k).type());
+			info.node((*k).node());
+			info.host((*k).host());
+			info.port((*k).port());
+			info.active(!(*k).offLine());
+			return info;
+		}
+	}
+
+	throw Retry(std::string("Cannot find any node for ") + type);
+}
+
+bool ClusterNodes::available(const std::string& type, const std::string& node)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && type == (*k).type() && node == (*k).node())
+			return (*k).available();
+	}
+
+	return false;
+}
+
+void ClusterNodes::offLine(const NodeInfo& info)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	const std::string& node = info.node();
+	const std::string& type = info.name();
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && type == (*k).type() && node == (*k).node())
+			(*k).offLine(true);
+	}
+}
+
+void ClusterNodes::offLine(const std::string& host,int port)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && host == (*k).host() && port == (*k).port())
+			(*k).offLine(true);
+	}
+}
+
+void ClusterNodes::onLine(const std::string& host,int port)
+{
+	pthread_once(&once, init);
+	AutoLock<NodeArray> lock(*nodeArray);
+
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active() && host == (*k).host() && port == (*k).port())
+			(*k).offLine(false);
+	}
+}
+
+void ClusterNodes::list(std::ostream& out)
+{
+	pthread_once(&once, init);
+
+	AutoLock<NodeArray> lock(*nodeArray);
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active())
+			out << *k << std::endl;
+	}
+}
+
+std::vector<NodeInfo> ClusterNodes::all()
+{
+	pthread_once(&once, init);
+    std::vector<NodeInfo> result;
+
+	AutoLock<NodeArray> lock(*nodeArray);
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active())
+        {
+			NodeInfo info;
+			info.name((*k).type());
+			info.node((*k).node());
+			info.host((*k).host());
+			info.port((*k).port());
+			info.active(!(*k).offLine());
+            result.push_back(info);
+		}
+	}
+
+    return result;
+}
+
+void ClusterNodes::json(JSON& j)
+{
+	pthread_once(&once, init);
+
+
+    j.startList();
+
+	AutoLock<NodeArray> lock(*nodeArray);
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active())
+		{
+			(*k).json(j);
+		}
+	}
+
+    j.endList();
+
+}
+
+void ClusterNodes::send(Stream& s)
+{
+	pthread_once(&once, init);
+
+	AutoLock<NodeArray> lock(*nodeArray);
+	for (NodeArray::const_iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+	{
+		if ((*k).active())
+		{
+			s << bool(true);
+			(*k).send(s);
+		}
+	}
+
+	s << bool(false);
+}
+
+void ClusterNodes::receive(Stream& s)
+{
+	pthread_once(&once, init);
+
+	AutoLock<NodeArray> lock(*nodeArray);
+	for (NodeArray::iterator k = nodeArray->begin(); k != nodeArray->end(); ++k)
+		(*k).active(false);
+
+	bool more;
+	NodeArray::iterator k = nodeArray->begin();
+
+	for (;;)
+	{
+
+		s >> more;
+
+		if (!more)
+			break;
+
+		ASSERT( k != nodeArray->end() );
+		(*k).receive(s);
+
+		++k;
+	}
+
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/cluster/ClusterNodes.h b/eckit/src/eckit/io/cluster/ClusterNodes.h
new file mode 100644
index 0000000..5c24acc
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/ClusterNodes.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClusterNodes.h
+// Baudouin Raoult - (c) ECMWF Jul 11
+
+#ifndef eckit_ClusterNodes_h
+#define eckit_ClusterNodes_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/cluster/NodeInfo.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+class JSON;
+
+class ClusterNodes {
+public:
+
+
+// -- Class methods
+	// None
+
+	static void reset();
+	static void cleanup();
+	static void forget(const NodeInfo&);
+	static void offLine(const NodeInfo&);
+	static void refresh(const NodeInfo&);
+    static void list(std::ostream& out);
+    static void json(JSON& out);
+
+    static void send(Stream& s);
+    static void receive(Stream& s);
+
+    static NodeInfo lookUp(const std::string&, const std::string&);
+    static NodeInfo any(const std::string&);
+
+    static bool available(const std::string&, const std::string&);
+
+	static void offLine(const std::string&, int);
+	static void onLine(const std::string&, int);
+
+    static std::vector<NodeInfo> all();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/io/cluster/NodeInfo.cc b/eckit/src/eckit/io/cluster/NodeInfo.cc
new file mode 100644
index 0000000..76039d1
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/NodeInfo.cc
@@ -0,0 +1,182 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <pwd.h>
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/thread/Once.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/config/Resource.h"
+#include "eckit/thread/ThreadSingleton.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static Once<Mutex> local_mutex;
+
+NodeInfo::NodeInfo() :
+	port_(0), active_(false), id_(0), task_(-1)
+{
+}
+
+NodeInfo& NodeInfo::init()
+{
+    AutoLock<Mutex> lock(local_mutex);
+	if(!name_.length())
+	{
+        static std::string myNode = Resource<std::string> ("node", "<missing-node-name>");
+        static std::string myHost = Resource<std::string> ("host", "");
+        static std::string myUser;
+
+		name_ = Main::instance().name();
+
+		host_ = myHost;
+		if(host_.length() == 0)
+		{
+			char host[1024];
+			SYSCALL(gethostname(host,sizeof(host)-1));
+			host_ = host;
+			host_ = host_.substr(0, host_.find("."));
+            myHost = host;
+		}
+
+		node_ = myNode;
+
+		if(node_.length() == 0)
+        {
+			node_  = host_;
+            myNode = node_;
+        }
+
+        user_ = myUser;
+
+        if(user_.length() == 0) {
+
+            user_ = "<nobody>";
+            char buf[4096];
+            struct passwd pwbuf;
+            struct passwd *pwbufp = 0;
+            SYSCALL(getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pwbufp));
+            if(pwbufp)
+            {
+                user_ = pwbuf.pw_name;
+            }
+
+            myUser = user_;
+        }
+
+		task_ = Monitor::instance().self();
+	}
+	return *this;
+}
+
+NodeInfo::~NodeInfo()
+{
+}
+
+static ThreadSingleton<NodeInfo> n;
+
+NodeInfo& NodeInfo::thisNode()
+{
+	return n.instance().init();
+}
+
+void operator<<(Stream& s, const NodeInfo& info)
+{
+	s << info.user();
+	s << info.name();
+	s << info.node();
+	s << info.host();
+	s << info.port();
+	s << info.id();
+	s << info.task();
+}
+
+void operator>>(Stream& s, NodeInfo& info)
+{
+	std::string p;
+	int l;
+	s >> p;
+	info.user(p);
+	s >> p;
+	info.name(p);
+	s >> p;
+	info.node(p);
+	s >> p;
+	info.host(p);
+	s >> l;
+	info.port(l);
+
+	TaskID id;
+	s >> id;
+	info.id(id);
+
+	long task;
+	s >> task;
+	info.task(task);
+}
+
+void NodeInfo::print(std::ostream& s) const
+{
+    s << "[" << name_ << ":" << std::setfill('0') << std::setw(3) << task_ << std::setfill(' ') << "," << node_ << "@" << host_ << ":" << port_ << "," << user_ << "]";
+}
+
+NodeInfo NodeInfo::acceptLogin(Stream& s)
+{
+	NodeInfo remote;
+	NodeInfo& here = thisNode();
+
+	s >> remote;
+	if(here.user() == remote.user())
+	{
+		s << here;
+	}
+	else
+	{
+        std::ostringstream os;
+        os << "User mismatch: " << here << " " << remote;
+        s << Exception(os.str());
+	}
+
+	Log::info() << "Connection established " << here << " <=> " << remote << std::endl;
+
+	return remote;
+}
+
+NodeInfo NodeInfo::sendLogin(Stream& s)
+{
+	NodeInfo remote;
+	NodeInfo& here = thisNode();
+	s << here;
+	s >> remote;
+
+	if(here.user() != remote.user())
+	{
+        std::ostringstream os;
+        os << "User mismatch: " << here << " " << remote;
+        s << Exception(os.str());
+	}
+
+	Log::info() << "Connection established " << here << " <=> " << remote << std::endl;
+
+	return remote;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/io/cluster/NodeInfo.h b/eckit/src/eckit/io/cluster/NodeInfo.h
new file mode 100644
index 0000000..14e228d
--- /dev/null
+++ b/eckit/src/eckit/io/cluster/NodeInfo.h
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NodeInfo.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_NodeInfo_h
+#define eckit_NodeInfo_h
+
+#include "eckit/types/Types.h"
+#include "eckit/runtime/TaskID.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class NodeInfo {
+public:
+
+// -- Contructors
+
+	NodeInfo();
+
+// -- Destructor
+
+	~NodeInfo();
+
+// -- Methods
+//
+
+	void  port(int p)    { port_ = p; }
+	int   port() const   { return port_; }
+
+	void  active(bool a)  { active_ = a; }
+    bool  active() const   { return active_; }
+
+    void  host(const std::string& h)    { host_ = h; }
+    const std::string&   host() const   { return host_; }
+
+    void  name(const std::string& h)    { name_ = h; }
+    const std::string&    name() const  { return name_; }
+
+    void  user(const std::string& h)    { user_ = h;}
+    const std::string&    user() const  { return user_; }
+
+    void  node(const std::string& h)    { node_ = h; }
+    const std::string&    node() const  { return node_; }
+
+
+	void  id(TaskID p)    { id_ = p; }
+	TaskID   id() const   { return id_; }
+
+	void  task(long p)    { task_ = p; }
+	long   task() const   { return task_; }
+
+
+	NodeInfo& init();
+
+    static NodeInfo& thisNode();
+    static NodeInfo acceptLogin(Stream&);
+    static NodeInfo sendLogin(Stream&);
+
+
+
+private:
+
+// No copy allowed
+
+	//NodeInfo(const NodeInfo&);
+	//NodeInfo& operator=(const NodeInfo&);
+
+    std::string name_;
+    std::string node_;
+    std::string user_;
+    std::string host_;
+	int    port_;
+    bool   active_;
+	TaskID id_;
+	long   task_;
+
+// -- Methods
+
+	void print(std::ostream&) const; 
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const NodeInfo& p)
+		{ p.print(s); return s; }
+
+	friend void operator<<(Stream&,const NodeInfo&);
+	friend void operator>>(Stream&,NodeInfo&);
+};
+
+// Used by MappedArray
+
+inline unsigned long version(NodeInfo*) { return 1; }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/CMakeLists.txt b/eckit/src/eckit/linalg/CMakeLists.txt
new file mode 100644
index 0000000..6b3ef67
--- /dev/null
+++ b/eckit/src/eckit/linalg/CMakeLists.txt
@@ -0,0 +1,41 @@
+list( APPEND eckit_la_srcs
+types.h
+LinearAlgebra.cc
+LinearAlgebra.h
+LinearAlgebraArmadillo.cc
+LinearAlgebraArmadillo.h
+LinearAlgebraCUDA.cc
+LinearAlgebraCUDA.h
+LinearAlgebraEigen.cc
+LinearAlgebraEigen.h
+LinearAlgebraGeneric.cc
+LinearAlgebraGeneric.h
+LinearAlgebraMKL.cc
+LinearAlgebraMKL.h
+LinearAlgebraViennaCL.cc
+LinearAlgebraViennaCL.h
+Matrix.cc
+Matrix.h
+SparseMatrix.cc
+SparseMatrix.h
+Triplet.h
+Vector.cc
+Vector.h
+)
+
+ecbuild_add_library( TARGET             eckit_linalg
+                     INSTALL_HEADERS    ALL
+                     HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/eckit/linalg
+                     SOURCES            ${eckit_la_srcs}
+                     INCLUDES           "${ARMADILLO_INCLUDE_DIRS}"
+                                        "${CUDA_INCLUDE_DIRS}"
+                                        "${MKL_INCLUDE_DIRS}"
+                                        "${VIENNACL_INCLUDE_DIRS}"
+                     LIBS               eckit
+                                        "${ARMADILLO_LIBRARIES}"
+                                        "${CUDA_LIBRARIES}" "${CUDA_cusparse_LIBRARY}"
+                                        "${MKL_LIBRARIES}" )
+
+if (CUDA_FOUND)
+cuda_add_cublas_to_target( eckit_linalg )
+endif()
diff --git a/eckit/src/eckit/linalg/LinearAlgebra.cc b/eckit/src/eckit/linalg/LinearAlgebra.cc
new file mode 100644
index 0000000..b301900
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebra.cc
@@ -0,0 +1,155 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit_config.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace {
+
+#ifdef ECKIT_HAVE_EIGEN
+    static const char* defaultBackend = "eigen";
+#else
+    static const char* defaultBackend = "generic";
+#endif
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+class BackendRegistry {
+
+public:
+    typedef std::map<std::string, const LinearAlgebra *> Map;
+
+    BackendRegistry() : default_(defaultBackend) {
+        char* envBackend = ::getenv("ECKIT_LINEAR_ALGEBRA_BACKEND");
+        if(envBackend) {
+            default_ = envBackend;
+        }
+        // std::cout << "Default Linear Algebra backend: " << default_ << std::endl;
+    }
+
+    void backend(const std::string& name);
+
+    const LinearAlgebra& find() const;
+    const LinearAlgebra& find(const std::string& name) const;
+
+    void list(std::ostream &out) const;
+
+    void add(const std::string& name, LinearAlgebra* backend);
+
+private: // members
+
+    Map map_;
+    std::string default_;
+    mutable Mutex mutex_;
+};
+
+static BackendRegistry* backends = 0;
+
+static void init() {
+    backends = new BackendRegistry();
+}
+
+void BackendRegistry::backend(const std::string& name) {
+    AutoLock<Mutex> lock(mutex_);
+    if (map_.find(name) == map_.end()) {
+        throw BadParameter("Invalid backend " + name, Here());
+    }
+    default_ = name;
+}
+
+const LinearAlgebra&BackendRegistry::find() const {
+    return find(default_);
+}
+
+const LinearAlgebra&BackendRegistry::find(const std::string& name) const {
+    AutoLock<Mutex> lock(mutex_);
+
+    BackendRegistry::Map::const_iterator it = map_.find(name);
+    if (it == map_.end()) {
+        eckit::Log::error() << "No Linear algebra backend named [" << name << "]" << std::endl;
+        eckit::Log::error() << "Linear algebra backends are:" << std::endl;
+        for (it = map_.begin() ; it != map_.end() ; ++it)
+            eckit::Log::error() << "   " << (*it).first << std::endl;
+        throw BadParameter("Linear algebra backend " + name + " not available.", Here());
+    }
+    Log::debug() << "Using LinearAlgebra backend " << it->first << std::endl;
+    return *(it->second);
+}
+
+void BackendRegistry::list(std::ostream& out) const {
+    AutoLock<Mutex> lock(mutex_);
+    const char *sep = "";
+    for (Map::const_iterator it = map_.begin() ; it != map_.end() ; ++it) {
+        out << sep << it->first;
+        sep = ", ";
+    }
+}
+
+void BackendRegistry::add(const std::string& name, LinearAlgebra* backend) {
+    AutoLock<Mutex> lock(mutex_);
+    ASSERT(map_.find(name) == map_.end());
+    map_[name] = backend;
+}
+
+}  // anonymous namespace
+
+//----------------------------------------------------------------------------------------------------------------------
+
+const LinearAlgebra& LinearAlgebra::backend() {
+    pthread_once(&once, init);
+    return backends->find();
+}
+
+const LinearAlgebra& LinearAlgebra::getBackend(const std::string& name) {
+    pthread_once(&once, init);
+    return backends->find(name);
+}
+
+void LinearAlgebra::backend(const std::string &name) {
+    pthread_once(&once, init);
+    backends->backend(name);
+    Log::info() << "Setting LinearAlgebra backend to " << name << std::endl;
+}
+
+void LinearAlgebra::list(std::ostream &out) {
+    pthread_once(&once, init);
+    backends->list(out);
+}
+
+void LinearAlgebra::dsptd(const Vector &, const SparseMatrix &, const Vector &, SparseMatrix &) const
+{
+    NOTIMP;
+}
+
+LinearAlgebra::LinearAlgebra(const std::string &name):
+    name_(name) {
+    pthread_once(&once, init);
+    backends->add(name, this);
+}
+
+const std::string& LinearAlgebra::name() const {
+    return name_;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
diff --git a/eckit/src/eckit/linalg/LinearAlgebra.h b/eckit/src/eckit/linalg/LinearAlgebra.h
new file mode 100644
index 0000000..6240e1f
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebra.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebra.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebra_h
+#define eckit_la_LinearAlgebra_h
+
+#include <string>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/linalg/types.h"
+
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebra : private NonCopyable {
+
+public:
+
+    const std::string& name() const;
+
+    // static methods
+
+    /// Get the currently selected backend
+    static const LinearAlgebra& backend();
+
+    /// Select the given backend as the default
+    static void backend(const std::string& name);
+
+    /// List all available backends
+    static void list(std::ostream &);
+
+public:  // virtual methods
+
+    /// Compute the inner product of vectors x and y
+    virtual Scalar dot(const Vector&, const Vector&) const = 0;
+
+    /// Compute the product of a dense matrix A and vector x. The caller is
+    /// responsible for allocating a properly sized output vector y.
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const = 0;
+
+    /// Compute the product of dense matrices A and B. The caller is
+    /// responsible for allocating a properly sized output matrix C.
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const = 0;
+
+    /// Compute the product of a sparse matrix A and vector x. The caller is
+    /// responsible for allocating a properly sized output vector y.
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const = 0;
+
+    /// Compute the product of sparse matrix A and dense matrix B. The caller is
+    /// responsible for allocating a properly sized output matrix C.
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const = 0;
+
+    /// Compute the product x A' y with x and y diagonal matrices stored as
+    /// vectors and A a sparse matrix. The caller does NOT need to initialise
+    /// the sparse output matrix C
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+protected:
+
+    LinearAlgebra(const std::string& name);
+
+    /// Get a backend by name
+    static const LinearAlgebra& getBackend(const std::string& name);
+
+private:
+
+    std::string name_;
+
+    virtual void print(std::ostream&) const = 0;
+
+    // -- Friends
+
+    friend std::ostream &operator<<(std::ostream &s, const LinearAlgebra &p) {
+        p.print(s);
+        return s;
+    }
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraArmadillo.cc b/eckit/src/eckit/linalg/LinearAlgebraArmadillo.cc
new file mode 100644
index 0000000..8ac0c12
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraArmadillo.cc
@@ -0,0 +1,105 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+//-----------------------------------------------------------------------------
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_ARMADILLO
+
+#include "eckit/linalg/LinearAlgebraArmadillo.h"
+
+#include <armadillo>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/Vector.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+LinearAlgebraArmadillo::LinearAlgebraArmadillo() : LinearAlgebra("armadillo") {}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::print(std::ostream& out) const {
+    out << "LinearAlgebraArmadillo[]";
+}
+
+//-----------------------------------------------------------------------------
+
+Scalar LinearAlgebraArmadillo::dot(const Vector& x, const Vector& y) const {
+    ASSERT( x.size() == y.size() );
+    // Armadillo requires non-const pointers to the data for views without copy
+    arma::vec xi(const_cast<Scalar*>(x.data()), x.size(), /* copy_aux_mem= */ false);
+    arma::vec yi(const_cast<Scalar*>(y.data()), y.size(), /* copy_aux_mem= */ false);
+    return arma::dot(xi, yi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // Armadillo requires non-const pointers to the data for views without copy
+    arma::mat Ai(const_cast<Scalar*>(A.data()), A.rows(), A.cols(), /* copy_aux_mem= */ false);
+    arma::vec xi(const_cast<Scalar*>(x.data()), x.size(), /* copy_aux_mem= */ false);
+    arma::vec yi(y.data(), y.size(), /* copy_aux_mem= */ false);
+    yi = Ai*xi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // Armadillo requires non-const pointers to the data for views without copy
+    arma::mat Ai(const_cast<Scalar*>(A.data()), A.rows(), A.cols(), /* copy_aux_mem= */ false);
+    arma::mat Bi(const_cast<Scalar*>(B.data()), B.rows(), B.cols(), /* copy_aux_mem= */ false);
+    arma::mat Ci(C.data(), C.rows(), C.cols(), /* copy_aux_mem= */ false);
+    Ci = Ai*Bi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+    // FIXME: Armadillo stores matrices in CSC format and does not provide
+    // constructors from existing storage. A sparse matrix would have to be
+    // copied from CSR to CSC format, which is probably not worth the cost
+    LinearAlgebra::getBackend("generic").spmv(A, x, y);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+    // FIXME: Armadillo stores matrices in CSC format and does not provide
+    // constructors from existing storage. A sparse matrix would have to be
+    // copied from CSR to CSC format, which is probably not worth the cost
+    LinearAlgebra::getBackend("generic").spmm(A, B, C);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraArmadillo::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+    LinearAlgebra::getBackend("generic").dsptd(x, A, y, B);
+}
+
+//-----------------------------------------------------------------------------
+
+static LinearAlgebraArmadillo LinearAlgebraArmadillo;
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_ARMDILLO
diff --git a/eckit/src/eckit/linalg/LinearAlgebraArmadillo.h b/eckit/src/eckit/linalg/LinearAlgebraArmadillo.h
new file mode 100644
index 0000000..ca450e7
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraArmadillo.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraArmadillo.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebraArmadillo_h
+#define eckit_la_LinearAlgebraArmadillo_h
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_ARMADILLO
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraArmadillo : public LinearAlgebra {
+
+public:
+    LinearAlgebraArmadillo();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_ARMADILLO
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraCUDA.cc b/eckit/src/eckit/linalg/LinearAlgebraCUDA.cc
new file mode 100644
index 0000000..8847243
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraCUDA.cc
@@ -0,0 +1,318 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_CUDA
+
+#include "eckit/linalg/LinearAlgebraCUDA.h"
+
+#include <cuda_runtime.h>
+#include <cublas_v2.h>
+#include <cusparse.h>
+
+//-----------------------------------------------------------------------------
+
+#define CALL_CUDA(e) \
+{ cudaError_t error; \
+    if( ( error = e ) != cudaSuccess ) printf("%s failed with error code %d @ %s +%d\n", #e, error, __FILE__, __LINE__), exit(EXIT_FAILURE); \
+}
+
+#define CALL_CUBLAS(e) \
+{ cublasStatus_t error; \
+    if( ( error = e ) != CUBLAS_STATUS_SUCCESS ) printf("%s failed with error code %d @ %s +%d\n", #e, error, __FILE__, __LINE__), exit(EXIT_FAILURE); \
+}
+
+#define CALL_CUSPARSE(e) \
+{ cusparseStatus_t error; \
+    if( ( error = e ) != CUSPARSE_STATUS_SUCCESS ) printf("%s failed with error code %d @ %s +%d\n", #e, error, __FILE__, __LINE__), exit(EXIT_FAILURE); \
+}
+
+//-----------------------------------------------------------------------------
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+LinearAlgebraCUDA::LinearAlgebraCUDA() : LinearAlgebra("cuda") {}
+
+//-----------------------------------------------------------------------------
+
+Scalar LinearAlgebraCUDA::dot(const Vector& x, const Vector& y) const {
+    ASSERT( x.size() == y.size() );
+    const Size size = x.size()*sizeof(Scalar);
+    Scalar r;
+
+    Scalar* d_x; ///< device memory vector x
+    Scalar* d_y; ///< device memory vector y
+    cublasHandle_t handle;
+
+    CALL_CUDA( cudaMalloc((void**) &d_x, size) );
+    CALL_CUDA( cudaMalloc((void**) &d_y, size) );
+
+    CALL_CUBLAS( cublasCreate(&handle) );
+
+    CALL_CUDA( cudaMemcpy(d_x, x.data(), size, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_y, y.data(), size, cudaMemcpyHostToDevice) );
+
+    // cublasStatus_t cublasDdot (cublasHandle_t handle, int n,
+    //                            const double *x, int incx,
+    //                            const double *y, int incy,
+    //                            double *result)
+    CALL_CUBLAS( cublasDdot(handle, x.size(), d_x, 1, d_y, 1, &r) );
+
+    CALL_CUBLAS( cublasDestroy(handle) );
+
+    CALL_CUDA( cudaFree(d_x) );
+    CALL_CUDA( cudaFree(d_y) );
+
+    return r;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    const Size sizeA = A.rows()*A.cols()*sizeof(Scalar);
+    const Size sizex = A.cols()*sizeof(Scalar);
+    const Size sizey = A.rows()*sizeof(Scalar);
+
+    Scalar* d_A; ///< device memory matrix A
+    Scalar* d_x; ///< device memory vector x
+    Scalar* d_y; ///< device memory vector y
+    cublasHandle_t handle;
+
+    CALL_CUDA( cudaMalloc((void**) &d_A, sizeA) );
+    CALL_CUDA( cudaMalloc((void**) &d_x, sizex) );
+    CALL_CUDA( cudaMalloc((void**) &d_y, sizey) );
+
+    CALL_CUBLAS( cublasCreate(&handle) );
+
+    CALL_CUDA( cudaMemcpy(d_A, A.data(), sizeA, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_x, x.data(), sizex, cudaMemcpyHostToDevice) );
+
+    const Scalar alpha = 1.0;
+    const Scalar beta  = 0.0;
+    // cublasStatus_t cublasDgemv(cublasHandle_t handle, cublasOperation_t trans,
+    //                            int m, int n,
+    //                            const double *alpha, const double *A, int lda, const double *x, int incx,
+    //                            const double *beta, double *y, int incy)
+    CALL_CUBLAS( cublasDgemv(handle, CUBLAS_OP_N,
+                             A.rows(), A.cols(),
+                             &alpha, d_A, A.rows(), d_x, 1,
+                             &beta, d_y, 1) );
+
+    CALL_CUDA( cudaMemcpy(y.data(), d_y, sizey, cudaMemcpyDeviceToHost) );
+
+    CALL_CUBLAS( cublasDestroy(handle) );
+
+    CALL_CUDA( cudaFree(d_A) );
+    CALL_CUDA( cudaFree(d_x) );
+    CALL_CUDA( cudaFree(d_y) );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    const Size sizeA = A.rows()*A.cols()*sizeof(Scalar);
+    const Size sizeB = B.rows()*B.cols()*sizeof(Scalar);
+    const Size sizeC = A.rows()*B.cols()*sizeof(Scalar);
+
+    Scalar* d_A; ///< device memory matrix A
+    Scalar* d_B; ///< device memory matrix B
+    Scalar* d_C; ///< device memory matrix C
+    cublasHandle_t handle;
+
+    CALL_CUDA( cudaMalloc((void**) &d_A, sizeA) );
+    CALL_CUDA( cudaMalloc((void**) &d_B, sizeB) );
+    CALL_CUDA( cudaMalloc((void**) &d_C, sizeC) );
+
+    CALL_CUBLAS( cublasCreate(&handle) );
+
+    CALL_CUDA( cudaMemcpy(d_A, A.data(), sizeA, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_B, B.data(), sizeB, cudaMemcpyHostToDevice) );
+
+    const Scalar alpha = 1.0;
+    const Scalar beta  = 0.0;
+    // cublasStatus_t cublasDgemm(cublasHandle_t handle, cublasOperation_t transa, cublasOperation_t transb,
+    //                            int m, int n, int k,
+    //                            const double *alpha, const double *A, int lda, const double *B, int ldb,
+    //                            const double *beta, double *C, int ldc)
+    CALL_CUBLAS( cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
+                             A.rows(), B.cols(), A.cols(),
+                             &alpha, d_A, A.rows(), d_B, B.rows(),
+                             &beta, d_C, A.rows()) );
+
+    CALL_CUDA( cudaMemcpy(C.data(), d_C, sizeC, cudaMemcpyDeviceToHost) );
+
+    CALL_CUBLAS( cublasDestroy(handle) );
+
+    CALL_CUDA( cudaFree(d_A) );
+    CALL_CUDA( cudaFree(d_B) );
+    CALL_CUDA( cudaFree(d_C) );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    const Size sizeArowptr = (A.rows()+1)*sizeof(Index);
+    const Size sizeAcolidx = A.nonZeros()*sizeof(Index);
+    const Size sizeAvalues = A.nonZeros()*sizeof(Scalar);
+    const Size sizex = A.cols()*sizeof(Scalar);
+    const Size sizey = A.rows()*sizeof(Scalar);
+
+    Index* d_A_rowptr; ///< device memory matrix A row pointers
+    Index* d_A_colidx; ///< device memory matrix A col indices
+    Scalar* d_A_values; ///< device memory matrix A values
+    Scalar* d_x; ///< device memory vector x
+    Scalar* d_y; ///< device memory vector y
+    cusparseHandle_t handle;
+    cusparseMatDescr_t descr;
+
+    CALL_CUDA( cudaMalloc((void**) &d_A_rowptr, sizeArowptr) );
+    CALL_CUDA( cudaMalloc((void**) &d_A_colidx, sizeAcolidx) );
+    CALL_CUDA( cudaMalloc((void**) &d_A_values, sizeAvalues) );
+    CALL_CUDA( cudaMalloc((void**) &d_x, sizex) );
+    CALL_CUDA( cudaMalloc((void**) &d_y, sizey) );
+
+    CALL_CUSPARSE( cusparseCreate(&handle) );
+    CALL_CUSPARSE( cusparseCreateMatDescr(&descr) );
+    cusparseSetMatType(descr,CUSPARSE_MATRIX_TYPE_GENERAL);
+    cusparseSetMatIndexBase(descr,CUSPARSE_INDEX_BASE_ZERO);
+
+    CALL_CUDA( cudaMemcpy(d_A_rowptr, A.outer(), sizeArowptr, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_A_colidx, A.inner(), sizeAcolidx, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_A_values, A.data(),  sizeAvalues, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_x,        x.data(),  sizex,       cudaMemcpyHostToDevice) );
+
+    const Scalar alpha = 1.0;
+    const Scalar beta  = 0.0;
+    // cusparseStatus_t
+    // cusparseDcsrmv(cusparseHandle_t handle, cusparseOperation_t transA,
+    //                int m, int n, int nnz,
+    //                const double *alpha, const cusparseMatDescr_t descrA,
+    //                const double *csrValA, const int *csrRowPtrA, const int *csrColIndA,
+    //                const double *x, const double *beta, double *y)
+    CALL_CUSPARSE( cusparseDcsrmv(handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
+                                  A.rows(), A.cols(), A.nonZeros(),
+                                  &alpha, descr,
+                                  d_A_values, d_A_rowptr, d_A_colidx,
+                                  d_x, &beta, d_y) );
+
+    CALL_CUDA( cudaMemcpy(y.data(), d_y, sizey, cudaMemcpyDeviceToHost) );
+
+    CALL_CUSPARSE( cusparseDestroyMatDescr(descr) );
+    CALL_CUSPARSE( cusparseDestroy(handle) );
+
+    CALL_CUDA( cudaFree(d_A_rowptr) );
+    CALL_CUDA( cudaFree(d_A_colidx) );
+    CALL_CUDA( cudaFree(d_A_values) );
+    CALL_CUDA( cudaFree(d_x) );
+    CALL_CUDA( cudaFree(d_y) );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    const Size sizeArowptr = (A.rows()+1)*sizeof(Index);
+    const Size sizeAcolidx = A.nonZeros()*sizeof(Index);
+    const Size sizeAvalues = A.nonZeros()*sizeof(Scalar);
+    const Size sizeB = B.rows()*B.cols()*sizeof(Scalar);
+    const Size sizeC = A.rows()*B.cols()*sizeof(Scalar);
+
+    Index* d_A_rowptr; ///< device memory matrix A row pointers
+    Index* d_A_colidx; ///< device memory matrix A col indices
+    Scalar* d_A_values; ///< device memory matrix A values
+    Scalar* d_B; ///< device memory matrix B
+    Scalar* d_C; ///< device memory matrix C
+    cusparseHandle_t handle;
+    cusparseMatDescr_t descr;
+
+    CALL_CUDA( cudaMalloc((void**) &d_A_rowptr, sizeArowptr) );
+    CALL_CUDA( cudaMalloc((void**) &d_A_colidx, sizeAcolidx) );
+    CALL_CUDA( cudaMalloc((void**) &d_A_values, sizeAvalues) );
+    CALL_CUDA( cudaMalloc((void**) &d_B, sizeB) );
+    CALL_CUDA( cudaMalloc((void**) &d_C, sizeC) );
+
+    CALL_CUSPARSE( cusparseCreate(&handle) );
+    CALL_CUSPARSE( cusparseCreateMatDescr(&descr) );
+    cusparseSetMatType(descr,CUSPARSE_MATRIX_TYPE_GENERAL);
+    cusparseSetMatIndexBase(descr,CUSPARSE_INDEX_BASE_ZERO);
+
+    CALL_CUDA( cudaMemcpy(d_A_rowptr, A.outer(), sizeArowptr, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_A_colidx, A.inner(), sizeAcolidx, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_A_values, A.data(),  sizeAvalues, cudaMemcpyHostToDevice) );
+    CALL_CUDA( cudaMemcpy(d_B,        B.data(),  sizeB,       cudaMemcpyHostToDevice) );
+
+    // FIXME: Should we transpose B and use cusparseDcsrmm2 instread?
+    // http://docs.nvidia.com/cuda/cusparse/index.html#cusparse-lt-t-gt-csrmm2
+    const Scalar alpha = 1.0;
+    const Scalar beta  = 0.0;
+    // cusparseStatus_t
+    // cusparseDcsrmm(cusparseHandle_t handle, cusparseOperation_t transA,
+    //                int m, int n, int k, int nnz,
+    //                const double *alpha, const cusparseMatDescr_t descrA,
+    //                const double *csrValA, const int *csrRowPtrA, const int *csrColIndA,
+    //                const double *B, int ldb, const double *beta, double *C, int ldc)
+    CALL_CUSPARSE( cusparseDcsrmm(handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
+                                  A.rows(), A.cols(), B.cols(), A.nonZeros(),
+                                  &alpha, descr,
+                                  d_A_values, d_A_rowptr, d_A_colidx,
+                                  d_B, B.rows(), &beta, d_C, C.rows()) );
+
+    CALL_CUDA( cudaMemcpy(C.data(), d_C, sizeC, cudaMemcpyDeviceToHost) );
+
+    CALL_CUSPARSE( cusparseDestroyMatDescr(descr) );
+    CALL_CUSPARSE( cusparseDestroy(handle) );
+
+    CALL_CUDA( cudaFree(d_A_rowptr) );
+    CALL_CUDA( cudaFree(d_A_colidx) );
+    CALL_CUDA( cudaFree(d_A_values) );
+    CALL_CUDA( cudaFree(d_B) );
+    CALL_CUDA( cudaFree(d_C) );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+    LinearAlgebra::getBackend("generic").dsptd(x, A, y, B);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraCUDA::print(std::ostream& out) const {
+    out << "LinearAlgebraCUDA[]";
+}
+
+//-----------------------------------------------------------------------------
+
+static LinearAlgebraCUDA linearAlgebraCUDA;
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_CUDA
diff --git a/eckit/src/eckit/linalg/LinearAlgebraCUDA.h b/eckit/src/eckit/linalg/LinearAlgebraCUDA.h
new file mode 100644
index 0000000..a819bc5
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraCUDA.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraCUDA.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebraCUDA_h
+#define eckit_la_LinearAlgebraCUDA_h
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_CUDA
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraCUDA : public LinearAlgebra {
+
+public:
+    LinearAlgebraCUDA();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_CUDA
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraEigen.cc b/eckit/src/eckit/linalg/LinearAlgebraEigen.cc
new file mode 100644
index 0000000..cd2d05c
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraEigen.cc
@@ -0,0 +1,119 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+//-----------------------------------------------------------------------------
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_EIGEN
+
+#include "eckit/linalg/LinearAlgebraEigen.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+#include "eckit/maths/Eigen.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+LinearAlgebraEigen::LinearAlgebraEigen() : LinearAlgebra("eigen") {}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::print(std::ostream& out) const {
+    out << "LinearAlgebraEigen[]";
+}
+
+//-----------------------------------------------------------------------------
+
+Scalar LinearAlgebraEigen::dot(const Vector& x, const Vector& y) const {
+    ASSERT( x.size() == y.size() );
+    // Eigen requires non-const pointers to the data
+    Eigen::VectorXd::MapType xi = Eigen::VectorXd::Map( const_cast<Scalar*>(x.data()), x.size() );
+    Eigen::VectorXd::MapType yi = Eigen::VectorXd::Map( const_cast<Scalar*>(y.data()), y.size() );
+    return xi.dot(yi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // Eigen requires non-const pointers to the data
+    Eigen::MatrixXd::MapType Ai = Eigen::MatrixXd::Map( const_cast<Scalar*>(A.data()), A.rows(), A.cols() );
+    Eigen::VectorXd::MapType xi = Eigen::VectorXd::Map( const_cast<Scalar*>(x.data()), x.size() );
+    Eigen::VectorXd::MapType yi = Eigen::VectorXd::Map( y.data(), y.size() );
+    yi = Ai*xi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // Eigen requires non-const pointers to the data
+    Eigen::MatrixXd::MapType Ai = Eigen::MatrixXd::Map( const_cast<Scalar*>(A.data()), A.rows(), A.cols() );
+    Eigen::MatrixXd::MapType Bi = Eigen::MatrixXd::Map( const_cast<Scalar*>(B.data()), B.rows(), B.cols() );
+    Eigen::MatrixXd::MapType Ci = Eigen::MatrixXd::Map( C.data(), C.rows(), C.cols() );
+    Ci = Ai*Bi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    // Eigen requires non-const pointers to the data
+    Eigen::MappedSparseMatrix<Scalar, Eigen::RowMajor, Index> Ai(A.rows(), A.cols(), A.nonZeros(),
+                                                                 const_cast<Index*>(A.outer()),
+                                                                 const_cast<Index*>(A.inner()),
+                                                                 const_cast<Scalar*>(A.data()));
+    Eigen::VectorXd::MapType xi = Eigen::VectorXd::Map( const_cast<Scalar*>(x.data()), x.size() );
+    Eigen::VectorXd::MapType yi = Eigen::VectorXd::Map( y.data(), y.size() );
+    yi = Ai*xi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    // Eigen requires non-const pointers to the data
+    Eigen::MappedSparseMatrix<Scalar, Eigen::RowMajor, Index> Ai(A.rows(), A.cols(), A.nonZeros(),
+                                                                 const_cast<Index*>(A.outer()),
+                                                                 const_cast<Index*>(A.inner()),
+                                                                 const_cast<Scalar*>(A.data()));
+    Eigen::MatrixXd::MapType Bi = Eigen::MatrixXd::Map( const_cast<Scalar*>(B.data()), B.rows(), B.cols() );
+    Eigen::MatrixXd::MapType Ci = Eigen::MatrixXd::Map( C.data(), C.rows(), C.cols() );
+    Ci = Ai*Bi;
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraEigen::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+    LinearAlgebra::getBackend("generic").dsptd(x, A, y, B);
+}
+
+//-----------------------------------------------------------------------------
+
+static LinearAlgebraEigen linearAlgebraEigen;
+
+//-----------------------------------------------------------------------------
+
+} // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_EIGEN
diff --git a/eckit/src/eckit/linalg/LinearAlgebraEigen.h b/eckit/src/eckit/linalg/LinearAlgebraEigen.h
new file mode 100644
index 0000000..b54af34
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraEigen.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraEigen.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebraEigen_h
+#define eckit_la_LinearAlgebraEigen_h
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_EIGEN
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraEigen : public LinearAlgebra {
+
+public:
+    LinearAlgebraEigen();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_EIGEN
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraGeneric.cc b/eckit/src/eckit/linalg/LinearAlgebraGeneric.cc
new file mode 100644
index 0000000..e0641d1
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraGeneric.cc
@@ -0,0 +1,144 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+
+
+#include "eckit/linalg/LinearAlgebraGeneric.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+
+
+
+namespace eckit {
+namespace linalg {
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+/// Multiply A by the diagonal matrix d (in vector form)
+static void dsp(const Vector& d, SparseMatrix& A) {
+    const Index* outer = A.outer();
+    // FIXME: better use InnerIterator
+    Scalar* data = const_cast<Scalar*>(A.data());
+    for (Size r = 0; r < A.rows(); ++r)
+        for (Index oi = outer[r]; oi < outer[r+1]; ++oi)
+            data[oi] *= d[r];
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+
+LinearAlgebraGeneric::LinearAlgebraGeneric() : LinearAlgebra("generic") {}
+
+
+
+void LinearAlgebraGeneric::print(std::ostream& out) const {
+    out << "LinearAlgebraGeneric[]";
+}
+
+
+
+Scalar LinearAlgebraGeneric::dot(const Vector& x, const Vector& y) const {
+
+    ASSERT(x.size() == y.size());
+    Scalar r = 0;
+    for (size_t i = 0; i < x.size(); ++i) r += x[i] * y[i];
+    return r;
+}
+
+
+
+void LinearAlgebraGeneric::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+
+    for (size_t r = 0; r < A.rows(); ++r) {
+        y[r] = 0.;
+        for (Size c = 0; c < A.cols(); ++c) y[r] += A(r, c) * x[c];
+    }
+}
+
+
+
+void LinearAlgebraGeneric::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+
+    C.setZero();
+    for (size_t c = 0; c < B.cols(); ++c)
+        for (size_t r = 0; r < A.rows(); ++r)
+            for (size_t k = 0; k < A.cols(); ++k)
+                C(r, c) += A(r, k) * B(k, c);
+}
+
+
+
+void LinearAlgebraGeneric::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+
+    ASSERT( A.outer()[0] == 0 );  // expect indices to be 0-based
+
+    const Index* outer = A.outer();
+    const Index* inner = A.inner();
+    const Scalar* val  = A.data();
+
+    for (size_t r = 0; r < A.rows(); ++r) {
+        y[r] = 0.;
+        for (Index oi = outer[r]; oi < outer[r+1]; ++oi)
+            y[r] += val[oi] * x[inner[oi]];
+    }
+}
+
+
+
+void LinearAlgebraGeneric::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+
+    C.setZero();
+
+    ASSERT( A.outer()[0] == 0 ); // expect indices to be 0-based
+
+    const Index* outer = A.outer();
+    const Index* inner = A.inner();
+    const Scalar* val = A.data();
+    for (size_t r = 0; r < A.rows(); ++r)
+        for (Index oi = outer[r]; oi < outer[r+1]; ++oi)
+            for (size_t c = 0; c < B.cols(); ++c)
+                C(r, c) += val[oi] * B(inner[oi], c);
+}
+
+
+
+void LinearAlgebraGeneric::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+
+    B = A;
+    dsp(y, B);
+    B.transpose();
+    dsp(x, B);
+}
+
+
+static LinearAlgebraGeneric linearAlgebraGeneric;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // namespace linalg
+} // namespace eckit
diff --git a/eckit/src/eckit/linalg/LinearAlgebraGeneric.h b/eckit/src/eckit/linalg/LinearAlgebraGeneric.h
new file mode 100644
index 0000000..e0b5861
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraGeneric.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraGeneric.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebraGeneric_h
+#define eckit_la_LinearAlgebraGeneric_h
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraGeneric : public LinearAlgebra {
+
+public:
+    LinearAlgebraGeneric();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraMKL.cc b/eckit/src/eckit/linalg/LinearAlgebraMKL.cc
new file mode 100644
index 0000000..7fbbe43
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraMKL.cc
@@ -0,0 +1,154 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_MKL
+
+#include "mkl.h"
+#include "mkl_cblas.h"
+
+//-----------------------------------------------------------------------------
+
+#include "eckit/linalg/LinearAlgebraMKL.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+LinearAlgebraMKL::LinearAlgebraMKL() : LinearAlgebra("mkl") {}
+
+Scalar LinearAlgebraMKL::dot(const Vector& x, const Vector& y) const {
+    ASSERT( x.size() == y.size() );
+    // double cblas_ddot (const MKL_INT n, const double *x, const MKL_INT incx, const double *y, const MKL_INT incy);
+    return cblas_ddot( x.size(), x.data(), 1, y.data(), 1 );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // void cblas_dgemv (const CBLAS_LAYOUT Layout, const CBLAS_TRANSPOSE trans,
+    //                   const MKL_INT m, const MKL_INT n,
+    //                   const double alpha, const double a, const MKL_INT lda, const double *x, const MKL_INT incx,
+    //                   const double beta, double *y, const MKL_INT incy);
+    cblas_dgemv( CblasColMajor, CblasNoTrans,
+                 A.rows(), A.cols(),
+                 1.0, A.data(), A.rows(), x.data(), 1,
+                 0.0, y.data(), 1 );
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // void cblas_dgemm (const CBLAS_LAYOUT Layout, const CBLAS_TRANSPOSE transa, const CBLAS_TRANSPOSE transb,
+    //                   const MKL_INT m, const MKL_INT n, const MKL_INT k,
+    //                   const double alpha, const double a, const MKL_INT lda, const double *b, const MKL_INT ldb,
+    //                   const double beta, double *c, const MKL_INT ldc);
+    cblas_dgemm( CblasColMajor, CblasNoTrans, CblasNoTrans,
+                 A.rows(), B.cols(), A.cols(),
+                 1.0, A.data(), A.rows(), B.data(), B.rows(),
+                 0.0, C.data(), A.rows());
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    MKL_INT m = A.rows();
+    MKL_INT k = A.cols();
+    double alpha = 1.;
+    double beta = 0.;
+    // void mkl_dcsrmv (char *transa, MKL_INT *m, MKL_INT *k,
+    //                  double *alpha, char *matdescra,
+    //                  double *val, MKL_INT *indx, MKL_INT *pntrb, MKL_INT *pntre,
+    //                  double *x, double *beta, double *y);
+    // std::cout << "Calling MKL::spmv()" << std::endl;
+    double* matrix = const_cast<double*>(A.data());
+    MKL_INT* inner = const_cast<MKL_INT*>(A.inner());
+    MKL_INT* outer = const_cast<MKL_INT*>(A.outer());
+    double* vector = const_cast<double*>(x.data());
+    mkl_dcsrmv ("N", &m, &k,
+                &alpha, "G__C",
+                matrix, inner, outer, outer+1,
+                vector, &beta, y.data());
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // We expect indices to be 0-based
+    ASSERT( A.outer()[0] == 0 );
+    MKL_INT m = A.rows();
+    MKL_INT n = C.cols();
+    MKL_INT k = A.cols();
+
+    double alpha = 1.;
+    double beta  = 0.;
+
+    // FIXME: with 0-based indexing, MKL assumes row-major ordering for B and C
+    // We need to use 1-based indexing i.e. offset outer and inner indices by 1
+
+    std::vector<MKL_INT> outer(m+1, 1);
+    for (size_t i = 0; i < A.rows()+1; ++i)
+        outer[i] += A.outer()[i];
+
+    std::vector<MKL_INT> inner(A.nonZeros(), 1);
+    for (size_t i = 0; i < A.nonZeros(); ++i)
+        inner[i] += A.inner()[i];
+
+    // void mkl_dcsrmm (char *transa, MKL_INT *m, MKL_INT *n, MKL_INT *k,
+    //                  double *alpha, char *matdescra,
+    //                  double *val, MKL_INT *indx, MKL_INT *pntrb, MKL_INT *pntre,
+    //                  double *b, MKL_INT *ldb, double *beta, double *c, MKL_INT *ldc);
+
+    double* a = const_cast<double*>(A.data());
+    double* b = const_cast<double*>(B.data());
+    mkl_dcsrmm( "N", &m, &n, &k,
+                &alpha, "G__F",
+                a, inner.data(), outer.data(), outer.data()+1,
+                b, &k, &beta, C.data(), &k);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+    LinearAlgebra::getBackend("generic").dsptd(x, A, y, B);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraMKL::print(std::ostream& out) const {
+    out << "LinearAlgebraMKL[]";
+}
+
+//-----------------------------------------------------------------------------
+
+static LinearAlgebraMKL linearAlgebraMKL;
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_MKL
diff --git a/eckit/src/eckit/linalg/LinearAlgebraMKL.h b/eckit/src/eckit/linalg/LinearAlgebraMKL.h
new file mode 100644
index 0000000..565cb93
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraMKL.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraMKL.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_LinearAlgebraMKL_h
+#define eckit_la_LinearAlgebraMKL_h
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_MKL
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraMKL : public LinearAlgebra {
+
+public:
+    LinearAlgebraMKL();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif // HAVE_MKL
+
+#endif
diff --git a/eckit/src/eckit/linalg/LinearAlgebraViennaCL.cc b/eckit/src/eckit/linalg/LinearAlgebraViennaCL.cc
new file mode 100644
index 0000000..0aecfed
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraViennaCL.cc
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+//-----------------------------------------------------------------------------
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_VIENNACL
+
+#include <viennacl/compressed_matrix.hpp>
+#include <viennacl/matrix.hpp>
+#include <viennacl/vector.hpp>
+#include <viennacl/linalg/inner_prod.hpp>
+#include <viennacl/linalg/prod.hpp>
+
+#include "eckit/linalg/LinearAlgebraViennaCL.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/Vector.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+namespace linalg {
+
+typedef viennacl::vector<Scalar> vec;
+typedef viennacl::matrix<Scalar, viennacl::column_major> mat;
+typedef viennacl::compressed_matrix<Scalar> spmat;
+
+//-----------------------------------------------------------------------------
+
+LinearAlgebraViennaCL::LinearAlgebraViennaCL() : LinearAlgebra("viennacl") {}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::print(std::ostream& out) const {
+    out << "LinearAlgebraViennaCL[]";
+}
+
+//-----------------------------------------------------------------------------
+
+Scalar LinearAlgebraViennaCL::dot(const Vector& x, const Vector& y) const {
+    ASSERT( x.size() == y.size() );
+    // ViennaCL requires non-const pointers to the data for views
+    vec xi(const_cast<Scalar*>(x.data()), viennacl::MAIN_MEMORY, x.size());
+    vec yi(const_cast<Scalar*>(y.data()), viennacl::MAIN_MEMORY, y.size());
+    return viennacl::linalg::inner_prod(xi, yi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::gemv(const Matrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    // ViennaCL requires non-const pointers to the data for views
+    mat Ai(const_cast<Scalar*>(A.data()), viennacl::MAIN_MEMORY, A.rows(), A.cols());
+    vec xi(const_cast<Scalar*>(x.data()), viennacl::MAIN_MEMORY, x.size());
+    vec yi(y.data(), viennacl::MAIN_MEMORY, y.size());
+    yi = viennacl::linalg::prod(Ai, xi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::gemm(const Matrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    // ViennaCL requires non-const pointers to the data for views
+    mat Ai(const_cast<Scalar*>(A.data()), viennacl::MAIN_MEMORY, A.rows(), A.cols());
+    mat Bi(const_cast<Scalar*>(B.data()), viennacl::MAIN_MEMORY, B.rows(), B.cols());
+    mat Ci(C.data(), viennacl::MAIN_MEMORY, C.rows(), C.cols());
+    Ci = viennacl::linalg::prod(Ai, Bi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::spmv(const SparseMatrix& A, const Vector& x, Vector& y) const {
+    ASSERT( x.size() == A.cols() && y.size() == A.rows() );
+    spmat Ai;
+    // FIXME: this will always copy!
+    Ai.set(A.outer(), A.inner(), A.data(), A.rows(), A.cols(), A.nonZeros());
+    vec xi(const_cast<Scalar*>(x.data()), viennacl::MAIN_MEMORY, x.size());
+    vec yi(y.data(), viennacl::MAIN_MEMORY, y.size());
+    yi = viennacl::linalg::prod(Ai, xi);
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::spmm(const SparseMatrix& A, const Matrix& B, Matrix& C) const {
+    ASSERT( A.cols() == B.rows() && A.rows() == C.rows() && B.cols() == C.cols() );
+    spmat Ai;
+    // FIXME: this will always copy!
+    Ai.set(A.outer(), A.inner(), A.data(), A.rows(), A.cols(), A.nonZeros());
+    // Emulate spmm by looping over columns of B
+    for (Size col = 0; col < B.cols(); ++col) {
+        vec xi(const_cast<Scalar*>(B.data() + col*B.rows()), viennacl::MAIN_MEMORY, B.rows());
+        vec yi(C.data() + col*C.rows(), viennacl::MAIN_MEMORY, C.rows());
+        yi = viennacl::linalg::prod(Ai, xi);
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void LinearAlgebraViennaCL::dsptd(const Vector& x, const SparseMatrix& A, const Vector& y, SparseMatrix& B) const {
+    LinearAlgebra::getBackend("generic").dsptd(x, A, y, B);
+}
+
+//-----------------------------------------------------------------------------
+
+static LinearAlgebraViennaCL LinearAlgebraViennaCL;
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_VIENNACL
diff --git a/eckit/src/eckit/linalg/LinearAlgebraViennaCL.h b/eckit/src/eckit/linalg/LinearAlgebraViennaCL.h
new file mode 100644
index 0000000..ba6de43
--- /dev/null
+++ b/eckit/src/eckit/linalg/LinearAlgebraViennaCL.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   LinearAlgebraViennaCL.h
+/// @author Florian Rathgeber
+/// @date   July 2015
+
+#ifndef eckit_la_LinearAlgebraViennaCL_h
+#define eckit_la_LinearAlgebraViennaCL_h
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_VIENNACL
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+class LinearAlgebraViennaCL : public LinearAlgebra {
+
+public:
+    LinearAlgebraViennaCL();
+
+private:
+
+    // Overridden methods
+
+    virtual Scalar dot(const Vector&, const Vector&) const;
+    virtual void gemv(const Matrix&, const Vector&, Vector&) const;
+    virtual void gemm(const Matrix&, const Matrix&, Matrix&) const;
+    virtual void spmv(const SparseMatrix&, const Vector&, Vector&) const;
+    virtual void spmm(const SparseMatrix&, const Matrix&, Matrix&) const;
+    virtual void dsptd(const Vector&, const SparseMatrix&, const Vector&, SparseMatrix&) const;
+
+    virtual void print(std::ostream&) const;
+};
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif  // ECKIT_HAVE_VIENNACL
+
+#endif
diff --git a/eckit/src/eckit/linalg/Matrix.cc b/eckit/src/eckit/linalg/Matrix.cc
new file mode 100644
index 0000000..edb47d0
--- /dev/null
+++ b/eckit/src/eckit/linalg/Matrix.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/linalg/Matrix.h"
+
+#include <cstring>
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+Matrix::Matrix() :
+    array_(0),
+    rows_(0),
+    cols_(0),
+    own_(false) {
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix::Matrix(Size rows, Size cols) :
+    array_(new Scalar[rows*cols]),
+    rows_(rows),
+    cols_(cols),
+    own_(true) {
+    ASSERT(size()>0);
+    ASSERT(array_);
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix::Matrix(Scalar* array, Size rows, Size cols) :
+    array_(array),
+    rows_(rows),
+    cols_(cols),
+    own_(false) {
+    ASSERT(size()>0);
+    ASSERT(array_);
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix::Matrix(Stream& stream) :
+    array_(0),
+    rows_(0),
+    cols_(0),
+    own_(false) {
+    Size rows, cols;
+    stream >> rows;
+    stream >> cols;
+    resize(rows, cols);
+
+    ASSERT(size()>0);
+    ASSERT(array_);
+    Buffer b(array_, (rows*cols)*sizeof(Scalar), /* dummy */ true);
+    stream >> b;
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix::Matrix(const Matrix& other) :
+    array_(new Scalar[other.size()]),
+    rows_(other.rows_),
+    cols_(other.cols_),
+    own_(true) {
+    ASSERT(size()>0);
+    ASSERT(array_);
+    ::memcpy(array_, other.array_, size() * sizeof(Scalar));
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix::~Matrix() {
+    if (own_) {
+        delete [] array_;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+Matrix& Matrix::operator=(const Matrix& other) {
+    // do not optimize for if size()==other.size(), as using copy constructor
+    // consistently retains ownership (no surprises in ownership behaviour)
+    Matrix copy(other);
+    swap(copy);
+    return *this;
+}
+
+//-----------------------------------------------------------------------------
+
+void Matrix::swap(Matrix& other) {
+    std::swap(array_, other.array_);
+    std::swap(rows_,  other.rows_);
+    std::swap(cols_,  other.cols_);
+    std::swap(own_,   other.own_);
+}
+
+//-----------------------------------------------------------------------------
+
+void Matrix::resize(Size rows, Size cols) {
+    // avoid reallocation if memory is the same
+    if (size() != rows*cols) {
+        Matrix m(rows, cols);
+        swap(m);
+    }
+    rows_ = rows;
+    cols_ = cols;
+}
+
+//-----------------------------------------------------------------------------
+
+void Matrix::setZero() {
+    ASSERT(size()>0);
+    ASSERT(array_);
+    ::memset(array_, 0, size()*sizeof(Scalar));
+}
+
+//-----------------------------------------------------------------------------
+
+void Matrix::fill(Scalar value) {
+    for (Size i = 0; i < size(); ++i) {
+        array_[i] = value;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void Matrix::encode(Stream& stream) const {
+  Buffer b(const_cast<Scalar*>(array_), rows_*cols_*sizeof(Scalar), /* dummy */ true);
+
+  stream << rows_;
+  stream << cols_;
+  stream << b;
+}
+
+//-----------------------------------------------------------------------------
+
+Stream& operator<<(Stream& stream, const Matrix& matrix) {
+    matrix.encode(stream);
+    return stream;
+}
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+}  // namespace eckit
diff --git a/eckit/src/eckit/linalg/Matrix.h b/eckit/src/eckit/linalg/Matrix.h
new file mode 100644
index 0000000..2b0853b
--- /dev/null
+++ b/eckit/src/eckit/linalg/Matrix.h
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   Matrix.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_linalg_Matrix_h
+#define eckit_linalg_Matrix_h
+
+#include "eckit/linalg/types.h"
+
+namespace eckit {
+class Stream;
+}
+
+namespace eckit {
+namespace linalg {
+
+//-----------------------------------------------------------------------------
+
+/// Dense matrix in column major storage order
+class Matrix {
+public:  // types
+    typedef eckit::linalg::Size Size;
+
+public:  // methods
+
+    // -- Constructors
+
+    /// Default constructor (empty matrix)
+    Matrix();
+
+    /// Construct matrix with given rows and columns (allocates memory, not initialised)
+    Matrix(Size rows, Size cols);
+
+    /// Construct matrix from existing data (does NOT take ownership)
+    Matrix(Scalar* array, Size rows, Size cols);
+
+    /// Constructor from Stream
+    Matrix(Stream&);
+
+    /// Copy constructor
+    Matrix(const Matrix&);
+
+    // TODO: make virtual if used as base class
+    ~Matrix();
+
+    // -- Mutators
+
+    Matrix& operator=(const Matrix&);
+
+    /// Swap this matrix with another
+    void swap(Matrix&);
+
+    /// Resize matrix to given number of rows/columns (invalidates data)
+    void resize(Size rows, Size cols);
+
+    /// Set data to zero
+    void setZero();
+
+    /// Fill vector with given scalar
+    void fill(Scalar);
+
+    // -- Serialisation
+
+    /// Serialise to a Stream
+    void encode(Stream&) const;
+
+    // -- Accessors
+
+    /// @returns size (rows * cols)
+    Size size() const { return rows_*cols_; }
+    /// @returns number of rows
+    Size rows() const { return rows_; }
+    /// @returns number of columns
+    Size cols() const { return cols_; }
+
+    /// Access by row and column
+    /// @note implements column-major (Fortran-style) ordering
+    Scalar& operator()(Size row, Size col) { return array_[col*rows_ + row]; }
+    const Scalar& operator()(Size row, Size col) const { return array_[col*rows_ + row]; }
+
+    /// Access to linearised storage
+    Scalar& operator[](Size i) { return array_[i]; }
+    const Scalar& operator[](Size i) const { return array_[i]; }
+
+    /// @returns modifiable view of the data
+    Scalar* data() { return array_; }
+    /// @returns read-only view of the data
+    const Scalar* data() const { return array_; }
+
+    /// @returns iterator to beginning of the data
+    Scalar* begin() { return array_; }
+    /// @returns const iterator to beginning of the data
+    const Scalar* begin() const { return array_; }
+    /// @returns iterator to end of the data
+    Scalar* end() { return array_ + size(); }
+    /// @returns const iterator to end of the data
+    const Scalar* end() const { return array_ + size(); }
+
+protected:  // member variables
+
+    /// Container
+    Scalar* array_;
+
+    /// Number of rows
+    Size rows_;
+
+    /// Number of columns
+    Size cols_;
+
+    /// Indicate ownership
+    bool own_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+Stream& operator<<(Stream&, const Matrix&);
+
+//-----------------------------------------------------------------------------
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/SparseMatrix.cc b/eckit/src/eckit/linalg/SparseMatrix.cc
new file mode 100644
index 0000000..2603415
--- /dev/null
+++ b/eckit/src/eckit/linalg/SparseMatrix.cc
@@ -0,0 +1,513 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/linalg/SparseMatrix.h"
+
+#include <algorithm>
+#include <iterator>
+#include <numeric>
+
+// For endianness
+#include "eckit/eckit.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/BufferedHandle.h"
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/io/MemoryHandle.h"
+
+
+namespace eckit {
+namespace linalg {
+
+#ifdef EC_LITTLE_ENDIAN
+static const bool littleEndian = true;
+#else
+static const bool littleEndian = false;
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SparseMatrix::SparseMatrix() :
+    data_(0),
+    size_(0),
+    outer_(0),
+    inner_(0),
+    rows_(0),
+    cols_(0),
+    own_(false) {
+}
+
+SparseMatrix::SparseMatrix(Size rows, Size cols) {
+    zero(*this);
+    reserve(rows, cols, 1);
+}
+
+SparseMatrix::SparseMatrix(Size rows, Size cols, const std::vector<Triplet>& triplets) {
+
+    zero(*this);
+
+    reserve(rows, cols, triplets.size()); // Allocate memory 1 triplet per non-zero
+
+    Size pos = 0;
+    Size row = 0;
+
+    outer_[0] = 0; /* first entry is always zero */
+
+    // Build vectors of inner indices and values, update outer index per row
+    for (std::vector<Triplet>::const_iterator it = triplets.begin(); it != triplets.end(); ++it, ++pos) {
+
+        // triplets are ordered by rows
+        ASSERT( it->row() >= row );
+        ASSERT( it->row() < rows_ );
+        ASSERT( it->col() >= 0 );
+        ASSERT( it->col() < cols_ );
+
+        // start a new row
+        while (it->row() > row) {
+            outer_[++row] = Index(pos);
+        }
+
+        inner_[pos] = Index(it->col());
+        data_[pos] = it->value();
+    }
+
+    while (row < rows_) {
+        outer_[++row] = Index(pos);
+    }
+
+    ASSERT(Size(outer_[ outerSize() - 1 ]) == nonZeros()); /* last entry is always the nnz */
+}
+
+
+SparseMatrix::SparseMatrix(Stream &s) {
+    zero(*this);
+    decode(s);
+}
+
+SparseMatrix::SparseMatrix(Scalar* values, Size size, Size rows, Size cols, Index* outer, Index* inner)  :
+    data_(values),
+    size_(size),
+    outer_(outer),
+    inner_(inner),
+    rows_(rows),
+    cols_(cols),
+    own_(false) {
+}
+
+
+struct SPMInfo {
+    Size         size_;   ///< non-zeros
+    Size         rows_;   ///< rows
+    Size         cols_;   ///< columns
+    ptrdiff_t    data_;
+    ptrdiff_t    outer_;
+    ptrdiff_t    inner_;
+};
+
+SparseMatrix::SparseMatrix(const eckit::Buffer& buffer)
+{
+    size_t buffsize = buffer.size();
+
+    const char* b = buffer;
+    char* addr = const_cast<char*>(b);
+
+    eckit::MemoryHandle mh(buffer);
+    mh.openForRead();
+
+    struct SPMInfo info;
+    mh.read(&info, sizeof(SPMInfo));
+
+    ASSERT(info.size_ && info.rows_ && info.cols_);
+    ASSERT(info.data_ > 0 && info.outer_ > 0 && info.inner_ > 0);
+
+    own_  = false;
+    size_ = info.size_;
+    rows_ = info.rows_;
+    cols_ = info.cols_;
+
+    ASSERT(buffer.size() >= sizeof(SPMInfo) +
+                           + sizeofData()
+                           + sizeofOuter()
+                           + sizeofInner() );
+
+    data_  = reinterpret_cast<Scalar*>(addr + info.data_);
+    outer_ = reinterpret_cast<Index*>(addr + info.outer_);
+    inner_ = reinterpret_cast<Index*>(addr + info.inner_);
+
+    // check offsets don't segfault
+
+    ASSERT(info.data_  + sizeofData()  <= buffsize);
+    ASSERT(info.outer_ + sizeofOuter() <= buffsize);
+    ASSERT(info.inner_ + sizeofInner() <= buffsize);
+}
+
+SparseMatrix::~SparseMatrix() {
+    reset();
+}
+
+SparseMatrix::SparseMatrix(const SparseMatrix& other) {
+
+    zero(*this);
+
+    if(!other.empty()) { // in case we copy an other that was constructed empty
+
+        reserve(other.rows(), other.cols(), other.nonZeros());
+
+        ::memcpy(data_,  other.data_,  sizeofData());
+        ::memcpy(outer_, other.outer_, sizeofOuter());
+        ::memcpy(inner_, other.inner_, sizeofInner());
+    }
+}
+
+SparseMatrix& SparseMatrix::operator=(const SparseMatrix& other) {
+    SparseMatrix copy(other);
+    swap(copy);
+    return *this;
+}
+
+
+void SparseMatrix::reset() {
+
+    if(own_) {
+        delete [] inner_;
+        delete [] outer_;
+        delete [] data_;
+    }
+
+    own_  = false;
+    rows_ = 0;
+    cols_ = 0;
+    size_ = 0;
+
+    data_ = 0;
+    outer_ = 0;
+    inner_ = 0;
+}
+
+
+// variables into this method must be by value
+void SparseMatrix::reserve(Size rows, Size cols, Size nnz) {
+
+    ASSERT( nnz );
+    ASSERT( nnz <= rows * cols );
+    ASSERT( rows > 0 && cols > 0 ); /* rows and columns must have some size */
+
+    reset();
+
+    rows_ = rows;
+    cols_ = cols;
+    size_ = nnz;
+
+    data_  = new Scalar[dataSize()];
+    outer_ = new Index[outerSize()];
+    inner_ = new Index[innerSize()];
+
+    own_  = true;
+}
+
+
+void SparseMatrix::save(const eckit::PathName &path) const {
+    FileStream s(path, "w");
+    s << *this;
+}
+
+
+void SparseMatrix::load(const eckit::PathName &path)  {
+    FileStream s(path, "r");
+    decode(s);
+}
+
+void SparseMatrix::dump(Buffer& buffer) const {
+
+    size_t minimum = sizeof(SPMInfo) + sizeofData() + sizeofOuter() + sizeofInner();
+    ASSERT( buffer.size() >= minimum);
+
+    MemoryHandle mh(buffer);
+    mh.openForWrite(buffer.size());
+
+    SPMInfo info;
+
+    info.size_  = nonZeros();
+    info.rows_  = rows();
+    info.cols_  = cols();
+
+    info.data_  = sizeof(SPMInfo);
+    info.outer_ = info.data_  + sizeofData();
+    info.inner_ = info.outer_ + sizeofOuter();
+
+    /// @todo we should try to get these memory aligned (to say 64 bytes)
+
+    mh.write(&info, sizeof(SPMInfo));
+
+    ASSERT(mh.write(data_,  sizeofData())  == long(sizeofData()));
+    ASSERT(mh.write(outer_, sizeofOuter()) == long(sizeofOuter()));
+    ASSERT(mh.write(inner_, sizeofInner()) == long(sizeofInner()));
+}
+
+void SparseMatrix::swap(SparseMatrix &other) {
+    std::swap(data_,  other.data_);
+    std::swap(size_,  other.size_);
+    std::swap(outer_, other.outer_);
+    std::swap(inner_, other.inner_);
+    std::swap(rows_,  other.rows_);
+    std::swap(cols_,  other.cols_);
+    std::swap(own_,   other.own_);
+}
+
+
+size_t SparseMatrix::footprint() const {
+    return sizeof(*this)
+            + sizeofData()
+            + sizeofOuter()
+            + sizeofInner();
+}
+
+void SparseMatrix::dump(std::ostream& os) const
+{
+    for(Size i = 0; i < rows(); ++i) {
+
+        const_iterator itr = begin(i);
+        const_iterator iend = end(i);
+
+        if(itr == iend) continue;
+        os << itr.row();
+
+        for(; itr != iend; ++itr) {
+            os << " " << itr.col() << " " << *itr;
+        }
+        os << std::endl;
+    }
+}
+
+void SparseMatrix::print(std::ostream& os) const
+{
+    os << "SparseMatrix["
+       << "nnz="  << size_ << ","
+       << "rows=" << rows_ << ","
+       << "cols=" << cols_ << "]";
+}
+
+SparseMatrix& SparseMatrix::setIdentity(Size rows, Size cols) {
+
+    ASSERT( rows > 0 && cols > 0 );
+
+    rows_ = rows;
+    cols_ = cols;
+
+    Size nnz = std::min(rows_, cols_);
+
+    reserve(rows_, cols_, nnz);
+
+    for(Size i = 0; i < nnz; ++i) {
+        outer_[i] = Index(i);
+        inner_[i] = Index(i);
+    }
+
+    for(Size i = nnz; i <= rows_; ++i) {
+        outer_[i] = Index(nnz);
+    }
+
+    for(Size i = 0; i < size_; ++i) {
+        data_[i] = Scalar(1);
+    }
+
+    return *this;
+}
+
+
+SparseMatrix& SparseMatrix::transpose() {
+
+    /// @note Can SparseMatrix::transpose() be done more efficiently?
+    ///       We are building another matrix and then swapping
+
+    std::vector<Triplet> triplets;
+    triplets.reserve(nonZeros());
+    for (Size r = 0; r < rows_; ++r) {
+        for (Index c = outer_[r]; c < outer_[r + 1]; ++c) {
+            ASSERT(inner_[c] >= 0);
+            triplets.push_back(Triplet(Size(inner_[c]), r, data_[c]));
+        }
+    }
+
+    std::sort(triplets.begin(), triplets.end()); // triplets must be sorted by row
+
+    SparseMatrix tmp(cols_, rows_, triplets);
+
+    swap(tmp);
+
+    return *this;
+}
+
+
+SparseMatrix& SparseMatrix::prune(linalg::Scalar val) {
+
+    std::vector<Scalar> v;
+    std::vector<Index> inner;
+
+    Size nnz = 0;
+    for(Size r = 0; r < rows_; ++r) {
+        const Index start = outer_[r];
+        outer_[r] = Index(nnz);
+        for(Index c = start; c < outer_[r + 1]; ++c)
+            if(data_[c] != val) {
+                v.push_back(data_[c]);
+                inner.push_back(inner_[c]);
+                ++nnz;
+            }
+    }
+    outer_[rows_] = Index(nnz);
+
+    SparseMatrix tmp;
+    tmp.reserve(rows_, cols_, nnz);
+
+    ::memcpy(tmp.data_,  v.data(),  nnz * sizeof(Scalar));
+    ::memcpy(tmp.outer_, outer_, outerSize() * sizeof(Index));
+    ::memcpy(tmp.inner_, inner.data(), nnz * sizeof(Index));
+
+    swap(tmp);
+
+    return *this;
+}
+
+
+void SparseMatrix::encode(Stream &s) const {
+
+    s << rows_;
+    s << cols_;
+    s << size_;
+
+    s << littleEndian;
+    s << sizeof(Index);
+    s << sizeof(Scalar);
+    s << sizeof(Size);
+
+    s.writeLargeBlob(outer_, outerSize() * sizeof(Index));
+    s.writeLargeBlob(inner_, innerSize() * sizeof(Index));
+    s.writeLargeBlob(data_,  dataSize()  * sizeof(Scalar));
+}
+
+
+void SparseMatrix::decode(Stream &s) {
+
+    Size rows;
+    Size cols;
+    Size nnz;
+
+    s >> rows;
+    s >> cols;
+    s >> nnz;
+
+    bool little_endian;
+    s >> little_endian; ASSERT(littleEndian == little_endian);
+
+    size_t index_size;
+    s >> index_size; ASSERT(index_size == sizeof(Index));
+
+    size_t scalar_size;
+    s >> scalar_size; ASSERT(scalar_size == sizeof(Scalar));
+
+    size_t size_size;
+    s >> size_size; ASSERT(size_size == sizeof(Size));
+
+    reserve(rows, cols, nnz);
+
+    s.readLargeBlob(outer_, outerSize() * sizeof(Index));
+    s.readLargeBlob(inner_, innerSize() * sizeof(Index));
+    s.readLargeBlob(data_,  dataSize()  * sizeof(Scalar));
+}
+
+
+Stream& operator<<(Stream& s, const SparseMatrix& v) {
+    v.encode(s);
+    return s;
+}
+
+
+SparseMatrix::const_iterator SparseMatrix::const_iterator::operator++(int) {
+    const_iterator it = *this;
+    ++(*this);
+    return it;
+}
+
+
+SparseMatrix::const_iterator& SparseMatrix::const_iterator::operator=(const SparseMatrix::const_iterator& other) {
+    matrix_ = other.matrix_;
+    index_  = other.index_;
+    row_    = other.row_;
+    return *this;
+}
+
+bool SparseMatrix::const_iterator::operator==(const SparseMatrix::const_iterator& other) const {
+    ASSERT(other.matrix_ == matrix_);
+    return other.index_ == index_;
+}
+
+
+SparseMatrix::const_iterator::const_iterator(const SparseMatrix& matrix) :
+    matrix_(const_cast<SparseMatrix*>(&matrix)),
+    index_(0),
+    row_(0)
+{
+    const Index* outer = matrix_->outer();
+    while(outer[row_+1] == 0) {
+        ++row_;
+    }
+}
+
+SparseMatrix::const_iterator::const_iterator(const SparseMatrix& matrix, Size row) :
+    matrix_(const_cast<SparseMatrix*>(&matrix)),
+    row_(row) {
+    const Size rows = matrix_->rows_;
+    if(row_ > rows) { row_ = rows; }
+    index_ = Size(matrix_->outer_[row_]);
+}
+
+Size SparseMatrix::const_iterator::col() const {
+    assert(matrix_ && index_ < matrix_->nonZeros());
+    return Size(matrix_->inner_[index_]);
+}
+
+Size SparseMatrix::const_iterator::row() const {
+    return row_;
+}
+
+
+SparseMatrix::const_iterator& SparseMatrix::const_iterator::operator++() {
+    if(lastOfRow()) {
+        row_++;
+    }
+    index_++;
+    return *this;
+}
+
+
+const Scalar& SparseMatrix::const_iterator::operator*() const {
+    assert(matrix_ && index_ < matrix_->nonZeros());
+    return matrix_->data_[index_];
+}
+
+void eckit::linalg::SparseMatrix::const_iterator::print(std::ostream& os) const {
+    os << "SparseMatrix::iterator(row=" << row_ << ", index=" << index_ << ")" << std::endl;
+}
+
+
+Scalar& SparseMatrix::iterator::operator*() {
+    assert(matrix_ && index_ < matrix_->nonZeros());
+    return matrix_->data_[index_];
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace linalg
+} // namespace eckit
diff --git a/eckit/src/eckit/linalg/SparseMatrix.h b/eckit/src/eckit/linalg/SparseMatrix.h
new file mode 100644
index 0000000..748a844
--- /dev/null
+++ b/eckit/src/eckit/linalg/SparseMatrix.h
@@ -0,0 +1,238 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   SparseMatrix.h
+/// @author Florian Rathgeber
+/// @author Tiago Quintino
+/// @date   June 2015
+
+#ifndef eckit_la_SparseMatrix_h
+#define eckit_la_SparseMatrix_h
+
+#include <iosfwd>
+#include <cassert>
+#include <iosfwd>
+#include <vector>
+
+#include "eckit/linalg/types.h"
+#include "eckit/linalg/Triplet.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/io/MemoryHandle.h"
+
+namespace eckit {
+
+class Stream;
+class PathName;
+
+namespace linalg {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Sparse matrix in CRS (compressed row storage) format
+///
+class SparseMatrix {
+
+public:  // methods
+
+    // -- Constructors
+
+    /// Default constructor, empty matrix
+    SparseMatrix();
+
+    /// Constructs an identity matrix with provided dimensions
+    SparseMatrix(Size rows, Size cols);
+
+    /// Constructor from triplets
+    SparseMatrix(Size rows, Size cols, const std::vector<Triplet>& triplets);
+
+    /// Construct vector from existing data (does NOT take ownership)
+    SparseMatrix(Scalar* values, Size size, Size rows, Size cols, Index* outer, Index* inner);
+
+    /// Construct vector from existing data (does NOT take ownership)
+    SparseMatrix(const eckit::Buffer& buffer);
+
+    /// Constructor from Stream
+    SparseMatrix(Stream& v);
+
+    ~SparseMatrix();
+
+    /// Copy constructor
+    SparseMatrix(const SparseMatrix&);
+
+    /// Assignment operator (allocates and copies data)
+    SparseMatrix& operator=(const SparseMatrix&);
+
+public:
+
+    /// Prune entries with exactly the given value
+    SparseMatrix& prune(Scalar val = Scalar(0));
+
+    /// Set matrix to the identity
+    SparseMatrix& setIdentity(Size rows, Size cols);
+
+    /// Transpose matrix in-place
+    SparseMatrix& transpose();
+
+    // I/O
+
+    void save(const eckit::PathName& path) const;
+    void load(const eckit::PathName& path);
+
+    void dump(eckit::Buffer& buffer) const;
+
+    void swap(SparseMatrix& other);
+
+    /// @returns number of rows
+    Size rows() const { return rows_; }
+
+    /// @returns number of columns
+    Size cols() const { return cols_; }
+
+    /// @returns number of non-zeros
+    Size nonZeros() const { return size_; }
+
+    /// @returns true if this matrix does not contain non-zero entries
+    bool empty() const { return !nonZeros(); }
+
+    /// @returns read-only view of the data vector
+    const Scalar* data() const { return data_; }
+
+    /// @returns read-only view of the outer index vector
+    const Index* outer() const { return outer_; }
+
+    /// @returns read-only view of the inner index vector
+    const Index* inner() const { return inner_; }
+
+    /// data size is the number of non-zeros
+    Size dataSize() const { return nonZeros(); }
+
+    /// inner size is the number of non-zeros
+    Size innerSize() const { return nonZeros(); }
+
+    /// @returns outer size is number of rows + 1
+    Size outerSize() const { return Size(rows_ + 1); }
+
+    /// Reserve memory for given number of non-zeros (invalidates all data arrays)
+    /// @note variables into this method must be by value
+    void reserve(Size rows, Size cols, Size nnz);
+
+    /// Returns the footprint of the matrix in memory
+    size_t footprint() const;
+
+    void dump(std::ostream& os) const;
+
+    void print(std::ostream& os) const;
+
+    friend std::ostream& operator<<(std::ostream& os, const SparseMatrix& m) { m.print(os); return os; }
+
+public: // iterators
+
+    struct const_iterator {
+
+        const_iterator(const SparseMatrix& matrix);
+        const_iterator(const SparseMatrix& matrix, Size row);
+
+        const_iterator(const const_iterator& other) {
+            *this = other;
+        }
+
+        Size col() const;
+        Size row() const;
+
+        operator bool() const { return matrix_ && ( index_ < matrix_->nonZeros() ); }
+
+        const_iterator& operator++();
+        const_iterator  operator++(int);
+        const_iterator& operator=(const const_iterator& other);
+
+        bool operator!=(const const_iterator& other) const { return !operator==(other); }
+        bool operator==(const const_iterator& other) const;
+
+        const Scalar& operator*() const;
+
+        void print(std::ostream& os) const;
+
+    protected:
+
+        /// checks if index is last of row
+        bool lastOfRow() const { return ((index_ + 1) == Size(matrix_->outer_[row_ + 1])); }
+
+        SparseMatrix* matrix_;
+        Size index_;
+        Size row_;
+
+    };
+
+    struct iterator : const_iterator {
+        iterator(SparseMatrix& matrix) : const_iterator(matrix) {}
+        iterator(SparseMatrix& matrix, Size row) : const_iterator(matrix, row) {}
+        Scalar& operator*();
+    };
+
+    /// const iterators to being/end of row
+    const_iterator begin(Size row)   const { return const_iterator(*this, row); }
+    const_iterator end(Size row)     const { return const_iterator(*this, row+1); }
+
+    /// const iterators to being/end of matrix
+    const_iterator begin()           const { return const_iterator(*this); }
+    const_iterator end()             const { return const_iterator(*this, rows_); }
+
+    /// iterators to being/end of row
+    iterator       begin(Size row)   { return iterator(*this, row); }
+    iterator       end(Size row)     { return iterator(*this, row+1); }
+
+    /// const iterators to being/end of matrix
+    iterator       begin()           { return iterator(*this); }
+    iterator       end()             { return iterator(*this, rows_); }
+
+private: // methods
+
+    size_t sizeofData() const  { return dataSize()  * sizeof(Scalar); }
+    size_t sizeofOuter() const { return outerSize() * sizeof(Index);  }
+    size_t sizeofInner() const { return innerSize() * sizeof(Index);  }
+
+    /// Resets the matrix to a deallocated state
+    void reset();
+
+    /// Serialise to a Stream
+    void encode(Stream& s) const;
+
+    /// Deserialise from a Stream
+    void decode(Stream& s);
+
+    /// Resize sparse matrix (invalidates all data arrays)
+    void resize(Size rows, Size cols);
+
+private: // members
+
+    Scalar*      data_;   ///< matrix entries, sized with number of non-zeros (nnz)
+
+    Size         size_;   ///< Size of the container (AKA number of non-zeros nnz)
+
+    Index*       outer_;  ///< Starts of rows
+    Index*       inner_;  ///< Column indices
+
+    Size         rows_;   ///< Number of rows
+    Size         cols_;   ///< Number of columns
+
+    bool own_;   ///< do we own the memory allocated in the containers ?
+
+    friend Stream& operator<<(Stream&, const SparseMatrix&);
+};
+
+Stream& operator<<(Stream&, const SparseMatrix&);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/Triplet.h b/eckit/src/eckit/linalg/Triplet.h
new file mode 100644
index 0000000..2849bc9
--- /dev/null
+++ b/eckit/src/eckit/linalg/Triplet.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   Triplet.h
+/// @author Florian Rathgeber
+/// @date   June 2015
+
+#ifndef eckit_la_Triplet_h
+#define eckit_la_Triplet_h
+
+#include "eckit/linalg/types.h"
+
+namespace eckit {
+namespace linalg {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Triplet of values compatible to Eigen::Triplet
+
+class Triplet {
+public:
+
+    Triplet() : row_(0), col_(0), val_(0) {}
+
+    Triplet(const Size& i, const Size& j, const Scalar& v = Scalar(0))
+        : row_(i), col_(j), val_(v)
+    {}
+
+    const Size& row() const { return row_; }
+
+    const Size& col() const { return col_; }
+
+    const Scalar& value() const { return val_; }
+    Scalar& value() { return val_; }
+
+    bool operator< (const Triplet& other) const
+    {
+        if(row_ == other.row_)
+        {
+            return col_ < other.col_;
+        }
+        return row_ < other.row_;
+    }
+
+protected:
+    Size row_;
+    Size col_;
+    Scalar val_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/Vector.cc b/eckit/src/eckit/linalg/Vector.cc
new file mode 100644
index 0000000..6673d9b
--- /dev/null
+++ b/eckit/src/eckit/linalg/Vector.cc
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/linalg/Vector.h"
+
+#include <cstring>
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace eckit {
+namespace linalg {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Vector::Vector() :
+    array_(0),
+    length_(0),
+    own_(false) {
+}
+
+
+
+Vector::Vector(Size length) :
+    array_(new Scalar[length]),
+    length_(length),
+    own_(true) {
+}
+
+
+
+Vector::Vector(Scalar* array, Size length) :
+    array_(&array[0]),
+    length_(length),
+    own_(false) {
+    ASSERT(array_ && length_ > 0);
+}
+
+
+
+Vector::Vector(Stream& stream) :
+    array_(0),
+    length_(0),
+    own_(false) {
+    Size length;
+    stream >> length;
+    resize(length);
+
+    ASSERT(length_ > 0);
+    Buffer b(array_, length*sizeof(Scalar), /* dummy */ true);
+    stream >> b;
+}
+
+
+
+Vector::Vector(const Vector& other) :
+    array_(new Scalar[other.length_]),
+    length_(other.length_),
+    own_(true) {
+    ::memcpy(array_, other.array_, length_ * sizeof(Scalar));
+}
+
+
+
+Vector::~Vector() {
+    if (own_) {
+        delete [] array_;
+    }
+}
+
+
+
+Vector&Vector::operator=(const Vector& other) {
+    Vector copy(other);
+    swap(copy);
+    return *this;
+}
+
+
+
+void Vector::swap(Vector& other) {
+    std::swap(array_,  other.array_);
+    std::swap(length_, other.length_);
+    std::swap(own_,    other.own_);
+}
+
+
+
+void Vector::resize(Size length) {
+    Vector v(length);
+    swap(v);
+}
+
+
+
+void Vector::setZero() {
+    ::memset(array_, 0, length_*sizeof(Scalar));
+}
+
+
+
+void Vector::fill(Scalar value) {
+    for (Size i = 0; i < length_; ++i) {
+        array_[i] = value;
+    }
+}
+
+
+
+void Vector::encode(Stream& stream) const {
+  Buffer b(array_, length_*sizeof(Scalar), /* dummy */ true);
+
+  stream << length_;
+  stream << b;
+}
+
+
+
+Stream& operator<<(Stream& stream, const Vector& vector) {
+    vector.encode(stream);
+    return stream;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace linalg
+}  // namespace eckit
diff --git a/eckit/src/eckit/linalg/Vector.h b/eckit/src/eckit/linalg/Vector.h
new file mode 100644
index 0000000..ee63d78
--- /dev/null
+++ b/eckit/src/eckit/linalg/Vector.h
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   Vector.h
+/// @author Florian Rathgeber
+/// @author Pedro Maciel
+/// @author Tiago Quintino
+/// @date   June 2015
+
+#ifndef eckit_linalg_Vector_h
+#define eckit_linalg_Vector_h
+
+#include "eckit/linalg/types.h"
+
+namespace eckit {
+class Stream;
+}
+
+namespace eckit {
+namespace linalg {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Vector for Linear Algebra operations
+///
+/// @todo provide a const view
+///
+class Vector {
+
+public: // methods
+
+    // -- Constructors
+
+    /// Default constructor (empty vector)
+    Vector();
+
+    /// Construct vector of given size (allocates memory, not initialised)
+    Vector(Size length);
+
+    /// Construct vector from existing data (does NOT take ownership)
+    Vector(Scalar* array, Size length);
+
+    /// Constructor from Stream
+    Vector(Stream&);
+
+    /// Copy constructor
+    Vector(const Vector&);
+
+    ~Vector();
+
+    // -- Mutators
+
+    Vector& operator=(const Vector&);
+
+    /// Swap this vector for another
+    void swap(Vector&);
+
+    /// Resize vector to given size (invalidates data)
+    void resize(Size length);
+
+    /// Set data to zero
+    void setZero();
+
+    /// Fill vector with given scalar
+    void fill(Scalar);
+
+    // -- Serialisation
+
+    /// Serialise to a Stream
+    void encode(Stream&) const;
+
+    // -- Accessors
+
+    /// @returns size (rows * cols)
+    Size size() const { return length_; }
+    /// @returns number of rows (i.e. size)
+    Size rows() const { return length_; }
+    /// @returns number of columns (always 1)
+    Size cols() const { return 1; }
+
+    Scalar& operator[](Size i) { return array_[i]; }
+    const Scalar& operator[](Size i) const { return array_[i]; }
+
+    /// @returns modifiable view of the data
+    Scalar* data() { return array_; }
+    /// @returns read-only view of the data
+    const Scalar* data() const { return array_; }
+
+    /// @returns iterator to beginning of the data
+    Scalar* begin() { return array_; }
+    /// @returns const iterator to beginning of the data
+    const Scalar* begin() const { return array_; }
+    /// @returns iterator to end of the data
+    Scalar* end() { return array_ + length_; }
+    /// @returns const iterator to end of the data
+    const Scalar* end() const { return array_ + length_; }
+
+protected:  // member variables
+
+    Scalar* array_;  ///< Container
+
+    Size length_;    ///< Vector length/size
+
+    bool own_;       ///< do we own the memory allocated in the container ?
+};
+
+Stream& operator<<(Stream&, const Vector&);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/linalg/types.h b/eckit/src/eckit/linalg/types.h
new file mode 100644
index 0000000..8c8cc78
--- /dev/null
+++ b/eckit/src/eckit/linalg/types.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   types.h
+/// @author Florian Rathgeber
+/// @author Tiago Quintino
+/// @date   June 2015
+
+#ifndef eckit_la_types_h
+#define eckit_la_types_h
+
+#include <cstddef>
+
+namespace eckit {
+namespace linalg {
+
+typedef double  Scalar;
+typedef int     Index;
+typedef size_t  Size;
+
+class Vector;
+class Matrix;
+class SparseMatrix;
+
+}  // namespace linalg
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/BigNum.cc b/eckit/src/eckit/log/BigNum.cc
new file mode 100644
index 0000000..c0cf27c
--- /dev/null
+++ b/eckit/src/eckit/log/BigNum.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/BigNum.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void BigNum::print(std::ostream& s,long long v)
+{
+	if(v >=1000)
+	{
+		print(s,v/1000);
+		s << ',';
+        s << std::setw(3) << std::setfill('0') << (v % 1000);
+	}
+	else
+		s << v;
+}
+
+void BigNum::print(std::ostream& s) const
+{
+	long long v = value_;
+	if(v<0) {
+		v = -v;
+		s << '-';
+	}
+	char oldfill = s.fill();
+	print(s,v);
+    s << std::setfill(oldfill);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/BigNum.h b/eckit/src/eckit/log/BigNum.h
new file mode 100644
index 0000000..0c0df18
--- /dev/null
+++ b/eckit/src/eckit/log/BigNum.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BigNum.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_BigNum_h
+#define eckit_BigNum_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// Class used to print large numbers
+
+class BigNum {
+public:
+
+// -- Contructors
+
+	BigNum(long long v) : value_(v) {}
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Methods
+
+	void print(std::ostream&) const; 
+
+// -- Class methods
+
+	static void print(std::ostream&,long long);
+
+// -- Members
+
+	long long value_;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const BigNum& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Bytes.cc b/eckit/src/eckit/log/Bytes.cc
new file mode 100644
index 0000000..c9e3a2c
--- /dev/null
+++ b/eckit/src/eckit/log/Bytes.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Timer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Bytes::Bytes(double bytes):
+	bytes_(bytes),
+	rate_(false)
+{
+}
+
+Bytes::Bytes(double bytes,Timer& timer):
+	bytes_(bytes/timer.elapsed()),
+	rate_(true)
+{
+}
+
+Bytes::Bytes(double bytes,double elapsed):
+	bytes_(bytes/elapsed),
+	rate_(true)
+{
+}
+
+static const char *names[] = {"","K","M","G","T","P","E","Z","Y"};
+
+std::ostream& operator<<(std::ostream& s, const Bytes& b)
+{
+	double x = b.bytes_;
+	int    n = 0;
+
+	if(x < 0)
+	{
+		s << '-';
+		x = -x;
+	}
+
+    while(x>=1024.0 && (size_t) n < NUMBER(names) )
+	{
+		x /= 1024.0;
+		n++;
+	}
+
+	s << x << ' ' << names[n] << "byte";
+
+	if(x !=1 )  s << 's';
+	if(b.rate_) s << " per seconds";
+
+	return s;
+}
+
+Bytes::operator std::string() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/Bytes.h b/eckit/src/eckit/log/Bytes.h
new file mode 100644
index 0000000..565fa08
--- /dev/null
+++ b/eckit/src/eckit/log/Bytes.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Bytes.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Bytes_h
+#define eckit_Bytes_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Forward declarations
+
+class Timer;
+
+class Bytes {
+public:
+
+// -- Contructors
+
+	Bytes(double);
+    Bytes(double,Timer&); // computes a rate
+	Bytes(double,double); // computes a rate
+
+// -- Operators
+
+    operator std::string() const;
+    friend std::ostream& operator<<(std::ostream&,const Bytes&);
+
+// -- Class methods
+    
+    static unsigned long long KiB( unsigned long long n ) { return 1024*n; }
+    static unsigned long long MiB( unsigned long long n ) { return 1024*KiB(n); }
+    static unsigned long long GiB( unsigned long long n ) { return 1024*MiB(n); }
+    static unsigned long long TiB( unsigned long long n ) { return 1024*GiB(n); }
+    static unsigned long long PiB( unsigned long long n ) { return 1024*TiB(n); }
+    static unsigned long long EiB( unsigned long long n ) { return 1024*PiB(n); }
+    static unsigned long long ZiB( unsigned long long n ) { return 1024*EiB(n); }
+    static unsigned long long YiB( unsigned long long n ) { return 1024*ZiB(n); }
+    
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Members
+
+	double bytes_;
+	bool   rate_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/log/CallbackTarget.cc b/eckit/src/eckit/log/CallbackTarget.cc
new file mode 100644
index 0000000..f6a1000
--- /dev/null
+++ b/eckit/src/eckit/log/CallbackTarget.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/CallbackTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+CallbackTarget::CallbackTarget(callback_t callback, void *context):
+    callback_(callback),
+    context_(context)
+{
+}
+
+CallbackTarget::~CallbackTarget()
+{
+//    std::cerr << "CallbackTarget::~CallbackTarget()" << std::endl;
+}
+
+void CallbackTarget::line(const char* line) {
+    callback_(context_, line);
+}
+
+void CallbackTarget::print(std::ostream& s) const
+{
+    s << "CallbackTarget()";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/CallbackTarget.h b/eckit/src/eckit/log/CallbackTarget.h
new file mode 100644
index 0000000..30b20fd
--- /dev/null
+++ b/eckit/src/eckit/log/CallbackTarget.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file CallbackTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_CallbackTarget_h
+#define eckit_log_CallbackTarget_h
+
+#include <utility>
+
+#include "eckit/log/LineBasedTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class CallbackTarget : public LineBasedTarget {
+public:
+
+    typedef void (*callback_t) (void* ctxt, const char* msg);
+
+    CallbackTarget(callback_t callback, void* context = 0);
+
+    virtual ~CallbackTarget();
+
+private:
+
+    virtual void line(const char* line);
+
+    virtual void print(std::ostream& s) const;
+
+private:
+    callback_t callback_;
+    void* context_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Channel.cc b/eckit/src/eckit/log/Channel.cc
new file mode 100644
index 0000000..7ce96a8
--- /dev/null
+++ b/eckit/src/eckit/log/Channel.cc
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/Channel.h"
+#include "eckit/log/ChannelBuffer.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Channel::Channel(LogTarget* target) :
+    std::ostream(new ChannelBuffer()),
+    buffer_(dynamic_cast<ChannelBuffer*>(rdbuf())) {
+    ASSERT( buffer_ );
+    if (target) {
+        buffer_->setTarget(target);
+    }
+//    std::cerr << "Channel::Channel()" << std::endl;
+}
+
+
+Channel::~Channel() {
+//    std::cerr << "Channel::~Channel() " << *this << std::endl;
+    delete buffer_;
+}
+
+bool Channel::operator !() const {
+    bool b = *this;
+    return  !b;
+}
+
+Channel::operator bool() const {
+    return buffer_->active();
+}
+
+void Channel::setCallback(channel_callback_t cb, void* data) {
+    ASSERT(cb);
+    buffer_->setCallback(cb, data);
+}
+
+void Channel::addCallback(channel_callback_t cb, void* data) {
+    ASSERT(cb);
+    buffer_->addCallback(cb, data);
+}
+
+void Channel::setTarget(LogTarget* target) {
+    ASSERT(target);
+    buffer_->setTarget(target);
+}
+
+void Channel::addTarget(LogTarget* target) {
+    ASSERT(target);
+    buffer_->addTarget(target);
+}
+
+void Channel::setStream(std::ostream& out) {
+    buffer_->setStream(out);
+}
+
+void Channel::addStream(std::ostream& out) {
+    buffer_->addStream(out);
+}
+
+
+void Channel::setFile(const std::string& path) {
+    buffer_->setFile(path);
+}
+
+void Channel::addFile(const std::string& path) {
+    buffer_->addFile(path);
+}
+
+void Channel::reset() {
+    buffer_->reset();
+}
+
+void Channel::print(std::ostream& s) const {
+    s << "Channel(buffer=" << *buffer_ << ")";
+}
+
+void Channel::indent(const char* space) {
+    buffer_->indent(space);
+}
+
+void Channel::unindent() {
+    buffer_->unindent();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/Channel.h b/eckit/src/eckit/log/Channel.h
new file mode 100644
index 0000000..94022dd
--- /dev/null
+++ b/eckit/src/eckit/log/Channel.h
@@ -0,0 +1,92 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Channel.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_Channel_h
+#define eckit_log_Channel_h
+
+#include <ostream>
+
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+class ChannelBuffer;
+class LogTarget;
+
+typedef void (*channel_callback_t) (void* data, const char* msg);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Output channel that is an std::ostream but more functional
+
+class Channel : public std::ostream, private NonCopyable {
+
+public: // methods
+
+    Channel(LogTarget* = 0);
+
+    virtual ~Channel();
+
+    bool operator !() const;
+    operator bool() const;
+
+    void indent(const char* prefix = "");
+    void unindent();
+
+    void setStream(std::ostream& out);
+    void addStream(std::ostream& out);
+
+    void setFile(const std::string& path);
+    void addFile(const std::string& path);
+
+    void setCallback(channel_callback_t cb, void* data = 0);
+    void addCallback(channel_callback_t cb, void* data = 0);
+
+    void setTarget(LogTarget*);
+    void addTarget(LogTarget*);
+
+    void reset();
+
+private: // members
+
+    friend std::ostream& operator<< (std::ostream& os, const Channel& c) {
+        c.print(os); return os;
+    }
+
+    void print(std::ostream& s) const;
+
+    ChannelBuffer* buffer_;
+
+    friend class Log;
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class AutoIndent {
+
+    Channel& channel_;
+
+public:
+
+    AutoIndent(Channel& channel, const char* prefix = "") : channel_(channel) { channel_.indent(prefix); }
+    ~AutoIndent() { channel_.unindent(); }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/ChannelBuffer.cc b/eckit/src/eckit/log/ChannelBuffer.cc
new file mode 100644
index 0000000..6c210e9
--- /dev/null
+++ b/eckit/src/eckit/log/ChannelBuffer.cc
@@ -0,0 +1,152 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <ostream>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/ChannelBuffer.h"
+#include "eckit/log/IndentTarget.h"
+#include "eckit/log/LogTarget.h"
+#include "eckit/log/TeeTarget.h"
+#include "eckit/log/CallbackTarget.h"
+#include "eckit/log/FileTarget.h"
+#include "eckit/log/OStreamTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ChannelBuffer::ChannelBuffer( std::size_t size ) :
+    std::streambuf(),
+    target_(0),
+    buffer_( size  )
+{
+    ASSERT( size );
+    char *base = &buffer_.front();
+    setp(base, base + buffer_.size() );
+}
+
+ChannelBuffer::~ChannelBuffer()
+{
+    reset();
+}
+
+bool ChannelBuffer::active() const {
+    return target_ != 0;
+}
+
+
+void ChannelBuffer::setTarget(LogTarget* target) {
+    ASSERT(target);
+
+    sync();
+
+    target->attach();
+
+    if (target_) {
+        target_->detach();
+    }
+
+    target_ = target;
+
+}
+
+
+void ChannelBuffer::addTarget(LogTarget* target) {
+    ASSERT(target);
+    setTarget(new TeeTarget(target_, target));
+}
+
+void ChannelBuffer::reset() {
+    sync();
+    if (target_) {
+        target_->detach();
+        target_ = 0;
+    }
+}
+
+bool ChannelBuffer::dumpBuffer()
+{
+    if (target_) {
+        target_->write(pbase(), pptr());
+    }
+    setp(pbase(), epptr());
+    return true;
+}
+
+void ChannelBuffer::indent(const char* space) {
+    if (target_) {
+        setTarget(new IndentTarget(space, target_));
+    }
+}
+
+void ChannelBuffer::unindent() {
+    IndentTarget*  indent = dynamic_cast<IndentTarget*>(target_);
+    if (indent == 0) {
+        throw SeriousBug("Attempt to unindent a Channel that is not indented");
+    }
+    setTarget(indent->target_);
+
+}
+
+void ChannelBuffer::addCallback(channel_callback_t cb, void* data) {
+    setTarget(new TeeTarget(target_, new CallbackTarget(cb, data)));
+}
+
+void ChannelBuffer::setCallback(channel_callback_t cb, void* data) {
+    setTarget(new CallbackTarget(cb, data));
+}
+
+void ChannelBuffer::addStream(std::ostream& out) {
+    setTarget(new TeeTarget(target_, new OStreamTarget(out)));
+}
+
+void ChannelBuffer::setStream(std::ostream& out) {
+    setTarget(new OStreamTarget(out));
+}
+
+void ChannelBuffer::addFile(const std::string& path) {
+    setTarget(new TeeTarget(target_, new FileTarget(path)));
+}
+
+void ChannelBuffer::setFile(const std::string& path) {
+    setTarget(new FileTarget(path));
+}
+
+std::streambuf::int_type ChannelBuffer::overflow(std::streambuf::int_type ch)
+{
+    if (ch == traits_type::eof() ) { return sync(); }
+    dumpBuffer();
+    sputc(ch);
+    return traits_type::to_int_type(ch);
+}
+
+std::streambuf::int_type ChannelBuffer::sync()
+{
+    if ( dumpBuffer() )
+    {
+        if (target_) {
+            target_->flush();
+        }
+        return 0;
+    }
+    else return -1;
+}
+
+void ChannelBuffer::print(std::ostream& s) const
+{
+    s << "ChannelBuffer(size=" << buffer_.size();
+    if(target_) { s << ", target=" << *target_; }
+    s << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/ChannelBuffer.h b/eckit/src/eckit/log/ChannelBuffer.h
new file mode 100644
index 0000000..79cbc04
--- /dev/null
+++ b/eckit/src/eckit/log/ChannelBuffer.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file ChannelBuffer.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_log_ChannelBuffer_h
+#define eckit_log_ChannelBuffer_h
+
+#include <vector>
+#include <streambuf>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/log/Channel.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LogTarget;
+
+/// Stream buffer to be usedby Channel
+class ChannelBuffer :
+        public std::streambuf,
+        private NonCopyable {
+
+private: // methods
+
+    /// constructor, taking ownership of stream
+    ChannelBuffer( std::size_t size = 1024 );
+
+    virtual ~ChannelBuffer();
+
+    bool active() const;
+
+    void reset();
+
+    void setTarget(LogTarget* target);
+    void addTarget(LogTarget* target);
+
+    void indent(const char* space = "   ");
+    void unindent();
+
+    void setStream(std::ostream& out);
+    void addStream(std::ostream& out);
+
+    void setFile(const std::string& path);
+    void addFile(const std::string& path);
+
+    void setCallback(channel_callback_t cb, void* data = 0);
+    void addCallback(channel_callback_t cb, void* data = 0);
+
+protected: // methods
+
+    /// override this to change buffer behavior
+    /// @returns true if no error occured
+    virtual bool dumpBuffer();
+
+    /// typically you don't need to override this
+    /// @see dumpBuffer
+    virtual int_type overflow(int_type ch);
+
+    /// typically you don't need to override this
+    /// @see dumpBuffer
+    virtual int_type sync();
+
+protected: // members
+
+    LogTarget* target_;
+
+    std::vector<char> buffer_;
+
+private:
+
+    friend std::ostream& operator<< (std::ostream& os, const ChannelBuffer& c) {
+        c.print(os); return os;
+    }
+
+    void print(std::ostream& s) const;
+
+    friend class Channel;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/CodeLocation.cc b/eckit/src/eckit/log/CodeLocation.cc
new file mode 100644
index 0000000..a3afe71
--- /dev/null
+++ b/eckit/src/eckit/log/CodeLocation.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+#include <cstring>
+
+#include "eckit/log/CodeLocation.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::string CodeLocation::asString() const {
+    std::ostringstream oss;
+    print(oss);
+    return oss.str();
+}
+
+CodeLocation::operator bool() const {
+    return file_ != 0;
+}
+
+void CodeLocation::print(std::ostream& os) const {
+    if ( file_ ) {
+        os << " (" << file_ << " +" << line_;
+        if ( strlen(func_) )
+            os << " " << func_;
+        os << ")";
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/CodeLocation.h b/eckit/src/eckit/log/CodeLocation.h
new file mode 100644
index 0000000..b483c66
--- /dev/null
+++ b/eckit/src/eckit/log/CodeLocation.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file CodeLocation.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_CodeLocation_h
+#define eckit_log_CodeLocation_h
+
+#include "eckit/eckit.h"
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class CodeLocation {
+
+public: // methods
+
+    friend std::ostream& operator<<( std::ostream& s, const CodeLocation& loc ) { loc.print(s); return s; }
+
+    /// Empty contructor
+    CodeLocation():
+        line_(0), file_(0), func_(0) {}
+
+    /// Full Contructor
+    CodeLocation( const char * file, int line, const char * func):
+        line_(line), file_(file), func_(func) {}
+
+    /// @return as std::string
+    std::string asString() const;
+
+    /// conversion operator
+    operator std::string() const { return asString(); }
+
+    /// conversion to bool for checking if location was set
+    operator bool() const;
+
+    /// accessor to line
+    int line() const { return line_; }
+    /// accessor to file
+    const char * file() const { return file_; }
+    /// accessor to function
+    const char * func() const { return func_; }
+
+private: // members
+
+    int          line_;
+    const char * file_;
+    const char * func_;
+
+protected: // methods
+
+     void print(std::ostream&) const;
+};
+
+// Macros
+
+#define Here() ::eckit::CodeLocation( __FILE__ , __LINE__ , __FUNCTION__ )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Colour.cc b/eckit/src/eckit/log/Colour.cc
new file mode 100644
index 0000000..9f9b434
--- /dev/null
+++ b/eckit/src/eckit/log/Colour.cc
@@ -0,0 +1,181 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "eckit/log/Colour.h"
+#include "eckit/config/Resource.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static int xindex = std::ios::xalloc();
+
+enum { RESET = 0, BOLD  =  1,  UNDERLINE  =  4, BLINK =  5, REVERSE = 7, HIDDEN =8 } ;
+enum { BLACK = 0, RED     =  1, GREEN = 2,  YELLOW    = 3, BLUE = 4, MAGENTA = 5, CYAN  = 6, WHITE = 7};
+
+//#define X(a) putchar(a)
+//#define X(a) out << (a)
+#define X(a) out << char(a)
+
+static bool connected_to_console() {
+    int save_errno = errno;
+    bool result = ::isatty(1); // This may change errno
+    errno = save_errno;
+    return result;
+}
+
+static std::ostream& put(std::ostream& out, int fg, int bg, int attr)
+{
+    static bool colourOutput = Resource<bool>("$ECKIT_COLOUR_OUTPUT;-colour;colourOutput", connected_to_console());
+
+	if (colourOutput && out.iword(xindex) == 0)
+    {
+        int n = 0;
+        X( char(0x1B) );
+        X( '[');
+
+        if (attr>=0)
+        {
+            n++;
+            X( attr + '0');
+        }
+
+        if (fg >= 0) {
+            if (n) {
+                X( ';');
+            }
+            X( '3');
+            X( fg + '0');
+            n++;
+        }
+        if (bg >= 0) {
+            if (n) {
+                X( ';');
+            }
+            X( '4');
+            X( bg + '0');
+            n++;
+        }
+
+        X( 'm');
+
+    }
+
+    return out;
+}
+
+
+
+std::ostream& Colour::on(std::ostream& s) {
+    s.iword(xindex) = 0;
+    return s;
+}
+
+std::ostream& Colour::off(std::ostream& s) {
+    s.iword(xindex) = 1;
+    return s;
+}
+
+std::ostream& Colour::reset(std::ostream& s) {
+    return put(s,-1,-1,RESET);
+}
+
+std::ostream& Colour::bold(std::ostream& s) {
+    return put(s,-1,-1,BOLD);
+}
+
+std::ostream& Colour::underline(std::ostream& s) {
+    return put(s,-1,-1,UNDERLINE);
+}
+
+std::ostream& Colour::blink(std::ostream& s) {
+    return put(s,-1,-1,BLINK);
+}
+
+std::ostream& Colour::reverse(std::ostream& s) {
+    return put(s,-1,-1,REVERSE);
+}
+
+std::ostream& Colour::hidden(std::ostream& s) {
+    return put(s,-1,-1,HIDDEN);
+}
+
+std::ostream& Colour::black(std::ostream& s) {
+    return put(s,BLACK,-1,-1);
+}
+
+std::ostream& Colour::red(std::ostream& s) {
+    return put(s,RED,-1,-1);
+}
+
+std::ostream& Colour::green(std::ostream& s) {
+    return put(s,GREEN,-1,-1);
+}
+
+std::ostream& Colour::yellow(std::ostream& s) {
+    return put(s,YELLOW,-1,-1);
+}
+
+std::ostream& Colour::magenta(std::ostream& s) {
+    return put(s,MAGENTA,-1,-1);
+}
+
+std::ostream& Colour::blue(std::ostream& s) {
+    return put(s,BLUE,-1,-1);
+}
+
+std::ostream& Colour::cyan(std::ostream& s) {
+    return put(s,CYAN,-1,-1);
+}
+
+std::ostream& Colour::white(std::ostream& s) {
+    return put(s,WHITE,-1,-1);
+}
+
+std::ostream& Colour::blackBackground(std::ostream& s) {
+    return put(s,-1,BLACK,-1);
+}
+std::ostream& Colour::redBackground(std::ostream& s) {
+    return put(s,-1,RED,-1);
+}
+std::ostream& Colour::greenBackground(std::ostream& s) {
+    return put(s,-1,GREEN,-1);
+}
+
+std::ostream& Colour::yellowBackground(std::ostream& s) {
+    return put(s,-1,YELLOW,-1);
+}
+
+
+std::ostream& Colour::magentaBackground(std::ostream& s) {
+    return put(s,-1,MAGENTA,-1);
+}
+
+
+std::ostream& Colour::blueBackground(std::ostream& s) {
+    return put(s,-1,BLUE,-1);
+}
+
+std::ostream& Colour::cyanBackground(std::ostream& s) {
+    return put(s,-1,CYAN,-1);
+}
+
+std::ostream& Colour::whiteBackground(std::ostream& s) {
+    return put(s,-1,WHITE,-1);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/Colour.h b/eckit/src/eckit/log/Colour.h
new file mode 100644
index 0000000..19ad0de
--- /dev/null
+++ b/eckit/src/eckit/log/Colour.h
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Colour.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Nov 2011
+
+#ifndef eckit_Colour_h
+#define eckit_Colour_h
+
+#include "eckit/eckit.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Colour {
+public:
+
+    static std::ostream& on(std::ostream&);
+    static std::ostream& off(std::ostream&);
+
+    static std::ostream& reset(std::ostream&);
+
+    static std::ostream& bold(std::ostream&);
+    static std::ostream& underline(std::ostream&);
+    static std::ostream& blink(std::ostream&);
+    static std::ostream& reverse(std::ostream&);
+    static std::ostream& hidden(std::ostream&);
+
+    static std::ostream& black(std::ostream&);
+    static std::ostream& red(std::ostream&);
+    static std::ostream& green(std::ostream&);
+    static std::ostream& yellow(std::ostream&);
+    static std::ostream& magenta(std::ostream&);
+    static std::ostream& blue(std::ostream&);
+    static std::ostream& cyan(std::ostream&);
+    static std::ostream& white(std::ostream&);
+
+    static std::ostream& blackBackground(std::ostream&);
+    static std::ostream& redBackground(std::ostream&);
+    static std::ostream& greenBackground(std::ostream&);
+    static std::ostream& yellowBackground(std::ostream&);
+    static std::ostream& magentaBackground(std::ostream&);
+    static std::ostream& blueBackground(std::ostream&);
+    static std::ostream& cyanBackground(std::ostream&);
+    static std::ostream& whiteBackground(std::ostream&);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/ColouringTarget.cc b/eckit/src/eckit/log/ColouringTarget.cc
new file mode 100644
index 0000000..d664e35
--- /dev/null
+++ b/eckit/src/eckit/log/ColouringTarget.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <ostream>
+
+#include "eckit/log/ColouringTarget.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ColouringTarget::ColouringTarget(LogTarget* target, ColouringTarget::colourproc begin, ColouringTarget::colourproc end) :
+    WrapperTarget(target) {
+
+    std::ostringstream beginss;
+    beginss << *begin;
+    begin_ = beginss.str();
+
+    std::ostringstream endss;
+    endss << *end;
+    end_ = endss.str();
+}
+
+ColouringTarget::~ColouringTarget()
+{
+}
+
+void ColouringTarget::writePrefix() {
+    target_->write(begin_.c_str(), begin_.c_str() + begin_.size());
+}
+
+void ColouringTarget::writeSuffix() {
+    target_->write(end_.c_str(), end_.c_str() + end_.size());
+}
+
+void ColouringTarget::print(std::ostream& s) const
+{
+    s << "ColouringTarget(";
+    if(target_)  {s << "target=" << *target_;}
+    s << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/ColouringTarget.h b/eckit/src/eckit/log/ColouringTarget.h
new file mode 100644
index 0000000..db5845e
--- /dev/null
+++ b/eckit/src/eckit/log/ColouringTarget.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file ColouringTarget.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_log_ColouringTarget_h
+#define eckit_log_ColouringTarget_h
+
+#include <string>
+
+#include "eckit/log/Colour.h"
+#include "eckit/log/WrapperTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ColouringTarget : public WrapperTarget {
+public:
+
+    typedef std::ostream& (*colourproc)(std::ostream&);
+
+    ColouringTarget(LogTarget* target, colourproc begin, colourproc end = &Colour::reset);
+
+    virtual ~ColouringTarget();
+
+protected:
+    void print(std::ostream& s) const;
+private:
+
+    virtual void writePrefix();
+    virtual void writeSuffix();
+
+    std::string begin_;
+    std::string end_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/ETA.cc b/eckit/src/eckit/log/ETA.cc
new file mode 100644
index 0000000..09366cf
--- /dev/null
+++ b/eckit/src/eckit/log/ETA.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/ETA.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ETA::ETA(double ETA):
+	ETA_(ETA)
+{
+}
+
+ETA::ETA(const ::timeval& time):
+	ETA_(time.tv_sec + time.tv_usec / 1000000.0)
+{
+}
+
+std::ostream& operator<<(std::ostream& s,const ETA&  sec)
+{
+	double t = sec.ETA_;
+    long n  = t;
+
+    long hour = n / (60 * 60); n %= (60 * 60);
+    long minutes = n / 60 ; n %= 60 ;
+
+    s << hour << ':' << std::setfill('0') << std::setw(2) << minutes << ':'  << std::setfill('0') << std::setw(2) << n;
+
+	return s;
+}
+
+ETA::operator std::string() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/ETA.h b/eckit/src/eckit/log/ETA.h
new file mode 100644
index 0000000..d1ba178
--- /dev/null
+++ b/eckit/src/eckit/log/ETA.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ETA.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_ETA_h
+#define eckit_ETA_h
+
+#include <sys/time.h>
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ETA {
+public:
+
+// -- Contructors
+
+	ETA(double);
+	ETA(const struct ::timeval&);
+
+// -- Operators
+
+	operator std::string() const;
+
+	friend std::ostream& operator<<(std::ostream&,const ETA&);
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Members
+
+	double ETA_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/FileTarget.cc b/eckit/src/eckit/log/FileTarget.cc
new file mode 100644
index 0000000..d212d1e
--- /dev/null
+++ b/eckit/src/eckit/log/FileTarget.cc
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/FileTarget.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+FileTarget::FileTarget(const PathName &path):
+    out_( path.asString().c_str() ), path_(path)
+{
+    if(!out_)
+        throw eckit::CantOpenFile(path.asString());
+//    std::cerr << "FileTarget created : " << path_ << std::endl;
+}
+
+FileTarget::~FileTarget() {
+//    std::cerr << "FileTarget::~FileTarget() -- " << path_ << std::endl;
+}
+
+void FileTarget::write(const char* start, const char* end) {
+    out_.write(start, end - start);
+}
+
+void FileTarget::flush() {
+    out_.flush();
+}
+
+void FileTarget::print(std::ostream& s) const
+{
+    s << "FileTarget(path=" << path_ << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/FileTarget.h b/eckit/src/eckit/log/FileTarget.h
new file mode 100644
index 0000000..e55a8d4
--- /dev/null
+++ b/eckit/src/eckit/log/FileTarget.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file FileTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_FileTarget_h
+#define eckit_log_FileTarget_h
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/LogTarget.h"
+
+
+namespace eckit {
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FileTarget : public LogTarget {
+public:
+
+    FileTarget( const PathName& path );
+    ~FileTarget();
+
+private:
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+
+    virtual void print(std::ostream& s) const;
+
+private:
+
+    std::ofstream out_;
+    
+    PathName path_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/IndentTarget.cc b/eckit/src/eckit/log/IndentTarget.cc
new file mode 100644
index 0000000..dea76a6
--- /dev/null
+++ b/eckit/src/eckit/log/IndentTarget.cc
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/IndentTarget.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+IndentTarget::IndentTarget(const std::string& prefix, LogTarget* target, const char* space):
+    PrefixTarget("   " + prefix, target, space)
+{
+
+}
+
+void IndentTarget::print(std::ostream& s) const
+{
+    s << "IndentTarget(prefix=" << prefix_ << ", space=" << space_;
+    if(target_) { s << ", target=" << *target_; }
+    s << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/IndentTarget.h b/eckit/src/eckit/log/IndentTarget.h
new file mode 100644
index 0000000..b6ed48d
--- /dev/null
+++ b/eckit/src/eckit/log/IndentTarget.h
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file IndentTarget.h
+/// @author Baudouin Raoult
+
+#ifndef eckit_log_IndentTarget_h
+#define eckit_log_IndentTarget_h
+
+#include <utility>
+
+#include "eckit/log/PrefixTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class IndentTarget : public PrefixTarget {
+public:
+
+    IndentTarget(const std::string& prefix, LogTarget* target, const char* space = " ");
+
+protected:
+    void print(std::ostream& s) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/LineBasedTarget.cc b/eckit/src/eckit/log/LineBasedTarget.cc
new file mode 100644
index 0000000..ee72a7b
--- /dev/null
+++ b/eckit/src/eckit/log/LineBasedTarget.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/LineBasedTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+LineBasedTarget::LineBasedTarget():
+    size_(4096),
+    buffer_(new char[size_]),
+    position_(0) {
+    ASSERT(buffer_);
+}
+
+LineBasedTarget::~LineBasedTarget() {
+    delete[] buffer_;
+}
+
+static inline size_t round(size_t x, size_t n) {
+    return ((x + n - 1) / n) * n;
+}
+
+void LineBasedTarget::reserve(size_t size) {
+    if (size_ < size) {
+        delete[] buffer_;
+        size_ = round(size, 1024 * 1024);
+        buffer_ = new char[size_];
+        ASSERT(buffer_);
+    }
+}
+
+void LineBasedTarget::write(const char* start, const char* end) {
+
+    reserve(position_ + (end - start) + 1);
+
+    while (start != end) {
+        if (*start == '\n') {
+            buffer_[position_] = 0;
+            line(buffer_);
+            position_ = 0;
+            start++;
+        }
+        else {
+            buffer_[position_ ++ ] = *start++;
+        }
+    }
+
+}
+
+void LineBasedTarget::flush() {
+    // LineBasedTarget doesn't flush() since the concrete classes treat each line independently
+    // and upon write(), and often don't require further flushing
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/LineBasedTarget.h b/eckit/src/eckit/log/LineBasedTarget.h
new file mode 100644
index 0000000..95af728
--- /dev/null
+++ b/eckit/src/eckit/log/LineBasedTarget.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file LineBasedTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_LineBasedTarget_h
+#define eckit_log_LineBasedTarget_h
+
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LineBasedTarget : public LogTarget {
+
+protected: // methods
+
+    LineBasedTarget();
+    ~LineBasedTarget();
+
+private:
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+    virtual void line(const char* line) = 0;
+
+    void reserve(size_t size);
+
+
+    size_t size_;
+    char* buffer_;
+    size_t position_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Log.cc b/eckit/src/eckit/log/Log.cc
new file mode 100644
index 0000000..16c2f7a
--- /dev/null
+++ b/eckit/src/eckit/log/Log.cc
@@ -0,0 +1,398 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/config/LibEcKit.h"
+#include "eckit/log/Log.h"
+
+#include "eckit/thread/AutoLock.h"
+
+#include "eckit/log/Channel.h"
+#include "eckit/log/UserChannel.h"
+#include "eckit/log/StatusTarget.h"
+#include "eckit/log/MessageTarget.h"
+#include "eckit/log/OStreamTarget.h"
+#include "eckit/log/PrefixTarget.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/log/FileTarget.h"
+
+#include "eckit/runtime/Main.h"
+#include "eckit/system/Library.h"
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/thread/ThreadSingleton.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if defined(_GNU_SOURCE)
+
+/* To use with GNU libc strerror_r */
+
+static void handle_strerror_r(std::ostream& s, int e, char[], char* p )
+{
+    if (p) {
+        s << " (" << p << ")";
+    }
+    else {
+        s << " (errno = " << e << ") ";
+    }
+}
+
+#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || _POSIX_VERSION >= 200112L || _XOPEN_VERSION >= 600)
+
+/* To use with XSI-compliant strerror_r
+ *
+ * Linux defines _POSIX_C_SOURCE and _XOPEN_SOURCE
+ * BSD defines _POSIX_VERSION and _XOPEN_VERSION
+ * glibc defines _GNU_SOURCE and implements a non-XSI compliant strerror_r
+ */
+
+static void handle_strerror_r(std::ostream& s, int e, char es[], int hs )
+{
+    if ( hs == 0 ) {
+        s << " (" << es << ") " ;
+    }
+    else {
+        s << " (errno = " << e << ") ";
+    }
+}
+
+#else /* we don't know what to do */
+
+#warning "eckit doesn't recognise stderror_r since this isn't a GNU libc or a supported UNIX"
+
+/* This uses the deprecated sys_errlist[] error arrays */
+
+static void handle_strerror_r(std::ostream& s, int e, ... ) {
+    if (e < sys_nerr)
+        s << " (" << sys_errlist[e] << ") " ;
+    else
+        s << " (errno = " << e << ") ";
+}
+
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct CreateStatusChannel {
+    Channel* operator()() {
+        return new Channel(new StatusTarget());
+    }
+};
+
+std::ostream& Log::status()
+{
+    static ThreadSingleton<Channel, CreateStatusChannel> x;
+    return x.instance();
+}
+
+struct CreateMessageChannel {
+    Channel* operator()() {
+        return new Channel(new MessageTarget());
+    }
+};
+
+std::ostream& Log::message()
+{
+    static ThreadSingleton<Channel, CreateMessageChannel> x;
+    return x.instance();
+}
+
+
+
+struct CreateLogChannel {
+
+    virtual Channel* createChannel() = 0;
+
+    Channel* operator()() {
+        try {
+            return createChannel();
+        }
+        catch (std::exception& e) {
+            std::cerr << "Exception caught when creating channel: " << e.what() << std::endl;
+            return new Channel(new OStreamTarget(std::cout));
+        }
+    }
+};
+
+struct CreateInfoChannel : public CreateLogChannel {
+    virtual Channel* createChannel() { return new Channel(Main::instance().createInfoLogTarget()); }
+};
+
+Channel& Log::info()
+{
+    if (!Main::ready()) {
+        static Channel empty(new PrefixTarget("PRE-MAIN-INFO", new OStreamTarget(std::cout)));
+        return empty;
+    }
+    static ThreadSingleton<Channel, CreateInfoChannel> x;
+    return x.instance();
+}
+
+struct CreateErrorChannel : public CreateLogChannel {
+    virtual Channel* createChannel() { return new Channel(Main::instance().createErrorLogTarget()); }
+};
+
+Channel& Log::error()
+{
+    if (!Main::ready()) {
+        static Channel empty(new PrefixTarget("PRE-MAIN-ERROR", new OStreamTarget(std::cout)));
+        return empty;
+    }
+    static ThreadSingleton<Channel, CreateErrorChannel> x;
+    return x.instance();
+}
+
+struct CreateWarningChannel : public CreateLogChannel {
+    virtual Channel* createChannel() { return new Channel(Main::instance().createWarningLogTarget()); }
+};
+
+Channel& Log::warning()
+{
+    if (!Main::ready()) {
+        static Channel empty(new PrefixTarget("PRE-MAIN-WARNING", new OStreamTarget(std::cout)));
+        return empty;
+    }
+    static ThreadSingleton<Channel, CreateWarningChannel> x;
+    return x.instance();
+}
+
+struct CreateDebugChannel : public CreateLogChannel {
+    virtual Channel* createChannel() {
+        return new Channel(Main::instance().createDebugLogTarget());
+    }
+};
+
+Channel& Log::debug()
+{
+    if (!Main::ready()) {
+
+
+        const char* e = getenv("DEBUG");
+
+        if (e && bool(Translator<std::string, bool>()(e))) {
+            static Channel empty(new PrefixTarget("PRE-MAIN-DEBUG", new OStreamTarget(std::cout)));
+            return empty;
+        }
+        else {
+            static Channel empty;
+            return empty;
+        }
+    }
+
+    if (!Main::instance().debug_) {
+        static Channel empty;
+        return empty;
+    }
+
+
+    static ThreadSingleton<Channel, CreateDebugChannel> x;
+    return x.instance();
+}
+
+
+std::ostream& Log::panic()
+{
+    try {
+        return Log::error();
+    }
+    catch (std::exception&) {
+        return std::cerr;
+    }
+}
+
+
+UserChannel& Log::user()
+{
+    static ThreadSingleton<UserChannel> x;
+    return x.instance();
+}
+
+std::ostream& Log::userInfo()
+{
+    UserChannel& u = user();
+    u.msgType(UserChannel::INFO);
+    return u;
+}
+
+std::ostream& Log::userError()
+{
+    UserChannel& u = user();
+    u.msgType(UserChannel::ERROR);
+    return u;
+}
+
+std::ostream& Log::userWarning()
+{
+    UserChannel& u = user();
+    u.msgType(UserChannel::WARN);
+    return u;
+}
+
+void Log::notifyClient(const std::string& msg)
+{
+    UserChannel& u = user();
+    UserMsg* um = u.userMsg();
+    if (um) um->notifyClient(msg);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void Log::setStream(std::ostream& out) {
+    info().setStream(out);
+    warning().setStream(out);
+    error().setStream(out);
+    if (debug()) {
+        debug().setStream(out);
+    }
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().setStream(out);
+    }
+}
+void Log::addStream(std::ostream& out) {
+    info().addStream(out);
+    warning().addStream(out);
+    error().addStream(out);
+    if (debug()) {
+        debug().addStream(out);
+    }
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().addStream(out);
+    }
+}
+
+void Log::setFile(const std::string& path) {
+
+    LogTarget* file = new FileTarget(path);
+
+    info().setTarget(file);
+    warning().setTarget(file);
+    error().setTarget(file);
+    if (debug()) {
+        debug().setTarget(file);
+    }
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().setTarget(file);
+    }
+}
+
+void Log::addFile(const std::string& path) {
+    LogTarget* file = new FileTarget(path);
+
+    info().addTarget(file);
+    warning().addTarget(file);
+    error().addTarget(file);
+    if (debug()) {
+        debug().addTarget(file);
+    }
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().addTarget(file);
+    }
+}
+
+void Log::setCallback(channel_callback_t cb, void* data) {
+    info().setCallback(cb, data);
+    warning().setCallback(cb, data);
+    error().setCallback(cb, data);
+    if (debug()) {
+        debug().setCallback(cb, data);
+    }
+
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        if (system::Library::lookup(*libname).debugChannel()) {
+            system::Library::lookup(*libname).debugChannel().setCallback(cb, data);
+        }
+    }
+}
+
+void Log::addCallback(channel_callback_t cb, void* data) {
+    info().addCallback(cb, data);
+    warning().addCallback(cb, data);
+    error().addCallback(cb, data);
+    if (debug()) {
+        debug().addCallback(cb, data);
+    }
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().addCallback(cb, data);
+    }
+}
+
+void Log::flush()
+{
+    info().flush();
+    warning().flush();
+    error().flush();
+    debug().flush();
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().flush();
+    }
+}
+
+void Log::reset() {
+    info().reset();
+    warning().reset();
+    error().reset();
+    debug().reset();
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        system::Library::lookup(*libname).debugChannel().reset();
+    }
+}
+
+void Log::print(std::ostream& os) {
+    os << "Log::info() "    << info()    << std::endl;
+    os << "Log::warning() " << warning() << std::endl;
+    os << "Log::error() "   << error()   << std::endl;
+    os << "Log::debug() "   << debug()   << std::endl;
+    std::vector<std::string> libs = eckit::system::Library::list();
+    for(std::vector<std::string>::iterator libname = libs.begin(); libname != libs.end(); ++libname) {
+        os << *libname << ".debug() " << system::Library::lookup(*libname).debugChannel() << std::endl;
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::ostream& Log::syserr(std::ostream& s)
+{
+    int e = errno;
+    char estr [256];
+    handle_strerror_r(s, e, estr, strerror_r( e, estr, sizeof(estr) ) );
+    return s;
+}
+
+static int xindex = std::ios::xalloc();
+
+int format(std::ostream& s)
+{
+    return s.iword(xindex);
+}
+
+std::ostream& setformat(std::ostream& s, int n)
+{
+    s.iword(xindex) = n;
+    return s;
+}
+
+template class ThreadSingleton<UserChannel>;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/Log.h b/eckit/src/eckit/log/Log.h
new file mode 100644
index 0000000..093e4be
--- /dev/null
+++ b/eckit/src/eckit/log/Log.h
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Log.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date May 1996
+
+#ifndef eckit_log_Log_h
+#define eckit_log_Log_h
+
+#include "eckit/log/Channel.h"
+#include "eckit/log/UserChannel.h"
+
+namespace eckit {
+
+typedef void (*channel_callback_t) (void* data, const char* msg);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Singleton holding global streams for logging
+///
+
+class Log {
+
+public: // types
+
+    /// Output formats
+    enum {
+        compactFormat = 0,
+        normalFormat = 1,
+        fullFormat = 2,
+        monitorFormat = 3,
+        applicationFormat = 4,  // Free to use for applications
+    };
+
+public: // methods
+
+    /// Channel for debug output
+    static Channel& debug();
+
+    /// Channel for informative messages
+    static Channel& info();
+
+    /// Channel for warning messages
+    static Channel& warning();
+
+    /// Channel for error messages
+    static Channel& error();
+
+    /// Channel for panic messages
+    static std::ostream& panic();
+
+    /// Channel accessible through category index
+    // static Channel& channel(int cat);
+
+    /// Channel for status messages to Application Monitor
+    static std::ostream& status();
+    /// Channel for status messages to Application Monitor
+    static std::ostream& message();
+
+    /// Get the channel for the user
+    static UserChannel& user();
+    /// Channel for informative messages tp remote user
+    static std::ostream& userInfo();
+    /// Channel for warning messages to remote user
+    static std::ostream& userWarning();
+    /// Channel for error messages to remote user
+    static std::ostream& userError();
+    /// Send messages to remote user directly -- not using a channel
+    static void notifyClient(const std::string&);
+
+    /// manipulator that will print the last error message as in perror(2)
+    static std::ostream& syserr(std::ostream&);
+
+    static Channel& null();
+
+    template<typename T>
+    static Channel& debug(const T* = 0) {
+        return T::instance().debugChannel();
+    }
+
+    static void setStream(std::ostream& out);
+    static void addStream(std::ostream& out);
+
+    static void setFile(const std::string& path);
+    static void addFile(const std::string& path);
+
+    static void setCallback(channel_callback_t cb, void* data = 0);
+    static void addCallback(channel_callback_t cb, void* data = 0);
+
+    static void flush();
+    static void reset();
+
+    static void print(std::ostream& os);
+
+private: // methods
+
+    Log();   ///< Private, non-instanciatable class
+    ~Log();  ///< Private, non-instanciatable class
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::ostream& setformat(std::ostream&, int);
+int format(std::ostream&);
+
+#define ECKIT_DEBUG_HERE std::cerr << " DEBUG () @ " << Here() << std::endl;
+#define ECKIT_DEBUG_VAR(x) std::cerr << " DEBUG (" << #x << ":" << x << ") @ " << Here() << std::endl;
+
+
+// Non-flushing version of std::endl
+inline std::ostream& newl(std::ostream& out) {
+    return out << '\n';
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/LogTarget.cc b/eckit/src/eckit/log/LogTarget.cc
new file mode 100644
index 0000000..a115e32
--- /dev/null
+++ b/eckit/src/eckit/log/LogTarget.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+LogTarget::LogTarget() {
+}
+
+LogTarget::~LogTarget() {
+}
+
+void LogTarget::print(std::ostream& s) const
+{
+    NOTIMP;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/LogTarget.h b/eckit/src/eckit/log/LogTarget.h
new file mode 100644
index 0000000..a7b94e1
--- /dev/null
+++ b/eckit/src/eckit/log/LogTarget.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file LogTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_LogTarget_h
+#define eckit_log_LogTarget_h
+
+#include <iosfwd>
+#include <vector>
+
+#include "eckit/log/CodeLocation.h"
+#include "eckit/memory/Counted.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LogTarget : public Counted {
+
+public: // methods
+
+    LogTarget();
+
+
+    virtual void write(const char* start, const char* end) = 0;
+    virtual void flush() = 0;
+
+    virtual ~LogTarget();
+
+protected:
+
+    friend std::ostream& operator<< (std::ostream& os, const LogTarget& c) {
+        c.print(os); return os;
+    }
+
+    virtual void print(std::ostream& s) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/MessageTarget.cc b/eckit/src/eckit/log/MessageTarget.cc
new file mode 100644
index 0000000..ebc03fc
--- /dev/null
+++ b/eckit/src/eckit/log/MessageTarget.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/MessageTarget.h"
+#include "eckit/runtime/Monitor.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MessageTarget::MessageTarget() {
+}
+
+void MessageTarget::line(const char* line) {
+    Monitor::instance().message(line);
+}
+
+void MessageTarget::print(std::ostream& s) const
+{
+    s << "MessageTarget()";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/MessageTarget.h b/eckit/src/eckit/log/MessageTarget.h
new file mode 100644
index 0000000..4e189f2
--- /dev/null
+++ b/eckit/src/eckit/log/MessageTarget.h
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file MessageTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_MessageTarget_h
+#define eckit_log_MessageTarget_h
+
+
+#include "eckit/log/LineBasedTarget.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MessageTarget : public LineBasedTarget {
+
+public: // methods
+
+    MessageTarget();
+
+private:
+
+    virtual void line(const char* line);
+    virtual void print(std::ostream& s) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/OStreamHandle.h b/eckit/src/eckit/log/OStreamHandle.h
new file mode 100644
index 0000000..4c1e817
--- /dev/null
+++ b/eckit/src/eckit/log/OStreamHandle.h
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file OStreamHandle.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_OStreamHandle_h
+#define eckit_log_OStreamHandle_h
+
+#include <iosfwd>
+#include <cassert>
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ostream_handle {
+public:
+    
+    explicit ostream_handle() : ptr_(0), owned_(false) {}
+    
+    explicit ostream_handle( std::ostream* os ) : ptr_(os), owned_(true) {}
+    
+    explicit ostream_handle( std::ostream& os ) : ptr_(&os), owned_(false) {}
+
+    ~ostream_handle(){ release(); }
+    
+    /// copy transfers ownership
+    ostream_handle(const ostream_handle& o) 
+    { 
+        ptr_ = o.ptr_;
+        owned_ = o.owned_;
+        o.owned_ = false;
+    }
+
+    /// assigment transfers ownership
+    ostream_handle& operator=(const ostream_handle& o)
+    {
+        release();
+        ptr_ = o.ptr_;
+        owned_ = o.owned_;
+        o.owned_ = false;
+        return *this;
+    }
+
+    std::ostream* get() const { return ptr_; }
+    
+    void reset( std::ostream* os )
+    {
+        release();
+        ptr_ = os;
+        owned_ = true;
+    }
+
+    void reset( std::ostream& os)
+    {
+        release();
+        ptr_ = &os;
+        owned_ = false;
+    }
+
+    /// Dereferences the pointee
+    std::ostream& operator*() const { assert(ptr_); return *ptr_; }
+
+    /// Calling operator
+    std::ostream* operator->() const { assert(ptr_); return ptr_; }
+
+    /// @returns true if pointer is not null
+    operator bool() const { return valid(); }
+
+private:
+    
+    bool valid() const { return (ptr_ != 0); }
+    void release() 
+    {
+        if( owned_ && valid() ) delete ptr_; 
+        ptr_ = 0; 
+    }
+    
+    std::ostream* ptr_;
+    mutable bool owned_;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/OStreamTarget.cc b/eckit/src/eckit/log/OStreamTarget.cc
new file mode 100644
index 0000000..3729c39
--- /dev/null
+++ b/eckit/src/eckit/log/OStreamTarget.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/OStreamTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+OStreamTarget::OStreamTarget(std::ostream& out):
+    out_(out) {
+}
+
+OStreamTarget::~OStreamTarget() {
+//    std::cerr << "OStreamTarget::~OStreamTarget()" << std::endl;
+}
+
+void OStreamTarget::write(const char* start, const char* end) {
+    out_.write(start, end - start);
+}
+void OStreamTarget::flush() {
+    out_.flush();
+}
+
+void OStreamTarget::print(std::ostream& s) const
+{
+    s << "OStreamTarget()";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/OStreamTarget.h b/eckit/src/eckit/log/OStreamTarget.h
new file mode 100644
index 0000000..7231dfa
--- /dev/null
+++ b/eckit/src/eckit/log/OStreamTarget.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file OStreamTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_OStreamTarget_h
+#define eckit_log_OStreamTarget_h
+
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class OStreamTarget : public LogTarget {
+
+public: // methods
+
+    OStreamTarget(std::ostream& out);
+
+    virtual ~OStreamTarget();
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+    virtual void print(std::ostream& s) const;
+
+private:
+
+    std::ostream& out_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Plural.h b/eckit/src/eckit/log/Plural.h
new file mode 100644
index 0000000..2651773
--- /dev/null
+++ b/eckit/src/eckit/log/Plural.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_Plural_h
+#define eckit_Plural_h
+
+#include "eckit/log/BigNum.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Plural {
+public:
+
+// -- Contructors
+
+	Plural(int count,const std::string& s) : s_(s),count_(count) {}
+
+// -- Destructor
+
+	~Plural() {}
+
+protected:
+
+// -- Methods
+	
+	void print(std::ostream& s) const 
+	{
+		s << BigNum(count_) << ' ' << s_;
+		if(count_>1) s << 's';
+	}
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+	std::string s_;
+	int count_;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const Plural& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/PrefixTarget.cc b/eckit/src/eckit/log/PrefixTarget.cc
new file mode 100644
index 0000000..8926281
--- /dev/null
+++ b/eckit/src/eckit/log/PrefixTarget.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/PrefixTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+PrefixTarget::PrefixTarget(const std::string& prefix, LogTarget* target, const char* space):
+    WrapperTarget(target),
+    prefix_(prefix),
+    space_(space),
+    prefixLength_(prefix.size()),
+    spaceLength_(::strlen(space))
+{
+}
+
+void PrefixTarget::writePrefix() {
+    const char* p = prefix_.c_str();
+    target_->write(p, p + prefixLength_);
+    target_->write(space_, space_ + spaceLength_);
+}
+
+
+void PrefixTarget::writeSuffix() {
+}
+
+void PrefixTarget::print(std::ostream& s) const
+{
+    s << "PrefixTarget(prefix=" << prefix_ << ", space=" << space_ << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/PrefixTarget.h b/eckit/src/eckit/log/PrefixTarget.h
new file mode 100644
index 0000000..b44045b
--- /dev/null
+++ b/eckit/src/eckit/log/PrefixTarget.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file PrefixTarget.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_log_PrefixTarget_h
+#define eckit_log_PrefixTarget_h
+
+#include <utility>
+
+#include "eckit/log/WrapperTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class PrefixTarget : public WrapperTarget {
+public:
+
+    PrefixTarget(const std::string& prefix, LogTarget* target = 0, const char* space = " ");
+
+private:
+
+    virtual void writePrefix();
+    virtual void writeSuffix();
+
+protected:
+
+    void print(std::ostream& s) const;
+
+protected:
+
+    std::string prefix_;
+    const char* space_;
+    size_t prefixLength_;
+    size_t spaceLength_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Progress.cc b/eckit/src/eckit/log/Progress.cc
new file mode 100644
index 0000000..fa39bdb
--- /dev/null
+++ b/eckit/src/eckit/log/Progress.cc
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/Progress.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Progress::Progress(const std::string& name, unsigned long long min, unsigned long long  max )
+{
+	Monitor::instance().progress(name,min,max);
+}
+
+Progress::~Progress()
+{
+	Monitor::instance().progress();
+}
+
+void Progress::operator()(unsigned long long value)
+{
+	Monitor::instance().progress(value);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/Progress.h b/eckit/src/eckit/log/Progress.h
new file mode 100644
index 0000000..7962e8f
--- /dev/null
+++ b/eckit/src/eckit/log/Progress.h
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Progress.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_Progress_h
+#define eckit_Progress_h
+
+#include <string>
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Progress {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	Progress(const std::string&,unsigned long long,unsigned long long);
+
+// -- Destructor
+
+	~Progress(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+
+	void operator()(unsigned long long);
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+
+	// void print(std::ostream&) const; 
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	Progress(const Progress&);
+	Progress& operator=(const Progress&);
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const Progress& p)
+	//	{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/ResourceUsage.cc b/eckit/src/eckit/log/ResourceUsage.cc
new file mode 100644
index 0000000..a05f3f3
--- /dev/null
+++ b/eckit/src/eckit/log/ResourceUsage.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/log/Seconds.h"
+#include "eckit/log/ResourceUsage.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Bytes.h"
+
+#include "eckit/system/SystemInfo.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ResourceUsage::ResourceUsage():
+    name_("unnamed"),
+    out_( std::cout )
+{
+    init();
+}
+
+ResourceUsage::ResourceUsage(const std::string& name, std::ostream& o ):
+    name_(name),
+    out_(o)
+{
+    init();
+}
+
+ResourceUsage::ResourceUsage(const char* name, std::ostream& o ):
+    name_(name),
+    out_(o)
+{
+    init();
+}
+
+void ResourceUsage::init() {
+
+    using namespace eckit::system;
+    const SystemInfo& sysinfo = SystemInfo::instance();
+
+    rss_ = sysinfo.memoryUsage().resident_size_;
+    malloc_ = sysinfo.memoryAllocated();
+
+    out_ << name_ << " => resident size: "
+         << eckit::Bytes(rss_);
+    out_ << ", allocated: "
+         << eckit::Bytes(malloc_);
+    out_ << std::endl;
+}
+
+ResourceUsage::~ResourceUsage()
+{
+    using namespace eckit::system;
+    const SystemInfo& sysinfo = SystemInfo::instance();
+
+    size_t rss = sysinfo.memoryUsage().resident_size_;
+    size_t malloc = sysinfo.memoryAllocated();
+
+    out_ << name_ << " <= resident size: "
+         << eckit::Bytes(rss);
+
+    if ( rss > rss_) {
+        out_ << " (+" << eckit::Bytes(rss - rss_) << ")";
+    }
+
+    if ( rss < rss_) {
+        out_ << " (-" << eckit::Bytes(rss_ - rss) << ")";
+    }
+
+    out_ << ", allocated: "
+         << eckit::Bytes(malloc);
+
+    if ( malloc > malloc_) {
+        out_ << " (+" << eckit::Bytes(malloc - malloc_) << ")";
+    }
+
+    if ( malloc < malloc_) {
+        out_ << " (-" << eckit::Bytes(malloc_ - malloc) << ")";
+    }
+    out_ << std::endl;
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/ResourceUsage.h b/eckit/src/eckit/log/ResourceUsage.h
new file mode 100644
index 0000000..ca5d974
--- /dev/null
+++ b/eckit/src/eckit/log/ResourceUsage.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ResourceUsage.h
+// Baudouin Raoult - ECMWF Oct 16
+
+#ifndef eckit_ResourceUsage_h
+#define eckit_ResourceUsage_h
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "eckit/log/Log.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ResourceUsage : private NonCopyable {
+public:
+
+    explicit ResourceUsage();
+
+    /// @param name of the timer, used for output
+    /// @param o output stream to use  for output
+    explicit ResourceUsage( const std::string& name, std::ostream& o = Log::info() );
+
+    /// @param name of the timer, used for output
+    /// @param o output stream to use  for output
+    explicit ResourceUsage( const char* name, std::ostream& o = Log::info() );
+
+    ~ResourceUsage();
+
+protected: // methods
+
+    void init();
+
+private: // members
+
+    std::string    name_;
+    std::ostream&  out_;
+
+    size_t rss_;
+    size_t malloc_;
+};
+
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class TraceResourceUsage : public ResourceUsage {
+public:
+
+    explicit TraceResourceUsage( const char* name):
+        ResourceUsage(name, eckit::Log::debug<T>()) {}
+
+    explicit TraceResourceUsage( const std::string& name):
+        ResourceUsage(name, eckit::Log::debug<T>()) {}
+};
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/RotationTarget.cc b/eckit/src/eckit/log/RotationTarget.cc
new file mode 100644
index 0000000..d266e2f
--- /dev/null
+++ b/eckit/src/eckit/log/RotationTarget.cc
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/RotationTarget.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/TimeStamp.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/StaticMutex.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static StaticMutex local_mutex;
+
+static std::ofstream* last      = 0;
+static time_t         lastTime  = 0;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static std::ostream& rotout() {
+
+    time_t now = ::time(0) / 86400;
+
+    if(now != lastTime || last == 0) {
+
+        static std::string logfileFormat = Resource<std::string>("logfileFormat","~/log/%Y-%m-%d/out");
+
+        TimeStamp ts(logfileFormat);
+        PathName path(ts);
+        path.mkdir(0777);
+
+        std::ostringstream os;
+        os << path  << "/" << Main::instance().name();
+
+        delete last;
+
+        /// @todo Find a way to set the close on exec flags
+        last = new std::ofstream(os.str().c_str(), std::ios::app);
+
+        lastTime = now;
+    }
+
+    return *last;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+RotationTarget::RotationTarget() {
+}
+
+RotationTarget::~RotationTarget() {
+}
+
+void RotationTarget::write(const char* start, const char* end) {
+    AutoLock<StaticMutex> lock(local_mutex);
+    rotout().write(start, end - start);
+}
+void RotationTarget::flush() {
+    AutoLock<StaticMutex> lock(local_mutex);
+    rotout().flush();
+}
+
+void RotationTarget::print(std::ostream& s) const
+{
+    static std::string logfileFormat = Resource<std::string>("logfileFormat","~/log/%Y-%m-%d/out");
+    s << "RotationTarget(format=" << logfileFormat << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/RotationTarget.h b/eckit/src/eckit/log/RotationTarget.h
new file mode 100644
index 0000000..9cb9651
--- /dev/null
+++ b/eckit/src/eckit/log/RotationTarget.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file RotationTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_RotationTarget_h
+#define eckit_log_RotationTarget_h
+
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class RotationTarget : public LogTarget {
+
+public: // methods
+
+    RotationTarget();
+
+    virtual ~RotationTarget();
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+
+protected:
+    void print(std::ostream& s) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/SavedStatus.cc b/eckit/src/eckit/log/SavedStatus.cc
new file mode 100644
index 0000000..b5f4fbb
--- /dev/null
+++ b/eckit/src/eckit/log/SavedStatus.cc
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/SavedStatus.h"
+#include "eckit/runtime/Monitor.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+SavedStatus::SavedStatus(): status_(Monitor::instance().status())
+{
+}
+
+SavedStatus::~SavedStatus()
+{
+    Monitor::instance().status(status_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/SavedStatus.h b/eckit/src/eckit/log/SavedStatus.h
new file mode 100644
index 0000000..f0a8788
--- /dev/null
+++ b/eckit/src/eckit/log/SavedStatus.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file SavedStatus.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_SavedStatus_h
+#define eckit_log_SavedStatus_h
+
+#include <string>
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// Saves and Restores Monitor status line
+class SavedStatus {
+    std::string status_;
+public:
+    SavedStatus();
+    ~SavedStatus();
+};    
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Seconds.cc b/eckit/src/eckit/log/Seconds.cc
new file mode 100644
index 0000000..ea8b3f6
--- /dev/null
+++ b/eckit/src/eckit/log/Seconds.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Seconds.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Seconds::Seconds(double seconds):
+	seconds_(seconds)
+{
+}
+
+Seconds::Seconds(const ::timeval& time):
+	seconds_(time.tv_sec + time.tv_usec / 1000000.0)
+{
+}
+
+static struct  {
+    int length_;
+    const char *name_;
+} periods[] = {
+    {7 * 24 * 60 * 60, "week",},    
+    {24 * 60 * 60, "day",}, 
+    {60 * 60, "hour",}, 
+    {60, "minute",},    
+    {1, "second",}, 
+    {0,0,},
+};
+
+std::ostream& operator<<(std::ostream& s,const Seconds&  sec)
+{
+	double t = sec.seconds_;
+    long n  = t;
+    int flg = 0;
+
+    for(int i=0;periods[i].length_;i++)
+    {
+        long m = n / periods[i].length_;
+        if(m) {
+            if(flg) s << ' ';
+            s << m << ' ' << periods[i].name_;
+            if(m>1) s << 's';
+            n %= periods[i].length_;
+            flg++;
+        }
+    }   
+
+    if(!flg) s << t << " second";
+
+	return s;
+}
+
+Seconds::operator std::string() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/Seconds.h b/eckit/src/eckit/log/Seconds.h
new file mode 100644
index 0000000..77665fc
--- /dev/null
+++ b/eckit/src/eckit/log/Seconds.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Seconds.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Seconds_h
+#define eckit_Seconds_h
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "eckit/eckit.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+//class Bless;
+
+class Seconds {
+public:
+
+// -- Contructors
+
+	Seconds(double);
+	Seconds(const struct ::timeval&);
+
+//#include "eckit/types/Seconds.b"
+
+// -- Operators
+
+	operator std::string() const;
+	operator double() const { return seconds_; }
+
+	friend std::ostream& operator<<(std::ostream&,const Seconds&);
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Members
+
+	double seconds_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Statistics.cc b/eckit/src/eckit/log/Statistics.cc
new file mode 100644
index 0000000..7015eda
--- /dev/null
+++ b/eckit/src/eckit/log/Statistics.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Statistics.h"
+
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace eckit {
+
+const size_t WIDTH = 34;
+
+Timer Statistics::timer_;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void Statistics::reportCount(std::ostream &out, const char *title, size_t value, const char *indent, bool always) {
+    if (value || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << eckit::BigNum(value)
+            << std::endl;
+    }
+}
+
+void Statistics::reportUnit(std::ostream &out, const char *title, const char* unit, double value, const char *indent, bool always) {
+    if (value || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << value
+            << " "
+            << unit
+            << std::endl;
+    }
+}
+
+
+void Statistics::reportRate(std::ostream &out, const char *title, unsigned long long value, const char *indent, bool always) {
+    if (value || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << eckit::BigNum(value)
+            << " bytes/s (" << eckit::Bytes(value) << "/s)"
+            << std::endl;
+    }
+}
+
+void Statistics::reportBytes(std::ostream &out, const char *title, unsigned long long value, const char *indent, bool always) {
+    if (value || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << eckit::BigNum(value)
+            << " (" << eckit::Bytes(value)
+            << ")" << std::endl;
+    }
+}
+
+void Statistics::reportTime(std::ostream &out, const char *title, const Timing &value, const char *indent, bool always) {
+    if (value.updates_ || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << eckit::Seconds(value.elapsed_) << " (" << eckit::Seconds(value.cpu_) << " CPU). Updates: "
+            << eckit::BigNum(value.updates_)
+            << std::endl;
+    }
+}
+
+void Statistics::reportTime(std::ostream &out, const char *title, double value, const char *indent, bool always) {
+    if (value || always)
+    {
+        out << indent
+            << title
+            << std::setw(WIDTH - strlen(title))
+            << " : "
+            << eckit::Seconds(value)
+            << std::endl;
+    }
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Timing &Timing::operator+=(const Timing & other) {
+    elapsed_ += other.elapsed_;
+    cpu_ += other.cpu_;
+    updates_ += other.updates_;
+    return *this;
+}
+
+
+Timing &Timing::operator/=(size_t n) {
+    elapsed_ /= n;
+    cpu_ /= n;
+    if (updates_) {
+        updates_ /= n;
+        if (!updates_) {
+            updates_ = 1;
+        }
+    }
+    return *this;
+}
+
+Timing Timing::operator-(const Timing & other) const {
+    return Timing(elapsed_ - other.elapsed_, cpu_ - other.cpu_, updates_ + other.updates_);
+}
+
+
+Stream &operator<<(Stream & s, const Timing & t) {
+    s << t.elapsed_;
+    s << t.cpu_;
+    s << t.updates_;
+    return s;
+}
+
+Stream &operator>>(Stream & s, Timing & t) {
+    s >> t.elapsed_;
+    s >> t.cpu_;
+    s >> t.updates_;
+    return s;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/Statistics.h b/eckit/src/eckit/log/Statistics.h
new file mode 100644
index 0000000..aa42704
--- /dev/null
+++ b/eckit/src/eckit/log/Statistics.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   Statistics.h
+/// @author Baudouin Raoult
+/// @date   April 2016
+
+#ifndef eckit_Statistics_H
+#define eckit_Statistics_H
+
+#include <iosfwd>
+
+#include "eckit/log/Timer.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Stream;
+
+struct Timing {
+    double elapsed_;
+    double cpu_;
+    size_t updates_;
+
+    Timing(): elapsed_(0), cpu_(0), updates_(0) {}
+    Timing(double elapsed, double cpu, size_t updates): elapsed_(elapsed), cpu_(cpu), updates_(updates) {}
+    Timing(Timer& timer): elapsed_(timer.elapsed()), cpu_(timer.elapsed_cpu()), updates_(1) {}
+    Timing& operator+=(const Timing&);
+    Timing operator-(const Timing&) const;
+    Timing& operator/=(size_t);
+};
+
+Stream& operator>>(Stream&, Timing&);
+Stream& operator<<(Stream&, const Timing&);
+
+
+class AutoTiming {
+    Timer& timer_;
+    Timing start_;
+    Timing& timing_;
+  public:
+    AutoTiming(Timer& timer, Timing& timing):
+        timer_(timer), start_(timer), timing_(timing) {}
+
+    ~AutoTiming() {
+        timing_ += Timing(timer_) - start_;
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Statistics {
+  public:
+    static void reportUnit(std::ostream& out, const char* title, const char* unit, double value, const char* indent = "", bool always=false);
+    static void reportRate(std::ostream& out, const char* title, unsigned long long value, const char* indent = "", bool always=false);
+    static void reportCount(std::ostream& out, const char* title, size_t value, const char* indent = "", bool always=false);
+    static void reportBytes(std::ostream& out, const char* title, unsigned long long value, const char* indent = "", bool always=false);
+    static void reportTime(std::ostream& out, const char* title, const Timing& value, const char* indent = "", bool always=false);
+    static void reportTime(std::ostream& out, const char* title, double value, const char* indent = "", bool always=false);
+    static Timer timer_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/StatusTarget.cc b/eckit/src/eckit/log/StatusTarget.cc
new file mode 100644
index 0000000..00ab21e
--- /dev/null
+++ b/eckit/src/eckit/log/StatusTarget.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/StatusTarget.h"
+#include "eckit/runtime/Monitor.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+StatusTarget::StatusTarget() {
+}
+
+void StatusTarget::line(const char* line) {
+    Monitor::instance().status(line);
+}
+
+void StatusTarget::print(std::ostream& s) const
+{
+    s << "StatusTarget()";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/StatusTarget.h b/eckit/src/eckit/log/StatusTarget.h
new file mode 100644
index 0000000..d8b642a
--- /dev/null
+++ b/eckit/src/eckit/log/StatusTarget.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file StatusTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_StatusTarget_h
+#define eckit_log_StatusTarget_h
+
+
+#include "eckit/log/LineBasedTarget.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class StatusTarget : public LineBasedTarget {
+public: // methods
+
+    StatusTarget();
+
+private:
+
+    virtual void line(const char* line);
+    virtual void print(std::ostream& s) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/TeeTarget.cc b/eckit/src/eckit/log/TeeTarget.cc
new file mode 100644
index 0000000..c9483e9
--- /dev/null
+++ b/eckit/src/eckit/log/TeeTarget.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/TeeTarget.h"
+#include "eckit/log/OStreamTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TeeTarget::TeeTarget(LogTarget* left, LogTarget* right):
+    left_(left),
+    right_(right) {
+
+    if (left_) {
+        left_->attach();
+    }
+
+    if (right_) {
+        right_->attach();
+    }
+}
+
+TeeTarget::~TeeTarget() {
+    if (left_) {
+        left_->detach();
+    }
+
+    if (right_) {
+        right_->detach();
+    }
+}
+
+void TeeTarget::write(const char* start, const char* end) {
+    if (left_) {
+        left_->write(start, end);
+    }
+
+    if (right_) {
+        right_->write(start, end);
+    }
+}
+
+void TeeTarget::flush() {
+    if (left_) {
+        left_->flush();
+    }
+
+    if (right_) {
+        right_->flush();
+    }
+}
+
+void TeeTarget::print(std::ostream& s) const
+{
+    s << "TeeTarget(";
+    if(left_)  {s << *left_;}
+    if(right_) {s << *right_;}
+    s << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/TeeTarget.h b/eckit/src/eckit/log/TeeTarget.h
new file mode 100644
index 0000000..b88077c
--- /dev/null
+++ b/eckit/src/eckit/log/TeeTarget.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file TeeTarget.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_log_TeeTarget_h
+#define eckit_log_TeeTarget_h
+
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TeeTarget : public LogTarget {
+
+public: // methods
+
+    TeeTarget(LogTarget* left, LogTarget* right);
+
+    virtual ~TeeTarget();
+
+protected:
+    void print(std::ostream& s) const;
+
+private:
+    LogTarget* left_;
+    LogTarget* right_;
+
+private:
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/TimeStamp.cc b/eckit/src/eckit/log/TimeStamp.cc
new file mode 100644
index 0000000..833b152
--- /dev/null
+++ b/eckit/src/eckit/log/TimeStamp.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/TimeStamp.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+const char* TimeStamp::defaultFormat_ = "%Y-%m-%d %H:%M:%S";
+
+TimeStamp::TimeStamp(const std::string& format):
+	time_(time(0)),
+	format_(format)
+{
+}
+
+TimeStamp::TimeStamp(time_t t,const std::string& format):
+	time_(t),
+	format_(format)
+{
+}
+
+std::ostream& operator<<(std::ostream& s,const TimeStamp&  x)
+{
+	char buf[80];
+#ifdef EC_HAVE_GMTIME_R
+	struct tm t;
+	strftime(buf,sizeof(buf),x.format_.c_str(),gmtime_r(&x.time_,&t)); 
+#else
+	strftime(buf,sizeof(buf),x.format_.c_str(),gmtime(&x.time_)); 
+#endif
+
+	s << buf;
+	
+	return s;
+}
+
+TimeStamp::operator std::string() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/log/TimeStamp.h b/eckit/src/eckit/log/TimeStamp.h
new file mode 100644
index 0000000..4a39b30
--- /dev/null
+++ b/eckit/src/eckit/log/TimeStamp.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TimeStamp.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_TimeStamp_h
+#define eckit_TimeStamp_h
+
+#include "eckit/eckit.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TimeStamp {
+public:
+
+// -- Contructors
+
+	TimeStamp(const std::string& = defaultFormat_);
+	TimeStamp(time_t,const std::string& = defaultFormat_);
+
+// -- Operators
+
+	operator std::string() const;
+
+	friend std::ostream& operator<<(std::ostream&,const TimeStamp&);
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Members
+
+	time_t        time_;
+	const std::string& format_;
+
+// -- Class members
+
+	static const char* defaultFormat_;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/TimeStampTarget.cc b/eckit/src/eckit/log/TimeStampTarget.cc
new file mode 100644
index 0000000..d394fb9
--- /dev/null
+++ b/eckit/src/eckit/log/TimeStampTarget.cc
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/TimeStampTarget.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/TimeStamp.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+TimeStampTarget::TimeStampTarget(const char* tag, LogTarget* target):
+    WrapperTarget(target),
+    tag_(tag)
+{
+}
+
+TimeStampTarget::~TimeStampTarget()
+{
+}
+
+void TimeStampTarget::writePrefix() {
+
+    std::ostringstream oss;
+    oss << std::setw(3)
+        << std::setfill('0')
+        << Monitor::instance().self()
+        << std::setfill(' ') << ' '
+        << TimeStamp() << ' ';
+
+    if(tag_ && *tag_) {
+        oss << tag_ << ' ';
+    }
+
+    std::string s = oss.str();
+    const char* p = s.c_str();
+    target_->write(p, p + s.size());
+}
+
+
+void TimeStampTarget::writeSuffix() {
+}
+
+void TimeStampTarget::print(std::ostream& s) const
+{
+    s << "TimeStampTarget(";
+    if(target_) { s<< "target=" << *target_ << ")"; }
+    s << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/TimeStampTarget.h b/eckit/src/eckit/log/TimeStampTarget.h
new file mode 100644
index 0000000..507fa4d
--- /dev/null
+++ b/eckit/src/eckit/log/TimeStampTarget.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file TimeStampTarget.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_TimeStampTarget_h
+#define eckit_log_TimeStampTarget_h
+
+#include <utility>
+
+#include "eckit/log/WrapperTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TimeStampTarget : public WrapperTarget {
+public:
+
+    TimeStampTarget(const char* tag = "", LogTarget* target = 0);
+
+    virtual ~TimeStampTarget();
+
+protected:
+    void print(std::ostream& s) const;
+
+private:
+
+    virtual void writePrefix();
+    virtual void writeSuffix();
+
+private:
+
+    const char* tag_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/Timer.cc b/eckit/src/eckit/log/Timer.cc
new file mode 100644
index 0000000..b2eec14
--- /dev/null
+++ b/eckit/src/eckit/log/Timer.cc
@@ -0,0 +1,135 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Seconds.h"
+#include "eckit/log/Timer.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Timer::Timer():
+    name_("unnamed timer"),
+    stopped_(true),
+    outputAtExit_(false),
+    cpuStart_(0),
+    cpuStop_(0),
+    out_( std::cout )
+{
+    this->start();
+}
+
+Timer::Timer(const std::string& name, std::ostream& o ):
+    name_(name),
+    stopped_(true),
+    outputAtExit_(true),
+    cpuStart_(0),
+    cpuStop_(0),
+    out_(o)
+{
+    this->start();
+}
+
+Timer::Timer(const char* name, std::ostream& o ):
+    name_(name),
+    stopped_(true),
+    outputAtExit_(true),
+    cpuStart_(0),
+    cpuStop_(0),
+    out_(o)
+{
+    this->start();
+}
+
+
+Timer::~Timer()
+{
+    stop();
+
+    if(outputAtExit_) report();
+}
+
+
+void Timer::start()
+{
+    if( !running() )
+    {
+        ::gettimeofday(&timeStart_,0);
+        timeStop_ = timeStart_;
+
+        cpuStart_ = ::clock();
+        cpuStop_  = cpuStart_;
+
+        stopped_ = false;
+    }
+}
+
+
+void Timer::stop()
+{
+    if( !running() ) return;
+    takeTime();
+    stopped_ = true;
+}
+
+
+double Timer::elapsed()
+{
+    if( running() ) takeTime();
+
+    ::timeval diff = timeStop_ - timeStart_;
+    return (double)diff.tv_sec + ((double)diff.tv_usec / 1000000.);
+}
+
+double Timer::elapsed_cpu()
+{
+    if( running() ) takeTime();
+
+    return double(cpuStop_ - cpuStart_) / CLOCKS_PER_SEC;
+}
+
+void Timer::report(const std::string& message) {
+    const double  s   = elapsed();
+    const double  cpu = elapsed_cpu();
+
+    out_ << (message.size() ? message : name_) << ": "
+         << Seconds(s) << " elapsed, "
+         << Seconds(cpu) << " cpu"
+         << std::endl;
+}
+
+void Timer::takeTime()
+{
+    cpuStop_ = ::clock();
+    ::gettimeofday(&timeStop_,0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+::timeval operator-(const ::timeval& a,const ::timeval& b)
+{
+    ::timeval diff;
+
+    diff.tv_sec  = a.tv_sec  - b.tv_sec;
+    diff.tv_usec = a.tv_usec - b.tv_usec;
+
+    if (diff.tv_usec < 0)
+    {
+        diff.tv_sec--;
+        diff.tv_usec += 1000000;
+    }
+
+    return diff;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/Timer.h b/eckit/src/eckit/log/Timer.h
new file mode 100644
index 0000000..294d64a
--- /dev/null
+++ b/eckit/src/eckit/log/Timer.h
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Timer.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_Timer_h
+#define eckit_Timer_h
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "eckit/log/Log.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Timer : private NonCopyable {
+public:
+
+    explicit Timer();
+
+    /// @param name of the timer, used for output
+    /// @param o output stream to use  for output
+    explicit Timer( const std::string& name, std::ostream& o = Log::info() );
+
+    /// @param name of the timer, used for output
+    /// @param o output stream to use  for output
+    explicit Timer( const char* name, std::ostream& o = Log::info() );
+
+    ~Timer();
+
+    void start();
+    void stop();
+
+	double elapsed();
+    double elapsed_cpu();
+
+    const std::string& name() const { return name_; }
+
+    bool running() const { return !stopped_; }
+
+    void report(const std::string& message = "");
+
+protected: // methods
+
+    void takeTime();
+
+private: // members
+
+    std::string    name_;
+
+    bool           stopped_;
+    bool           outputAtExit_;
+
+    struct ::timeval timeStart_;
+    struct ::timeval timeStop_;
+
+    clock_t        cpuStart_;
+    clock_t        cpuStop_;
+
+    std::ostream&  out_;
+};
+
+//-----------------------------------------------------------------------------
+
+::timeval operator-(const ::timeval&,const ::timeval&);
+
+//-----------------------------------------------------------------------------
+
+template<class T>
+class TraceTimer : public Timer {
+public:
+
+    explicit TraceTimer( const char* name):
+        Timer(name, eckit::Log::debug<T>()) {}
+
+    explicit TraceTimer( const std::string& name):
+        Timer(name, eckit::Log::debug<T>()) {}
+};
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/UserChannel.cc b/eckit/src/eckit/log/UserChannel.cc
new file mode 100644
index 0000000..3996253
--- /dev/null
+++ b/eckit/src/eckit/log/UserChannel.cc
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/UserChannel.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class UserBuffer: public std::streambuf {
+public:
+
+    typedef UserChannel::MsgType MsgType;
+
+    UserBuffer( std::size_t size = 1024 ) :
+        std::streambuf(),
+        buffer_( size + 1 ),
+        msgType_(UserChannel::NONE),
+        user_(0)
+    {
+        ASSERT( size );
+        char *base = &buffer_.front();
+        setp( base, base + buffer_.size() - 1 ); // don't consider the space for '\0'
+    }
+
+    ~UserBuffer() { sync(); }
+
+    void msgType( MsgType t ) { msgType_ = t; }
+    MsgType msgType() const { return msgType_; }
+
+    void userMsg( UserMsg* p ) { user_ = p; }
+    UserMsg* userMsg() const   { return user_; }
+
+private:
+
+    std::vector<char>    buffer_;  ///< internal buffer
+    MsgType              msgType_; ///< type of next message
+    UserMsg*             user_;    ///< interface to user messeges
+
+    bool dumpBuffer()
+    {
+        std::replace(pbase(), epptr(), '\n', '\0');
+
+        switch ( msgType_)
+        {
+        case UserChannel::NONE:
+            break;
+        case UserChannel::INFO:
+            if (user_) user_->infoMsg( pbase() );
+            break;
+        case UserChannel::WARN:
+            if (user_) user_->warningMsg( pbase() );
+            break;
+        case UserChannel::ERROR:
+            if (user_) user_->errorMsg( pbase() );
+            break;
+        }
+
+        setp(pbase(), epptr());
+        return true;
+    }
+
+protected:
+
+    virtual int_type overflow(int_type ch)
+    {
+        if (ch == traits_type::eof() ) { return sync(); }
+        dumpBuffer();
+        sputc(ch);
+        return traits_type::to_int_type(ch);
+    }
+
+    virtual int_type sync()
+    {
+        return ( dumpBuffer() ? 0 : -1 );
+    }
+
+};
+
+//-----------------------------------------------------------------------------
+
+UserChannel::UserChannel() :
+    std::ostream( new UserBuffer() ),
+    buffer_( dynamic_cast<UserBuffer*>( rdbuf() ) )
+{
+    ASSERT( buffer_ );
+}
+
+UserChannel::~UserChannel()
+{
+    delete buffer_;
+}
+
+void UserChannel::msgType(UserChannel::MsgType t)
+{
+    buffer_->msgType(t);
+}
+
+void UserChannel::userMsg(UserMsg* p)
+{
+    buffer_->userMsg(p);
+}
+
+UserMsg* UserChannel::userMsg() const
+{
+    return buffer_->userMsg();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/UserChannel.h b/eckit/src/eckit/log/UserChannel.h
new file mode 100644
index 0000000..e56af3f
--- /dev/null
+++ b/eckit/src/eckit/log/UserChannel.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file UserChannel.h
+/// @author Tiago Quintino
+
+#ifndef eckit_log_UserChannel_h
+#define eckit_log_UserChannel_h
+
+
+#include <ostream>
+
+#include "eckit/memory/NonCopyable.h"
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class UserMsg {
+public:
+    virtual ~UserMsg() {}
+    virtual void infoMsg(const std::string&)      = 0;
+    virtual void warningMsg(const std::string&)   = 0;
+    virtual void errorMsg(const std::string&)     = 0;
+    virtual void notifyClient(const std::string&) = 0;
+};
+
+//-----------------------------------------------------------------------------
+
+class UserBuffer;
+
+class UserChannel : public std::ostream, private NonCopyable {
+public: // types
+
+    enum MsgType { NONE, INFO, ERROR, WARN };
+
+public: // methods
+
+    /// Constructor
+    UserChannel();
+
+    /// Destructor
+    ~UserChannel();
+
+    /// type for next message
+    void msgType( MsgType t );
+
+    void userMsg( UserMsg* );
+    UserMsg* userMsg() const;
+
+protected:
+
+    UserBuffer* buffer_;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/log/WrapperTarget.cc b/eckit/src/eckit/log/WrapperTarget.cc
new file mode 100644
index 0000000..353672c
--- /dev/null
+++ b/eckit/src/eckit/log/WrapperTarget.cc
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/log/WrapperTarget.h"
+#include "eckit/log/OStreamTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+WrapperTarget::WrapperTarget(LogTarget* target):
+    target_(target),
+    prefix_(true) {
+
+    if (!target_) {
+        target_ = new OStreamTarget(std::cout);
+    }
+
+    target_->attach();
+}
+
+WrapperTarget::~WrapperTarget() {
+    target_->detach();
+}
+
+void WrapperTarget::write(const char* start, const char* end) {
+
+    const char *begin = start;
+
+    while (start != end) {
+        if (*start == '\n') {
+            target_->write(begin, start);
+            writeSuffix();
+            target_->write(start, start + 1);
+            prefix_ = true;
+            begin = start + 1;
+        }
+        else {
+            if (prefix_) {
+                writePrefix();
+                prefix_ = false;
+            }
+        }
+        start++;
+    }
+
+    if (begin != end) {
+        if (prefix_) {
+            writePrefix();
+            prefix_ = false;
+        }
+        target_->write(begin, end);
+    }
+
+}
+
+void WrapperTarget::flush() {
+    target_->flush();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/log/WrapperTarget.h b/eckit/src/eckit/log/WrapperTarget.h
new file mode 100644
index 0000000..c82ebf2
--- /dev/null
+++ b/eckit/src/eckit/log/WrapperTarget.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file WrapperTarget.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_log_WrapperTarget_h
+#define eckit_log_WrapperTarget_h
+
+#include "eckit/log/LogTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class WrapperTarget : public LogTarget {
+
+protected: // methods
+
+    WrapperTarget(LogTarget* target = 0);
+
+    virtual ~WrapperTarget();
+
+protected: // members
+
+    LogTarget* target_;
+
+private:
+
+    virtual void write(const char* start, const char* end);
+    virtual void flush();
+
+    virtual void writePrefix() = 0;
+    virtual void writeSuffix() = 0;
+
+protected:
+
+    bool prefix_;
+
+    friend class ChannelBuffer;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/maths/CMakeLists.txt b/eckit/src/eckit/maths/CMakeLists.txt
new file mode 100644
index 0000000..c800ba4
--- /dev/null
+++ b/eckit/src/eckit/maths/CMakeLists.txt
@@ -0,0 +1,15 @@
+list( APPEND eckit_maths_lib_srcs
+Eigen.h
+Lapack.h
+Lapack.cc
+Matrix.h
+MatrixEigen.h
+MatrixLapack.h
+)
+
+ecbuild_add_library( TARGET              eckit_maths
+                     INSTALL_HEADERS     ALL
+                     HEADER_DESTINATION  ${INSTALL_INCLUDE_DIR}/eckit/maths
+                     SOURCES             ${eckit_maths_lib_srcs}
+                     INCLUDES            "${EIGEN3_INCLUDE_DIR}"
+                     LIBS                eckit "${LAPACK_LIBRARIES}" "${BLAS_LIBRARIES}" )
diff --git a/eckit/src/eckit/maths/Eigen.h b/eckit/src/eckit/maths/Eigen.h
new file mode 100644
index 0000000..3851cfb
--- /dev/null
+++ b/eckit/src/eckit/maths/Eigen.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Eigen.h
+/// @author Tiago Quintino
+/// @date March 2014
+
+#ifndef eckit_maths_Eigen_h
+#define eckit_maths_Eigen_h
+
+#include "eckit/eckit_config.h"
+
+//--------------------------------------------------------------------------------------------
+
+#ifdef ECKIT_HAVE_EIGEN
+
+#define EIGEN_NO_AUTOMATIC_RESIZING
+#define EIGEN_DONT_ALIGN
+#define EIGEN_DONT_VECTORIZE
+
+#ifdef ECKIT_CONTRIB_EIGEN
+
+#include "eigen3/Eigen/Core"
+#include "eigen3/Eigen/Dense"
+#include "eigen3/Eigen/Geometry"
+#include "eigen3/Eigen/Sparse"
+
+#else
+
+#include <Eigen/Core>
+#include <Eigen/Dense>
+#include <Eigen/Geometry>
+#include <Eigen/Sparse>
+
+#endif
+
+#endif
+
+//--------------------------------------------------------------------------------------------
+
+#endif
diff --git a/eckit/src/eckit/maths/Functions.cc b/eckit/src/eckit/maths/Functions.cc
new file mode 100644
index 0000000..e734920
--- /dev/null
+++ b/eckit/src/eckit/maths/Functions.cc
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/maths/Functions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+size_t round(size_t x, size_t n) {
+    return ((x + n - 1) / n) * n;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/maths/Functions.h b/eckit/src/eckit/maths/Functions.h
new file mode 100644
index 0000000..8a447fe
--- /dev/null
+++ b/eckit/src/eckit/maths/Functions.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#ifndef eckit_Functions_h
+#define eckit_Functions_h
+
+#include <cctype>
+#include <cstdlib>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// @returns rounds x to multiple of n
+size_t round(size_t x, size_t n);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/maths/Lapack.cc b/eckit/src/eckit/maths/Lapack.cc
new file mode 100644
index 0000000..e263d4a
--- /dev/null
+++ b/eckit/src/eckit/maths/Lapack.cc
@@ -0,0 +1,64 @@
+#include "eckit/log/Log.h"
+#include "eckit/maths/Lapack.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+namespace maths {
+namespace lapack {
+
+#ifdef ECKIT_HAVE_LAPACK
+
+extern "C" {
+    void dgetrf_ (int *M, int *N, double *A, int *lda, int *ipiv, int *info);
+    void sgetrf_ (int *M, int *N, float  *A, int *lda, int *ipiv, int *info);
+    void dgetri_ (int *M, double *A, int *lda, int *ipiv, double *work, int *lwork, int *info);
+    void sgetri_ (int *M, float  *A, int *lda, int *ipiv, float  *work, int *lwork, int *info);
+}
+
+void getrf(int* M, int* N, double* data, int* lda, int* ipiv, int* info)
+{
+	dgetrf_(M, N, data, lda, ipiv, info);
+}
+
+void getrf(int* M, int* N, float* data, int* lda, int* ipiv, int* info)
+{
+	sgetrf_(M, N, data, lda, ipiv, info);
+}
+
+void getri(int* M, double* data, int* lda, int* ipiv, double* work, int* lwork, int* info)
+{
+	dgetri_(M, data, lda, ipiv, work, lwork, info);
+}
+
+void getri(int* M, float* data, int* lda, int* ipiv, float* work, int* lwork, int* info)
+{
+	sgetri_(M, data, lda, ipiv, work, lwork, info);
+}
+
+#else
+
+void getrf(int* M, int* N, double* data, int* lda, int* ipiv, int* info)
+{
+	NOTIMP;
+}
+
+void getrf(int* M, int* N, float* data, int* lda, int* ipiv, int* info)
+{
+	NOTIMP;
+}
+
+void getri(int* M, double* data, int* lda, int* ipiv, double* work, int* lwork, int* info)
+{
+	NOTIMP;
+}
+
+void getri(int* M, float* data, int* lda, int* ipiv, float* work, int* lwork, int* info)
+{
+	NOTIMP;
+}
+
+#endif
+
+} // namespace lapack
+} // namespace maths
+} // namespace eckit
diff --git a/eckit/src/eckit/maths/Lapack.h b/eckit/src/eckit/maths/Lapack.h
new file mode 100644
index 0000000..8cf87e9
--- /dev/null
+++ b/eckit/src/eckit/maths/Lapack.h
@@ -0,0 +1,20 @@
+#ifndef eckit_maths_lapack_h
+#define eckit_maths_lapack_h
+
+#include "eckit/eckit_config.h"
+
+namespace eckit {
+namespace maths {
+namespace lapack {
+
+void getrf(int* M, int* N, float* data, int* lda, int* ipiv, int* info);
+void getrf(int* M, int* N, double* data, int* lda, int* ipiv, int* info);
+
+void getri(int* M, float* data, int* lda, int* ipiv, float* work, int* lwork, int* info);
+void getri(int* M, double* data, int* lda, int* ipiv, double* work, int* lwork, int* info);
+
+} // namespace lapack
+} // namespace maths
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/maths/Matrix.h b/eckit/src/eckit/maths/Matrix.h
new file mode 100644
index 0000000..3cefcf7
--- /dev/null
+++ b/eckit/src/eckit/maths/Matrix.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/**
+ * @file
+ * @author Willem Deconinck
+ * @date   Sept 2014
+ *
+ * This file introduces classes
+ *  - RowVector
+ *  - ColVector
+ *  - Matrix
+ *
+ * By default, they are straight inherited from Eigen classes
+ * Due to current inability to compile Eigen on CRAY,
+ * Transitional classes have been created, which only implement
+ * a small subset of Eigen functionality, and to let at least
+ * code be compiled
+ * It is strongly advised to only use the subset in the
+ * transitional classes, or implement as needed
+ */
+#ifndef eckit_maths_Matrix_h
+#define eckit_maths_Matrix_h
+
+#include "eckit/eckit_config.h"
+
+namespace eckit {
+
+namespace maths {
+
+template < typename scalar, typename index > class RowVector;
+template < typename scalar, typename index > class ColVector;
+template < typename scalar, typename index > class Matrix;
+
+} // namespace maths
+
+} // namespace eckit
+
+#ifdef ECKIT_HAVE_EIGEN
+
+// Implementation using Eigen
+#include "eckit/maths/MatrixEigen.h"
+
+#else
+
+// Own implementation using Lapack if available
+#include "eckit/maths/MatrixLapack.h"
+
+#endif
+
+#endif
diff --git a/eckit/src/eckit/maths/MatrixEigen.h b/eckit/src/eckit/maths/MatrixEigen.h
new file mode 100644
index 0000000..3913f4b
--- /dev/null
+++ b/eckit/src/eckit/maths/MatrixEigen.h
@@ -0,0 +1,151 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_maths_Matrix_h
+#error Include "eckit/maths/Matrix.h" instead of "eckit/maths/MatrixEigen.h"
+#endif
+
+#ifndef eckit_maths_MatrixEigen_h
+#define eckit_maths_MatrixEigen_h
+
+#include "eckit/maths/Eigen.h"
+
+namespace eckit {
+
+namespace maths {
+
+//========================================================================================
+
+/// @brief Matrix class, with internal Column-Major storage ordering
+///
+/// This class is inherited from Eigen's matrix class
+/// Eigen::ColMajor is also Eigen's default (recommended) storage ordering.
+/// Furthermore it is directly compatible with Fortran matrix manipulation routines
+template <typename scalar, typename index = std::ptrdiff_t>
+class Matrix : public Eigen::Matrix<scalar,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor>
+{
+	typedef Eigen::Matrix<scalar,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> Base;
+public:
+	typedef typename Base::MapType Proxy;
+	typedef typename Base::ConstMapType ConstProxy;
+
+public:
+
+	// Default constructor
+	Matrix(void)
+		: Base()
+	{ }
+
+	// Constructor that allocates matrix with sizes
+	template<typename T0, typename T1>
+	Matrix(const T0& x, const T1& y)
+		: Base(x,y)
+	{ }
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	template<typename OtherDerived>
+	Matrix(const Eigen::MatrixBase<OtherDerived>& other)
+		: Base(other)
+	{ }
+
+	// This method allows you to assign Eigen expressions to Matrix
+	template<typename OtherDerived>
+	Matrix& operator= (const Eigen::MatrixBase <OtherDerived>& other)
+	{
+		this->Base::operator=(other);
+		return *this;
+	}
+};
+
+//========================================================================================
+
+/// @brief Row-Vector class
+///
+/// This class is inherited from Eigen's matrix class
+template <typename scalar, typename index = std::ptrdiff_t>
+class RowVector : public Eigen::Matrix<scalar,1,Eigen::Dynamic>
+{
+	typedef Eigen::Matrix<scalar,1,Eigen::Dynamic> Base;
+public:
+	typedef typename Base::MapType Proxy;
+public:
+
+	// Default constructor
+	RowVector(void)
+		: Base()
+	{ }
+
+	// Constructor that allocates matrix with sizes
+	template<typename T>
+	RowVector(const T& x)
+		: Base(x)
+	{ }
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	template<typename OtherDerived>
+	RowVector(const Eigen::MatrixBase<OtherDerived>& other)
+		: Base(other)
+	{ }
+
+	// This method allows you to assign Eigen expressions to Matrix
+	template<typename OtherDerived>
+	RowVector& operator= (const Eigen::MatrixBase <OtherDerived>& other)
+	{
+		this->Base::operator=(other);
+		return *this;
+	}
+};
+
+//========================================================================================
+
+/// @brief Column-Vector class
+///
+/// This class is inherited from Eigen's matrix class
+template <typename scalar, typename index = std::ptrdiff_t>
+class ColVector : public Eigen::Matrix<scalar,Eigen::Dynamic,1>
+{
+	typedef Eigen::Matrix<scalar,Eigen::Dynamic,1> Base;
+public:
+	typedef typename Base::MapType Proxy;
+public:
+
+	// Default constructor
+	ColVector(void)
+		: Base()
+	{ }
+
+	// Constructor that allocates matrix with sizes
+	template<typename T>
+	ColVector(const T& x)
+		: Base(x)
+	{ }
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	template<typename OtherDerived>
+	ColVector(const Eigen::MatrixBase<OtherDerived>& other)
+		: Base(other)
+	{ }
+
+	// This method allows you to assign Eigen expressions to Matrix
+	template<typename OtherDerived>
+	ColVector& operator= (const Eigen::MatrixBase <OtherDerived>& other)
+	{
+		this->Base::operator=(other);
+		return *this;
+	}
+};
+
+//========================================================================================
+
+} // namespace maths
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/maths/MatrixLapack.h b/eckit/src/eckit/maths/MatrixLapack.h
new file mode 100644
index 0000000..4de6b5a
--- /dev/null
+++ b/eckit/src/eckit/maths/MatrixLapack.h
@@ -0,0 +1,741 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/**
+ * @file
+ * @author Willem Deconinck
+ * @date   Sept 2014
+ *
+ * Do not include this file directly. Rather include "eckit/maths/Matrix.h" instead.
+ *
+ * This file implements classes
+ *  - RowVector
+ *  - ColVector
+ *  - Matrix
+ *
+ * These classes follow Eigen's naming of functions, and could be used
+ * as a drop-in replacement, depending on compilation choices
+ *
+ * The reasoning is that some compilers (e.g. CRAY) have problems compiling
+ * Eigen.
+ *
+ * Where possible, BLAS and LAPACK implementations are used, in case
+ * BLAS or LAPACK have been found.
+ */
+
+#ifndef eckit_maths_Matrix_h
+#error Include "eckit/maths/Matrix.h" instead of "eckit/maths/MatrixLapack.h"
+#endif
+
+#ifndef eckit_maths_MatrixLapack_h
+#define eckit_maths_MatrixLapack_h
+
+#include <cmath>
+#include "eckit/exception/Exceptions.h"
+#include "eckit/maths/Lapack.h"
+
+#ifdef minor
+#undef minor
+#endif
+
+namespace eckit {
+
+namespace maths {
+
+namespace detail
+{
+	namespace ColMajor_4x4
+	{
+		template<typename T> struct remove_const          { typedef T type; };
+		template<typename T> struct remove_const<T const> { typedef T type; };
+
+		template< typename Scalar > inline void invert(
+			Scalar m[16],
+			typename remove_const<Scalar>::type inv[16]
+		);
+		template< typename Scalar > inline Scalar det(Scalar m[16]);
+	}
+}
+
+template <typename Scalar, typename Index = std::ptrdiff_t>
+class Matrix {
+
+protected:
+
+	Scalar* data_;
+	Index nr_, nc_;
+	bool is_proxy_;
+
+public:
+
+	typedef Matrix<Scalar> Proxy;
+	typedef Matrix<const Scalar> ConstProxy;
+
+	Matrix()
+	{
+		is_proxy_ = false;
+		nr_ = 0.;
+		nc_ = 0.;
+		data_ = NULL;
+	}
+
+	// Constructor that allocates matrix with sizes
+	template<typename T0, typename T1>
+	Matrix(const T0& nr, const T1& nc)
+	{
+		is_proxy_ = false;
+		nr_ = 0.;
+		nc_ = 0.;
+		data_ = NULL;
+		resize(nr,nc);
+	}
+
+	template<typename T0, typename T1>
+	Matrix(Scalar* data, const T0& nr, const T1& nc)
+	{
+		is_proxy_ = true;
+		data_ = data;
+		nr_ = nr;
+		nc_ = nc;
+	}
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	Matrix(const Matrix& other)
+	{
+		is_proxy_ = false;
+		nr_ = 0.;
+		nc_ = 0.;
+		data_ = NULL;
+		resize(other.nr_,other.nc_);
+		memcpy( data_, other.data(), sizeof(Scalar)*nr_*nc_ );
+	}
+
+  Matrix& noalias() { return *this; }
+
+	void resize(Index nr, Index nc)
+	{
+		if( is_proxy_ )
+		{
+			throw eckit::Exception("Illegal call: Trying to resize a proxy matrix", Here() );
+		}
+		else
+		{
+			if( nr != nr_ || nc != nc_ )
+			{
+				if( data_ )
+				{
+					delete[] data_;
+				}
+				data_ = new Scalar[nr*nc];
+				nr_ = nr;
+				nc_ = nc;
+			}
+		}
+	}
+
+	Matrix& operator= (const Matrix& other)
+	{
+		resize(other.nr_,other.nc_);
+		memcpy( data_, other.data(), sizeof(Scalar)*nr_*nc_ );
+		return *this;
+	}
+
+	Scalar* data()
+	{
+		return data_;
+	}
+
+	const Scalar* data() const
+	{
+		return data_;
+	}
+
+	Index size() const { return nr_*nc_; }
+
+	Index rows() const { return nr_; }
+
+	Index cols() const { return nc_; }
+
+	template<typename T>
+	Scalar& operator[](const T& i)
+	{
+		return data_[i];
+	}
+
+	template<typename T>
+	const Scalar& operator[](const T& i) const
+	{
+		return data_[i];
+	}
+
+	template<typename T>
+	Scalar& operator()(const T& i)
+	{
+		return data_[i];
+	}
+
+	template<typename T>
+	const Scalar& operator()(const T& i) const
+	{
+		return data_[i];
+	}
+
+	template<typename T0, typename T1>
+	Scalar& operator()(const T0& i, const T1& j)
+	{
+		return at(i,j);
+	}
+
+	template<typename T0, typename T1>
+	const Scalar& operator()(const T0& i, const T1& j) const
+	{
+		return at(i,j);
+	}
+
+	template<typename T>
+	Matrix row(const T& i) const
+	{
+		Matrix r(1,nc_);
+		for( Index j=0; j<nc_; ++j )
+			r(j)=at(i,j);
+		return r;
+	}
+
+	template<typename T>
+	Matrix col(const T& j) const
+	{
+		Matrix c(nc_,1);
+		for( Index i=0; i<nc_; ++i )
+			c(i)=at(i,j);
+		return c;
+	}
+
+	Matrix inverse() const
+	{
+		Matrix inv(nr_,nc_);
+		switch( nr_ ) {
+		case 1: {
+			inv(0,0) = 1./at(0,0);
+			break;
+		}
+		case 2: {
+			Scalar deti = 1./(at(0,0) * at(1,1) - at(0,1) * at(1,0));
+			inv(0,0) =  at(1,1)*deti;
+			inv(0,1) = -at(0,1)*deti;
+			inv(1,0) = -at(1,0)*deti;
+			inv(1,1) =  at(0,0)*deti;
+			break;
+		}
+		case 3: {
+			Scalar deti = 1./(
+					at(0,0)*at(1,1)*at(2,2) + at(1,0)*at(2,1)*at(0,2) + at(2,0)*at(0,1)*at(1,2)
+				 -at(0,0)*at(2,1)*at(1,2) - at(2,0)*at(1,1)*at(0,2) - at(1,0)*at(0,1)*at(2,2) );
+
+			inv(0,0) = at(1,1)*at(2,2)-at(1,2)*at(2,1);
+			inv(0,1) = at(0,2)*at(2,1)-at(0,1)*at(2,2);
+			inv(0,2) = at(0,1)*at(1,2)-at(0,2)*at(1,1);
+			inv(1,0) = at(1,2)*at(2,0)-at(1,0)*at(2,2);
+			inv(1,1) = at(0,0)*at(2,2)-at(0,2)*at(2,0);
+			inv(1,2) = at(0,2)*at(1,0)-at(0,0)*at(1,2);
+			inv(2,0) = at(1,0)*at(2,1)-at(1,1)*at(2,0);
+			inv(2,1) = at(0,1)*at(2,0)-at(0,0)*at(2,1);
+			inv(2,2) = at(0,0)*at(1,1)-at(0,1)*at(1,0);
+
+			for( Index i=0; i<9; ++i )
+				inv.data()[i] *= deti;
+			break;
+		}
+		case 4: {
+			detail::ColMajor_4x4::invert(data(),inv.data());
+			break;
+		}
+		default: { // invert with LU-factorization
+#ifdef ECKIT_HAVE_LAPACK
+			inv = *this;
+			int M = nr_;
+			int N = nc_;
+			int lda = nr_;
+			int info;
+			int *ipiv = new int[std::min(M,N)];
+			lapack::getrf(&M, &N, inv.data(), &lda, ipiv, &info);
+			if(info == 0) {
+				int lwork = M * 4;
+				double *work = new double[lwork];
+				lapack::getri(&M, inv.data(), &lda, ipiv, work, &lwork, &info);
+				delete[] work;
+			}
+			delete[] ipiv;
+			if(info > 0)
+			{
+				std::stringstream stream; stream << "U("<<info<<","<<info<<")=0 in matrix inversion";
+				throw eckit::Exception(stream.str(), Here() );
+			}
+			else if(info < 0)
+			{
+				std::stringstream stream; stream << "Wrong "<<-info<<"-th argument in matrix inversion";
+				throw eckit::Exception(stream.str(), Here() );
+			}
+#else
+			// This algorithm inverts a matrix based on the Gauss Jordan method.
+			int n = nr_;
+			Scalar det, pivot, factor;
+			Matrix work(n,n);
+			det = 1;
+
+			for (int i = 0; i < n; i++)
+			{
+				for (int j = 0; j < n; j++)
+				{
+					inv(i,j) = 0;
+					work(i,j) = at(i,j);
+				}
+				inv(i,i) = 1.;
+			}
+			// The current pivot row is jpass.
+			// For each pass, first find the maximum element in the pivot column.
+			for (int jpass = 0; jpass < n; jpass++)
+			{
+				int imx = jpass;
+				for (int jrow = jpass; jrow < n; jrow++)
+				{
+					if (std::abs(work(jrow,jpass)) > std::abs(work(imx,jpass)))
+							imx = jrow;
+				}
+				// Interchange the elements of row jpass and row imx in both A and AInverse.
+				if (imx != jpass)
+				{
+					for (int jcol = 0; jcol < n; jcol++)
+					{
+						Scalar temp = inv(jpass,jcol);
+						inv(jpass,jcol) = inv(imx,jcol);
+						inv(imx,jcol) = temp;
+
+						if (jcol >= jpass)
+						{
+							temp = work(jpass,jcol);
+							work(jpass,jcol) = work(imx,jcol);
+							work(imx,jcol) = temp;
+						}
+					}
+				}
+
+				// The current pivot is now A[jpass][jpass].
+				// The determinant is the product of the pivot elements.
+				pivot = work(jpass,jpass);
+				det *= pivot;
+				if (det == 0)
+				{
+					throw eckit::SeriousBug("Cannot invert zero determinant matrix",Here());
+				}
+
+				for (int jcol = 0; jcol < n; jcol++)
+				{
+					// Normalize the pivot row by dividing by the pivot element.
+					inv(jpass,jcol) = inv(jpass,jcol) / pivot;
+					if (jcol >= jpass)
+						work(jpass,jcol) = work(jpass,jcol) / pivot;
+				}
+
+				for (int jrow = 0; jrow < n; jrow++)
+				// Add a multiple of the pivot row to each row.	The multiple factor
+				// is chosen so that the element of A on the pivot column is 0.
+				{
+					if (jrow != jpass)
+						factor = work(jrow,jpass);
+					for (int jcol = 0; jcol < n; jcol++)
+					{
+						if (jrow != jpass)
+						{
+							inv(jrow,jcol)  -= factor * inv(jpass,jcol);
+							work(jrow,jcol) -= factor * work(jpass,jcol);
+						}
+					}
+				}
+			}
+#endif
+		} }
+		return inv;
+	}
+
+	Matrix transpose()
+	{
+		Matrix transposed(nc_,nr_);
+		for(Index i=0; i<nr_; ++i)
+			for(Index j=0; j<nc_; ++j)
+				transposed(j,i) = at(i,j);
+		return transposed;
+	}
+
+	Scalar determinant() const
+	{
+		switch( nr_ ) {
+		case 1: {
+			return at(0,0);
+		}
+		case 2: {
+			return (at(0,0) * at(1,1) - at(0,1) * at(1,0));
+		}
+		case 3: {
+			return (
+					at(0,0)*at(1,1)*at(2,2) + at(1,0)*at(2,1)*at(0,2) + at(2,0)*at(0,1)*at(1,2)
+					-at(0,0)*at(2,1)*at(1,2) - at(2,0)*at(1,1)*at(0,2) - at(1,0)*at(0,1)*at(2,2) );
+		}
+		case 4: {
+			return detail::ColMajor_4x4::det(data());
+		}
+		default: {
+			Scalar d = 0;    // value of the determinant
+			// this is a matrix of 5x5 or larger
+			for (int c = 0; c < cols(); c++)
+			{
+				Matrix M = minor(0, c);
+				d += (2*((c+1)%2) - 1) * at(0, c) * M.determinant();
+			}
+			return d;
+		}
+		}
+		return 0.;
+	}
+
+	template<typename S, typename I>
+	friend std::ostream& operator<<( std::ostream& os, const Matrix<S, I>& v);
+
+#define UNARY_OPERATOR_Scalar(OP) \
+	Matrix& operator OP (const Scalar& scal) \
+	{ \
+		for (Index i=0; i<size(); ++i) \
+			data_[i] OP scal; \
+		return *this; \
+	}
+	UNARY_OPERATOR_Scalar(+=)
+	UNARY_OPERATOR_Scalar(-=)
+	UNARY_OPERATOR_Scalar(*=)
+	UNARY_OPERATOR_Scalar(/=)
+#undef UNARY_OPERATOR_Scalar
+
+
+	// Matrix Matrix multiply
+	Matrix dot(const Matrix &m) const
+	{
+		// Very naive implementation. We should use BLAS later
+		Matrix result(rows(),m.cols());
+		result *= 0.;
+		for(Index i=0; i<rows(); ++i) {
+			for(Index j=0; j<m.cols(); ++j) {
+				result(i,j) = 0.;
+				for(Index k=0; k<cols(); ++k)
+					result(i,j) += at(i,k)*m(k,j);
+			}
+		}
+		return result;
+	}
+
+	Matrix cwiseProduct(const Matrix &m) const
+	{
+		Matrix result(*this);
+		for (Index i=0; i<size(); ++i)
+			result.data_[i] += m.data_[i];
+		return result;
+	}
+
+	Matrix operator + (const Matrix& m) const
+	{
+		Matrix result(*this);
+		result += m;
+		return result;
+	}
+
+	Matrix operator - (const Matrix& m) const
+	{
+		Matrix result(*this);
+		result -= m;
+		return result;
+	}
+
+	// Matrix Matrix multiply
+	Matrix operator * (const Matrix &m) const
+	{
+		return dot(m);
+	}
+
+	Matrix& operator += (const Matrix &m)
+	{
+		for (Index i=0; i<size(); ++i)
+			data_[i] += m.data_[i];
+		return *this;
+	}
+
+
+	Matrix& operator -= (const Matrix &m)
+	{
+		for (Index i=0; i<size(); ++i)
+			data_[i] -= m.data_[i];
+		return *this;
+	}
+
+private:
+
+	Scalar& at(const Index i, const Index j)
+	{
+		return data_[i+nr_*j];
+	}
+
+	const Scalar& at(const Index i, const Index j) const
+	{
+		if( ! data_ )
+			throw eckit::Exception("data_ is null",Here());
+		return data_[i+nr_*j];
+	}
+
+	template<typename T0, typename T1>
+	Matrix minor(const T0& row, const T1& col) const
+	{
+		Matrix m(rows()-1,cols()-1);
+
+		Index mr(0);
+		for( Index r=0; r<rows(); ++mr)
+		{
+			if( r==row ) ++r;
+			if( r==rows() ) break;
+			Index mc(0);
+			for( Index c=0; c<(cols()); ++mc)
+			{
+				if( c==col ) ++c;
+				if( c==cols() ) break;
+				m(mr,mc) = at(r,c);
+				++c;
+			}
+			++r;
+		}
+		return m;
+	}
+
+};
+
+template< typename Scalar, typename Index = std::ptrdiff_t >
+class RowVector : public Matrix<Scalar, Index>
+{
+	typedef Matrix<Scalar, Index> Base;
+
+public:
+	typedef RowVector<Scalar> Proxy;
+	typedef RowVector<const Scalar> ConstProxy;
+
+public:
+
+	RowVector() : Base() { }
+
+	// Constructor that allocates matrix with sizes
+	template<typename T0>
+	RowVector(const T0& nc) : Base(1,nc) { }
+
+	template<typename T0>
+	RowVector(Scalar* data, const T0& nc) : Base(data,1,nc) { }
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	RowVector(const Base& other) : Base(other) { }
+
+	void resize(Index nc)
+	{
+		Base::resize(1,nc);
+	}
+
+	RowVector& operator= (const Base& other)
+	{
+		resize(other.cols());
+		memcpy( this->data(), other.data(), sizeof(Scalar)*this->cols() );
+		return *this;
+	}
+
+	// RowVector ColVector multiply
+	Scalar operator * (const ColVector<Scalar,Index> &c) const
+	{
+		Scalar s(0);
+		for( Index j=0; j<Base::nc_; ++j)
+			s += Base::data_[j] * c[j];
+		return s;
+	}
+
+	// RowVector Matrix multiply
+	Base operator * (const Base &m) const
+	{
+		return Base::operator *(m);
+	}
+
+};
+
+template< typename Scalar, typename Index = std::ptrdiff_t >
+class ColVector : public Matrix<Scalar, Index>
+{
+	typedef Matrix<Scalar, Index> Base;
+
+public:
+	typedef ColVector<Scalar> Proxy;
+	typedef ColVector<const Scalar> ConstProxy;
+
+public:
+
+	ColVector() : Base() { }
+
+	// Constructor that allocates matrix with sizes
+	template<typename T0>
+	ColVector(const T0& nr) : Base(nr,1) { }
+
+	template<typename T0>
+	ColVector(Scalar* data, const T0& nr) : Base(data,nr,1) { }
+
+	// This constructor allows you to construct Matrix from Eigen expressions
+	ColVector(const Base& other) : Base(other) { }
+
+	void resize(Index nr)
+	{
+		Base::resize(nr,1);
+	}
+
+	ColVector& operator= (const Base& other)
+	{
+		resize(other.rows(),1);
+		memcpy( this->data(), other.data(), sizeof(Scalar)*this->cols() );
+		return *this;
+	}
+
+	// ColVector RowVector multiply
+	Matrix<Scalar,Index> operator * (const RowVector<Scalar,Index> &r) const
+	{
+		Matrix<Scalar,Index> m(Base::nc_,r.nr_);
+		for(Index i=0; i<m.nr_; ++i ) {
+			for(Index j=0; j<m.nc_; ++j ) {
+				m(i,j) = Base::data_[i] * r[j];
+			}
+		}
+		return m;
+	}
+
+};
+
+
+template < typename Scalar, typename Index >
+std::ostream& operator<<( std::ostream& os, const Matrix<Scalar, Index>& m )
+{
+	for(Index i=0; i<m.nr_; ++i) {
+		if (i>0) os << "\n";
+		for(Index j=0; j<m.nc_; ++j) {
+			os << m(i,j) << " ";
+		}
+	}
+	return os;
+}
+
+namespace detail {
+
+namespace ColMajor_4x4
+{
+
+template< typename Scalar >
+inline void invert(
+	Scalar m[16],
+	typename remove_const<Scalar>::type inv[16] )
+{
+
+	inv[ 0] =  m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
+	inv[ 4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];
+	inv[ 8] =  m[4] * m[ 9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[ 9];
+	inv[12] = -m[4] * m[ 9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[ 9];
+	inv[ 1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];
+	inv[ 5] =  m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];
+	inv[ 9] = -m[0] * m[ 9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[ 9];
+	inv[13] =  m[0] * m[ 9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[ 9];
+	inv[ 2] =  m[1] * m[ 6] * m[15] - m[1] * m[ 7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[ 7] - m[13] * m[3] * m[ 6];
+	inv[ 6] = -m[0] * m[ 6] * m[15] + m[0] * m[ 7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[ 7] + m[12] * m[3] * m[ 6];
+	inv[10] =  m[0] * m[ 5] * m[15] - m[0] * m[ 7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[ 7] - m[12] * m[3] * m[ 5];
+	inv[14] = -m[0] * m[ 5] * m[14] + m[0] * m[ 6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[ 6] + m[12] * m[2] * m[ 5];
+	inv[ 3] = -m[1] * m[ 6] * m[11] + m[1] * m[ 7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[ 9] * m[2] * m[ 7] + m[ 9] * m[3] * m[ 6];
+	inv[ 7] =  m[0] * m[ 6] * m[11] - m[0] * m[ 7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[ 8] * m[2] * m[ 7] - m[ 8] * m[3] * m[ 6];
+	inv[11] = -m[0] * m[ 5] * m[11] + m[0] * m[ 7] * m[ 9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[ 9] - m[ 8] * m[1] * m[ 7] + m[ 8] * m[3] * m[ 5];
+	inv[15] =  m[0] * m[ 5] * m[10] - m[0] * m[ 6] * m[ 9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[ 9] + m[ 8] * m[1] * m[ 6] - m[ 8] * m[2] * m[ 5];
+
+	typename remove_const<Scalar>::type det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
+
+	if(det == 0)
+	{
+		Matrix<Scalar> mat(m,4,4);
+		std::stringstream msg;
+		msg << "Trying to invert 4x4 matrix with zero determinant.\nMatrix = \n" << mat;
+		throw eckit::Exception(msg.str(), Here() );
+	}
+
+	det = 1. / det;
+
+	for(int i = 0; i < 16; i++)
+			inv[i] *= det;
+}
+
+template< typename Scalar >
+Scalar det(Scalar m[16])
+{
+	Scalar inv[4] = {
+		 m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10],
+		-m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10],
+		 m[4] * m[ 9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[ 9],
+		-m[4] * m[ 9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[ 9],
+	};
+	return m[0] * inv[0] + m[1] * inv[1] + m[2] * inv[2] + m[3] * inv[3];
+}
+
+} // namespace ColMajor_4x4
+
+namespace RowMajor_4x4 {
+
+// minor
+template< typename Scalar >
+inline Scalar mnr( Scalar m[16], int r0, int r1, int r2, int c0, int c1, int c2)
+{
+	return m[4*r0+c0] * (m[4*r1+c1] * m[4*r2+c2] - m[4*r2+c1] * m[4*r1+c2]) -
+				 m[4*r0+c1] * (m[4*r1+c0] * m[4*r2+c2] - m[4*r2+c0] * m[4*r1+c2]) +
+				 m[4*r0+c2] * (m[4*r1+c0] * m[4*r2+c1] - m[4*r2+c0] * m[4*r1+c1]);
+}
+
+template< typename Scalar >
+inline void adjoint(Scalar m[16], Scalar adj[16])
+{
+	adj[ 0] =  mnr(m,1,2,3,1,2,3); adj[ 1] = -mnr(m,0,2,3,1,2,3); adj[ 2] =  mnr(m,0,1,3,1,2,3); adj[ 3] = -mnr(m,0,1,2,1,2,3);
+	adj[ 4] = -mnr(m,1,2,3,0,2,3); adj[ 5] =  mnr(m,0,2,3,0,2,3); adj[ 6] = -mnr(m,0,1,3,0,2,3); adj[ 7] =  mnr(m,0,1,2,0,2,3);
+	adj[ 8] =  mnr(m,1,2,3,0,1,3); adj[ 9] = -mnr(m,0,2,3,0,1,3); adj[10] =  mnr(m,0,1,3,0,1,3); adj[11] = -mnr(m,0,1,2,0,1,3);
+	adj[12] = -mnr(m,1,2,3,0,1,2); adj[13] =  mnr(m,0,2,3,0,1,2); adj[14] = -mnr(m,0,1,3,0,1,2); adj[15] =  mnr(m,0,1,2,0,1,2);
+}
+
+template< typename Scalar >
+inline Scalar det(Scalar m[16])
+{
+		return m[0] * mnr(m, 1, 2, 3, 1, 2, 3) -
+					 m[1] * mnr(m, 1, 2, 3, 0, 2, 3) +
+					 m[2] * mnr(m, 1, 2, 3, 0, 1, 3) -
+					 m[3] * mnr(m, 1, 2, 3, 0, 1, 2);
+}
+
+template< typename Scalar >
+inline void invert(Scalar m[16], Scalar inv[16])
+{
+	adjoint(m, inv);
+	Scalar inv_det = 1.0 / det(m);
+	for(int i = 0; i < 16; ++i)
+		inv[i] = inv[i] * inv_det;
+}
+
+} // namespace RowMajor_4x4
+
+
+} // namespace detail
+
+} // namespace maths
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/Builder.cc b/eckit/src/eckit/memory/Builder.cc
new file mode 100644
index 0000000..cbd165f
--- /dev/null
+++ b/eckit/src/eckit/memory/Builder.cc
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/memory/Builder.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+Builder::~Builder()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/memory/Builder.h b/eckit/src/eckit/memory/Builder.h
new file mode 100644
index 0000000..a751ab6
--- /dev/null
+++ b/eckit/src/eckit/memory/Builder.h
@@ -0,0 +1,291 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_memory_Builder_h
+#define eckit_memory_Builder_h
+
+/// @file Builder.h
+/// @author Tiago Quintino
+/// @date Jul 2014
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/Factory.h"
+#include "eckit/value/Params.h"
+
+// #define DEBUG_ECKIT_BUILDERS
+#ifdef  DEBUG_ECKIT_BUILDERS
+#define DEBUG_BUILDER(x) std::cerr << " DEBUG (" << x << ") " << Here() << std::endl;
+#else
+#define DEBUG_BUILDER(x)
+#endif
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//------------------------------------------------------------------------------------------------------
+
+class Builder : private NonCopyable {
+public:
+
+	typedef std::string key_t;
+
+	virtual ~Builder();
+
+	virtual key_t name() const = 0;
+	virtual key_t build_type() const = 0;
+
+	friend std::ostream& operator<<( std::ostream& os, const Builder& o) { o.print(os); return os;}
+
+private: // methods
+
+	virtual void print( std::ostream& os ) const { os << "Builder(" << build_type() << "):" << name(); }
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base >
+class BuilderT0 : public Builder {
+
+public: // types
+
+	BuilderT0() {}
+
+	~BuilderT0() {}
+
+	typedef Base product_t;
+	typedef product_t* product_ptr;
+	typedef Builder::key_t key_t;
+	typedef typename Factory<Base>::builder_ptr builder_ptr;
+
+	virtual product_ptr create() const  = 0;
+
+public: // methods
+
+	virtual key_t build_type() const { return Base::className(); }
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base >
+class BuilderT1 : public Builder {
+
+public: // types
+
+	BuilderT1() {}
+
+	~BuilderT1() {}
+
+	typedef Base product_t;
+	typedef product_t* product_ptr;
+	typedef Builder::key_t key_t;
+	typedef typename Factory<Base>::builder_ptr builder_ptr;
+
+	typedef typename product_t::ARG1 ARG1;
+
+	virtual product_ptr create( ARG1 p1 ) const = 0;
+
+public: // methods
+
+	virtual key_t build_type() const { return Base::className(); }
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base >
+class BuilderT2 : public Builder {
+
+public: // types
+
+	BuilderT2() {};
+
+	~BuilderT2() {}
+
+	typedef Base product_t;
+	typedef product_t* product_ptr;
+	typedef Builder::key_t key_t;
+	typedef typename Factory<Base>::builder_ptr builder_ptr;
+
+	typedef typename product_t::ARG1 ARG1;
+	typedef typename product_t::ARG2 ARG2;
+
+	virtual product_ptr create( ARG1 p1, ARG2 p2 ) const = 0;
+
+public: // methods
+
+	virtual key_t build_type() const { return Base::className(); }
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base, class T >
+class ConcreteBuilderT0 : public BuilderT0<Base> {
+
+public: // types
+
+	typedef BuilderT0<Base> base_t;
+
+	typedef typename base_t::key_t key_t;
+	typedef typename base_t::product_t product_t;
+	typedef typename base_t::product_ptr product_ptr;
+	typedef typename base_t::builder_ptr builder_ptr;
+
+public: // methods
+
+	ConcreteBuilderT0() : k_(name())
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT0() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this) );
+	}
+
+	ConcreteBuilderT0(const key_t& k) : k_(k)
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT0() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this) );
+	}
+
+	virtual ~ConcreteBuilderT0()
+	{
+		DEBUG_BUILDER( "~ConcreteBuilderT0() -- " << T::className() );
+		Factory<product_t>::instance().unregist( k_ );
+	}
+
+	virtual typename base_t::key_t name() const { return T::className(); }
+
+	virtual product_ptr create() const { return new T(); }
+
+private:
+
+	key_t k_;
+
+};
+
+#define register_BuilderT0(ABSTRACT,CONCRETE,NAME)\
+  static struct Register__##ABSTRACT##__##CONCRETE##__T0 {\
+    Register__##ABSTRACT##__##CONCRETE##__T0()\
+    {\
+      static eckit::ConcreteBuilderT0<ABSTRACT,CONCRETE> builder(NAME); }\
+    } register_##ABSTRACT##__##CONCRETE##_T0
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base, class T >
+class ConcreteBuilderT1 : public BuilderT1<Base> {
+public: // types
+
+	typedef BuilderT1<Base> base_t;
+
+	typedef typename base_t::key_t key_t;
+	typedef typename base_t::product_t product_t;
+	typedef typename base_t::product_ptr product_ptr;
+	typedef typename base_t::builder_ptr builder_ptr;
+
+	typedef typename base_t::ARG1 ARG1;
+
+public: // methods
+
+	ConcreteBuilderT1() : k_(name())
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT1() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this) );
+	}
+
+	ConcreteBuilderT1(const key_t& k) : k_(k)
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT1() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this) );
+	}
+
+	virtual ~ConcreteBuilderT1()
+	{
+		DEBUG_BUILDER( "~ConcreteBuilderT1() -- " << T::className() );
+		Factory<product_t>::instance().unregist( k_ );
+	}
+
+	virtual typename base_t::key_t name() const { return T::className(); }
+
+	virtual product_ptr create( ARG1 p1 ) const { return new T(p1); }
+
+private:
+
+	key_t k_;
+
+};
+
+#define register_BuilderT1(ABSTRACT,CONCRETE,NAME)\
+  static struct Register__##ABSTRACT##__##CONCRETE##__T1 {\
+    Register__##ABSTRACT##__##CONCRETE##__T1()\
+    {\
+      static eckit::ConcreteBuilderT1<ABSTRACT,CONCRETE> builder(NAME); }\
+    } register_##ABSTRACT##__##CONCRETE##_T1
+
+//------------------------------------------------------------------------------------------------------
+
+template< class Base, class T >
+class ConcreteBuilderT2 : public BuilderT2<Base> {
+
+public: // types
+
+	typedef BuilderT2<Base> base_t;
+
+	typedef typename base_t::key_t key_t;
+	typedef typename base_t::product_t product_t;
+	typedef typename base_t::product_ptr product_ptr;
+	typedef typename base_t::builder_ptr builder_ptr;
+
+	typedef typename base_t::ARG1 ARG1;
+	typedef typename base_t::ARG2 ARG2;
+
+public: // methods
+
+	ConcreteBuilderT2() : k_(name())
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT2() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this) );
+	}
+
+	ConcreteBuilderT2(const key_t& k) : k_(k)
+	{
+		DEBUG_BUILDER( "ConcreteBuilderT2() -- " << T::className() );
+		Factory<product_t>::instance().regist(k_, builder_ptr(this));
+	}
+
+	virtual ~ConcreteBuilderT2()
+	{
+		DEBUG_BUILDER( "~ConcreteBuilderT2() -- " << T::className() );
+		Factory<product_t>::instance().unregist( k_ );
+	}
+
+	virtual typename base_t::key_t name() const { return T::className(); }
+
+	virtual product_ptr create( ARG1 p1, ARG2 p2 ) const { return new T(p1,p2); }
+
+private:
+
+	key_t k_;
+
+};
+
+#define register_BuilderT2(ABSTRACT,CONCRETE,NAME)\
+  static struct Register__##ABSTRACT##__##CONCRETE##__T2 {\
+    Register__##ABSTRACT##__##CONCRETE##__T2()\
+    {\
+      static eckit::ConcreteBuilderT2<ABSTRACT,CONCRETE> builder(NAME); }\
+    } register_##ABSTRACT##__##CONCRETE##_T2
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_memory_Builder_h
diff --git a/eckit/src/eckit/memory/Counted.cc b/eckit/src/eckit/memory/Counted.cc
new file mode 100644
index 0000000..f4e186b
--- /dev/null
+++ b/eckit/src/eckit/memory/Counted.cc
@@ -0,0 +1,22 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/memory/Counted.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Counted::~Counted() {}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/memory/Counted.h b/eckit/src/eckit/memory/Counted.h
new file mode 100644
index 0000000..457db50
--- /dev/null
+++ b/eckit/src/eckit/memory/Counted.h
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Counted.h
+/// @date Jun 1996
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_Counted_h
+#define eckit_Counted_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/exception/Exceptions.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace memory {
+namespace detail {
+
+class ThreadedLock {
+public:
+    void lock() const    { mutex_.lock(); }
+    void unlock()  const { mutex_.unlock(); }
+
+    mutable Mutex mutex_;
+};
+
+class NoLock {
+public:
+    void lock() const   {}
+    void unlock() const {}
+};
+
+} // detail
+} // memory
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Reference counting objects
+/// Subclass from this class if you want reference counting object.
+/// @note Remember to use 'virtual' inheritance in case of multiple inheritance
+
+class Counted :
+    private NonCopyable,
+    private memory::detail::ThreadedLock {
+
+public: // methods
+
+
+    void attach() const
+    {
+        lock();
+        count_++;
+        unlock();
+    }
+
+    void detach() const
+    {
+        lock();
+        if ( --count_ == 0 )
+        {
+            unlock();
+            delete this;
+        }
+        else {
+            unlock();
+        }
+    }
+
+    size_t count() const { return count_; }
+
+    void lock() const {
+        memory::detail::ThreadedLock::lock();
+    }
+
+    void unlock() const {
+        memory::detail::ThreadedLock::unlock();
+    }
+
+public:
+
+    Counted() : count_(0) {}
+
+    virtual ~Counted();
+
+//  void *operator new(size_t s)  { return MemoryPool::fastAllocate(s);}
+//  void operator delete(void* p) { MemoryPool::fastDeallocate(p);     }
+
+private: // members
+
+    mutable size_t count_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/Factory.h b/eckit/src/eckit/memory/Factory.h
new file mode 100644
index 0000000..5fe48d2
--- /dev/null
+++ b/eckit/src/eckit/memory/Factory.h
@@ -0,0 +1,194 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_memory_Factory_h
+#define eckit_memory_Factory_h
+
+/// @file Factory.h
+/// @author Tiago Quintino
+/// @date Jul 2014
+
+#include <iomanip>
+#include "eckit/memory/SharedPtr.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/AutoLock.h"
+
+namespace eckit {
+
+//------------------------------------------------------------------------------------------------------
+
+template< class T >
+class Factory {
+
+public: // types
+
+	typedef std::string key_t;
+
+	typedef T product_t;
+	typedef typename product_t::builder_t builder_t;
+	typedef builder_t* builder_ptr;
+
+	typedef std::map<key_t,builder_ptr> storage_t;
+
+public: // methods
+
+	/// @return the instance of this singleton factory
+	static Factory<T>& instance();
+
+	/// @returns class name of the type built by this factory
+	static std::string build_type() { return T::className(); }
+
+	/// Checks if a builder is registered
+	/// @param name of the builder
+	bool exists( const key_t& k) const;
+
+	/// Registers a builder
+	/// @param builder pointer
+	/// @throw BadParameter if the builder already registered
+	void regist( const key_t&, builder_ptr );
+
+	/// Remove a registered builder
+	/// @throw BadParameter if the builder is not registered
+	void unregist( const key_t& );
+
+	/// Gets the builder registered to the associated key
+	/// @param name of the builder
+	const builder_t& get( const key_t& name ) const;
+
+	/// @returns the number of builders registered to the factory
+	size_t size() const;
+
+	std::vector<key_t> keys() const;
+
+	friend std::ostream& operator<<( std::ostream& os, const Factory<T>& o) { o.print(os); return os;}
+
+private: // methods
+
+	void print( std::ostream& ) const;
+
+	Factory()
+	{
+		// std::cout << "Building Factory of " << build_type() << std::endl;
+	}
+
+	~Factory()
+	{		
+//		std::cout << "Destroying Factory of " << build_type() << std::endl;
+//		if( store_.size() != 0 )
+//		{
+//			std::cout << "WARNING : Factory of " << build_type() << " still has " << size() << " providers" << std::endl;
+//			std::cout << *this << std::endl;
+//		}
+
+		ASSERT( store_.size() == 0 );
+	}
+
+private: // members
+
+	mutable Mutex mutex_;          ///< mutex protecting Factory singleton
+	storage_t store_;              ///< storage for the builders in a map indexed by key_t
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+template <class T>
+Factory<T>& Factory<T>::instance()
+{
+	static Factory<T> obj;
+	return obj;
+}
+
+template <class T>
+bool Factory<T>::exists( const key_t& k ) const
+{
+	AutoLock<Mutex> lock(mutex_);
+	return (store_.find(k) != store_.end() );
+}
+
+template <class T>
+void Factory<T>::regist( const key_t& k, builder_ptr b )
+{
+	AutoLock<Mutex> lock(mutex_);
+	ASSERT( b );
+	if( exists(k) )
+	{
+		throw BadParameter( "Factory of " + build_type() + " has already a builder for " + k, Here() );
+	}
+	store_[k] = b;
+}
+
+template <class T>
+void Factory<T>::unregist(const key_t& k)
+{
+	AutoLock<Mutex> lock(mutex_);
+	if( !exists(k) )
+	{
+		throw BadParameter( "Factory of " + build_type() + " has no builder for " + k, Here() );
+	}
+	store_.erase( k );
+}
+
+template <class T>
+size_t Factory<T>::size() const
+{
+	AutoLock<Mutex> lock(mutex_);
+	return store_.size();
+}
+
+template <class T>
+const typename Factory<T>::builder_t& Factory<T>::get(const key_t& k) const
+{
+	AutoLock<Mutex> lock(mutex_);
+	if( !exists(k) )
+	{
+		throw BadParameter( "Factory of " + build_type() + " has no builder for " + k, Here() );
+	}
+	return *store_.find(k)->second;
+}
+
+template <class T>
+void Factory<T>::print(std::ostream& os) const
+{
+	AutoLock<Mutex> lock(mutex_);
+	os << "Factory(" << build_type() << ")" << std::endl;
+
+	size_t key_width = 0;
+	for( typename storage_t::const_iterator i = store_.begin(); i != store_.end(); ++i )
+	{
+		key_width = std::max(i->first.size(),key_width);
+	}
+
+	for( typename storage_t::const_iterator i = store_.begin(); i != store_.end(); ++i )
+	{
+		os << "    " << std::setw(key_width) << std::left << i->first
+		   << "  --  " << (*(*i).second) << std::endl;
+	}
+}
+
+template <class T>
+std::vector< typename Factory<T>::key_t > Factory<T>::keys() const
+{
+	AutoLock<Mutex> lock(mutex_);
+
+	std::vector<key_t> keysv; keysv.reserve( store_.size() );
+	for( typename storage_t::const_iterator i = store_.begin(); i != store_.end(); ++i )
+	{
+		keysv.push_back(i->first);
+	}
+	return keysv;
+}
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_memory_Factory_h
diff --git a/eckit/src/eckit/memory/MapAllocator.cc b/eckit/src/eckit/memory/MapAllocator.cc
new file mode 100644
index 0000000..4675d4d
--- /dev/null
+++ b/eckit/src/eckit/memory/MapAllocator.cc
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "eckit/eckit.h"
+#include "eckit/memory/MapAllocator.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static size_t whole_page(size_t size)
+{
+        static size_t page = sysconf(_SC_PAGE_SIZE);
+        return ((size + page - 1) / page) * page;
+}
+
+MapAllocatorTooSmall::MapAllocatorTooSmall(size_t,size_t):
+        Exception("MapAllocator too small")
+{
+}
+
+MapAllocator::MapAllocator(size_t length):
+        fd_(-1),    
+        length_(whole_page(length)),
+        count_(0),
+        more_(0)
+{
+        addr_ = ::mmap(0,length_,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,fd_,0);
+
+        if(addr_ == MAP_FAILED)
+            throw FailedSystemCall("mmap",Here());
+
+        next_ = (char*)addr_;
+        left_ = length_;
+//	Log::warning() << "MapAllocator created " << length_ << std::endl;
+}
+
+MapAllocator::~MapAllocator()
+{
+//	Log::warning() << "MapAllocator deleted " << length_ << std::endl;
+        munmap(addr_,length_);
+        if(fd_>=0) close(fd_);
+        delete more_;
+}
+
+union Align {
+        char      char_;
+        double    double_;
+        long long longlong_;
+        void*     voidstar_;
+        float     float_;
+};
+
+const int WORD = sizeof(Align);
+
+void *MapAllocator::allocate(size_t size)
+{
+        //return ::operator new(size);
+
+        size = ((size + WORD - 1) / WORD) * WORD;
+
+        if(size > left_)
+        {
+                if(!more_)
+                {
+//			Log::warning() << "MapAllocator too small: length=" << length_ << " left=" << left_ << " request=" << size << std::endl;
+                        more_ = new MapAllocator(std::max(length_,size));
+                        ASSERT(more_);
+                }
+                return more_->allocate(size);
+        }
+
+        char *addr = next_;
+        next_ += size;
+        left_ -= size;
+        count_++;
+
+        return addr;
+}
+
+void MapAllocator::deallocate(void* addr)
+{
+        //::operator delete(addr); return;
+
+        char* p = (char*)addr;
+        char* q = (char*)addr_;
+
+        if(p >= q && p < q + length_)
+        {
+                count_--;
+                if(count_ == 0)
+                {
+//			Log::warning() << "MapAllocator empty" << std::endl;
+                        next_ = (char*)addr_;
+                        left_ = length_;
+                }
+        }
+        else
+        {
+                ASSERT(more_);
+                more_->deallocate(addr);
+                if(more_->count_ == 0 && more_->more_ == 0)
+                {
+                        delete more_;
+                        more_ = 0;
+                }
+        }
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/memory/MapAllocator.h b/eckit/src/eckit/memory/MapAllocator.h
new file mode 100644
index 0000000..bed27ea
--- /dev/null
+++ b/eckit/src/eckit/memory/MapAllocator.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MapAllocator.h
+// MARS (Baudouin Raoult) - ECMWF Nov 01
+
+#ifndef eckit_MapAllocator_h
+#define eckit_MapAllocator_h
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MapAllocatorTooSmall : public Exception {
+public:	
+	MapAllocatorTooSmall(size_t,size_t);
+};
+
+//-----------------------------------------------------------------------------
+
+class MapAllocator : private eckit::NonCopyable {
+public:
+
+	MapAllocator(size_t);
+
+	~MapAllocator(); 
+
+	void *allocate(size_t);
+	void deallocate(void*);
+
+private: // members
+
+	int      fd_;
+    void*    addr_;
+    char*    next_;
+	size_t   length_;
+	size_t   left_;
+	Ordinal  count_;
+
+	MapAllocator* more_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/MemoryPool.cc b/eckit/src/eckit/memory/MemoryPool.cc
new file mode 100644
index 0000000..1330ce9
--- /dev/null
+++ b/eckit/src/eckit/memory/MemoryPool.cc
@@ -0,0 +1,425 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include "eckit/log/Bytes.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/MemoryPool.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static pthread_mutex_t the_lock;
+
+static void get_lock()
+{
+    // std::cout << pthread_self() << " get_lock" << std::endl;
+	pthread_mutex_lock(&the_lock);
+}
+
+static void release_lock()
+{
+    // std::cout << pthread_self() << " release_lock" << std::endl;
+	pthread_mutex_unlock(&the_lock);
+}
+
+static void release_lock_parent()
+{
+    // std::cout << pthread_self() << " release_lock" << std::endl;
+	pthread_mutex_unlock(&the_lock);
+}
+
+static void release_lock_child()
+{
+	// see ECKIT-140
+
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
+
+	pthread_mutex_init(&the_lock,&attr);
+
+    // std::cout << pthread_self() << " release_lock" << std::endl;
+}
+
+// Don't use STL , Mutex, or Log to avoid re-entrance problems...
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void _init(void)
+{
+    // std::cout << pthread_self() << " init" << std::endl;
+
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
+
+	pthread_mutex_init(&the_lock,&attr);
+
+	// see ECKIT-140
+	pthread_atfork(get_lock,
+				   release_lock_parent,
+				   release_lock_child);
+}
+
+static void init()
+{
+	pthread_once(&once,_init);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+union Align {
+	char      char_;
+	double    double_;
+	long long longlong_;
+	void*     voidstar_;
+	float     float_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct MemBlk {
+
+	static MemBlk* memList_;
+
+	static unsigned long long allocateCount_;
+	static unsigned long long releaseCount_;
+	static unsigned long long reuseCount_;
+	static unsigned long long newCount_;
+
+
+	MemBlk        *left_;
+	MemBlk        *right_;
+	unsigned long size_;
+	unsigned long reuse_;
+
+	// Must be last member
+	unsigned long check_;
+
+	MemBlk(size_t s): left_(0),right_(0),size_(s),reuse_(0),check_(0) {}
+
+	void* addr() { return ((char*)&check_ + sizeof(check_)); }
+
+	static MemBlk* find(size_t s);
+
+	bool check()        { return check_ == size_;  }
+	bool inUse()        { return check_;           }
+	void inUse(bool on) { check_ = on?size_:0;     }
+	void reuse()        { reuse_++;                }
+
+	void dump(std::ostream&,int depth = 0);
+	void size(unsigned long long&,unsigned long long&);
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+MemBlk* MemBlk::memList_ = 0;
+unsigned long long MemBlk::allocateCount_ = 0;
+unsigned long long MemBlk::releaseCount_  = 0;
+unsigned long long MemBlk::reuseCount_    = 0;
+unsigned long long MemBlk::newCount_      = 0;
+
+MemBlk* MemBlk::find(size_t size)
+{
+	MemBlk *m = memList_;
+	MemBlk *p = 0;
+	bool left = false;
+
+	while(m)
+	{
+		if(size == m->size_ && !m->inUse())
+		{
+			reuseCount_++;
+			m->inUse(true);
+			m->reuse();
+//			std::cout << "Reusing a " << size << " block" << std::endl;
+			return m;
+		}
+		p = m;
+		if(size <= m->size_)
+		{
+			left = true;
+			m = m->left_;
+		}
+		else
+		{
+			left = false;
+			m = m->right_;
+		}
+	}
+
+//	std::cout << "Allocating a " << size << " block" << std::endl;
+
+	newCount_++;
+
+	void *blk = new char[size + sizeof(MemBlk)];
+	m         = new(blk) MemBlk(size);
+
+	if(p == 0)
+		memList_ = m;
+	else if(left)
+		p->left_ = m;
+	else
+		p->right_ = m;
+
+	m->inUse(true);
+
+	return m;
+}
+
+
+void MemBlk::dump(std::ostream& s,int depth)
+{
+	if(left_)  left_->dump(s,depth+1);
+	s << "size= " << size_ << " active= " << (check_?1:0) <<
+	" reuse= " << reuse_ << " "<< Bytes(size_) << std::endl;
+	if(right_) right_->dump(s,depth+1);
+}
+
+void MemBlk::size(unsigned long long& total,unsigned long long& inuse)
+{
+	if(left_)  left_->size(total,inuse);
+	if(right_) right_->size(total,inuse);
+	total += size_;
+	if(check_) inuse += size_;
+}
+
+void* MemoryPool::largeAllocate(size_t size)
+{
+	init();
+
+	MemBlk *m  = 0;
+	void *addr = 0;
+
+	get_lock();
+	MemBlk::allocateCount_++;
+	m = MemBlk::find(size>0?size:1);
+	addr = m->addr();
+	release_lock();
+
+	return addr;
+}
+
+void MemoryPool::largeDeallocate(void* addr)
+{
+	init();
+
+	get_lock();
+	MemBlk::releaseCount_++;
+
+	MemBlk *m = (MemBlk*)((char*)addr - sizeof(MemBlk));
+
+	if(!(m->check() && m->inUse())) {
+        std::cerr << "deallocating a bad block" << std::endl;
+		abort();
+	}
+
+//	std::cout << "Releasing " << m->size_ << std::endl;
+	m->inUse(false);
+
+	release_lock();
+}
+
+
+
+const size_t WORD = sizeof(Align);
+
+struct memblk {
+	memblk *next_;
+    size_t count_;
+    size_t left_;
+    size_t size_;
+    char   buffer_[WORD];
+};
+
+const long HEADER_SIZE = (sizeof(memblk) - WORD);
+
+
+
+eckit::MemPool eckit::MemPool::transientPool = {
+	1, // 1 page
+	0, // don't zero
+};
+
+eckit::MemPool eckit::MemPool::permanentPool = {
+	1, // 1 page
+	0, // don't zero
+};
+
+
+
+void *MemoryPool::fastAllocate(size_t s,MemPool& pool)
+{
+#ifdef __hpux
+	return new char[s];
+#endif
+	init();
+
+	get_lock();
+
+	memblk *m = pool.first_;
+
+	/* align */
+
+	s = ((s+WORD-1)/WORD)*WORD;
+
+	while(m && (m->left_ < s))
+		m = m->next_;
+
+	if(m == 0)
+	{
+		static int page_size = getpagesize();
+
+        size_t size = page_size*pool.pages_;
+
+		if(s > size - HEADER_SIZE)
+		{
+//			std::cerr << "Object of " << s << " bytes is too big for " << __FUNCTION__ << std::endl;
+//			std::cerr << "Block size is " << size - HEADER_SIZE << std::endl;
+			size = ((s + HEADER_SIZE + (page_size-1)) / page_size) * page_size;
+		}
+
+		memblk* p = static_cast<memblk*>(largeAllocate(size));
+		if(pool.clear_) ::memset(p,0,size);
+
+		p->next_    = pool.first_;
+		p->count_   = 0;
+		p->size_    = p->left_ = size-HEADER_SIZE;
+		m           = p;
+		pool.first_ = p;
+	}
+
+	void *p   = &m->buffer_[m->size_ - m->left_];
+	m->left_ -= s;
+	m->count_++;
+
+	release_lock();
+
+	return p;
+}
+
+void MemoryPool::fastDeallocate(void *p,MemPool& pool)
+{
+#ifdef __hpux
+	char* x = (char*)p;
+	delete[] x;
+	return;
+#endif
+
+	init();
+	get_lock();
+
+	memblk *m = pool.first_;
+	memblk *n = 0;
+
+	while(m)
+	{
+		if( ((char*)p >= (char*)&m->buffer_[0]) &&
+		    ((char*)p < (char*)&m->buffer_[m->size_]))
+		{
+			m->count_--;
+			if(m->count_ == 0)
+			{
+				if(n) n->next_   = m->next_;
+				else pool.first_ = m->next_;
+				largeDeallocate(m);
+			}
+			release_lock();
+			return;
+		}
+
+		n = m;
+		m = m->next_;
+	}
+
+    std::cerr << Here() << std::endl;
+	abort();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void MemoryPool::info(std::ostream& out)
+{
+    {
+        init();
+
+        get_lock();
+
+#if 0
+
+        static struct mallinfo l = { 0,};
+        struct mallinfo m = mallinfo();
+        out << "total " << m.arena
+            <<  "  "  << m.arena - l.arena << std::endl;
+        out << "used  " << m.uordblks << " (" << int(double(m.uordblks)/double(m.arena)*100 + 0.5) << "%)"
+            <<  "  "  << m.uordblks - l.uordblks << std::endl;
+        out << "free  " << m.fordblks << " (" << int(double(m.fordblks)/double(m.arena)*100 + 0.5) << "%)"
+            <<  "  "  << m.fordblks - l.fordblks << std::endl;
+        l = m;
+        out << m.arena << " (total space in arena)" << std::endl;
+        out << m.ordblks << " (number of ordinary blocks)" << std::endl;
+        out << m.smblks << " (number of small blocks)" << std::endl;
+        out << m.hblks << " (number of holding blocks)" << std::endl;
+        out << m.hblkhd << " (space in holding block headers)" << std::endl;
+        out << m.usmblks << " (space in small blocks in use)" << std::endl;
+        out << m.fsmblks << " (space in free small blocks)" << std::endl;
+        out << m.uordblks << " (space in ordinary blocks in use)" << std::endl;
+        out << m.fordblks << " (space in free ordinary blocks)" << std::endl;
+        out << m.keepcost << " (cost of enabling keep option)" << std::endl;
+#ifdef SUNINFO
+        out << m.mxfast << " (max size of small block)" << std::endl;
+        out << m.nblks << " (number of small blocks in holding block)" << std::endl;
+        out << m.grain << " (small block rounding factor)" << std::endl;
+        out << m.uordbytes << " (space allocated in ordinary blocks)" << std::endl;
+        out << m.allocated << " (number of ordinary blocks allocated)" << std::endl;
+        out << m.treeoverhead << " (bytes used in maintaining in free tree)" << std::endl;
+#endif
+#endif
+    }
+
+	out << "---" << std::endl;
+	out << "allocate " << MemBlk::allocateCount_ << std::endl;
+	out << "release  " << MemBlk::releaseCount_  << std::endl;
+	out << "reuse    " << MemBlk::reuseCount_ << std::endl;
+	out << "new      " << MemBlk::newCount_ << std::endl;
+
+	unsigned long long total = 0;
+	unsigned long long left = 0;
+
+	MemBlk::memList_->size(total,left);
+
+	out << "total    " << total << std::endl;
+	out << "left     " << left  << std::endl;
+
+	MemBlk::memList_->dump(out,0);
+
+	out << "Transient pool: " << std::endl;
+	memblk *m = MemPool::transientPool.first_;
+
+	while(m)
+	{
+		out << "Size = " << m->size_ << " left = " << m->left_
+			 << " count = " << m->count_ << std::endl;
+		m = m->next_;
+	}
+
+    release_lock();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/memory/MemoryPool.h b/eckit/src/eckit/memory/MemoryPool.h
new file mode 100644
index 0000000..c66360a
--- /dev/null
+++ b/eckit/src/eckit/memory/MemoryPool.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MemoryPool.h
+// Baudouin Raoult - ECMWF May 97
+
+#ifndef eckit_MemoryPool_h
+#define eckit_MemoryPool_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+struct memblk;
+
+struct MemPool {
+
+	int    pages_;     /* Number of pages to allocate */
+	bool   clear_;     /* clear newly allocated memory */
+	memblk *first_;
+
+	static MemPool transientPool;
+	static MemPool permanentPool;
+};
+
+class MemoryPool : private NonCopyable {
+
+public: // class methods
+
+
+	static void dump(std::ostream&);
+	static void handler();
+
+	static void* largeAllocate(size_t);
+	static void  largeDeallocate(void*);
+
+	static void* fastAllocate(size_t,MemPool& = MemPool::transientPool);
+	static void  fastDeallocate(void*,MemPool& = MemPool::transientPool);
+
+	static void info(std::ostream&);
+
+private: // methods
+
+	MemoryPool();
+	~MemoryPool();
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/NonCopyable.cc b/eckit/src/eckit/memory/NonCopyable.cc
new file mode 100644
index 0000000..9981dbe
--- /dev/null
+++ b/eckit/src/eckit/memory/NonCopyable.cc
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+NonCopyable::NonCopyable(){}
+
+NonCopyable::~NonCopyable(){}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/memory/NonCopyable.h b/eckit/src/eckit/memory/NonCopyable.h
new file mode 100644
index 0000000..19e8174
--- /dev/null
+++ b/eckit/src/eckit/memory/NonCopyable.h
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_NonCopyable_h
+#define eckit_NonCopyable_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// Inherit from this class to make a NonCopyable class
+
+class NonCopyable {
+protected:
+
+    NonCopyable();
+    ~NonCopyable();
+    
+private: // No copy allowed    
+    
+    NonCopyable(const NonCopyable&);
+	NonCopyable& operator=(const NonCopyable&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/Owned.h b/eckit/src/eckit/memory/Owned.h
new file mode 100644
index 0000000..010fc70
--- /dev/null
+++ b/eckit/src/eckit/memory/Owned.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Owned.h
+/// @author Tiago Quintino
+/// @date May 2014
+
+#ifndef eckit_memory_Owned_h
+#define eckit_memory_Owned_h
+
+#include "eckit/memory/Counted.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Reference counting objects
+/// Subclass from this class to use a SharedPtr class
+
+template < typename LOCK >
+class OwnedT : private NonCopyable,
+               public LOCK {
+
+public: // methods
+
+    OwnedT() : count_(0) {}
+
+    virtual ~OwnedT() {}
+
+    void attach() const
+    {
+        LOCK::lock();
+        count_++;
+        LOCK::unlock();
+    }
+
+    void detach() const
+    {
+        LOCK::lock();
+        --count_;
+        LOCK::unlock();
+    }
+
+    size_t owners() const { return count_; }
+
+private: // members
+
+    mutable size_t count_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Owned object without thread lockable resource
+typedef OwnedT< memory::detail::ThreadedLock >  OwnedLock;
+
+/// Owned object with thread lockable resource
+typedef OwnedT< memory::detail::NoLock >  OwnedNoLock;
+
+/// Default Owned type
+/// Same as OwnedNoLock
+typedef OwnedNoLock Owned;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/Padded.h b/eckit/src/eckit/memory/Padded.h
new file mode 100644
index 0000000..a7d72bb
--- /dev/null
+++ b/eckit/src/eckit/memory/Padded.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Padded.h
+// Baudouin Raoult - ECMWF Jan 97
+
+#ifndef eckit_Padded_h
+#define eckit_Padded_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Use this class to pad an object to a given size
+
+template<class T,int size>
+class Padded : public T {
+private:
+
+	// Add the padding 
+
+    enum {
+			align_ = size,
+			osize_ = sizeof(T)
+	};
+
+	char padding_[((osize_+align_-1)/align_)*align_ - osize_];
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/ScopedPtr.h b/eckit/src/eckit/memory/ScopedPtr.h
new file mode 100644
index 0000000..ee8f027
--- /dev/null
+++ b/eckit/src/eckit/memory/ScopedPtr.h
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_ScopedPtr_h
+#define eckit_ScopedPtr_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// A smart pointer that deletes the pointee object when going out of scope.
+/// This should have a very similar interface to boost scoped_ptr or std unique_ptr
+/// so that once C++11 is suported overall compilers we can swithc easily.
+/// However due to lack of C++11 support, it does not support move semantics.
+
+template < typename T >
+class ScopedPtr : private NonCopyable {
+
+public: // types
+
+    typedef T  element_type;
+    typedef T* pointer_type;
+    typedef T& reference_type;
+
+public: // methods
+
+    /// Constructor
+    /// @throws nothing
+    explicit ScopedPtr( pointer_type ptr = 0 ) : ptr_(ptr) {}
+
+    /// Destructor
+    /// @throws nothing
+    ~ScopedPtr() { destroy(); }
+
+    /// Resets the pointee
+    /// @throws nothing
+    void reset( pointer_type ptr = 0 )
+    {
+        destroy();
+        ptr_ = ptr;
+    }
+
+    /// Releases the ownership of the pointee
+    /// @throws nothing
+    pointer_type release()
+    {
+        pointer_type r = ptr_;
+        ptr_ = 0;
+        return r;
+    }
+
+    /// Assignement operator transfers ownership to another ScopedPtr
+    const ScopedPtr& operator= (ScopedPtr& other)
+    {
+        reset( other.release() );
+        return *this;
+    }
+
+    /// Dereferences the pointee
+    reference_type operator*() const { ASSERT(ptr_); return *ptr_; }
+
+    /// Dereferences object member
+    pointer_type operator->() const { ASSERT(ptr_); return ptr_; }
+
+    /// @returns a pointer to the managed object or null if no object is owned.
+    /// Should be used with caution, because of issues dealing with raw pointers.
+    /// However, get makes it possible to explicitly test whether the stored point is NULL.
+    /// The function never throws. get is typically used when calling functions
+    /// that require a raw pointer.
+    /// Note: previously this asserted ptr_ was not null, however this is inconsistent
+    ///       with the standard boost scoped_ptr or std unique_ptr
+    pointer_type get() const { return ptr_; }
+
+    /// @returns true if pointer is not null
+    /// @throws nothing
+    operator bool() const { return (ptr_ != 0); }
+
+    /// Swaps the pointee with another ScopedPtr
+    /// @throws nothing
+    void swap( ScopedPtr<T>& other )
+    {
+        pointer_type tmp( ptr_ );
+        ptr_ = other.ptr_;
+        other.ptr_ = tmp;
+    }
+
+protected: // methods
+
+    void destroy() { delete ptr_; ptr_ = 0; }
+
+private: // members
+
+    pointer_type ptr_;
+
+};
+
+/// non-member function overload
+template< typename T >
+void swap( ScopedPtr<T>& a, ScopedPtr<T>& b )
+{
+    a.swap(b);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/memory/SharedPtr.h b/eckit/src/eckit/memory/SharedPtr.h
new file mode 100644
index 0000000..0fc95b9
--- /dev/null
+++ b/eckit/src/eckit/memory/SharedPtr.h
@@ -0,0 +1,243 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_memory_SharedPtr_h
+#define eckit_memory_SharedPtr_h
+
+/// @file SharedPtr.h
+/// @author Tiago Quintino
+/// @date Mar 2014
+
+#include "eckit/exception/Exceptions.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// @brief Deletes a pointer and makes sure it is set to 0 afterwards
+/// Do not use this function with data allocate with new [].
+/// @pre p has been allocated with operator new
+/// @param p pointer to be deleted
+/// @post  p equals 0
+template <class T>
+void delete_ptr(T*& p)
+{
+    delete p;
+    p = 0;
+}
+
+/// @brief Deletes a pointer and makes sure it is set to 0 afterwards
+/// Do not use this function with data allocate with new.
+/// @pre p has been allocated with operator new []
+/// @param p pointer to be deleted
+/// @post  p equals CFNULL
+template <class T>
+void delete_ptr_array(T*& p)
+{
+    delete [] p;
+    p = 0;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template <class T>
+struct NewDealloc
+{
+    static void deallocate( T*& p ) { delete_ptr(p); }
+};
+
+template <class T>
+struct NewArrayDealloc
+{
+    static void deallocate( T*& p ) { delete_ptr_array(p); }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// A smart pointer that allows to share resources that have derived from Owned
+/// @see Owned
+
+template< class T, class ALLOC = NewDealloc<T> >
+class SharedPtr {
+
+public: // types
+
+    typedef T  element_type;
+    typedef T* pointer_type;
+
+public: // methods
+
+    /// Constructor
+    SharedPtr() : ptr_(0) {}
+
+    /// Constructor.
+    /// @param ptr naked pointer
+    explicit SharedPtr(T* ptr) : ptr_(ptr)
+    {
+        if( ! null() )
+            ptr_->attach();
+	}
+
+    /// Copy constructor
+    SharedPtr ( const SharedPtr& other ) : ptr_(other.ptr_)
+    {
+        if( ! null() )
+            ptr_->attach();
+    }
+
+    /// Destructor
+    ~SharedPtr()
+    {
+        release();
+    }
+
+    /// Release ownership
+    /// @post ptr_ = 0
+    void release()
+    {
+        if( ! null() )
+        {
+            ptr_->lock();
+            ptr_->detach(); /* lock/unlock in detach() isn't sufficient, else there is race condition on owners() */
+
+            if( ptr_->owners() == 0 ) {
+                ptr_->unlock();
+                ALLOC::deallocate( ptr_ ); // also zeros ptr_
+                return;
+            }
+            ptr_->unlock();
+            ptr_ = 0;
+        }
+    }
+
+
+    /// Reset the ptr
+    /// @post ptr_ = other
+    void reset(T* other)
+    {
+        if(other == ptr_) return;
+
+        release();
+
+        ptr_ = other;
+
+        if( ! null() )
+            ptr_->attach();
+    }
+
+    /// Reset the ptr
+    /// @post ptr_ = other
+    void reset(const SharedPtr& other)
+    {
+        release();
+
+        ptr_ = other.ptr_;
+
+        if( ! null() )
+            ptr_->attach();
+    }
+
+    /// Overloading of "=" with SharedPtr
+    /// @return missing documentation
+    const SharedPtr& operator= (const SharedPtr& other)
+    {
+        if( ptr_ != other.ptr_ )
+            reset(other);
+        return *this;
+    }
+
+    /// Overloading of "=" with SharedPtr
+    /// @return missing documentation
+    const SharedPtr& operator= (T* other)
+    {
+        if( ptr_ != other )
+            reset(other);
+        return *this;
+    }
+
+    /// Swap internal pointer with other SharedPtr
+    void swap ( SharedPtr& other )
+    {
+        std::swap( ptr_, other.ptr_ );
+    }
+
+    /// Overloading of "=="
+    /// @return true if pointees have the same address
+    operator bool () const
+    {
+        return ! null();
+    }
+
+    /// Overloading of "=="
+    /// @return true if pointees have the same address
+    bool operator== (const SharedPtr& other) const
+    {
+        return ( ptr_ == other.ptr_ );
+    }
+
+    /// Overloading of "!="
+    /// @return true if pointees have the different address
+    bool operator!= (const SharedPtr& other) const
+    {
+        return ! operator==( other );
+    }
+
+    /// @returns the naked pointer to the object
+    T* get() const { return ptr_; }
+
+    /// @returns true if it is a unique owner
+    bool unique() const
+    {
+        ASSERT( ! null() );
+        return ptr_->owners() == 1;
+    }
+
+    /// Overloading of "->"
+    /// @returns the naked pointer to the object
+    T* operator->() const
+    {
+        ASSERT( ! null() );
+        return ptr_;
+    }
+
+    /// Overloading of "*"
+    /// @return dereferrences the internal pointer
+    T& operator*() const
+    {
+        ASSERT( ! null() );
+        return *ptr_;
+    }
+
+    size_t owners() const
+    {
+        if( !null() )
+            return ptr_->owners();
+        return 0;
+    }
+
+    size_t use_count() const { return owners(); }
+
+private: // methods
+
+    /// @returns true if ptr_ == 0
+    bool null() const { return ( ptr_ == 0 ); }
+
+private: // members
+
+    pointer_type ptr_; ///< raw pointer
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_memory_SharedPtr_h
diff --git a/eckit/src/eckit/mpi/Buffer.h b/eckit/src/eckit/mpi/Buffer.h
new file mode 100644
index 0000000..bc35bfd
--- /dev/null
+++ b/eckit/src/eckit/mpi/Buffer.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Buffer_h
+#define eckit_mpi_Buffer_h
+
+#include <vector>
+#include <cstddef>
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Buffer handles colleciton of vector pieces into a larger vector
+
+template <typename DATA_TYPE>
+struct Buffer
+{
+    typedef DATA_TYPE value_type;
+    typedef typename std::vector<DATA_TYPE>::iterator iterator;
+
+    int cnt;
+
+    std::vector<int>       counts;
+    std::vector<int>       displs;
+    std::vector<DATA_TYPE> buffer;
+
+    Buffer(size_t size)
+    {
+        counts.resize( size );
+        displs.resize( size );
+    }
+
+    iterator begin() { return buffer.begin(); }
+    iterator end()   { return buffer.end();   }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/CMakeLists.txt b/eckit/src/eckit/mpi/CMakeLists.txt
new file mode 100644
index 0000000..8ddfb80
--- /dev/null
+++ b/eckit/src/eckit/mpi/CMakeLists.txt
@@ -0,0 +1,49 @@
+list( APPEND eckit_mpi_srcs
+Buffer.h
+Comm.cc
+Comm.h
+DataType.cc
+DataType.h
+Operation.cc
+Operation.h
+Request.cc
+Request.h
+Serial.cc
+Serial.h
+SerialData.h
+SerialStatus.cc
+SerialStatus.h
+SerialRequest.cc
+SerialRequest.h
+Status.cc
+Status.h
+)
+
+if( HAVE_MPI )
+
+    list( APPEND eckit_mpi_srcs
+        Parallel.cc
+        Parallel.h
+        ParallelStatus.cc
+        ParallelStatus.h
+        ParallelRequest.cc
+        ParallelRequest.h
+        )
+
+    set(eckit_mpi_defs ${MPI_CXX_DEFINITIONS} )
+    set(eckit_mpi_incs ${MPI_CXX_INCLUDE_PATH} )
+    set(eckit_mpi_libs ${MPI_CXX_LIBRARIES} )
+endif()
+
+ecbuild_add_library(TARGET eckit_mpi
+                    INSTALL_HEADERS ALL
+                    HEADER_DESTINATION
+                        ${INSTALL_INCLUDE_DIR}/eckit/mpi
+                    SOURCES
+                        ${eckit_mpi_srcs}
+                    DEFINITIONS
+                        "${eckit_mpi_defs}"
+                    INCLUDES
+                        "${eckit_mpi_incs}"
+                    LIBS
+                        eckit ${eckit_mpi_libs} )
diff --git a/eckit/src/eckit/mpi/Comm.cc b/eckit/src/eckit/mpi/Comm.cc
new file mode 100644
index 0000000..aa0214d
--- /dev/null
+++ b/eckit/src/eckit/mpi/Comm.cc
@@ -0,0 +1,231 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Comm.h"
+
+#include <map>
+
+#include "eckit/config/LibEcKit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Environment {
+public:
+
+    static const char* getDefaultComm() {
+        // Force a given communicator (only required if e.g. running serial applications with MPI)
+        if (const char* forcedComm = ::getenv("ECKIT_MPI_FORCE")) {
+            return forcedComm;
+        // Use parallel communicator if in an MPI environment
+        } else if (::getenv("OMPI_COMM_WORLD_SIZE") || // OpenMPI
+                   ::getenv("ALPS_APP_PE")          || // Cray PE
+                   ::getenv("PMI_SIZE")) {             // Intel
+            return "parallel";
+        // Use serial communicator otherwise
+        } else {
+            return "serial";
+        }
+    }
+
+    static Environment& instance() {
+        static Environment env;
+        return env;
+    }
+
+    void initDefault() {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        ASSERT(!default_);
+
+        Comm* world = CommFactory::build(getDefaultComm());
+
+        communicators["world"] = world;
+        communicators["self"]  = world->self();
+
+        default_ = world;
+    }
+
+    void setDefaut(const char* name) {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        std::map<std::string, Comm*>::iterator itr = communicators.find(name);
+        if(itr != communicators.end()) {
+            default_ = itr->second;
+            return;
+        }
+
+        eckit::Log::error() << "Cannot set default communicator to '" << name << "', no communicator with that name was found" << std::endl;
+        eckit::Log::error() << "Current communicators are:" << std::endl;
+        for(itr = communicators.begin() ; itr != communicators.end() ; ++itr)
+            eckit::Log::error() << "   " << (*itr).first << std::endl;
+        throw eckit::SeriousBug(std::string("No communicator called ") + name);
+    }
+
+    bool hasComm(const char* name) {
+        AutoLock<Mutex> lock(mutex_);
+        std::map<std::string, Comm*>::iterator itr = communicators.find(name);
+        if(itr != communicators.end()) {
+            return true;
+        }
+        return false;
+    }
+
+    Comm& getComm(const char* name = 0) {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        if(!name && default_) return *default_; /* most common case first */
+
+        if(!default_) { initDefault(); }
+
+        if(!name) {
+            ASSERT(default_);  /* sanity check init was successful */
+            return *default_;
+        }
+
+        std::map<std::string, Comm*>::iterator itr = communicators.find(name);
+        if(itr != communicators.end()) {
+            return *itr->second;
+        }
+
+        eckit::Log::error() << "No Communicator '" << name << "'" << std::endl;
+        eckit::Log::error() << "Current communicators are:" << std::endl;
+        for(itr = communicators.begin() ; itr != communicators.end() ; ++itr)
+            eckit::Log::error() << "   " << (*itr).first << std::endl;
+        throw eckit::SeriousBug(std::string("No communicator called ") + name);
+    }
+
+    void addComm(const char* name, int comm) {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        std::map<std::string, Comm*>::iterator itr = communicators.find(name);
+        if(itr != communicators.end()) {
+            eckit::Log::error() << "Cannot create communicator '" << name << "', communicator with that name already exists" << std::endl;
+            eckit::Log::error() << "Current communicators are:" << std::endl;
+            for(itr = communicators.begin() ; itr != communicators.end() ; ++itr)
+                eckit::Log::error() << "   " << (*itr).first << std::endl;
+            throw eckit::SeriousBug(std::string("Communicator alredy exists: ") + name);
+        }
+
+        Comm* pComm = CommFactory::build(getDefaultComm(), comm);
+        communicators[name] = pComm;
+    }
+
+    CommFactory& getFactory(const std::string& name) {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        std::map<std::string, CommFactory *>::const_iterator j = factories.find(name);
+
+        if (j != factories.end()) { return *(j->second); }
+
+        eckit::Log::error() << "No CommFactory for [" << name << "]" << std::endl;
+        eckit::Log::error() << "CommFactories are:" << std::endl;
+        for (j = factories.begin() ; j != factories.end() ; ++j)
+            eckit::Log::error() << "   " << (*j).first << std::endl;
+
+        throw eckit::SeriousBug(std::string("No CommFactory called ") + name);
+    }
+
+    void registFactory(const std::string& name, CommFactory* f) {
+        AutoLock<Mutex> lock(mutex_);
+        ASSERT(factories.find(name) == factories.end());
+        factories[name] = f;
+    }
+
+    void unregistFactory(const std::string& name) {
+        AutoLock<Mutex> lock(mutex_);
+        factories.erase(name);
+    }
+
+    Environment() : default_(0) {}
+
+    ~Environment() {
+
+        AutoLock<Mutex> lock(mutex_);
+
+        if(default_) {
+            for(std::map<std::string,Comm*>::iterator itr = communicators.begin() ; itr != communicators.end() ; ++itr) {
+                delete itr->second;
+            }
+            default_ = 0;
+        }
+    }
+
+    Comm* default_;
+
+    std::map<std::string, Comm*> communicators;
+
+    std::map<std::string, CommFactory*> factories;
+
+    eckit::Mutex mutex_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+CommFactory::CommFactory(const std::string &name):
+    name_(name) {
+    Environment::instance().registFactory(name, this);
+}
+
+CommFactory::~CommFactory() {
+    Environment::instance().unregistFactory(name_);
+}
+
+Comm* CommFactory::build(const std::string& name) {
+    return Environment::instance().getFactory(name).make();
+}
+
+Comm*CommFactory::build(const std::string& name, int comm)
+{
+    return Environment::instance().getFactory(name).make(comm);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Comm::Comm() {
+}
+
+Comm::~Comm() {
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Comm& comm(const char* name) {
+    return Environment::instance().getComm(name);
+}
+
+void setCommDefault(const char* name)
+{
+    Environment::instance().setDefaut(name);
+}
+
+void addComm(const char* name, int comm)
+{
+    Environment::instance().addComm(name, comm);
+}
+
+bool hasComm(const char* name) {
+    return Environment::instance().hasComm(name);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Comm.h b/eckit/src/eckit/mpi/Comm.h
new file mode 100644
index 0000000..e10f6cb
--- /dev/null
+++ b/eckit/src/eckit/mpi/Comm.h
@@ -0,0 +1,843 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Comm_h
+#define eckit_mpi_Comm_h
+
+#include <cstddef>
+#include <iterator>
+#include <vector>
+#include <string>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/NonCopyable.h"
+
+#include "eckit/mpi/Buffer.h"
+#include "eckit/mpi/DataType.h"
+#include "eckit/mpi/Operation.h"
+#include "eckit/mpi/Request.h"
+#include "eckit/mpi/Status.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Environment;
+
+/// @returns the communicator registered with associated name, or default communicator when NULL is passed
+Comm& comm(const char* name = 0);
+
+/// Set a communicator as default
+void setCommDefault(const char* name);
+
+/// Register a communicator comming from Fortran code
+void addComm(const char* name, int comm);
+
+/// Check if a communicator is registered
+bool hasComm(const char* name);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Comm : private eckit::NonCopyable {
+
+    friend class Environment;
+
+public: // class methods
+
+    static Comm& comm(const char* name = 0);
+
+public:  // methods
+
+    /// @brief Creates a communicator to self
+    virtual eckit::mpi::Comm* self() const = 0;
+
+    /// @brief Returns name of processor according to MPI
+    virtual std::string processorName() const = 0;
+
+    /// @brief MPI rank of process in this communicator
+    virtual size_t rank() const = 0;
+
+    /// @brief MPI size of this communicator
+    virtual size_t size() const = 0;
+
+    /// @brief MPI barrier for this communicator
+    virtual void barrier() const = 0;
+
+    /// @brief MPI abort for this communicator
+    virtual void abort(int errorcode = -1) const = 0;
+
+    /// @brief Wait for Request to be completed, ignoring the return status
+    virtual Status wait(Request&) const = 0;
+
+    /// @brief Probe for incomming messages
+    virtual Status probe(int source, int tag) const = 0;
+
+    virtual int anySource() const = 0;
+
+    virtual int anyTag() const = 0;
+
+    virtual Status status() const = 0;
+
+    virtual Request request(int) const = 0;
+
+    template<typename T>
+    size_t getCount(Status& status) const;
+
+    ///
+    /// Broadcast, pointer to data (also covers scalars)
+    ///
+
+    template <typename T>
+    void broadcast(T& value, size_t root) const;
+
+    template <typename T>
+    void broadcast(T* first, T* last, size_t root) const;
+
+    template <typename T>
+    void broadcast(T buffer[], size_t count, size_t root) const;
+
+    template <typename T>
+    void broadcast(typename std::vector<T>& v, size_t root) const;
+
+    template<class Iter>
+    void broadcast(Iter first, Iter last, size_t root) const;
+
+    ///
+    /// Gather methods to one root, equal data sizes per rank
+    ///
+
+    template<class CIter, class Iter>
+    void gather(CIter first, CIter last, Iter rfirst, Iter rlast, size_t root) const;
+
+    template <typename T>
+    void gather(const T send, std::vector<T>& recv, size_t root) const;
+
+    template <typename T>
+    void gather(const std::vector<T>& send, std::vector<T>& recv, size_t root) const;
+
+    ///
+    /// Gather methods to one root, variable data sizes per rank
+    ///
+
+    template<typename Value>
+    void gatherv(const Value* sendbuf, size_t sendcount, Value* recvbuf, const int recvcounts[], const int displs[], size_t root) const;
+
+    template<class CIter, class Iter>
+    void gatherv(CIter first, CIter last, Iter rfirst, Iter rlast, const int recvcounts[], const int displs[], size_t root) const;
+
+    template<class CIter, class Iter>
+    void gatherv(CIter first, CIter last, Iter rfirst, Iter rlast, const std::vector<int>& recvcounts,
+                 const std::vector<int>& displs, size_t root) const;
+
+    template <typename T>
+    void gatherv(const std::vector<T>& send, std::vector<T>& recv, const std::vector<int>& recvcounts,
+                 const std::vector<int>& displs, size_t root ) const;
+
+    ///
+    /// Scatter methods from one root
+    ///
+
+    template <typename T>
+    void scatter(const std::vector<T>& send, T& recv, size_t root) const;
+
+    template <typename T>
+    void scatter(const std::vector<T>& send, std::vector<T>& recv, size_t root) const;
+
+    ///
+    /// Scatter methods from one root, variable data sizes per rank, pointer to data (also covers scalar case)
+    ///
+
+    template< typename Value >
+    void scatterv(const Value* sendbuf, const int sendcounts[], const int displs[], Value* recvbuf, int recvcount, size_t root) const;
+
+    template<class CIter, class Iter>
+    void scatterv(CIter first, CIter last, const int sendcounts[], const int displs[],
+                  Iter rfirst, Iter rlast, size_t root) const;
+
+    template<class CIter, class Iter>
+    void scatterv(CIter first, CIter last, const std::vector<int>& sendcounts, const std::vector<int>& displs,
+                  Iter rfirst, Iter rlast, size_t root) const;
+
+    ///
+    /// All reduce operations, separate buffers
+    ///
+
+    template <typename T>
+    void allReduce(T send, T& recv, Operation::Code op) const;
+
+    template <typename T>
+    void allReduce(const T* send, T* recv, size_t count, Operation::Code op) const;
+
+    template <typename T>
+    void allReduce(const std::vector<T>& send, std::vector<T>& recv, Operation::Code op) const;
+
+    ///
+    /// All reduce operations, in place buffer
+    ///
+
+
+    template <typename T>
+    void allReduceInPlace(T* sendrecvbuf, size_t count, Operation::Code op) const;
+
+    template <typename T>
+    void allReduceInPlace(T& sendrecvbuf, Operation::Code op) const ;
+
+    template<class Iter>
+    void allReduceInPlace(Iter first, Iter last, Operation::Code op) const;
+
+    ///
+    /// Gather methods from all, equal data sizes per rank
+    ///
+
+    template <typename T, typename Iter>
+    void allGather(T sendval, Iter rfirst, Iter rlast) const;
+
+    ///
+    /// Gather methods from all, variable data sizes per rank
+    ///
+
+    template <typename CIter, typename Iter>
+    void allGatherv(CIter first, CIter last, Iter recvbuf, const int recvcounts[], const int displs[]) const;
+
+    template <typename T, typename CIter>
+    void allGatherv(CIter first, CIter last, mpi::Buffer<T>& recv) const;
+
+    ///
+    /// All to all methods, fixed data size
+    ///
+
+    template <typename T>
+    void allToAll(const std::vector<T>& send, std::vector<T>& recv) const;
+
+    ///
+    /// All to All, variable data size
+    ///
+
+    template <typename T>
+    void allToAllv(const T* sendbuf, const int sendcounts[], const int sdispls[],
+                         T* recvbuf, const int recvcounts[], const int rdispls[]) const;
+
+    ///
+    ///  Non-blocking receive
+    ///
+
+    template <typename T>
+    Request iReceive(T* recv, size_t count, int source, int tag) const;
+
+    template <typename T>
+    Request iReceive(T& recv, int source, int tag) const;
+
+    ///
+    ///  Blocking receive
+    ///
+
+    template <typename T>
+    Status receive(T* recv, size_t count, int source, int tag) const;
+
+    template <typename T>
+    Status receive(T& recv, int source, int tag) const;
+
+    ///
+    /// Blocking send
+    ///
+
+    template <typename T>
+    void send(T* sendbuf, size_t count, int dest, int tag) const;
+
+    template <typename T>
+    void send(T& sendbuf, int dest, int tag) const;
+
+    ///
+    /// Non-blocking send
+    ///
+
+    template <typename T>
+    Request iSend(T* sendbuf, size_t count, int dest, int tag) const;
+
+    template <typename T>
+    Request iSend(T& sendbuf, int dest, int tag) const;
+
+    ///
+    /// All to all of vector< vector<> >
+    ///
+
+    template <typename T>
+    void allToAll(const std::vector< std::vector<T> >& sendvec, std::vector< std::vector<T> >& recvvec) const;
+
+
+
+    /// @brief The communicator
+    virtual int communicator() const = 0;
+
+  private: // class methods
+
+      static void initDefaultComm();
+
+  protected: // methods
+
+      virtual size_t getCount(Status& status, Data::Code datatype) const = 0;
+
+      virtual void broadcast(void* buffer, size_t count, Data::Code datatype, size_t root) const = 0;
+
+      virtual void gather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code datatype, size_t root) const = 0;
+
+      virtual void scatter(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code datatype, size_t root) const = 0;
+
+      virtual void gatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int recvcounts[], const int displs[], Data::Code datatype, size_t root) const = 0;
+
+      virtual void scatterv(const void* sendbuf, const int sendcounts[], const int displs[], void* recvbuf, size_t recvcount, Data::Code datatype, size_t root) const = 0;
+
+      virtual void allReduce(const void* sendbuf, void* recvbuf, size_t count, Data::Code datatype, Operation::Code op) const = 0;
+
+      virtual void allReduceInPlace(void* sendrecvbuf, size_t count, Data::Code datatype, Operation::Code op) const = 0;
+
+      virtual void allGather(const void *sendbuf, size_t sendcount, void *recvbuf, size_t recvcount, Data::Code datatype) const = 0;
+
+      virtual void allGatherv(const void *sendbuf, size_t sendcount,
+                              void *recvbuf, const int recvcounts[], const int displs[], Data::Code datatype) const = 0;
+
+      virtual void allToAll(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code datatype) const = 0;
+
+      virtual void allToAllv(const void *sendbuf, const int sendcounts[], const int sdispls[],
+                             void *recvbuf, const int recvcounts[], const int rdispls[],
+                             Data::Code datatype) const = 0;
+
+      virtual Status receive(void* recv, size_t count, Data::Code datatype, int source, int tag) const = 0;
+
+      virtual void send(const void* send, size_t count, Data::Code datatype, int dest, int tag) const = 0;
+
+      virtual Request iReceive(void* recv, size_t count, Data::Code datatype, int source, int tag) const = 0;
+
+      virtual Request iSend(const void* send, size_t count, Data::Code datatype, int dest, int tag) const = 0;
+
+private: // methods
+
+    virtual void print(std::ostream&) const = 0;
+
+    friend std::ostream& operator<<(std::ostream& s, const Comm& o) {
+        o.print(s); return s;
+    }
+
+protected: // methods
+
+    Comm();
+
+    virtual ~Comm();
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class CommFactory {
+
+    friend class eckit::mpi::Environment;
+
+    std::string name_;
+    virtual Comm* make() = 0;
+    virtual Comm* make(int) = 0;
+
+  protected:
+
+    CommFactory(const std::string &);
+    virtual ~CommFactory();
+
+    static Comm* build(const std::string&);
+    static Comm* build(const std::string&, int);
+};
+
+template< class T>
+class CommBuilder : public CommFactory {
+    virtual Comm* make() {
+        return new T();
+    }
+    virtual Comm* make(int comm) {
+        return new T(comm);
+    }
+  public:
+    CommBuilder(const std::string &name) : CommFactory(name) {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+template<typename T>
+size_t eckit::mpi::Comm::getCount(Status& status) const {
+    return getCount(status, Data::Type<T>::code());
+}
+
+///
+/// Broadcast, pointer to data (also covers scalars)
+///
+
+template <typename T>
+void eckit::mpi::Comm::broadcast(T& value, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    T* p = &value;
+
+    broadcast(p, p+1, root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::broadcast(T* first, T* last, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    broadcast(first, (last-first), Data::Type<T>::code(), root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::broadcast(T buffer[], size_t count, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    broadcast(buffer, count, Data::Type<T>::code(), root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::broadcast(typename std::vector<T>& v, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    broadcast(v.begin(), v.end(), root);
+}
+
+template<class Iter>
+void eckit::mpi::Comm::broadcast(Iter first, Iter last, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    typename std::iterator_traits<Iter>::difference_type n = std::distance(first, last);
+    Data::Code type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+
+    broadcast(&(*first), n, type, root);
+}
+
+///
+/// Gather methods to one root, equal data sizes per rank
+///
+
+template<class CIter, class Iter>
+void eckit::mpi::Comm::gather(CIter first, CIter last, Iter rfirst, Iter rlast, size_t root) const {
+
+    typedef typename std::iterator_traits<CIter>::difference_type diff_t;
+
+    const size_t commsize = size();
+    ASSERT(root < commsize);
+
+    const diff_t sendcount = std::distance(first, last);
+    const diff_t rsize = std::distance(rfirst, rlast);
+    ASSERT(rsize % commsize == 0); /* receiving size is multiple of comm().size() */
+    const diff_t recvcount = rsize / commsize;
+    ASSERT(sendcount == recvcount);
+
+    Data::Code ctype = Data::Type<typename std::iterator_traits<CIter>::value_type>::code();
+    Data::Code  type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+    ASSERT(ctype == type);
+
+    gather(&(*first), sendcount, &(*rfirst), recvcount, type, root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::gather(const T send, std::vector<T>& recv, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(recv.size() % commsize == 0); /* receiving size is multiple of comm().size() */
+    size_t recvcount = recv.size() / commsize;
+    size_t sendcount = 1;
+    ASSERT(recvcount == sendcount);
+
+    gather(&send, sendcount, recv.data(), recvcount, Data::Type<T>::code(), root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::gather(const std::vector<T>& send, std::vector<T>& recv, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(recv.size() % commsize == 0); /* receiving size is multiple of comm().size() */
+    size_t recvcount = recv.size() / commsize;
+    ASSERT(send.size() == recvcount);
+
+    gather(send.data(), send.size(), recv.data(), recvcount, Data::Type<T>::code(), root);
+}
+
+///
+/// Gather methods to one root, variable data sizes per rank
+///
+
+
+template<typename Value>
+void eckit::mpi::Comm::gatherv(const Value* sendbuf, size_t sendcount, Value* recvbuf, const int recvcounts[], const int displs[], size_t root) const  {
+
+  gatherv(sendbuf, sendcount, recvbuf, recvcounts, displs, Data::Type<Value>::code(), root);
+}
+
+template<class CIter, class Iter>
+void eckit::mpi::Comm::gatherv(CIter first, CIter last, Iter rfirst, Iter rlast, const int recvcounts[], const int displs[], size_t root) const {
+
+    typename std::iterator_traits<CIter>::difference_type sendcount = std::distance(first, last);
+    // typename std::iterator_traits<CIter>::difference_type recvsize  = std::distance(rfirst, rlast);
+    Data::Code ctype = Data::Type<typename std::iterator_traits<CIter>::value_type>::code();
+    Data::Code  type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+    ASSERT(ctype == type);
+
+    gatherv(&(*first), sendcount, &(*rfirst), recvcounts, displs, type, root);
+}
+
+template<class CIter, class Iter>
+void eckit::mpi::Comm::gatherv(CIter first, CIter last, Iter rfirst, Iter rlast, const std::vector<int>& recvcounts, const std::vector<int>& displs, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(recvcounts.size() == commsize);
+    ASSERT(displs.size() == commsize);
+
+    gatherv(first, last, rfirst, rlast, recvcounts.data(), displs.data(), root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::gatherv(const std::vector<T>& send, std::vector<T>& recv, const std::vector<int>& recvcounts, const std::vector<int>& displs, size_t root ) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(recvcounts.size() == commsize);
+    ASSERT(displs.size() == commsize);
+
+    gatherv(send.begin(), send.end(), recv.begin(), recv.end(), recvcounts.data(), displs.data(), root);
+}
+
+///
+/// Scatter methods from one root
+///
+
+template <typename T>
+void eckit::mpi::Comm::scatter(const std::vector<T>& send, T& recv, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(send.size() % commsize == 0);
+    size_t sendcount = send.size() / commsize;
+    size_t recvcount = 1;
+    ASSERT(recvcount == sendcount);
+
+    scatter(send.data(), sendcount, &recv, recvcount, Data::Type<T>::code(), root);
+}
+
+template <typename T>
+void eckit::mpi::Comm::scatter(const std::vector<T>& send, std::vector<T>& recv, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(send.size() % commsize == 0);
+    size_t sendcount = send.size() / commsize;
+    size_t recvcount = recv.size();
+    ASSERT(recvcount == sendcount);
+
+    scatter(send.data(), sendcount, recv.data(), recvcount, Data::Type<T>::code(), root);
+}
+
+///
+/// Scatter methods from one root, variable data sizes per rank, pointer to data (also covers scalar case)
+///
+
+template< typename Value >
+void eckit::mpi::Comm::scatterv(const Value* sendbuf, const int sendcounts[], const int displs[], Value* recvbuf, int recvcount, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    scatterv(sendbuf, sendcounts, displs, recvbuf, recvcount, Data::Type<Value>::code(), root);
+}
+
+template<class CIter, class Iter>
+void eckit::mpi::Comm::scatterv(CIter first, CIter last, const int sendcounts[], const int displs[], Iter rfirst, Iter rlast, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+
+    typename std::iterator_traits<Iter>::difference_type recvcounts = std::distance(rfirst, rlast);
+    // typename std::iterator_traits<Iter>::difference_type sendsize   = std::distance(first, last);
+    Data::Code ctype = Data::Type<typename std::iterator_traits<CIter>::value_type>::code();
+    Data::Code  type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+    ASSERT(ctype == type);
+
+    scatterv(&(*first), sendcounts, displs, &(*rfirst), recvcounts, type, root);
+}
+
+template<class CIter, class Iter>
+void eckit::mpi::Comm::scatterv(CIter first, CIter last, const std::vector<int>& sendcounts, const std::vector<int>& displs, Iter rfirst, Iter rlast, size_t root) const {
+
+    size_t commsize = size();
+    ASSERT(root < commsize);
+    ASSERT(sendcounts.size() == commsize);
+    ASSERT(displs.size() == commsize);
+
+    scatterv(first, last, sendcounts.data(), displs.data(), rfirst, rlast, root);
+}
+
+///
+/// All reduce operations, separate buffers
+///
+
+template <typename T>
+void eckit::mpi::Comm::allReduce(T send, T& recv, Operation::Code op) const {
+    allReduce(&send, &recv, 1, Data::Type<T>::code(), op);
+}
+
+template <typename T>
+void eckit::mpi::Comm::allReduce(const T* send, T* recv, size_t count, Operation::Code op) const {
+    allReduce(send, recv, count, Data::Type<T>::code(), op);
+}
+
+template <typename T>
+void eckit::mpi::Comm::allReduce(const std::vector<T>& send, std::vector<T>& recv, Operation::Code op) const {
+    ASSERT(send.size() == recv.size());
+    allReduce(send.data(), recv.data(), send.size(), Data::Type<T>::code(), op);
+}
+
+///
+/// All reduce operations, in place buffer
+///tatus
+
+
+template <typename T>
+void eckit::mpi::Comm::allReduceInPlace(T* sendrecvbuf, size_t count, Operation::Code op) const {
+    allReduceInPlace(sendrecvbuf, count, Data::Type<T>::code(), op);
+}
+
+template <typename T>
+void eckit::mpi::Comm::allReduceInPlace(T& sendrecvbuf, Operation::Code op) const {
+    allReduceInPlace(&sendrecvbuf, 1, Data::Type<T>::code(), op);
+}
+
+template<class Iter>
+void eckit::mpi::Comm::allReduceInPlace(Iter first, Iter last, Operation::Code op) const {
+    typename std::iterator_traits<Iter>::difference_type count = std::distance(first, last);
+    Data::Code  type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+    allReduceInPlace(&(*first), count, type, op);
+}
+
+///
+/// Gather methods from all, equal data sizes per rank
+///
+
+template <typename T, typename Iter>
+void eckit::mpi::Comm::allGather(T sendval, Iter rfirst, Iter rlast) const {
+
+    Data::Code ctype = Data::Type<T>::code();
+    Data::Code  type = Data::Type<typename std::iterator_traits<Iter>::value_type>::code();
+    ASSERT(ctype == type);
+
+    typename std::iterator_traits<Iter>::difference_type rsize = std::distance(rfirst, rlast);
+    ASSERT(rsize % size() == 0);
+    size_t recvcount = rsize / size();
+    ASSERT(recvcount == 1);
+
+    allGather(&sendval, 1, &(*rfirst), recvcount, type);
+}
+
+///
+/// Gather methods from all, variable data sizes per rank
+///
+
+template <typename CIter, typename Iter>
+void eckit::mpi::Comm::allGatherv(CIter first, CIter last, Iter recvbuf, const int recvcounts[], const int displs[]) const {
+
+    typename std::iterator_traits<CIter>::difference_type sendcount = std::distance(first, last);
+    Data::Code ctype = Data::Type<typename std::iterator_traits<CIter>::value_type>::code();
+    Data::Code  type = Data::Type<typename std::iterator_traits<CIter>::value_type>::code();
+    ASSERT(ctype == type);
+    allGatherv(&(*first), sendcount, &(*recvbuf), recvcounts, displs, type);
+}
+
+///
+/// All to all methods, fixed data size
+///
+
+template <typename T>
+void eckit::mpi::Comm::allToAll(const std::vector<T>& send, std::vector<T>& recv) const {
+
+    size_t commsize = size();
+    ASSERT(send.size() % commsize == 0);
+    ASSERT(recv.size() % commsize == 0);
+
+    size_t sendcount = send.size() / commsize;
+    size_t recvcount = recv.size() / commsize;
+
+    allToAll(send.data(), sendcount, recv.data(), recvcount, Data::Type<T>::code());
+}
+
+///
+/// All to All, variable data size
+///
+
+template <typename T>
+void eckit::mpi::Comm::allToAllv(const T* sendbuf, const int sendcounts[], const int sdispls[],
+                     T* recvbuf, const int recvcounts[], const int rdispls[]) const {
+    allToAllv(sendbuf, sendcounts, sdispls, recvbuf, recvcounts, rdispls, Data::Type<T>::code());
+}
+
+///
+///  Non-blocking receive
+///
+
+template <typename T>
+eckit::mpi::Request eckit::mpi::Comm::iReceive(T* recv, size_t count, int source, int tag) const {
+    return iReceive(recv, count, Data::Type<T>::code(), source, tag);
+}
+
+template <typename T>
+eckit::mpi::Request eckit::mpi::Comm::iReceive(T& recv, int source, int tag) const {
+    return iReceive(&recv, 1, Data::Type<T>::code(), source, tag);
+}
+
+///
+///  Blocking receive
+///
+
+template <typename T>
+eckit::mpi::Status eckit::mpi::Comm::receive(T* recv, size_t count, int source, int tag) const {
+    return receive(recv, count, Data::Type<T>::code(), source, tag);
+}
+
+template <typename T>
+eckit::mpi::Status eckit::mpi::Comm::receive(T& recv, int source, int tag) const {
+    return receive(&recv, 1, Data::Type<T>::code(), source, tag);
+}
+
+///
+/// Blocking send
+///
+
+template <typename T>
+void eckit::mpi::Comm::send(T* sendbuf, size_t count, int dest, int tag) const {
+    send(sendbuf, count, Data::Type<T>::code(), dest, tag);
+}
+
+template <typename T>
+void eckit::mpi::Comm::send(T& sendbuf, int dest, int tag) const {
+    send(&sendbuf, 1, Data::Type<T>::code(), dest, tag);
+}
+
+///
+/// Non-blocking send
+///
+
+template <typename T>
+eckit::mpi::Request eckit::mpi::Comm::iSend(T* sendbuf, size_t count, int dest, int tag) const {
+    return iSend(sendbuf, count, Data::Type<T>::code(), dest, tag);
+}
+
+template <typename T>
+eckit::mpi::Request eckit::mpi::Comm::iSend(T& sendbuf, int dest, int tag) const {
+    return iSend(&sendbuf, 1, Data::Type<T>::code(), dest, tag);
+}
+
+template <typename T, typename CIter>
+void eckit::mpi::Comm::allGatherv(CIter first, CIter last, mpi::Buffer<T>& recv) const {
+
+    int sendcnt = int(std::distance(first, last));
+
+    allGather(sendcnt, recv.counts.begin(), recv.counts.end());
+
+    recv.displs[0] = 0;
+    recv.cnt = recv.counts[0];
+    size_t mpiSize = size();
+    for(size_t jpart = 1; jpart < mpiSize; ++jpart) {
+        recv.displs[jpart] = recv.displs[jpart-1] + recv.counts[jpart-1];
+        recv.cnt += recv.counts[jpart];
+    }
+
+    recv.buffer.resize(recv.cnt);
+
+    allGatherv(first, last, recv.buffer.data(), recv.counts.data(), recv.displs.data());
+}
+
+template <typename T>
+void eckit::mpi::Comm::allToAll(const std::vector< std::vector<T> >& sendvec, std::vector< std::vector<T> >& recvvec) const {
+
+    size_t commsize = size();
+
+    ASSERT( sendvec.size() == commsize );
+    ASSERT( recvvec.size() == commsize );
+
+    // Get send-information
+    std::vector<int> sendcounts(commsize);
+    std::vector<int> senddispls(commsize);
+    int sendcnt;
+    senddispls[0] = 0;
+    sendcounts[0] = sendvec[0].size();
+    sendcnt = sendcounts[0];
+
+    for(size_t jproc = 1; jproc < commsize; ++jproc)
+    {
+        senddispls[jproc] = senddispls[jproc-1] + sendcounts[jproc-1];
+        sendcounts[jproc] = sendvec[jproc].size();
+        sendcnt += sendcounts[jproc];
+    }
+
+
+    // Get recv-information
+    std::vector<int> recvcounts(commsize);
+    std::vector<int> recvdispls(commsize);
+    int recvcnt;
+
+    allToAll(sendcounts, recvcounts);
+
+    recvdispls[0] = 0;
+    recvcnt = recvcounts[0];
+    for(size_t jproc = 1; jproc < commsize; ++jproc)
+    {
+        recvdispls[jproc] = recvdispls[jproc-1] + recvcounts[jproc-1];
+        recvcnt += recvcounts[jproc];
+    }
+
+    // Communicate
+    std::vector<T> sendbuf(sendcnt);
+    std::vector<T> recvbuf(recvcnt);
+    int cnt = 0;
+    for(size_t jproc = 0; jproc < commsize; ++jproc)
+    {
+        for(int i = 0; i < sendcounts[jproc]; ++i)
+        {
+            sendbuf[cnt++] = sendvec[jproc][i];
+        }
+    }
+
+    allToAllv( sendbuf.data(), sendcounts.data(), senddispls.data(),
+               recvbuf.data(), recvcounts.data(), recvdispls.data());
+
+    cnt=0;
+    for(size_t jproc = 0; jproc < commsize; ++jproc)
+    {
+        recvvec[jproc].resize(recvcounts[jproc]);
+        for(int i = 0; i < recvcounts[jproc]; ++i)
+        {
+            recvvec[jproc][i] = recvbuf[cnt++];
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#endif
diff --git a/eckit/src/eckit/mpi/DataType.cc b/eckit/src/eckit/mpi/DataType.cc
new file mode 100644
index 0000000..e2d6217
--- /dev/null
+++ b/eckit/src/eckit/mpi/DataType.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <utility>
+
+#include "eckit/mpi/DataType.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<> const char* Data::Type<void>::name() { return "byte"; }
+template<> Data::Code  Data::Type<void>::code() { return Data::BYTE; }
+template<> size_t      Data::Type<void>::size() { return 1; }
+
+template<> const char* Data::Type<char>::name() { return "char"; }
+template<> Data::Code  Data::Type<char>::code() { return Data::CHAR; }
+template<> size_t      Data::Type<char>::size() { return sizeof(char); }
+
+template<> const char* Data::Type<int>::name() { return "int"; }
+template<> Data::Code  Data::Type<int>::code() { return Data::INT; }
+template<> size_t      Data::Type<int>::size() { return sizeof(int); }
+
+template<> const char* Data::Type<long>::name() { return "long"; }
+template<> Data::Code  Data::Type<long>::code() { return Data::LONG; }
+template<> size_t      Data::Type<long>::size() { return sizeof(long); }
+
+template<> const char* Data::Type<float>::name() { return "float"; }
+template<> Data::Code  Data::Type<float>::code() { return Data::FLOAT; }
+template<> size_t      Data::Type<float>::size() { return sizeof(float); }
+
+template<> const char* Data::Type<double>::name() { return "double"; }
+template<> Data::Code  Data::Type<double>::code() { return Data::DOUBLE; }
+template<> size_t      Data::Type<double>::size() { return sizeof(double); }
+
+template<> const char* Data::Type<size_t>::name() { return "size_t"; }
+template<> Data::Code  Data::Type<size_t>::code() { return Data::UNSIGNED_LONG; }
+template<> size_t      Data::Type<size_t>::size() { return sizeof(size_t); }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<> const char* Data::Type< std::pair<int, int> >::name() { return "<int,int>"; }
+template<> Data::Code  Data::Type< std::pair<int, int> >::code() { return Data::INT_INT; }
+template<> size_t      Data::Type< std::pair<int, int> >::size() { return sizeof(std::pair<int, int>); }
+
+template<> const char* Data::Type< std::pair<long, int> >::name() { return "<long,int>"; }
+template<> Data::Code  Data::Type< std::pair<long, int> >::code() { return Data::LONG_INT; }
+template<> size_t      Data::Type< std::pair<long, int> >::size() { return sizeof(std::pair<long, int>); }
+
+template<> const char* Data::Type< std::pair<float, int> >::name() { return "<float,int>"; }
+template<> Data::Code  Data::Type< std::pair<float, int> >::code() { return Data::FLOAT_INT; }
+template<> size_t      Data::Type< std::pair<float, int> >::size() { return sizeof(std::pair<float, int>); }
+
+template<> const char* Data::Type< std::pair<double, int> >::name() { return "<double,int>"; }
+template<> Data::Code  Data::Type< std::pair<double, int> >::code() { return Data::DOUBLE_INT; }
+template<> size_t      Data::Type< std::pair<double, int> >::size() { return sizeof(std::pair<double, int>); }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/DataType.h b/eckit/src/eckit/mpi/DataType.h
new file mode 100644
index 0000000..ad3686c
--- /dev/null
+++ b/eckit/src/eckit/mpi/DataType.h
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_DataType_h
+#define eckit_mpi_DataType_h
+
+#include <cstddef>
+#include <vector>
+
+#include "eckit/mpi/DataType.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Comm;
+
+class Data {
+
+public:
+
+    enum Code {
+        CHAR = 0,
+        WCHAR,
+        SHORT,
+        INT,
+        LONG,
+        SIGNED_CHAR,
+        UNSIGNED_CHAR,
+        UNSIGNED_SHORT,
+        UNSIGNED,
+        UNSIGNED_LONG,
+        FLOAT,
+        DOUBLE,
+        LONG_DOUBLE,
+//        BOOL,
+        COMPLEX,
+        DOUBLE_COMPLEX,
+//        LONG_DOUBLE_COMPLEX,
+        BYTE,
+        PACKED,
+        SHORT_INT,
+        INT_INT,
+        LONG_INT,
+        FLOAT_INT,
+        DOUBLE_INT,
+        LONG_DOUBLE_INT,
+        MAX_DATA_CODE
+    };
+
+    template <class T>
+    struct Type {
+        static const char* name();
+        static Code code();
+        static size_t size();
+    };
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/Operation.cc b/eckit/src/eckit/mpi/Operation.cc
new file mode 100644
index 0000000..3603a82
--- /dev/null
+++ b/eckit/src/eckit/mpi/Operation.cc
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Operation.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Operation::Code sum()   { return Operation::SUM; }
+
+Operation::Code prod()  { return Operation::PROD; }
+
+Operation::Code max()   { return Operation::MAX; }
+
+Operation::Code min()   { return Operation::MIN; }
+
+Operation::Code maxloc(){ return Operation::MAXLOC; }
+
+Operation::Code minloc(){ return Operation::MINLOC; }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Operation.h b/eckit/src/eckit/mpi/Operation.h
new file mode 100644
index 0000000..91ca762
--- /dev/null
+++ b/eckit/src/eckit/mpi/Operation.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Operation_h
+#define eckit_mpi_Operation_h
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct Operation {
+
+    enum Code {
+        SUM = 0,
+        PROD,
+        MAX,
+        MIN,
+        MAXLOC,
+        MINLOC,
+        MAX_OPERATION_CODE,
+    };
+};
+
+Operation::Code sum();
+Operation::Code prod();
+Operation::Code max();
+Operation::Code min();
+Operation::Code maxloc();
+Operation::Code minloc();
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/Parallel.cc b/eckit/src/eckit/mpi/Parallel.cc
new file mode 100644
index 0000000..4612e16
--- /dev/null
+++ b/eckit/src/eckit/mpi/Parallel.cc
@@ -0,0 +1,462 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Parallel.h"
+
+#include <limits>
+#include <sstream>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/mpi/ParallelRequest.h"
+#include "eckit/mpi/ParallelStatus.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static eckit::Mutex *localMutex = 0;
+static size_t initCounter;
+
+static void init() {
+    localMutex = new eckit::Mutex();
+    initCounter = 0;
+}
+//----------------------------------------------------------------------------------------------------------------------
+
+static MPI_Datatype mpi_datacode [Data::MAX_DATA_CODE] = {
+    /*[Data::CHAR]                 = */ MPI_CHAR,
+    /*[Data::WCHAR]                = */ MPI_WCHAR,
+    /*[Data::SHORT]                = */ MPI_SHORT,
+    /*[Data::INT]                  = */ MPI_INT,
+    /*[Data::LONG]                 = */ MPI_LONG,
+    /*[Data::SIGNED_CHAR]          = */ MPI_SIGNED_CHAR,
+    /*[Data::SIGNED_CHAR]          = */ MPI_UNSIGNED_CHAR,
+    /*[Data::UNSIGNED_SHORT]       = */ MPI_UNSIGNED_SHORT,
+    /*[Data::UNSIGNED]             = */ MPI_UNSIGNED,
+    /*[Data::UNSIGNED_LONG]        = */ MPI_UNSIGNED_LONG,
+    /*[Data::FLOAT]                = */ MPI_FLOAT,
+    /*[Data::DOUBLE]               = */ MPI_DOUBLE,
+    /*[Data::LONG_DOUBLE]          = */ MPI_LONG_DOUBLE,
+//    /*[Data::BOOL]                 = */ MPI_BOOL,
+    /*[Data::COMPLEX]              = */ MPI_COMPLEX,
+    /*[Data::DOUBLE_COMPLEX]       = */ MPI_DOUBLE_COMPLEX,
+//    /*[Data::LONG_DOUBLE_COMPLEX]  = */ MPI_LONG_DOUBLE_COMPLEX,
+    /*[Data::BYTE]                 = */ MPI_BYTE,
+    /*[Data::PACKED]               = */ MPI_PACKED,
+    /*[Data::SHORT_INT]            = */ MPI_SHORT_INT,
+    /*[Data::INT_INT]              = */ MPI_2INT,
+    /*[Data::LONG_INT]             = */ MPI_LONG_INT,
+    /*[Data::FLOAT_INT]            = */ MPI_FLOAT_INT,
+    /*[Data::DOUBLE_INT]           = */ MPI_DOUBLE_INT,
+    /*[Data::LONG_DOUBLE_INT]      = */ MPI_LONG_DOUBLE_INT
+};
+
+static MPI_Datatype toType(Data::Code code) {
+    return mpi_datacode[code];
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static MPI_Op mpi_opcode[Operation::MAX_OPERATION_CODE] = {
+    /*[Data::SUM]       = */ MPI_SUM,
+    /*[Data::PROD]      = */ MPI_PROD,
+    /*[Data::MAX]       = */ MPI_MAX,
+    /*[Data::MIN]       = */ MPI_MIN,
+    /*[Data::MAXLOC]    = */ MPI_MAXLOC,
+    /*[Data::MINLOC]    = */ MPI_MINLOC
+};
+
+static MPI_Op toOp(Operation::Code code) {
+    return mpi_opcode[code];
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MPIError : public eckit::Exception {
+public:
+  MPIError(const std::string& msg, const eckit::CodeLocation& loc) : eckit::Exception(msg, loc)
+  {
+    std::ostringstream s;
+    s << "MPI Error: " << msg << " in " << loc;
+    reason(s.str());
+  }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static inline void MPICall(int code, const char* mpifunc, const eckit::CodeLocation& loc)
+{
+    if (code != MPI_SUCCESS) {
+
+        char error[10240];
+        int len = sizeof(error) - 1;
+        MPI_Error_string(code, error, &len);
+        error[len] = 0;
+
+        std::ostringstream oss;
+        oss << "MPI call failed with error '" << error << "' while calling " << mpifunc;
+        throw MPIError(oss.str(), loc);
+    }
+}
+
+#define MPI_CALL(a) MPICall(a,#a,Here())
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Parallel::Parallel() /* don't use member initialisation list */ {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(localMutex);
+
+    if(initCounter == 0) { initialize(); }
+    initCounter++;
+
+    comm_ = MPI_COMM_WORLD;
+}
+
+Parallel::Parallel(MPI_Comm comm, bool) /* don't use member initialisation list */ {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(localMutex);
+
+    if(initCounter == 0) { initialize(); }
+    initCounter++;
+
+    comm_ = comm;
+}
+
+Parallel::Parallel(int comm) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(localMutex);
+
+    if( initCounter == 0 ) { initialize(); }
+    initCounter++;
+
+    comm_ = MPI_Comm_f2c(comm);
+}
+
+Parallel::~Parallel() {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(localMutex);
+
+    initCounter--;
+
+    if(initCounter == 0) { finalize(); }
+}
+
+Comm* Parallel::self() const
+{
+    return new Parallel(MPI_COMM_SELF, true);
+}
+
+void Parallel::initialize() {
+
+    if(!initialized()) {
+
+        int argc(0);
+        char **argv(0);
+
+        if( eckit::Main::ready() ) {
+            argc = eckit::Main::instance().argc();
+            argv = eckit::Main::instance().argv();
+        }
+
+        MPI_CALL( MPI_Init(&argc, &argv) );
+    }
+}
+
+void Parallel::finalize() {
+    if(not finalized()) {
+        MPI_CALL( MPI_Finalize() );
+    }
+}
+
+bool Parallel::initialized() {
+
+    int result = 1;
+    MPI_CALL( MPI_Initialized(&result) );
+    return bool(result);
+}
+
+bool Parallel::finalized() {
+
+    int result = 1;
+    MPI_CALL( MPI_Finalized(&result) );
+    return bool(result);
+}
+
+std::string Parallel::processorName() const
+{
+    char hostname[256];
+    int size = sizeof(hostname);
+    MPI_CALL( MPI_Get_processor_name(hostname, &size) );
+    return hostname;
+}
+
+size_t Parallel::rank() const
+{
+    int rank;
+    MPI_CALL( MPI_Comm_rank(comm_, &rank) );
+    return size_t(rank);
+}
+
+size_t Parallel::size() const
+{
+    int size;
+    MPI_CALL( MPI_Comm_size(comm_, &size) );
+    return size_t(size);
+}
+
+void Parallel::barrier() const
+{
+    MPI_CALL( MPI_Barrier(comm_) );
+}
+
+void Parallel::abort(int errorcode) const
+{
+    MPI_CALL( MPI_Abort(comm_, errorcode) );
+}
+
+Status Parallel::wait(Request& req) const
+{
+    Status st   = createStatus();
+
+    MPI_CALL( MPI_Wait(toRequest(req), toStatus(st)) );
+
+    return st;
+}
+
+Status Parallel::probe(int source, int tag) const
+{
+    Status st = createStatus();
+
+    MPI_CALL( MPI_Probe(source, tag, comm_, toStatus(st)) );
+
+    return st;
+}
+
+int Parallel::anySource() const
+{
+    return MPI_ANY_SOURCE;
+}
+
+int Parallel::anyTag() const
+{
+    return MPI_ANY_TAG;
+}
+
+size_t Parallel::getCount(Status& st, Data::Code type) const
+{
+    int count = 0;
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Get_count(toStatus(st), mpitype, &count) );
+
+    ASSERT(count >= 0);
+    return size_t(count);
+}
+
+void Parallel::broadcast(void* buffer, size_t count, Data::Code type, size_t root) const
+{
+    ASSERT(root  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(count < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Bcast(buffer, int(count), mpitype, int(root), comm_) );
+}
+
+void Parallel::gather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(recvcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(root       < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Gather(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, int(recvcount), mpitype, int(root), comm_) );
+}
+
+void Parallel::scatter(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(recvcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(root       < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Scatter(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, int(recvcount), mpitype, int(root), comm_) );
+}
+
+void Parallel::gatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int recvcounts[], const int displs[], Data::Code type, size_t root) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(root       < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Gatherv(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, const_cast<int*>(recvcounts), const_cast<int*>(displs), mpitype, int(root), comm_) );
+}
+
+void Parallel::scatterv(const void* sendbuf, const int sendcounts[], const int displs[], void* recvbuf, size_t recvcount, Data::Code type, size_t root) const
+{
+    ASSERT(recvcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(root       < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Scatterv(const_cast<void*>(sendbuf), const_cast<int*>(sendcounts), const_cast<int*>(displs), mpitype, recvbuf, int(recvcount), mpitype, int(root), comm_) );
+}
+
+void Parallel::allReduce(const void* sendbuf, void* recvbuf, size_t count, Data::Code type, Operation::Code op) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+    MPI_Op       mpiop   = toOp(op);;
+
+    MPI_CALL( MPI_Allreduce(const_cast<void*>(sendbuf), recvbuf, int(count), mpitype, mpiop, comm_) );
+}
+
+void Parallel::allReduceInPlace(void* sendrecvbuf, size_t count, Data::Code type, Operation::Code op) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+    MPI_Op       mpiop   = toOp(op);
+
+    MPI_CALL( MPI_Allreduce(MPI_IN_PLACE, sendrecvbuf, int(count), mpitype, mpiop, comm_) );
+}
+
+void Parallel::allGather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(recvcount  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Allgather(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, int(recvcount), mpitype, comm_) );
+}
+
+void Parallel::allGatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int recvcounts[], const int displs[], Data::Code type) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Allgatherv(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, const_cast<int*>(recvcounts), const_cast<int*>(displs), mpitype, comm_) );
+}
+
+void Parallel::allToAll(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type) const
+{
+    ASSERT(sendcount  < size_t(std::numeric_limits<int>::max()));
+    ASSERT(recvcount  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Alltoall(const_cast<void*>(sendbuf), int(sendcount), mpitype, recvbuf, int(recvcount), mpitype, comm_) );
+}
+
+void Parallel::allToAllv(const void* sendbuf, const int sendcounts[], const int sdispls[], void* recvbuf, const int recvcounts[], const int rdispls[], Data::Code type) const
+{
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Alltoallv(const_cast<void*>(sendbuf), const_cast<int*>(sendcounts), const_cast<int*>(sdispls), mpitype,
+                            recvbuf, const_cast<int*>(recvcounts), const_cast<int*>(rdispls), mpitype, comm_) );
+}
+
+Status Parallel::receive(void* recv, size_t count, Data::Code type, int source, int tag) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    Status status = createStatus();
+
+    MPI_CALL( MPI_Recv(recv, int(count), mpitype, source, tag, comm_, toStatus(status)) );
+
+    return status;
+}
+
+void Parallel::send(const void* send, size_t count, Data::Code type, int dest, int tag) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Send(const_cast<void*>(send), int(count), mpitype, dest, tag, comm_) );
+}
+
+Request Parallel::iReceive(void* recv, size_t count, Data::Code type, int source, int tag) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    Request req( new ParallelRequest() );
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Irecv(recv, int(count), mpitype, source, tag, comm_, toRequest(req)) );
+
+    return req;
+}
+
+Request Parallel::iSend(const void* send, size_t count, Data::Code type, int dest, int tag) const
+{
+    ASSERT(count  < size_t(std::numeric_limits<int>::max()));
+
+    Request req( new ParallelRequest() );
+
+    MPI_Datatype mpitype = toType(type);
+
+    MPI_CALL( MPI_Isend(const_cast<void*>(send), int(count), mpitype, dest, tag, comm_, toRequest(req)) );
+
+    return req;
+}
+
+void Parallel::print(std::ostream& os) const {
+    os << "Parallel()";
+    /// @note maybe add information about the MPI backend: opem-mpi? mpich? etc...
+}
+
+MPI_Status* Parallel::toStatus(Status& st) {
+    return &(st.as<ParallelStatus>().status_);
+}
+
+Status Parallel::createStatus() {
+    return Status(new ParallelStatus());
+}
+
+Request Parallel::request(int request) const {
+    return Request(new ParallelRequest(MPI_Request_f2c(request)));
+}
+
+MPI_Request* Parallel::toRequest(Request& req) {
+    return &(req.as<ParallelRequest>().request_);
+}
+
+int Parallel::communicator() const {
+    return MPI_Comm_c2f(comm_);
+}
+
+CommBuilder<Parallel> ParallelBuilder("parallel");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Parallel.h b/eckit/src/eckit/mpi/Parallel.h
new file mode 100644
index 0000000..bdc257a
--- /dev/null
+++ b/eckit/src/eckit/mpi/Parallel.h
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Parallel_h
+#define eckit_mpi_Parallel_h
+
+#include <mpi.h>
+
+#include "eckit/mpi/Comm.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Parallel : public eckit::mpi::Comm {
+
+protected:  // methods
+
+    template< class T> friend class CommBuilder;
+
+    Parallel();
+    Parallel(MPI_Comm comm, bool);
+    Parallel(int comm);
+
+    virtual ~Parallel();
+
+    virtual eckit::mpi::Comm* self() const;
+
+    virtual std::string processorName() const;
+
+    virtual size_t rank() const;
+
+    virtual size_t size() const;
+
+    virtual void barrier() const;
+
+    virtual void abort(int errorcode = -1) const;
+
+    virtual Status wait(Request&) const;
+
+    virtual Status probe(int source, int tag) const;
+
+    virtual int anySource() const;
+
+    virtual int anyTag() const;
+
+    virtual size_t getCount(Status& st, Data::Code type) const;
+
+    virtual void broadcast(void* buffer, size_t count, Data::Code type, size_t root) const;
+
+    virtual void gather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void scatter(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void gatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int recvcounts[], const int displs[], Data::Code type, size_t root) const;
+
+    virtual void scatterv(const void* sendbuf, const int sendcounts[], const int displs[], void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void allReduce(const void* sendbuf, void* recvbuf, size_t count, Data::Code type, Operation::Code op) const;
+
+    virtual void allReduceInPlace(void* sendrecvbuf, size_t count, Data::Code type, Operation::Code op) const;
+
+    virtual void allGather(const void *sendbuf, size_t sendcount, void *recvbuf, size_t recvcount, Data::Code type) const;
+
+    virtual void allGatherv(const void *sendbuf, size_t sendcount,
+                            void *recvbuf, const int recvcounts[], const int displs[], Data::Code type) const;
+
+    virtual void allToAll(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type) const;
+
+    virtual void allToAllv(const void *sendbuf, const int sendcounts[], const int sdispls[],
+                           void *recvbuf, const int recvcounts[], const int rdispls[],
+                           Data::Code type) const;
+
+    virtual Status receive(void* recv, size_t count, Data::Code type, int source, int tag) const;
+
+    virtual void send(const void* send, size_t count, Data::Code type, int dest, int tag) const;
+
+    virtual Request iReceive(void* recv, size_t count, Data::Code type, int source, int tag) const;
+
+    virtual Request iSend(const void* send, size_t count, Data::Code type, int dest, int tag) const;
+
+    virtual void print(std::ostream&) const;
+
+    virtual Status status() const    { return createStatus(); }
+
+    virtual Request request(int) const;
+
+    virtual int communicator() const;
+
+    static Status  createStatus();
+
+    static MPI_Status* toStatus(Status&);
+
+    static MPI_Request* toRequest(Request&);
+
+private: // methods
+
+    static void initialize();
+
+    static void finalize();
+
+    static bool initialized();
+
+    static bool finalized();
+
+private: // members
+
+    MPI_Comm comm_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/ParallelRequest.cc b/eckit/src/eckit/mpi/ParallelRequest.cc
new file mode 100644
index 0000000..391bba0
--- /dev/null
+++ b/eckit/src/eckit/mpi/ParallelRequest.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/ParallelRequest.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ParallelRequest::ParallelRequest() {
+}
+
+ParallelRequest::ParallelRequest(MPI_Request request) : request_(request) {
+}
+
+void ParallelRequest::print(std::ostream& os) const {
+    os << "ParallelRequest("
+       << ")";
+}
+
+int ParallelRequest::request() const {
+    return MPI_Request_c2f(request_);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/ParallelRequest.h b/eckit/src/eckit/mpi/ParallelRequest.h
new file mode 100644
index 0000000..5865cf5
--- /dev/null
+++ b/eckit/src/eckit/mpi/ParallelRequest.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_ParallelRequest_h
+#define eckit_mpi_ParallelRequest_h
+
+#include <mpi.h>
+
+#include "eckit/mpi/Request.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Parallel;
+
+class ParallelRequest : public RequestContent {
+
+private: // constructor
+
+    ParallelRequest();
+    ParallelRequest(MPI_Request);
+
+private: // methods
+
+    virtual void print(std::ostream&) const;
+
+    virtual int request() const;
+
+private: // members
+
+    friend class Parallel;
+
+    MPI_Request request_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/ParallelStatus.cc b/eckit/src/eckit/mpi/ParallelStatus.cc
new file mode 100644
index 0000000..5a3895d
--- /dev/null
+++ b/eckit/src/eckit/mpi/ParallelStatus.cc
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/ParallelStatus.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void ParallelStatus::print(std::ostream& os) const {
+    os << "ParallelStatus("
+       << "source=" << source()
+       << ",tag=" << tag()
+       << ",error=" << error()
+       << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/ParallelStatus.h b/eckit/src/eckit/mpi/ParallelStatus.h
new file mode 100644
index 0000000..5604594
--- /dev/null
+++ b/eckit/src/eckit/mpi/ParallelStatus.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_ParallelStatus_h
+#define eckit_mpi_ParallelStatus_h
+
+#include <mpi.h>
+
+#include "eckit/mpi/Status.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Parallel;
+
+class ParallelStatus : public StatusContent {
+
+private: // methods
+
+    virtual int source() const { return status_.MPI_SOURCE; }
+    virtual int tag() const    { return status_.MPI_TAG; }
+    virtual int error() const  { return status_.MPI_ERROR; }
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    friend class Parallel;
+
+    MPI_Status status_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/Request.cc b/eckit/src/eckit/mpi/Request.cc
new file mode 100644
index 0000000..7b6a77e
--- /dev/null
+++ b/eckit/src/eckit/mpi/Request.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Request.h"
+
+#include "eckit/mpi/Comm.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class NullRequestContent : public RequestContent {
+public:
+
+    virtual ~NullRequestContent() {}
+
+    virtual void print(std::ostream& os) const { os << "NullRequest()"; }
+
+    virtual int request() const { return -1; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Request::Request() :
+    content_( new NullRequestContent()) {
+    content_->attach();
+}
+
+Request::Request(int request) :
+    content_(0) {
+    *this = eckit::mpi::comm().request(request);
+}
+
+Request::Request(RequestContent* p) :
+    content_(p) {
+    content_->attach();
+}
+
+Request::~Request() {
+    content_->detach();
+}
+
+Request::Request(const Request& s) : content_(s.content_) {
+    content_->attach();
+}
+
+Request& Request::operator=(const Request& s) {
+    if( content_ ) { content_->detach(); }
+    content_ = s.content_;
+    content_->attach();
+    return *this;
+}
+
+int Request::request() const {
+    return content_->request();
+}
+
+RequestContent::~RequestContent() {
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Request.h b/eckit/src/eckit/mpi/Request.h
new file mode 100644
index 0000000..ba5ec5a
--- /dev/null
+++ b/eckit/src/eckit/mpi/Request.h
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Request_h
+#define eckit_mpi_Request_h
+
+#include <iosfwd>
+
+#include "eckit/memory/Counted.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class RequestContent : public Counted {
+public:
+
+    virtual ~RequestContent();
+
+    virtual void print(std::ostream&) const = 0;
+
+    virtual int request() const = 0;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Request by construction has always a valid content_
+/// @invariant content_ is not null
+
+class Request {
+
+public: // methods
+
+    /// Null request constructor
+    Request();
+    /// Request constructor from the Request() integer
+    /// Use only for interfacing with Fortran
+    Request(int);
+    /// Constructor
+    Request(RequestContent*);
+
+    ~Request();
+
+    Request(const Request&);
+
+    Request& operator=(const Request&);
+
+    template <class T>
+    T& as() {
+        return dynamic_cast<T&>(*content_);
+    }
+
+    /// Returns this request interpreted as a int by the underlying implementation
+    /// Use only for interfacing with Fortran
+    int request() const;
+
+private: // methods
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const Request& o) {
+        o.print(s); return s;
+    }
+
+private: // members
+
+    RequestContent* content_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/Serial.cc b/eckit/src/eckit/mpi/Serial.cc
new file mode 100644
index 0000000..61ebe7c
--- /dev/null
+++ b/eckit/src/eckit/mpi/Serial.cc
@@ -0,0 +1,321 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Serial.h"
+
+#include <unistd.h>
+#include <limits>
+#include <deque>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/mpi/SerialData.h"
+#include "eckit/mpi/SerialRequest.h"
+#include "eckit/mpi/SerialStatus.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/maths/Functions.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SerialSendReceive : private NonCopyable {
+public:
+
+    static SerialSendReceive& instance() {
+        static SerialSendReceive inst;
+        return inst;
+    }
+
+    void addSend( const Request& send_request )
+    {
+      send_queue_.push_back(send_request);
+    }
+
+    Request nextSend()
+    {
+      Request send = send_queue_.front();
+      send_queue_.pop_front();
+      return send;
+    }
+
+    void lock() { mutex_.lock(); }
+    void unlock() { mutex_.unlock(); }
+
+private:
+
+    SerialSendReceive() {}
+    ~SerialSendReceive() {}
+
+    std::deque<Request> send_queue_;
+
+    eckit::Mutex mutex_; ///< instance() creation is thread safe, but access thereon isn't so we need a mutex
+};
+
+
+
+class SerialRequestPool : private NonCopyable {
+public:
+
+    static SerialRequestPool& instance() {
+        static SerialRequestPool request_pool;
+        return request_pool;
+    }
+
+    Request createSendRequest(const void* buffer, size_t count, Data::Code type, int tag) {
+        Request r = registerRequest(new SendRequest(buffer,count,type,tag));
+        send_[tag] = r;
+        return r;
+    }
+
+    Request createReceiveRequest(void* buffer, size_t count, Data::Code type, int tag) {
+        SerialRequest* request = new ReceiveRequest(buffer,count,type,tag);
+        return registerRequest(request);
+    }
+
+    Request operator[](int request) {
+        return requests_[request];
+    }
+
+    Request sendRequest(int tag) {
+        return send_[tag];
+    }
+
+    void lock() { mutex_.lock(); }
+    void unlock() { mutex_.unlock(); }
+
+private:
+
+    Request registerRequest(SerialRequest* request) {
+        ++n_;
+        if( size_t(n_) == requests_.size() ) n_ = 0;
+        request->request_ = n_;
+        Request r(request);
+        requests_[n_] = r;
+        return r;
+    }
+
+    SerialRequestPool()
+    {
+        n_ = -1;
+        requests_.resize(100);
+    }
+
+    ~SerialRequestPool() {}
+
+    std::vector<Request> requests_;
+
+    std::map<int,Request> send_;
+
+    int n_;
+
+    eckit::Mutex mutex_; ///< instance() creation is thread safe, but access thereon isn't so we need a mutex
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Serial::Serial() {
+}
+
+Serial::Serial(int) {
+}
+
+Serial::~Serial() {
+}
+
+Comm* Serial::self() const {
+    return new Serial();
+}
+
+std::string Serial::processorName() const {
+    char hostname[256];
+    SYSCALL(::gethostname(hostname, sizeof(hostname)));
+    return hostname;
+}
+
+size_t Serial::rank() const {
+    return 0;
+}
+
+size_t Serial::size() const {
+    return 1;
+}
+
+void Serial::barrier() const {
+    return;
+}
+
+void Serial::abort(int) const {
+    throw Abort("MPI Abort called");
+}
+
+Status Serial::wait(Request& req) const {
+
+    AutoLock<SerialRequestPool> lock(SerialRequestPool::instance());
+
+    if( req.as<SerialRequest>().isReceive() ) {
+
+      ReceiveRequest& recvReq = req.as<ReceiveRequest>();
+
+      int tag = recvReq.tag();
+
+      SendRequest& sendReq = SerialRequestPool::instance().sendRequest(tag).as<SendRequest>();
+
+      memcpy( recvReq.buffer(), sendReq.buffer(), sendReq.count() * dataSize[sendReq.type()] );
+
+      SerialStatus* st = new SerialStatus();
+
+      (*st).count_  = sendReq.count();
+      (*st).source_ = 0;
+      (*st).error_  = 0;
+
+      return Status(st);
+
+    } else {
+
+      SerialStatus* st = new SerialStatus();
+
+      (*st).error_ = 0;
+
+      return Status(st);
+    }
+}
+
+Status Serial::probe(int source, int) const {
+    ASSERT(source == 0);
+    return status();
+}
+
+int Serial::anySource() const {
+    return 0;
+}
+
+int Serial::anyTag() const {
+    return std::numeric_limits<int>::max();
+}
+
+size_t Serial::getCount(Status& st, Data::Code) const {
+    return st.as<SerialStatus>().count_;
+}
+
+void Serial::broadcast(void*, size_t, Data::Code, size_t) const {
+    return;
+}
+
+void Serial::gather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t, Data::Code type, size_t) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, sendcount * dataSize[type] );
+}
+
+void Serial::scatter(const void* sendbuf, size_t, void* recvbuf, size_t recvcount, Data::Code type, size_t) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, recvcount * dataSize[type] );
+}
+
+void Serial::gatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int [], const int [], Data::Code type, size_t) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, sendcount * dataSize[type] );
+}
+
+void Serial::scatterv(const void* sendbuf, const int[], const int[], void* recvbuf, size_t recvcount, Data::Code type, size_t) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, recvcount * dataSize[type] );
+}
+
+void Serial::allReduce(const void* sendbuf, void* recvbuf, size_t count, Data::Code type, Operation::Code) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, count * dataSize[type] );
+}
+
+void Serial::allReduceInPlace(void*, size_t, Data::Code, Operation::Code) const {
+    return;
+}
+
+void Serial::allGather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t, Data::Code type) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, sendcount * dataSize[type] );
+}
+
+void Serial::allGatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int [], const int [], Data::Code type) const {
+    if( recvbuf != sendbuf )
+        memcpy( recvbuf, sendbuf, sendcount * dataSize[type] );
+}
+
+void Serial::allToAll(const void* sendbuf, size_t sendcount, void* recvbuf, size_t, Data::Code type) const {
+    if( recvbuf != sendbuf )
+       memcpy( recvbuf, sendbuf, sendcount * dataSize[type] );
+}
+
+void Serial::allToAllv(const void* sendbuf, const int sendcounts[], const int[], void* recvbuf, const int[], const int[], Data::Code type) const {
+    if( recvbuf != sendbuf )
+       memcpy( recvbuf, sendbuf, sendcounts[0] * dataSize[type] );
+}
+
+Status Serial::receive(void* recv, size_t count, Data::Code type, int source, int tag) const
+{
+    AutoLock<SerialSendReceive> lock(SerialSendReceive::instance());
+
+    Request send_request = SerialSendReceive::instance().nextSend();
+    SendRequest& send = send_request.as<SendRequest>();
+    if( tag != anyTag() ) {
+        ASSERT( tag == send.tag() );
+    }
+    ASSERT( count == send.count()  );
+    memcpy( recv, send.buffer(), send.count() * dataSize[send.type()] );
+
+    SerialStatus* st = new SerialStatus();
+    (*st).count_  = send.count();
+    (*st).source_ = 0;
+    (*st).tag_    = send.tag();
+    (*st).error_  = 0;
+
+    return Status(st);
+}
+
+void Serial::send(const void* send, size_t count, Data::Code type, int dest, int tag) const
+{
+    AutoLock<SerialSendReceive> lock(SerialSendReceive::instance());
+    SerialSendReceive::instance().addSend( Request( new SendRequest(send,count,type,tag) ) );
+}
+
+Request Serial::iReceive(void* recv, size_t count, Data::Code type, int source, int tag) const {
+    AutoLock<SerialRequestPool> lock(SerialRequestPool::instance());
+    return SerialRequestPool::instance().createReceiveRequest(recv,count,type,tag);
+}
+
+Request Serial::iSend(const void* send, size_t count, Data::Code type, int dest, int tag) const {
+    AutoLock<SerialRequestPool> lock(SerialRequestPool::instance());
+    return SerialRequestPool::instance().createSendRequest(send,count,type,tag);
+}
+
+Status Serial::createStatus() {
+    return Status(new SerialStatus());
+}
+
+Request Serial::request(int request) const {
+    AutoLock<SerialRequestPool> lock(SerialRequestPool::instance());
+    return SerialRequestPool::instance()[request];
+}
+
+void Serial::print(std::ostream& os) const {
+    os << "Serial()";
+}
+
+int Serial::communicator() const {
+    return 0;
+}
+
+CommBuilder<Serial> SerialBuilder("serial");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Serial.h b/eckit/src/eckit/mpi/Serial.h
new file mode 100644
index 0000000..38f1b9d
--- /dev/null
+++ b/eckit/src/eckit/mpi/Serial.h
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Serial_h
+#define eckit_mpi_Serial_h
+
+#include "eckit/mpi/Comm.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Serial : public eckit::mpi::Comm {
+
+protected:  // methods
+
+    template< class T> friend class CommBuilder;
+
+    Serial();
+    Serial(int);
+
+    virtual ~Serial();
+
+    virtual eckit::mpi::Comm* self() const;
+
+    virtual std::string processorName() const;
+
+    virtual size_t rank() const;
+
+    virtual size_t size() const;
+
+    virtual void barrier() const;
+
+    virtual void abort(int errorcode = -1) const;
+
+    virtual Status wait(Request&) const;
+
+    virtual Status probe(int source, int tag) const;
+
+    virtual int anySource() const;
+
+    virtual int anyTag() const;
+
+    virtual size_t getCount(Status& st, Data::Code type) const;
+
+    virtual void broadcast(void* buffer, size_t count, Data::Code type, size_t root) const;
+
+    virtual void gather(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void scatter(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void gatherv(const void* sendbuf, size_t sendcount, void* recvbuf, const int recvcounts[], const int displs[], Data::Code type, size_t root) const;
+
+    virtual void scatterv(const void* sendbuf, const int sendcounts[], const int displs[], void* recvbuf, size_t recvcount, Data::Code type, size_t root) const;
+
+    virtual void allReduce(const void* sendbuf, void* recvbuf, size_t count, Data::Code type, Operation::Code op) const;
+
+    virtual void allReduceInPlace(void* sendrecvbuf, size_t count, Data::Code type, Operation::Code op) const;
+
+    virtual void allGather(const void *sendbuf, size_t sendcount, void *recvbuf, size_t recvcount, Data::Code type) const;
+
+    virtual void allGatherv(const void *sendbuf, size_t sendcount,
+                            void *recvbuf, const int recvcounts[], const int displs[], Data::Code type) const;
+
+    virtual void allToAll(const void* sendbuf, size_t sendcount, void* recvbuf, size_t recvcount, Data::Code type) const;
+
+    virtual void allToAllv(const void *sendbuf, const int sendcounts[], const int sdispls[],
+                           void *recvbuf, const int recvcounts[], const int rdispls[],
+                           Data::Code type) const;
+
+    virtual Status receive(void* recv, size_t count, Data::Code type, int source, int tag) const;
+
+    virtual void send(const void* send, size_t count, Data::Code type, int dest, int tag) const;
+
+    virtual Request iReceive(void* recv, size_t count, Data::Code type, int source, int tag) const;
+
+    virtual Request iSend(const void* send, size_t count, Data::Code type, int dest, int tag) const;
+
+    virtual void print(std::ostream&) const;
+
+    virtual Status status() const    { return createStatus(); }
+
+    virtual Request request(int tag) const;
+
+    static Status  createStatus();
+
+    virtual int communicator() const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/SerialData.h b/eckit/src/eckit/mpi/SerialData.h
new file mode 100644
index 0000000..eab1236
--- /dev/null
+++ b/eckit/src/eckit/mpi/SerialData.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+#ifndef eckit_mpi_SerialData_h
+#define eckit_mpi_SerialData_h
+
+#include <complex>
+#include "eckit/mpi/DataType.h"
+
+namespace eckit {
+namespace mpi {
+
+static size_t dataSize [Data::MAX_DATA_CODE] = {
+    /*[Data::CHAR]                 = */ sizeof(char),
+    /*[Data::WCHAR]                = */ sizeof(wchar_t),
+    /*[Data::SHORT]                = */ sizeof(short),
+    /*[Data::INT]                  = */ sizeof(int),
+    /*[Data::LONG]                 = */ sizeof(long),
+    /*[Data::SIGNED_CHAR]          = */ sizeof(signed char),
+    /*[Data::UNSIGNED_CHAR]        = */ sizeof(unsigned char),
+    /*[Data::UNSIGNED_SHORT]       = */ sizeof(unsigned short),
+    /*[Data::UNSIGNED]             = */ sizeof(unsigned int),
+    /*[Data::UNSIGNED_LONG]        = */ sizeof(unsigned long),
+    /*[Data::FLOAT]                = */ sizeof(float),
+    /*[Data::DOUBLE]               = */ sizeof(double),
+    /*[Data::LONG_DOUBLE]          = */ sizeof(long double),
+//    /*[Data::BOOL]                 = */ sizeof(bool),
+    /*[Data::COMPLEX]              = */ sizeof(std::complex<float>),
+    /*[Data::DOUBLE_COMPLEX]       = */ sizeof(std::complex<double>),
+//    /*[Data::LONG_DOUBLE_COMPLEX]  = */ sizeof(std::complex<long double>),
+    /*[Data::BYTE]                 = */ sizeof(char),
+    /*[Data::PACKED]               = */ sizeof(char),
+    /*[Data::SHORT_INT]            = */ sizeof(std::pair<short,int>),
+    /*[Data::INT_INT]              = */ sizeof(std::pair<int,int>),
+    /*[Data::LONG_INT]             = */ sizeof(std::pair<long,int>),
+    /*[Data::FLOAT_INT]            = */ sizeof(std::pair<float,int>),
+    /*[Data::DOUBLE_INT]           = */ sizeof(std::pair<double,int>),
+    /*[Data::LONG_DOUBLE_INT]      = */ sizeof(std::pair<long double,int>),
+};
+
+} // namespace mpi
+} // namepsace eckit
+
+#endif
\ No newline at end of file
diff --git a/eckit/src/eckit/mpi/SerialRequest.cc b/eckit/src/eckit/mpi/SerialRequest.cc
new file mode 100644
index 0000000..dd4844b
--- /dev/null
+++ b/eckit/src/eckit/mpi/SerialRequest.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/SerialData.h"
+#include "eckit/mpi/SerialRequest.h"
+#include "eckit/log/Log.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SerialRequest::SerialRequest() :
+    request_(-1) {
+}
+
+SerialRequest::~SerialRequest() {
+}
+
+
+void SerialRequest::print(std::ostream& os) const {
+    os << "SerialRequest("
+       << ")";
+}
+
+int SerialRequest::request() const {
+    return request_;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SendRequest::SendRequest(const void* buffer, size_t count, Data::Code type, int tag) :
+    buffer_(static_cast<const char*>(buffer), count * dataSize[type]),
+    count_(count),
+    tag_(tag),
+    type_(type) {
+}
+
+SendRequest::~SendRequest() {
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ReceiveRequest::ReceiveRequest(void* buffer, size_t count, Data::Code type, int tag) :
+    buffer_(buffer, count * dataSize[type], false),
+    count_(count),
+    tag_(tag),
+    type_(type) {
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/SerialRequest.h b/eckit/src/eckit/mpi/SerialRequest.h
new file mode 100644
index 0000000..65801a2
--- /dev/null
+++ b/eckit/src/eckit/mpi/SerialRequest.h
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_SerialRequest_h
+#define eckit_mpi_SerialRequest_h
+
+#include "eckit/io/Buffer.h"
+#include "eckit/mpi/Request.h"
+#include "eckit/mpi/DataType.h"
+
+namespace eckit {
+namespace mpi {
+
+  class SerialRequestPool;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SerialRequest : public RequestContent {
+
+public: // methods
+
+    SerialRequest();
+
+    virtual ~SerialRequest();
+
+    virtual int request() const;
+
+    virtual int tag() const = 0;
+
+    virtual bool isReceive() const = 0;
+
+private: // methods
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    friend class SerialRequestPool;
+    int request_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SendRequest : public SerialRequest {
+
+public: // methods
+
+    SendRequest(const void* buffer, size_t count, Data::Code type, int tag);
+
+    virtual ~SendRequest();
+
+    virtual bool isReceive() const { return false; }
+
+    const void* buffer() const { return buffer_; }
+
+    size_t count() const { return count_; }
+
+    virtual int tag() const { return tag_; }
+
+    Data::Code type() const { return type_; }
+
+private:
+
+    eckit::Buffer buffer_;
+    size_t count_;
+    int tag_;
+    Data::Code type_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ReceiveRequest : public SerialRequest {
+
+public: // methods
+
+    ReceiveRequest(void* buffer, size_t count, Data::Code type, int tag);
+
+    virtual bool isReceive() const { return true; }
+
+    void* buffer() { return buffer_; }
+
+    size_t count() const { return count_; }
+
+    virtual int tag() const { return tag_; }
+
+    Data::Code type() const { return type_; }
+
+private:
+
+    eckit::Buffer buffer_;  ///< this buffer does not own the memory (see constructor with dummy bool)
+    size_t count_;
+    int tag_;
+    Data::Code type_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/SerialStatus.cc b/eckit/src/eckit/mpi/SerialStatus.cc
new file mode 100644
index 0000000..faf2f1b
--- /dev/null
+++ b/eckit/src/eckit/mpi/SerialStatus.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/SerialStatus.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SerialStatus::SerialStatus() :
+    source_(0),
+    tag_(0),
+    error_(0)
+{
+}
+
+void SerialStatus::print(std::ostream& os) const {
+    os << "SerialStatus("
+       << "source=" << source()
+       << ",tag=" << tag()
+       << ",error=" << error()
+       << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/SerialStatus.h b/eckit/src/eckit/mpi/SerialStatus.h
new file mode 100644
index 0000000..a995595
--- /dev/null
+++ b/eckit/src/eckit/mpi/SerialStatus.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_SerialStatus_h
+#define eckit_mpi_SerialStatus_h
+
+#include "eckit/mpi/Status.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Serial;
+
+class SerialStatus : public StatusContent {
+
+    SerialStatus();
+
+private: // methods
+
+    friend class Serial;
+
+    virtual int source() const { return source_; }
+    virtual int tag() const    { return tag_; }
+    virtual int error() const  { return error_; }
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    int source_;
+    int tag_;
+    int error_;
+
+    size_t count_; ///< counts number of elements transfered in comm
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/mpi/Status.cc b/eckit/src/eckit/mpi/Status.cc
new file mode 100644
index 0000000..a0cbb4d
--- /dev/null
+++ b/eckit/src/eckit/mpi/Status.cc
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/mpi/Status.h"
+
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Status::Status() :
+    content_( new NullStatus() ) {
+    content_->attach();
+}
+
+Status::Status(StatusContent* p) :
+    content_(p) {
+    ASSERT(p);
+    content_->attach();
+}
+
+Status::~Status() {
+    content_->detach();
+}
+
+Status::Status(const Status& s) : content_(s.content_) {
+    content_->attach();
+}
+
+Status& Status::operator=(const Status& s) {
+    content_->detach();
+    content_ = s.content_;
+    content_->attach();
+    return *this;
+}
+
+StatusContent::~StatusContent() {
+}
+
+void NullStatus::print(std::ostream& os) const {
+    os << "NullStatus("
+       << "source=" << source()
+       << ",tag=" << tag()
+       << ",error=" << error()
+       << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mpi
+} // namepsace eckit
diff --git a/eckit/src/eckit/mpi/Status.h b/eckit/src/eckit/mpi/Status.h
new file mode 100644
index 0000000..7de7bc5
--- /dev/null
+++ b/eckit/src/eckit/mpi/Status.h
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_mpi_Status_h
+#define eckit_mpi_Status_h
+
+#include <iosfwd>
+
+#include "eckit/memory/Counted.h"
+
+namespace eckit {
+namespace mpi {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class StatusContent : public Counted {
+public:
+
+    virtual ~StatusContent();
+
+    virtual int source() const = 0;
+    virtual int tag() const = 0;
+    virtual int error() const = 0;
+
+    virtual void print(std::ostream&) const = 0;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class NullStatus : public StatusContent {
+public:
+
+    virtual ~NullStatus() {}
+
+    virtual int source() const { return -1; };
+    virtual int tag() const { return -1; };
+    virtual int error() const { return 1; };
+
+    virtual void print(std::ostream&) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+/// Status by construction has always a valid content_
+/// @invariant content_ is not null
+
+class Status {
+
+public: // methods
+
+    /// Null Status constructor
+    Status();
+
+    /// @pre content is not null
+    Status(StatusContent* content);
+
+    ~Status();
+
+    Status(const Status&);
+
+    Status& operator=(const Status&);
+
+    int source() const { return content_->source(); }
+    int tag() const    { return content_->tag(); }
+    int error() const  { return content_->error(); }
+
+    template <class T>
+    T& as() {
+        return dynamic_cast<T&>(*content_);
+    }
+
+private: // methods
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const Status& o) {
+        o.print(s); return s;
+    }
+
+private: // members
+
+    StatusContent* content_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace mpi
+}  // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/Connector.cc b/eckit/src/eckit/net/Connector.cc
new file mode 100644
index 0000000..eb46a4b
--- /dev/null
+++ b/eckit/src/eckit/net/Connector.cc
@@ -0,0 +1,463 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Connector.cc
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+
+#include "eckit/io/cluster/ClusterNodes.h"
+#include "eckit/net/Connector.h"
+#include "eckit/config/Resource.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPStream.h"
+#include "eckit/thread/ThreadSingleton.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static void offLine( const std::string& host, int port)
+{
+    static bool setNodeOfflineOnError = Resource<bool>("setNodeOfflineOnError", false);
+
+    if(setNodeOfflineOnError)
+        ClusterNodes::offLine(host, port);
+}
+
+Connector::Connector(const std::string& host, int port) :
+	host_(host), port_(port), locked_(false), memoize_(false), sent_(false), life_(0)
+{
+	Log::info() << "Connector::Connector(" << host << "," << port << ")" << std::endl;
+}
+
+Connector::~Connector()
+{
+   try
+   {
+      if (socket_.isConnected())
+      {
+         (*this) << "bye";
+      }
+   } catch (std::exception& e)
+   {
+      Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+      Log::error() << "** Exception is ignored" << std::endl;
+   }
+
+}
+
+TCPSocket& Connector::socket()
+{
+
+
+	if (!socket_.isConnected())
+	{
+	   try
+	   {
+	      NodeInfo remote;
+	      TCPClient client;
+	      Log::info() << "Connector::stream connecting to " << host_ << ":" << port_ << std::endl;
+	      socket_ = client.connect(host_, port_);
+	      InstantTCPStream s(socket_);
+
+	      // Login
+	      remote = NodeInfo::sendLogin(s);
+
+	      ClusterNodes::onLine(host_, port_);
+	   } catch (std::exception& e)
+	   {
+
+	      Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+	      Log::error() << "** Exception is handled" << std::endl;
+
+	      offLine(host_, port_);
+
+          std::ostringstream os;
+          os << name() << ": " << e.what();
+          throw ConnectorException(os.str());
+	   }
+	}
+	return socket_;
+}
+
+void Connector::check()
+{
+   if (socket_.isConnected())
+   {
+      try
+      {
+         if (!socket_.stillConnected())
+         {
+            socket_.close();
+            offLine(host_, port_); /// @todo maybe remove this from here, substitute with a descriptive exception
+         }
+      } catch (std::exception& e)
+      {
+
+         Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+         Log::error() << "** Exception is handled" << std::endl;
+         socket_.close();
+         offLine(host_, port_); /// @todo maybe remove this from here
+      }
+   }
+}
+
+void Connector::print(std::ostream&) const
+{
+}
+
+class ConnectorCache {
+
+    typedef std::multimap< std::pair<std::string, int> , Connector*> Cache;
+	Cache cache_;
+
+public:
+
+	Connector& find(const std::string& host, int port)
+	{
+        std::pair<std::string, int> p(host, port);
+
+        std::pair<Cache::iterator, Cache::iterator> r = cache_.equal_range(p);
+		for (Cache::iterator j = r.first; j != r.second; ++j)
+		{
+			if (!((*j).second)->locked())
+			{
+				(*j).second->check();
+				return *(*j).second;
+			}
+		}
+
+		Connector* c = new Connector(host, port);
+		cache_.insert(make_pair(p, c));
+		return *c;
+	}
+
+	~ConnectorCache()
+	{
+		for (Cache::iterator j = cache_.begin(); j != cache_.end(); ++j)
+		{
+			Connector *c = (*j).second;
+			delete c;
+		}
+	}
+
+	void reset()
+	{
+		for (Cache::iterator j = cache_.begin(); j != cache_.end(); ++j)
+		{
+			Connector *c = (*j).second;
+			c->reset();
+		}
+	}
+
+};
+
+class NodeInfoCache {
+
+    typedef std::map<std::pair<std::string, std::string> , NodeInfo> Cache;
+	Cache cache_;
+
+public:
+
+	NodeInfo& find(Stream& s, const std::string& name, const std::string& node)
+	{
+        std::pair<std::string, std::string> p(name, node);
+
+		Cache::iterator j = cache_.find(p);
+		if (j != cache_.end())
+			return (*j).second;
+
+		//Log::info() << "Connector::nodeInfo(" << name << "," << node << ")" << std::endl;
+
+		s << "info";
+		s << name;
+		s << node;
+
+		NodeInfo info;
+		bool ok;
+
+		s >> ok;
+
+		if (!ok)
+		{
+            std::ostringstream os;
+            os << "Cannot get node info for " << name << "@" << node;
+            throw ConnectorException(os.str());
+		}
+
+		s >> cache_[p];
+
+		return cache_[p];
+	}
+
+	~NodeInfoCache()
+	{
+	}
+
+	void reset()
+	{
+		cache_.clear();
+	}
+
+};
+
+static ThreadSingleton<ConnectorCache> cache;
+static ThreadSingleton<NodeInfoCache> infos;
+
+Connector& Connector::get(const std::string& host, int port)
+{
+	//Log::info() << "Connector::get(" << host << "," << port << ")" << std::endl;
+	return cache.instance().find(host, port);
+}
+
+Connector& Connector::service(const std::string& name, const std::string& node)
+{
+	//Log::info() << "Connector::service(" << name << "," << node << ")" << std::endl;
+	NodeInfo info = ClusterNodes::lookUp(name, node);
+	return get(info.host(), info.port());
+}
+
+Connector& Connector::service(const std::string& name, const std::map<std::string, Length>& cost)
+{
+	std::string host;
+	int port = 0;
+	Length best = 0;
+
+	for (std::map<std::string, Length>::const_iterator j = cost.begin(); j != cost.end(); ++j)
+	{
+		if ((*j).second > best || port == 0)
+		{
+			best = (*j).second;
+			if (ClusterNodes::available(name, (*j).first))
+			{
+				NodeInfo info = ClusterNodes::lookUp(name, (*j).first);
+				host = info.host();
+				port = info.port();
+			}
+            else {
+                Log::warning() << "Service not available: " << name << "@"  << (*j).first << std::endl;
+            }
+		}
+	}
+
+	if (!port)
+	{
+		NodeInfo info = ClusterNodes::any(name);
+		host = info.host();
+		port = info.port();
+        Log::warning() << "Using node: " << info << std::endl;
+	}
+
+	ASSERT(port);
+
+	return get(host, port);
+}
+
+void Connector::lock()
+{
+	ASSERT(!locked_);
+	locked_ = true;
+}
+
+void Connector::unlock()
+{
+	ASSERT(locked_);
+	locked_ = false;
+	//reset();
+}
+
+void Connector::reset()
+{
+   in_.reset();
+   out_.reset();
+   cache_.clear();
+
+   try
+   {
+      socket_.close();
+   } catch (std::exception& e)
+   {
+      Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+      Log::error() << "** Exception is ignored" << std::endl;
+   }
+}
+
+std::string Connector::name() const
+{
+    std::ostringstream os;
+    os << "Connector[" << host_ << ":" << port_ << "]";
+    return os.str();
+}
+
+template<class T, class F>
+long Connector::socketIo(F proc, T buf, long len, const char* msg)
+{
+	TCPSocket& s = socket();
+    long l = (s.*proc)(buf, len);
+	if (l != len)
+	{
+		reset();
+		cache.instance().reset();
+		infos.instance().reset();
+        std::ostringstream os;
+        os << "Connector::socketIo(" << name() << ") only " << l << " byte(s) " << msg << " intead of " << len << Log::syserr;
+		//throw ConnectorException(std::string(os));
+        throw Retry(os.str());
+	}
+	return l;
+}
+
+long Connector::write(const void *buf, long len)
+{
+	if (in_.count())
+	{
+		in_.reset();
+		out_.count();
+	}
+
+	if (memoize_)
+	{
+
+		sent_ = false;
+		out_.add(buf, len);
+
+		return len;
+	}
+
+	return socketIo(&TCPSocket::write, buf, len, "written");
+}
+
+long Connector::read(void *buf, long len)
+{
+	if (memoize_)
+	{
+		if (!sent_)
+		{
+			std::map<BufferCache, BufferCache>::iterator j = cache_.find(out_);
+			bool useCache = false;
+			if (j != cache_.end())
+			{
+//               cout << "MEMOIZE IN CACHE " << (*j).first << std::endl;
+                 if( (::time(0) - (*j).second.updated()) > long(life_) )
+                 {
+                     // cout << "  CACHE IS STALE" << (*j).first << std::endl;
+                 }
+                 else
+                 {
+					useCache = true;
+					cached_.buffer_ = (const char*) (*j).second.buffer();
+					cached_.size_ = (*j).second.count();
+					cached_.pos_ = 0;
+					sent_ = true;
+                 }
+            }
+
+			if (!useCache)
+			{
+				cached_.buffer_ = 0;
+				try
+				{
+                    ASSERT( (size_t) socketIo(&TCPSocket::write,out_.buffer(), out_.count(), "written") == out_.count());
+				} catch (...)
+				{
+					reset();
+					throw;
+				}
+				sent_ = true;
+			}
+
+		}
+
+		if (cached_.buffer_)
+		{
+
+			long left = cached_.size_ - cached_.pos_;
+			long l = left < len ? left : len;
+
+			if (l != len)
+			{
+                std::ostringstream os;
+                os << "Connector::socketIo(" << name() << ") only " << l << " byte(s) memoized intead of " << len << Log::syserr;
+				reset();
+                throw ConnectorException(os.str());
+			}
+
+			::memcpy(buf, cached_.buffer_ + cached_.pos_, len);
+			cached_.pos_ += len;
+
+			return len;
+
+		}
+	}
+
+	try
+	{
+		len = socketIo(&TCPSocket::read, buf, len, "read");
+	} catch (...)
+	{
+		reset();
+		throw;
+	}
+
+	if (memoize_)
+	{
+		ASSERT(len > 0);
+		in_.add(buf, len);
+	}
+
+	return len;
+
+}
+
+void Connector::memoize(bool on, unsigned long life)
+{
+	ASSERT(on != memoize_);
+	memoize_ = on;
+	life_ = life;
+
+	if (on)
+	{
+		ASSERT(in_.count() == 0);
+		ASSERT(out_.count() == 0);
+		sent_ = false;
+
+		cached_.buffer_ = 0;
+
+		if (cache_.size() > 10000)
+		{
+			//Log::info() << "Clear memoize cache" << std::endl;
+			cache_.clear();
+		}
+	} else
+	{
+		//cout << "Connector::memoize " << in_.count() << " " << out_.count() << std::endl;
+		//cout << "-> " << out_ << std::endl;
+		if (cached_.buffer_)
+		{
+			//cout << "   CACHED" << std::endl;
+			//cout << " .... " << cache_[out_] << std::endl;
+		} else
+		{
+			//cout << "<- " << in_ << std::endl;
+
+			cache_[out_] = in_;
+		}
+		in_.reset();
+		out_.reset();
+
+	}
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/net/Connector.h b/eckit/src/eckit/net/Connector.h
new file mode 100644
index 0000000..4f128ec
--- /dev/null
+++ b/eckit/src/eckit/net/Connector.h
@@ -0,0 +1,154 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Connector.h
+// Baudouin Raoult - (c) ECMWF Jun 11
+
+#ifndef eckit_Connector_h
+#define eckit_Connector_h
+
+#include "eckit/io/BufferCache.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/cluster/NodeInfo.h"
+#include "eckit/net/TCPSocket.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ConnectorException : public Exception {
+	virtual bool retryOnServer() const        { return true; }
+	virtual bool retryOnClient() const        { return true; }
+public:
+	ConnectorException(const std::string& what) : Exception(what) {}
+};
+
+//-----------------------------------------------------------------------------
+
+class Connector : public Stream {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	Connector(const std::string& name, const std::string& node);
+
+// -- Destructor
+
+	~Connector(); 
+
+// -- Methods
+
+	void lock();
+	void unlock();
+    void reset();
+    void check();
+
+	bool locked() const { return locked_; }
+
+	const std::string& host() const { return host_; }
+
+	void memoize(bool on, unsigned long time);
+
+// -- Class methods
+	// None
+
+    static Connector& service(const std::string& name, const std::string& node);
+    static Connector& service(const std::string& name, const std::map<std::string,Length>& cost);
+    /* static Connector& clusterNode(); */
+
+    static NodeInfo nodeInfo(const std::string& name, const std::string& node);
+
+protected:
+
+// -- Members
+	// None
+	Connector(const std::string&, int);
+
+// -- Methods
+	
+	void print(std::ostream&) const; 	
+
+// -- Class members
+	// None
+    static Connector& get(const std::string& host, int port);
+
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+
+    std::string host_;
+    int port_;
+    TCPSocket socket_;
+    bool locked_;
+
+
+	// Memoisation 
+    bool memoize_;
+    bool sent_;
+	unsigned long life_;
+
+	BufferCache out_;
+	BufferCache in_;
+
+    std::map<BufferCache, BufferCache> cache_;
+
+	struct {
+		const char *buffer_;
+		size_t pos_;
+		size_t size_;
+	} cached_;
+
+// -- Methods
+	// None
+
+    TCPSocket& socket();
+    template<class T, class F> long socketIo(F proc, T buf, long len, const char*);
+
+// -- Overridden methods
+	// None
+    
+    // From Stream
+	virtual long write(const void* buf,long len) ;
+	virtual long read(void* buf,long len);
+    virtual std::string name() const;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const Connector& p)
+		{ p.print(s); return s; }
+
+    friend class ConnectorCache;
+
+};
+
+class AutoMemoize {
+	Connector& c_;
+	unsigned long  t_;
+public:
+	AutoMemoize(Connector& c,unsigned long t): c_(c), t_(t) { c_.memoize(true,t_); }
+	~AutoMemoize() { c_.memoize(false,t_); }
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/NetAddress.cc b/eckit/src/eckit/net/NetAddress.cc
new file mode 100644
index 0000000..2f92485
--- /dev/null
+++ b/eckit/src/eckit/net/NetAddress.cc
@@ -0,0 +1,92 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NetAddress.cc
+// T. Quintino - ECMWF Feb 2012
+
+#include "NetAddress.h"
+
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Stream& operator<<(Stream& s,const NetAddress& addr)
+{
+    s << addr.host_ << addr.port_;
+    return s;
+}
+
+Stream& operator>>(Stream& s,NetAddress& addr)
+{
+    s >> addr.host_ >> addr.port_;
+    return s;
+}
+
+
+NetAddress::NetAddress(const std::string& host, const int port) :
+    host_(host),
+    port_(port)
+{
+}
+
+NetAddress::NetAddress(const std::string& s)
+{
+    Tokenizer tokenize(":");
+    std::vector<std::string> tokens;
+    tokenize(s,tokens);
+    ASSERT(tokens.size() == 2);
+    host_ = tokens[0];
+    port_ = Translator<std::string,int>()( tokens[1] );
+
+    // check that port_ > 0
+}
+
+NetAddress::NetAddress(const NetAddress& other) :
+    host_(other.host_),
+    port_(other.port_)
+{
+}
+
+NetAddress::~NetAddress()
+{
+}
+
+NetAddress& NetAddress::operator=(const NetAddress& other)
+{
+	if(this != &other) {
+        host_ = other.host_;
+        port_ = other.port_;
+    }
+	return *this;
+}
+
+#if 0
+std::string NetAddress::asString() const
+{
+    stringstream os;
+    os << *this;
+    return os.std::string();
+}
+#endif
+
+void NetAddress::print(std::ostream& s) const
+{
+    s << host_ << ":" << port_;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/NetAddress.h b/eckit/src/eckit/net/NetAddress.h
new file mode 100644
index 0000000..65dd30d
--- /dev/null
+++ b/eckit/src/eckit/net/NetAddress.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NetAddress.h
+// T. Quintino - ECMWF Feb 2012
+
+#ifndef eckit_NetAddress_h
+#define eckit_NetAddress_h
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// The class NetAddress represents a host plus its connection port
+
+class NetAddress {
+public:
+
+    friend Stream& operator<<(Stream&,const NetAddress&);
+    friend Stream& operator>>(Stream&,NetAddress&);
+	
+    friend std::ostream& operator<<(std::ostream& s,const NetAddress& p)
+        { p.print(s); return s; }
+
+// Contructors
+
+    NetAddress(const std::string&); // parses the std::string formated as hostname:port
+    NetAddress(const std::string&, const int);
+    NetAddress(const Stream&);
+
+    NetAddress(const NetAddress&);
+
+// Destructor
+
+    ~NetAddress();
+
+// Assignment
+
+    NetAddress& operator=(const NetAddress&);
+
+// Convertors
+
+    /// @returns std::string formated as hostname:port
+//    std::string asString() const;
+    /// @returns std::string formated as hostname:port
+//    operator std::string() const { return asString(); }
+
+// Operators
+
+// Methods
+
+    std::string host() const { return host_; }
+    int    port() const { return port_; }
+
+private:
+
+// Members
+
+    std::string               host_;
+    int                  port_;
+
+// Methods
+
+    void print(std::ostream&) const;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/NetService.cc b/eckit/src/eckit/net/NetService.cc
new file mode 100644
index 0000000..ead23e7
--- /dev/null
+++ b/eckit/src/eckit/net/NetService.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/net/NetService.h"
+#include "eckit/net/NetUser.h"
+#include "eckit/thread/ThreadControler.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+NetService::NetService(int port, bool visible):
+    server_(port),
+    visible_(visible)
+{
+}
+
+NetService::~NetService()
+{
+}
+
+std::string NetService::hostname() const {
+    return server_.localHost();
+}
+
+int NetService::port() const {
+    return server_.localPort();
+}
+
+void NetService::run()
+{
+    Monitor::instance().show(visible_);
+	Monitor::instance().name(name());
+	Monitor::instance().kind(name());
+    Log::message() << hostname() << ":" << port() << std::endl;
+
+	while(!stopped())
+	{
+		ThreadControler t(newUser(server_.accept()));
+		t.start();
+	}
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/NetService.h b/eckit/src/eckit/net/NetService.h
new file mode 100644
index 0000000..acd6f62
--- /dev/null
+++ b/eckit/src/eckit/net/NetService.h
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NetService.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_NetService_h
+#define eckit_NetService_h
+
+#include "eckit/net/TCPServer.h"
+#include "eckit/thread/Thread.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class NetUser;
+
+class NetService : public Thread {
+
+public:
+
+// -- Contructors
+
+    /// @param[in]  port     TCP port to listen on
+    /// @param[in]  visible  Make the thread this service is running in visible
+    ///                      on the Monitor (defaults to false)
+    NetService(int port, bool visible = false);
+
+// -- Destructor
+
+	~NetService();
+
+// -- Methods
+
+    /// @returns hostname to which this server is answering
+    std::string hostname() const;
+    /// @returns port to which this server is answering
+    int port() const;
+
+// -- Overriden methods
+
+	// From Task
+
+	virtual void run();
+
+private:
+
+// -- Members
+
+	TCPServer server_;
+
+    bool visible_; ///< Visible on the Monitor?
+
+// -- Methods
+
+	virtual NetUser* newUser(TCPSocket&) = 0;	
+	virtual std::string   name() = 0;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/net/NetUser.cc b/eckit/src/eckit/net/NetUser.cc
new file mode 100644
index 0000000..53a016a
--- /dev/null
+++ b/eckit/src/eckit/net/NetUser.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/net/NetUser.h"
+#include "eckit/io/SockBuf.h"
+#include "eckit/net/TCPStream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+NetUser::NetUser(TCPSocket& protocol):
+	protocol_(protocol)
+{
+	//Log::info() << "New service connection from " << protocol_.remoteHost() << std::endl;
+}
+
+NetUser::~NetUser()
+{
+	//Log::info() << "End of service connection from " << protocol_.remoteHost() << std::endl;
+}			
+
+void NetUser::run()
+{
+	SockBuf         buf(protocol_);
+    std::ostream         out(&buf);
+    std::istream         in(&buf);
+	InstantTCPStream  stream(protocol_);
+
+	serve(stream,in,out);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/NetUser.h b/eckit/src/eckit/net/NetUser.h
new file mode 100644
index 0000000..53c01dd
--- /dev/null
+++ b/eckit/src/eckit/net/NetUser.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_NetUser_h
+#define eckit_NetUser_h
+
+#include "eckit/net/TCPSocket.h"
+#include "eckit/thread/Thread.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+
+class NetUser : public Thread {
+public:
+
+
+// -- Contructors
+
+	NetUser(TCPSocket&);
+
+// -- Destructor
+
+	~NetUser(); 
+
+
+protected:
+
+// -- Members
+	
+	TCPSocket protocol_;
+
+private:
+
+// -- Methods
+
+    virtual void serve(Stream&, std::istream&, std::ostream&) = 0;
+
+// -- Overridden methods
+	// From Task
+
+	virtual void run();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/Port.cc b/eckit/src/eckit/net/Port.cc
new file mode 100644
index 0000000..fba2d90
--- /dev/null
+++ b/eckit/src/eckit/net/Port.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/net/Port.h"
+#include "eckit/config/Resource.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Port::Port(const std::string& name, int port):
+    port_(port)
+{
+    int offset = Resource<int>("portOffset",0);
+    port_ = Resource<int>(name + "Port", port) + offset;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/Port.h b/eckit/src/eckit/net/Port.h
new file mode 100644
index 0000000..b80c3fe
--- /dev/null
+++ b/eckit/src/eckit/net/Port.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   Port.h
+/// @author Baudouin Raoult
+/// @date   Jul 96
+
+#ifndef eckit_Port_h
+#define eckit_Port_h
+
+#include "eckit/eckit.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Port {
+public:
+
+// -- Contructors
+
+	Port(const std::string&, int );
+
+// -- Operators
+
+    operator int() const { return port_; }
+
+private:
+
+// There is no private copy constructor as this will confuse g++ 4.x.x
+
+// -- Members
+
+	int   port_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/TCPClient.cc b/eckit/src/eckit/net/TCPClient.cc
new file mode 100644
index 0000000..dd25e2d
--- /dev/null
+++ b/eckit/src/eckit/net/TCPClient.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/config/Resource.h"
+#include "eckit/net/TCPClient.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TCPClient::TCPClient(int port):
+	TCPSocket(),
+	port_(port)
+{
+}
+
+TCPClient::~TCPClient()
+{
+}
+
+void TCPClient::bind()
+{
+	if(socket_ == -1)
+		socket_ = newSocket(port_);
+}
+
+std::string TCPClient::bindingAddress() const
+{
+	//return  Resource<std::string>("localBindingAddr","127.0.0.1");
+	return  Resource<std::string>("localBindingAddr","");
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/TCPClient.h b/eckit/src/eckit/net/TCPClient.h
new file mode 100644
index 0000000..1d43c08
--- /dev/null
+++ b/eckit/src/eckit/net/TCPClient.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TCPClient.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_TCPClient_h
+#define eckit_TCPClient_h
+
+#include "eckit/net/NetAddress.h"
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TCPClient : public TCPSocket {
+public:
+
+// -- Contructors
+
+	TCPClient(int port = 0);
+
+// -- Destructor
+
+	~TCPClient();
+
+// -- Methods
+
+	TCPSocket& connect(const std::string& host,int port, int retries = 5, int timeout = 0);
+
+private:
+
+// No copy allowed
+
+	TCPClient(const TCPClient&);
+	TCPClient& operator=(const TCPClient&);
+
+// -- Members
+	
+	int port_;
+
+// -- Overridden methods
+
+	// From TCPSocket
+
+	virtual void bind();
+	virtual std::string bindingAddress() const;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // TCPClient_H
diff --git a/eckit/src/eckit/net/TCPServer.cc b/eckit/src/eckit/net/TCPServer.cc
new file mode 100644
index 0000000..e623f4b
--- /dev/null
+++ b/eckit/src/eckit/net/TCPServer.cc
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "eckit/log/Log.h"
+#include "eckit/io/Select.h"
+#include "eckit/net/TCPServer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TCPServer::TCPServer(int port,const std::string& addr):
+    TCPSocket(),
+    port_(port),
+    listen_(-1),
+    addr_(addr),
+    closeExec_(true)
+{
+}
+
+TCPServer::~TCPServer()
+{
+    if(listen_ >= 0)
+        ::close(listen_);
+}
+
+// Accept a client
+
+TCPSocket& TCPServer::accept(const std::string& message, int timeout, bool* connected)
+{
+    bind();
+
+
+    sockaddr_in from;
+#ifdef SGI
+    int    fromlen = sizeof(from);
+#else
+    socklen_t fromlen = sizeof(from);
+#endif
+
+    for(;;)
+    {
+    int delay = timeout ? timeout : 10;
+
+    Select select(listen_);
+    Log::status() << message << std::endl;
+
+    while(!select.ready(delay)) {
+        if(timeout && !connected) throw TimeOut(message, timeout);
+        if(connected) {
+            *connected = false;
+            return *this;
+        }
+        Log::status() << message << std::endl;
+    }
+
+        if((socket_ = ::accept(listen_,
+            reinterpret_cast<sockaddr*>(&from), &fromlen))>=0)
+            break;
+
+        if(errno != EINTR)
+            throw FailedSystemCall("accept");
+    }
+
+    remoteAddr_ = from.sin_addr;
+    remoteHost_ = addrToHost(from.sin_addr);
+    remotePort_ = from.sin_port;
+
+    // Set the 'close on exec'
+
+	if(closeExec_)
+	  SYSCALL(fcntl(socket_,F_SETFD,FD_CLOEXEC));
+
+    /// @todo change this to sigaction
+
+    ::signal(SIGPIPE,SIG_IGN);
+
+    Log::status() << "Get connection from " << remoteHost() << std::endl;
+
+        if(connected) {
+            *connected = true;
+        }
+
+    return *this;
+}
+
+void TCPServer::close()
+{
+    TCPSocket::close();
+    if(listen_ >= 0)    
+        ::close(listen_);
+    listen_ = -1;
+}
+
+void TCPServer::bind()
+{
+    if(listen_ == -1)
+    {
+        listen_ = newSocket(port_);
+        ::listen(listen_,5);
+		
+		//if(!willFork_)
+		//  SYSCALL(fcntl(socket_,F_SETFD,FD_CLOEXEC));
+    }
+}
+
+
+int TCPServer::socket()
+{
+    ((TCPServer*)this)->bind();
+    return listen_;
+}
+
+std::string TCPServer::bindingAddress() const
+{
+    return addr_;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/TCPServer.h b/eckit/src/eckit/net/TCPServer.h
new file mode 100644
index 0000000..b44d9d2
--- /dev/null
+++ b/eckit/src/eckit/net/TCPServer.h
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TCPServer.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_TCPServer_h
+#define eckit_TCPServer_h
+
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// AIX #define accept to naccept. It took me a while :-(
+// this clashes with the method accept()
+// so I need to include socket here so all client of this
+// class actually see this #define. So much for encapsulation.
+
+class TCPServer : public TCPSocket {
+public:
+
+// -- Contructors
+
+    TCPServer(int port = 0, const std::string& addr = "");
+	
+
+// -- Destructor
+
+    ~TCPServer();
+
+// -- Methods
+	
+	void willFork(bool);
+	
+
+    // accept a client, more can be accepted
+
+    TCPSocket& accept(const std::string& message = "Waiting for connection", int timeout = 0, bool* connected= 0);
+	 void closeExec(bool on) { closeExec_ = on; }
+
+// -- Overridden methods
+
+    // From TCPSocket
+
+	 virtual int socket();
+   
+    virtual void close();
+
+
+protected:
+
+// -- Members
+
+    int port_;
+    int listen_;
+    std::string addr_;
+
+// -- Overridden methods
+
+    // From TCPSocket
+
+    virtual void bind();
+
+private:
+
+// No copy allowed
+
+    TCPServer(const TCPServer&);
+    TCPServer& operator=(const TCPServer&);
+
+    // To be used by Select
+   
+    virtual std::string bindingAddress() const;
+	
+    bool closeExec_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/TCPSocket.cc b/eckit/src/eckit/net/TCPSocket.cc
new file mode 100644
index 0000000..43ca0da
--- /dev/null
+++ b/eckit/src/eckit/net/TCPSocket.cc
@@ -0,0 +1,704 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <netdb.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+#include <setjmp.h>
+
+
+#include "eckit/os/AutoAlarm.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/log/Log.h"
+#include "eckit/thread/Once.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/config/Resource.h"
+#include "eckit/io/Select.h"
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPSocket.h"
+#include "eckit/log/Seconds.h"
+
+#ifdef _AIX
+//TODO: Add check to cmake
+typedef void (*sighandler_t) (int);
+#endif
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static in_addr none = { INADDR_NONE };
+
+static Once<Mutex> local_mutex;
+
+TCPSocket::UnknownHost::UnknownHost(const std::string& host):
+    Exception(std::string("Unknown host ") + host)
+{
+}
+
+
+TCPSocket::TCPSocket():
+    socket_(-1),
+    localPort_(-1),
+    remotePort_(-1),
+    remoteAddr_(none),
+    localAddr_(none),
+    bufSize_(0)
+{
+}
+
+// This contructor performs a cahnge of ownership of the socket
+TCPSocket::TCPSocket(TCPSocket& other):
+    socket_(other.socket_),
+    localPort_(other.remotePort_),
+    remotePort_(other.remotePort_),
+    remoteHost_(other.remoteHost_),
+    remoteAddr_(other.remoteAddr_),
+    localHost_(other.remoteHost_),
+    localAddr_(other.remoteAddr_),
+    bufSize_(0)
+{
+    other.socket_ = -1;  // Detach socket from other
+}
+
+TCPSocket::~TCPSocket()
+{
+    close();
+}
+
+// This contructor performs a change of ownership of the socket
+TCPSocket& TCPSocket::operator=(TCPSocket& other)
+{
+    socket_     = other.socket_;
+
+    remotePort_ = other.remotePort_;
+    remoteAddr_ = other.remoteAddr_;
+    remoteHost_ = other.remoteHost_;
+    localAddr_  = other.localAddr_ ;
+    localHost_  = other.localHost_ ;
+    localPort_  = other.remotePort_;
+
+    other.socket_ = -1;  // Detach socket from other
+
+    return *this;
+}
+
+void TCPSocket::closeOutput()
+{
+  SYSCALL(::shutdown(socket_, SHUT_WR));
+}
+
+void TCPSocket::closeInput()
+{
+  SYSCALL(::shutdown(socket_, SHUT_RD));
+}
+
+long TCPSocket::write(const void* buf, long length) {
+
+    // Allow zero length packets
+    if(length == 0)
+        return ::write(socket_,buf,length);
+
+    long sent     = 0;
+    const char *p = (const char*)buf;
+
+    while(length > 0)
+    {
+        long len = ::write(socket_,p,length);
+
+        if(len <  0) {
+            Log::error() << "Socket write" << Log::syserr << std::endl;
+            return len;
+        }
+
+        if(len == 0) return sent;
+
+        sent   += len;
+        length -= len;
+        p      += len;
+
+    }
+
+    return sent;
+}
+
+long TCPSocket::read(void *buf,long length)
+{
+    if(length <= 0 ) return length;
+
+    static bool useSelectOnTCPSocket = Resource<bool>("useSelectOnTCPSocket", false);
+		long received = 0;
+    char *p = (char*)buf;
+    bool nonews = false;
+
+    while(length > 0)
+    {
+			long len;
+			if (useSelectOnTCPSocket) {
+				static long socketSelectTimeout = Resource<long>("socketSelectTimeout", 0);
+				Select select(socket_);
+				bool more = socketSelectTimeout > 0;
+				while(more)
+				{
+					more = false;
+					if(!select.ready(socketSelectTimeout))
+					{
+						SavedStatus save;
+
+						Log::warning() << "No news from " << remoteHost()
+						               << " from " << Seconds(socketSelectTimeout) << std::endl;
+
+						Log::status() << "No news from " << remoteHost()
+						              << " from " << Seconds(socketSelectTimeout) << std::endl;
+
+						// FIXME: enable the nonews flag here?
+						// nonews = true;
+
+						// Time out, write 0 bytes to check that peer is alive
+						if(::write(socket_,0,0) != 0)
+						{
+							Log::error() << "TCPSocket::read write" <<
+							                Log::syserr << std::endl;
+							return -1;
+						}
+						more = true;
+						break;
+					}
+				}
+
+				len = -1;
+
+				if(nonews)
+				{
+					AutoAlarm alarm(60,true);
+					Log::status() << "Resuming transfer" << std::endl;
+					len = ::read(socket_,p,length);
+				}
+				else
+					len = ::read(socket_,p,length);
+			} else {
+				len = ::read(socket_,p,length);
+			}
+
+			if(len <  0) {
+				Log::error() << "Socket read" << Log::syserr << std::endl;
+				return len;
+			}
+
+			if(len == 0) return received;
+
+			received  += len;
+			length    -= len;
+			p         += len;
+    }
+
+    return received;
+}
+
+void TCPSocket::close()
+{
+    if(socket_ != -1) SYSCALL(::close(socket_));
+    socket_ = -1;
+    remotePort_  = localPort_  = -1;
+    localHost_ = remoteHost_ = "";
+    localAddr_ = remoteAddr_ = none;
+}
+
+static jmp_buf env;
+
+static void catch_alarm(int sig)
+{
+    longjmp(env,1);
+}
+
+
+// This should be in the TCPClient.cc, but I want to reuse the Mutex
+// to lock any call to NIS with the same one
+
+TCPSocket& TCPClient::connect(const std::string& remote, int port, int retries, int timeout)
+{
+    std::string host = hostName(remote);
+
+    in_addr_t addr;
+    in_addr_t none = (in_addr_t)-1;
+    hostent *him;
+
+    sockaddr_in sin;
+    ::memset(&sin,0,sizeof(sin));
+
+
+    { // Block for local_mutex
+
+        AutoLock<Mutex> lock(local_mutex);
+
+        sin.sin_port   = htons(port);
+        sin.sin_family = AF_INET;
+
+        addr = ::inet_addr(remote.c_str());
+        sin.sin_addr.s_addr = addr;
+
+
+        if(addr == none) {
+
+// For some reason, sgi verion of gethostbyname_r is broken
+#if 0
+            hostent_data data;
+            hostent      host;
+
+            // Due to a STUPID aix bug we need to clear
+            zero(host);
+            zero(data);
+
+            if(gethostbyname_r(remote.c_str(),&host,&data))
+                him = 0;
+            else
+                him = &host;
+#else
+            him=::gethostbyname(remote.c_str());
+#endif
+            if (him == 0)
+            {
+//				Log::error() << "Unknown host [" << remote << "]" << std::endl;
+                throw UnknownHost(remote);
+            }
+
+            sin.sin_family = him->h_addrtype;
+            ::memcpy(&sin.sin_addr,him->h_addr_list[0],him->h_length);
+        }
+
+    } // End of local_mutex
+
+    int tries = 0;
+    int status = 0;
+
+
+    do
+    {
+        int save_errno = 0;
+
+        bind();
+
+        if(timeout)
+        {
+            if(setjmp(env)==0)
+            {
+                void (*old)(int) = signal(SIGALRM,catch_alarm);
+                alarm(timeout);
+                status = ::connect(socket_,reinterpret_cast<sockaddr*>(&sin),sizeof(sin));
+                save_errno = errno;
+                alarm(0);
+                /// @todo change this to sigaction
+                signal(SIGALRM,old);
+            }
+            else
+            {
+                throw TimeOut("connect", timeout);
+            }
+        }
+        else
+        {
+            status = ::connect(socket_,reinterpret_cast<sockaddr*>(&sin),sizeof(sin));
+            save_errno = errno;
+        }
+
+        if (status < 0)
+        {
+            errno = save_errno;
+            Log::error() << "connect to " << host << " " << port << Log::syserr << std::endl;
+
+            Log::status() << "Connect: " << host << ":" << port << Log::syserr << " "
+                          << tries << '/' << retries << std::endl;
+
+            ::close(socket_);
+            socket_ = -1;
+            errno = save_errno;
+
+            switch(errno)
+            {
+                case ECONNREFUSED:
+                    if(++tries >= retries)
+                        if(retries >= 0)
+                            throw TooManyRetries(tries);
+                    ::sleep(5);
+                    break;
+
+                case EINPROGRESS:
+                    //TODO: Potential file descriptor leak
+                    /* ::close(socket_); */
+                    /* socket_ = -1; */
+                    throw FailedSystemCall("connect");
+                    break;
+
+#if 0
+                case ETIMEDOUT:
+                    Log::info() << "Waiting for network "
+                        << host << ":" << port
+                        << Log::syserr << std::endl;
+                    Log::status() << "Waiting for network "
+                        << host << ":" << port
+                        << Log::syserr << std::endl;
+                    break;
+#endif
+#if 0
+                case ENETUNREACH:
+                case ETIMEDOUT:
+                case ECONNRESET:
+                case EADDRNOTAVAIL:
+                case EAGAIN:
+#endif
+                default:
+
+                    Log::status() << "Waiting for network " << host << ":" << port << Log::syserr << std::endl;
+
+#if 0
+                    if(++tries >= retries)
+                        if(retries != 0)
+                            throw TooManyRetries(tries);
+#endif
+                    ::sleep(120);
+                    break;
+
+#if 0
+                default:
+                    throw FailedSystemCall("connect");
+                    break;
+#endif
+            }
+
+        }
+
+    }while(status<0);
+
+    remotePort_ = sin.sin_port;
+    remoteAddr_ = sin.sin_addr;
+    remoteHost_ = addrToHost(sin.sin_addr);
+
+    /// @todo change this to sigaction
+    ::signal(SIGPIPE,SIG_IGN);
+    return *this;
+}
+
+
+int TCPSocket::newSocket(int port)
+{
+
+    localPort_ = port;
+
+    int s = ::socket(AF_INET, SOCK_STREAM, 0);
+
+    if(s < 0)
+        throw FailedSystemCall("::socket");
+
+    int flg = 1 ;
+    if(::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flg, sizeof(flg))<0)
+        Log::warning() << "setsockopt SO_REUSEADDR" << Log::syserr << std::endl;
+
+    flg = 1 ;
+    if(::setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &flg, sizeof(flg))<0)
+        Log::warning() << "setsockopt SO_KEEPALIVE" << Log::syserr << std::endl;
+
+
+#ifdef SO_REUSEPORT
+    flg = 1 ;
+    if(::setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &flg, sizeof(flg))<0)
+        Log::warning() << "setsockopt SO_REUSEPORT" << Log::syserr << std::endl;
+#endif
+
+#ifdef SO_LINGER
+    linger ling;
+    ling.l_onoff  = 0;
+    ling.l_linger = 0;
+    if(::setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))<0)
+        Log::warning() << "setsockopt SO_LINGER" << Log::syserr << std::endl;
+#endif
+
+#ifdef SO_DONTLINGER
+    if(::setsockopt(s, SOL_SOCKET, SO_DONTLINGER, NULL, 0)<0)
+        Log::warning() << "setsockopt SO_DONTLINGER" << Log::syserr << std::endl;
+#endif
+
+/* #ifdef IPPROTO_IP */
+/* #ifdef IP_TOS */
+/* #ifndef IPTOS_LOWDELAY */
+/* #define IPTOS_LOWDELAY 0x10 */
+/* #endif */
+    int tos = IPTOS_LOWDELAY;
+
+    if(::setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))<0)
+          Log::warning() << "setsockopt IP_TOS" << Log::syserr << std::endl;
+
+/* #endif */
+/* #endif */
+
+
+
+#if 1
+    int flag = 1;
+    if(::setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(flag))<0)
+        Log::warning() << "setsockopt TCP_NODELAY" << Log::syserr << std::endl;
+#endif
+
+    if(bufSize_)
+    {
+
+        Log::info() << "SOCKET SIZE " << bufSize_ << std::endl;
+
+        int flg = 0;
+        socklen_t flgsize = sizeof(flg);
+
+        if(getsockopt(s, SOL_SOCKET, SO_SNDBUF,&flg, &flgsize)<0)
+            Log::warning() << "getsockopt SO_SNDBUF " << Log::syserr << std::endl;
+
+        if(flg != bufSize_)
+        {
+            if(setsockopt(s, SOL_SOCKET, SO_SNDBUF,&bufSize_, sizeof(bufSize_)) < 0)
+                Log::warning() << "setsockopt SO_SNDBUF " << Log::syserr << std::endl;
+        }
+
+        if(getsockopt(s, SOL_SOCKET, SO_RCVBUF,&flg, &flgsize)<0)
+            Log::warning() << "getsockopt SO_RCVBUF " << Log::syserr << std::endl;
+
+        if(flg != bufSize_)
+        {
+            if(setsockopt(s, SOL_SOCKET, SO_RCVBUF,&bufSize_, sizeof(bufSize_)) < 0)
+                Log::warning() << "setsockopt SO_RCVBUF " << Log::syserr << std::endl;
+        }
+    }
+
+
+    sockaddr_in sin;
+    ::memset(&sin, 0, sizeof(struct sockaddr_in));
+    sin.sin_port        = htons(localPort_);
+    sin.sin_family      = AF_INET;
+
+    std::string addr = bindingAddress();
+
+    if(addr.length() == 0)
+        sin.sin_addr.s_addr = INADDR_ANY;
+    else
+        sin.sin_addr.s_addr = ::inet_addr(addr.c_str());
+
+    while(::bind(s,reinterpret_cast<sockaddr*>(&sin),sizeof(sin)) == -1)
+    {
+        Log::warning() << "bind port " << localPort_ << " " << addr << Log::syserr << std::endl;
+        ::sleep(5);
+    }
+
+    AutoLock<Mutex> lock(local_mutex);
+#ifdef SGI
+    int    len = sizeof(sin);
+#else
+    socklen_t len = sizeof(sin);
+#endif
+    ::getsockname(s,reinterpret_cast<sockaddr*>(&sin),&len);
+    localPort_ = ntohs(sin.sin_port);
+    localAddr_ = sin.sin_addr;
+    localHost_ = addrToHost(sin.sin_addr);
+
+    if(localHost_ == "0.0.0.0")
+    {
+        if(addr.length() == 0)
+        {
+            AutoLock<Mutex> lock(local_mutex);
+            localHost_= Resource<std::string> ("host", "");
+            if(localHost_.length() == 0)
+            {
+                char host[1024];
+                SYSCALL(gethostname(host,sizeof(host)-1));
+                localHost_ = host;
+            }
+		}
+        else
+        {
+            localHost_ = addr;
+        }
+    }
+
+    // Set the socket 'close on exec'
+    SYSCALL(fcntl(s,F_SETFD,FD_CLOEXEC));
+
+    return s;
+}
+
+void TCPSocket::bind()
+{
+}
+
+
+static std::map<uint32_t,std::string> cache;
+
+std::string TCPSocket::addrToHost(in_addr addr)
+{
+    AutoLock<Mutex> lock(local_mutex);
+
+    // For some reason IBM's gethostbyaddr_r dumps core
+    // from time to time, so let's cache the result
+    // to minimise the cores
+
+    std::map<uint32_t,std::string>::iterator j = cache.find(addr.s_addr);
+
+    if(j != cache.end())
+        return (*j).second;
+
+    hostent *h;
+
+// For some reason, sgi verion of gethostbyname_r is broken
+#ifdef _AIX
+
+
+
+    hostent_data data;
+    hostent      host;
+
+    // Due to a STUPID aix bug we need to clear
+    zero(host);
+    zero(data);
+
+    if(gethostbyaddr_r(reinterpret_cast<char*>(&addr),
+        sizeof(addr),AF_INET,&host,&data))
+        h = 0;
+    else
+        h = &host;
+#else
+    h = gethostbyaddr(reinterpret_cast<char*>(&addr),
+        sizeof(addr),AF_INET);
+#endif
+
+    std::string s = h ? h->h_name : ::inet_ntoa(addr);
+    cache[addr.s_addr] = s;
+    return s;
+}
+
+
+std::string TCPSocket::hostName(const std::string& h, bool full)
+{
+    in_addr_t addr = ::inet_addr(h.c_str());
+    if(addr == (in_addr_t)-1)
+    {
+        if(full)
+            return h;
+        else
+            return h.substr(0,h.find('.'));
+    }
+
+    struct in_addr a;
+    a.s_addr = addr;
+    std::string s =  addrToHost(a);
+
+    if(!full && !isdigit(s[0])) {
+        return s.substr(0,s.find('.'));
+    }
+
+    return s;
+}
+
+int TCPSocket::socket()
+{
+    return socket_;
+}
+
+in_addr TCPSocket::remoteAddr() const
+{
+    return remoteAddr_;
+}
+
+const std::string& TCPSocket::remoteHost() const
+{
+    return remoteHost_;
+}
+
+int TCPSocket::remotePort() const
+{
+    return remotePort_;
+}
+
+in_addr TCPSocket::localAddr() const
+{
+    ((TCPSocket*)this)->bind();
+    return localAddr_;
+}
+
+const std::string& TCPSocket::localHost() const
+{
+    ((TCPSocket*)this)->bind();
+    return localHost_;
+}
+
+int TCPSocket::localPort() const
+{
+    ((TCPSocket*)this)->bind();
+    return localPort_;
+}
+
+std::string TCPSocket::bindingAddress() const
+{
+    return "";
+}
+
+long TCPSocket::rawRead(void* buf,long length)
+{
+    return ::read(socket_,buf,length);
+}
+
+bool TCPSocket::stillConnected() const
+{
+    if(socket_ == -1)
+        return false;
+
+    fd_set r;
+    fd_set e;
+    fd_set w;
+
+    FD_ZERO(&r); FD_SET(socket_, &r);
+    FD_ZERO(&e); FD_SET(socket_, &e);
+    FD_ZERO(&w); FD_SET(socket_, &w);
+
+    ::timeval tv = { 0, 0};
+
+    if(::select(socket_+1,&r, &w, &e, &tv) >= 0)
+    {
+        if(!FD_ISSET(socket_, &r))
+            return true;
+
+        int n = 0;
+        if(::ioctl(socket_, FIONREAD, &n) < 0)
+        {
+            Log::info() << "TCPSocket::stillConnected(FIONREAD) failed " << Log::syserr << std::endl;
+            return false;
+        }
+
+        if(n == 0) {
+            Log::warning() << "TCPSocket::stillConnected => connection lost" << std::endl;
+            return false;
+        }
+
+        return true;
+    }
+    else
+    {
+        Log::info() << "TCPSocket::stillConnected(select) failed " << Log::syserr << std::endl;
+        return false;
+
+    }
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/TCPSocket.h b/eckit/src/eckit/net/TCPSocket.h
new file mode 100644
index 0000000..146bb47
--- /dev/null
+++ b/eckit/src/eckit/net/TCPSocket.h
@@ -0,0 +1,144 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_TCPSocket_h
+#define eckit_TCPSocket_h
+
+#include <netinet/in.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/utils/Hash.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// NOTE: I am not realy happy about those classes, so they may change...
+//       Note that this class calls:
+//          signal(SIGPIPE,SIG_IGN);
+
+class TCPSocket  {
+public:
+
+// -- Execptions
+
+    class UnknownHost : public Exception { public: UnknownHost(const std::string&); };
+
+// -- Contructors
+
+	TCPSocket();
+
+	// From an existing TCPSocket (see TCPServer::accept)
+
+    /// @warning
+    /// **** NOTE: copying gives ownership of the socket to new object
+    /// **** Beware of 'slicing', i.e copying subclasses.
+
+	TCPSocket(TCPSocket&);
+
+// -- Destructor
+
+	virtual ~TCPSocket();
+
+// -- Operators
+
+	// See note on copy constructor
+
+	TCPSocket& operator=(TCPSocket&);
+
+// -- Methods
+
+	// I/O
+    long write(const void* buf, long length);
+
+	/// Read from a TCP socket
+	///
+	/// \param buf The buffer to read into
+	/// \param length The maximum number of bytes to read
+	///
+	/// **Configuration flags**
+	/// \arg **useSelectOnTCPSocket** (*bool*): use select for improved resilience
+	///   on flaky connections
+	/// \arg **socketSelectTimeout** (*long*): timeout in seconds for the select
+	///   (only if **useSelectOnTCPSocket** is enabled)
+	long read(void * buf,long length);
+
+	long rawRead(void*,long); // Non-blocking version
+
+	bool isConnected() const { return socket_ != -1; }
+	bool stillConnected() const;
+
+	// close sockets
+	virtual void close();
+
+	// peer name
+
+	in_addr       remoteAddr() const;
+    const std::string& remoteHost() const;
+	int           remotePort() const;
+
+	in_addr       localAddr() const;
+    const std::string& localHost() const;
+	int           localPort() const;
+
+	void          bufferSize(int n) { bufSize_ = n; }
+	virtual int  socket();
+	
+	void closeOutput();
+    void closeInput();
+
+// -- Class methods 
+
+    static std::string  addrToHost(in_addr);
+    static in_addr hostToAddr(const std::string&);
+    static std::string hostName(const std::string& h, bool full = false);
+
+protected:
+
+// -- Members
+
+	int      socket_;      // i/o socket
+	int      localPort_;   // effective port
+	int      remotePort_;  // remote port
+    std::string   remoteHost_;  // remote host
+	in_addr  remoteAddr_;  // remote ip adress
+    std::string   localHost_;   // local host
+	in_addr  localAddr_;   // local ip adress
+	int      bufSize_;
+
+// -- Methods
+
+	int newSocket(int);
+
+
+
+private:
+
+// -- Methods
+
+	virtual void bind();  // The socket must be made
+    virtual std::string bindingAddress() const;
+
+// -- Friends
+
+
+};
+
+std::ostream& operator<<(std::ostream&,in_addr);
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/TCPStream.cc b/eckit/src/eckit/net/TCPStream.cc
new file mode 100644
index 0000000..49fc778
--- /dev/null
+++ b/eckit/src/eckit/net/TCPStream.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/net/TCPStream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TCPStream::TCPStream(TCPSocket& socket):
+	socket_(socket)
+{
+}
+
+TCPStream::~TCPStream()
+{
+}
+
+void TCPStream::closeOutput()
+{	
+  socket_.closeOutput();
+}
+//======================
+// Tricky solution to be removed when 'mutable' is available
+//
+std::string TCPStreamBase::nonConstName()
+{
+    std::ostringstream r;
+
+    r << "TCP stream from " << socket().localHost() << " at " << socket().localPort()
+      << " to " << socket().remoteHost() << " at " << socket().remotePort();
+    return r.str();
+}
+
+std::string TCPStreamBase::name() const
+{
+	return ((TCPStreamBase*)this)->nonConstName();
+}
+
+SharedTCPStream::SharedTCPStream(TCPSocket& s):
+	TCPStream(s)
+{
+}
+
+SharedTCPStream::~SharedTCPStream() 
+{ 
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/TCPStream.h b/eckit/src/eckit/net/TCPStream.h
new file mode 100644
index 0000000..88fe5c8
--- /dev/null
+++ b/eckit/src/eckit/net/TCPStream.h
@@ -0,0 +1,153 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TCPStream.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_TCPStream_h
+#define eckit_TCPStream_h
+
+#include "eckit/memory/Counted.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/net/TCPSocket.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TCPServer;
+
+// Choose from 
+
+class TCPStreamBase : public Stream {
+public:
+
+// -- Contructors
+
+	TCPStreamBase() {}
+
+// -- Overridden methods
+
+	// From Stream
+
+	virtual long write(const void* buf,long len) 
+		{ return socket().write(buf,len); }
+
+	virtual long read(void* buf,long len)
+		{ return socket().read(buf,len); }
+
+protected:
+
+// -- Overridden methods
+
+	// From Stream
+
+	virtual std::string name() const;
+	
+
+private:
+
+// -- Methods
+
+	std::string nonConstName();
+	virtual TCPSocket& socket() = 0;
+
+};
+
+class TCPStream : public TCPStreamBase {
+public:
+
+// -- Contructors
+
+	// Take ownership of TCPSocket;
+
+	TCPStream(TCPSocket&); 
+
+// -- Destructor
+
+	~TCPStream();
+
+    // From TCPStreamBase
+
+    virtual TCPSocket& socket() { return socket_; }
+
+protected:
+
+// -- Members
+
+	TCPSocket socket_;
+
+private:
+
+	TCPStream(TCPServer&); 
+
+
+// -- Overridden methods
+	
+	// From Stream
+	
+    virtual void closeOutput();
+};
+
+class InstantTCPStream : public TCPStreamBase {
+public:
+
+// -- Constructior
+
+	// Does not take ownership of TCPSocket
+
+	InstantTCPStream(TCPSocket& socket):
+		socket_(socket) {}
+
+    // -- Overridden methods
+
+    // From TCPStream
+
+    virtual TCPSocket& socket() { return socket_; }
+
+private:
+
+	InstantTCPStream(TCPServer&);
+
+// -- Members
+
+    TCPSocket& socket_;
+};
+
+class SharedTCPStream : public TCPStream, public Counted {
+public:
+
+// -- Contructors
+
+	SharedTCPStream(TCPSocket&);
+
+
+private:
+
+// -- Destructor
+
+	~SharedTCPStream();
+
+// -- Contructors
+    
+	SharedTCPStream(TCPServer&);
+
+// -- Members
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/Telnet.cc b/eckit/src/eckit/net/Telnet.cc
new file mode 100644
index 0000000..864ab85
--- /dev/null
+++ b/eckit/src/eckit/net/Telnet.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/runtime/Monitor.h"
+#include "eckit/net/Telnet.h"
+#include "eckit/net/TelnetUser.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+Telnet::Telnet(int port):
+	NetService(port)
+{
+}
+
+Telnet::~Telnet()
+{
+}
+
+
+NetUser* Telnet::newUser(TCPSocket& protocol)
+{
+	return new TelnetUser(protocol);
+}
+
+//=======================================================================
+
+TelnetUser::TelnetUser(TCPSocket& protocol):
+	NetUser(protocol),
+	from_(protocol_.remoteHost())
+{
+}
+
+TelnetUser::~TelnetUser()
+{
+}
+
+void TelnetUser::serve(Stream&, std::istream& in,std::ostream& out)
+{
+
+	Log::debug() << "Starting a telnet connection " << std::endl;
+
+	Monitor::instance().kind("telnet");
+	Monitor::instance().name(from_);
+
+	while(!stopped())
+	{
+		out << "Telnet not supported any more (for now anyway)" << std::endl;
+		break;
+	}
+	Log::info() << "Exiting telnet user loop..." << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/Telnet.h b/eckit/src/eckit/net/Telnet.h
new file mode 100644
index 0000000..4681bf1
--- /dev/null
+++ b/eckit/src/eckit/net/Telnet.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Telnet.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef eckit_Telnet_h
+#define eckit_Telnet_h
+
+#include "eckit/net/NetService.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Telnet : public NetService {
+public:
+
+// -- Contructors
+
+	Telnet(int);
+
+// -- Destructor
+
+	~Telnet();
+
+private:
+
+// No copy allowed
+
+	Telnet(const Telnet&);
+	Telnet& operator=(const Telnet&);
+
+// -- Overridden methods
+
+	// From NetService
+
+	virtual NetUser* newUser(TCPSocket&);
+	virtual std::string   name() { return "telnet"; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/TelnetUser.h b/eckit/src/eckit/net/TelnetUser.h
new file mode 100644
index 0000000..7cf133f
--- /dev/null
+++ b/eckit/src/eckit/net/TelnetUser.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TelnetUser.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef eckit_TelnetUser_h
+#define eckit_TelnetUser_h
+
+#include "eckit/net/NetUser.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TelnetUser : public NetUser {
+public:
+	TelnetUser(TCPSocket&);
+	virtual ~TelnetUser();
+static void terminate(TelnetUser& other)	{ other.stop(); }
+
+private:
+    virtual void serve(Stream&, std::istream&, std::ostream&);
+	std::string from_;
+};
+
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/net/Telnetable.cc b/eckit/src/eckit/net/Telnetable.cc
new file mode 100644
index 0000000..7c2d514
--- /dev/null
+++ b/eckit/src/eckit/net/Telnetable.cc
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/net/Telnet.h"
+#include "eckit/net/Telnetable.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Telnetable::Telnetable(int port):
+	telnet_(new Telnet(port))
+{
+	if(port != 0)
+		telnet_.start();
+}
+
+Telnetable::~Telnetable()
+{
+	telnet_.stop();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/net/Telnetable.h b/eckit/src/eckit/net/Telnetable.h
new file mode 100644
index 0000000..87655aa
--- /dev/null
+++ b/eckit/src/eckit/net/Telnetable.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Telnetable.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_Telnetable_h
+#define eckit_Telnetable_h
+
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// A telnet-able object
+
+class Telnetable {
+public:
+
+// -- Contructors
+
+	Telnetable(int port);
+
+// -- Destructor
+
+	~Telnetable();
+
+private:
+
+// No copy allowed
+
+	Telnetable(const Telnetable&);
+	Telnetable& operator=(const Telnetable&);
+
+// -- Members
+
+	ThreadControler telnet_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/option/CMakeLists.txt b/eckit/src/eckit/option/CMakeLists.txt
new file mode 100644
index 0000000..55e8b84
--- /dev/null
+++ b/eckit/src/eckit/option/CMakeLists.txt
@@ -0,0 +1,29 @@
+ecbuild_add_library(
+
+    TARGET eckit_option
+
+    INSTALL_HEADERS LISTED
+
+    HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/eckit/option
+
+	SOURCES
+        FactoryOption.cc
+        FactoryOption.h
+        Option.cc
+        Option.h
+        Separator.cc
+        Separator.h
+        SimpleOption.cc
+        SimpleOption.h
+        VectorOption.cc
+        VectorOption.h
+        CmdArgs.cc
+        CmdArgs.h
+
+    TEMPLATES
+        FactoryOption.cc
+        VectorOption.cc
+        SimpleOption.cc
+
+    LIBS eckit )
+
diff --git a/eckit/src/eckit/option/CmdArgs.cc b/eckit/src/eckit/option/CmdArgs.cc
new file mode 100644
index 0000000..a6cfc80
--- /dev/null
+++ b/eckit/src/eckit/option/CmdArgs.cc
@@ -0,0 +1,176 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Pedro Maciel
+/// @author Simon Smart
+/// @date March 2016
+
+
+#include <iostream>
+#include <map>
+
+#include "eckit/option/CmdArgs.h"
+#include "eckit/option/Option.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/runtime/Main.h"
+
+
+namespace eckit {
+namespace option {
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+CmdArgs::CmdArgs(usage_proc usage, int args_count, int minimum_args, bool throw_on_error) {
+    init(usage, args_count, minimum_args, throw_on_error);
+}
+
+CmdArgs::CmdArgs(usage_proc usage, std::vector<Option*>& options, int args_count, int minimum_args, bool throw_on_error) {
+    std::swap(options_, options); // Take ownership so it can be destroyed
+    init(usage, args_count, minimum_args, throw_on_error);
+}
+
+void CmdArgs::init(usage_proc usage, int args_count, int minimum_args, bool throw_on_error)  {
+    Main &ctx = Main::instance();
+    tool_ = ctx.name();
+    size_t argc = ctx.argc();
+    bool error = false;
+
+    std::map<std::string, const option::Option *> opts;
+
+    for (std::vector<option::Option *>::const_iterator j = options_.begin(); j != options_.end(); ++j) {
+        if ((*j)->active()) {
+            ASSERT(opts.find((*j)->name()) == opts.end());
+            opts[(*j)->name()] = *j;
+            keys_.insert((*j)->name());
+        }
+    }
+
+    Tokenizer parse("=");
+    for (size_t i = 1; i < argc; i++) {
+
+        std::string a = ctx.argv(i);
+        if (a.size() > 2 && a[0] == '-' && a[1] == '-') {
+            std::vector<std::string> v;
+            parse(a.substr(2), v);
+
+            std::map<std::string, const option::Option *>::const_iterator j = opts.find(v[0]);
+            if (j != opts.end()) {
+                try {
+                    if (v.size() == 1) {
+                        (*j).second->set(*this);
+                    } else {
+                        std::vector<std::string>::const_iterator b = v.begin();
+                        ++b;
+                        (*j).second->set(StringTools::join("=", b, v.end()), *this);
+                    }
+                } catch(UserError& e) {
+                    Log::info() << "Invalid value for option --" << v[0] << std::endl;
+                    error = true;
+                }
+            } else {
+                Log::info() << "Invalid option --" << v[0] << std::endl;
+                error = true;
+            }
+        } else {
+            args_.push_back(a);
+        }
+    }
+
+
+    if (args_count >= 0) {
+        if (args_.size() != size_t(args_count)) {
+            Log::info() << tool_ << ": invalid argument count: expected " << args_count
+                        << ", got: " << args_.size() << "." << std::endl;
+            error = true;
+        }
+    }
+
+    if (minimum_args >= 0) {
+        if (args_.size() < size_t(minimum_args)) {
+            Log::info() << tool_ << ": invalid argument count: expected at least " << minimum_args
+                        << ", got: " << args_.size() << std::endl;
+            error = true;
+        }
+    }
+
+    if (error) {
+        usage(tool_);
+        if (options_.size()) {
+            Log::info() << std::endl;
+            Log::info() << "Options are:" << std::endl;
+            Log::info() << "===========:" << std::endl << std::endl;
+            for (std::vector<option::Option *>::const_iterator j = options_.begin(); j != options_.end(); ++j) {
+                Log::info() << *(*j) << std::endl << std::endl;
+            }
+            Log::info() << std::endl;
+        }
+
+        if (throw_on_error) {
+            for (std::vector<option::Option*>::iterator j = options_.begin(); j  != options_.end(); ++j) {
+                delete (*j);
+            }
+            throw UserError("An error occurred in argument parsing", Here());
+        } else {
+            ::exit(1);
+        }
+    }
+}
+
+
+CmdArgs::~CmdArgs() {
+    for (std::vector<option::Option*>::iterator j = options_.begin(); j  != options_.end(); ++j) {
+        delete (*j);
+    }
+}
+
+
+void CmdArgs::configure(Configured& c) const {
+    for (std::vector<option::Option*>::const_iterator j = options_.begin(); j  != options_.end(); ++j) {
+        (*j)->copy(*this, c);
+    }
+}
+
+
+void CmdArgs::print(std::ostream& out) const {
+    out << "CmdArgs[";
+    LocalConfiguration::print(out);
+    out << "]";
+}
+
+// const std::set<std::string>& CmdArgs::keys() const {
+//     return keys_;
+// }
+
+// const std::vector<std::string>& CmdArgs::args() const {
+//     return args_;
+// }
+
+const std::string &CmdArgs::operator()(size_t i) const {
+    ASSERT(i < args_.size());
+    return args_[i];
+}
+
+size_t CmdArgs::count() const
+{
+    return args_.size();
+}
+
+const std::string& CmdArgs::tool() const {
+    return tool_;
+}
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace option
+}  // namespace eckit
+
diff --git a/eckit/src/eckit/option/CmdArgs.h b/eckit/src/eckit/option/CmdArgs.h
new file mode 100644
index 0000000..9507b0f
--- /dev/null
+++ b/eckit/src/eckit/option/CmdArgs.h
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Pedro Maciel
+/// @author Simon Smart
+/// @date March 2016
+
+
+#ifndef eckit_option_CmdArgs_H
+#define eckit_option_CmdArgs_H
+
+#include "eckit/config/LocalConfiguration.h"
+#include "eckit/memory/NonCopyable.h"
+
+#include <set>
+#include <vector>
+
+namespace eckit {
+namespace option {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Option;
+
+
+class CmdArgs : public LocalConfiguration, private NonCopyable {
+
+public: // types
+
+    typedef void (*usage_proc)(const std::string& name);
+
+public: // methods
+
+    CmdArgs(usage_proc usage, int args_count = -1, int minimum_args = 0, bool throw_on_error = false);
+
+    /// Initialise argument parser with a list of options
+    /// @note Will take ownership of the contents of the vector, and delete them in destructor
+    /// @todo This should probably have some form of smart pointer.
+    CmdArgs(usage_proc usage, std::vector<Option*>& options, int args_count = -1, int minimum_args = 0, bool throw_on_error = false);
+
+    ~CmdArgs();
+
+    // Accessors
+
+    // const std::set<std::string>& keys() const;
+    // const std::vector<std::string>& args() const;
+    // const std::string& args(size_t) const;
+
+    const std::string& operator()(size_t) const;
+    const std::string& tool() const;
+
+    size_t count() const;
+
+
+    void configure(Configured&) const;
+
+    // has, get and set methods are inherited from LocalConfiguration in their entirety
+
+private: // methods
+
+    void init(usage_proc usage, int args_count, int minumum_args, bool throw_on_errror);
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    std::set<std::string> keys_;
+    std::vector<std::string> args_;
+    std::vector<Option*> options_;
+
+    std::string tool_;
+
+    bool throwOnError_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace option
+}  // namespace eckit
+
+#endif // eckit_option_CmdArgs_H
+
diff --git a/eckit/src/eckit/option/FactoryOption.cc b/eckit/src/eckit/option/FactoryOption.cc
new file mode 100644
index 0000000..ef9ce34
--- /dev/null
+++ b/eckit/src/eckit/option/FactoryOption.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+#include <iostream>
+
+#include "eckit/config/Configured.h"
+#include "eckit/config/Configuration.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/option/FactoryOption.h"
+#include "eckit/utils/Translator.h"
+
+namespace eckit {
+namespace option {
+
+
+template<class T>
+FactoryOption<T>::FactoryOption(const std::string &name, const std::string &description):
+    Option(name, description) {
+}
+
+template<class T>
+FactoryOption<T>::~FactoryOption() {
+}
+
+template<class T>
+void FactoryOption<T>::set(const std::string &value, Configured &parametrisation) const {
+    parametrisation.set(name_, value);
+}
+
+template<class T>
+void FactoryOption<T>::copy(const Configuration &from, Configured &to) const {
+    std::string v;
+    if(from.get(name_, v)) {
+        to.set(name_, v);
+    }
+}
+
+template<class T>
+void FactoryOption<T>::print(std::ostream &out) const {
+    out << "   --" << name_ << "=name" << " (" << description_ << ")";
+    out << std::endl << "     Values are: ";
+    T::list(out);
+}
+
+} // namespace option
+} // namespace eckit
+
diff --git a/eckit/src/eckit/option/FactoryOption.h b/eckit/src/eckit/option/FactoryOption.h
new file mode 100644
index 0000000..86aec5e
--- /dev/null
+++ b/eckit/src/eckit/option/FactoryOption.h
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef eckit_option_FactoryOption_H
+#define eckit_option_FactoryOption_H
+
+#include <iosfwd>
+
+#include "eckit/option/Option.h"
+
+namespace eckit {
+namespace option {
+
+
+/// @note Factory option essentially provides a std::string option, whose acceptable values are listed on the
+///       command line (typename T may be any class that implements the list() method). It does no checks on
+///       the validity of input received, and just returns the appropriate string
+
+
+template<class T>
+class FactoryOption : public Option {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    FactoryOption(const std::string& name, const std::string& description);
+
+// -- Destructor
+
+    virtual ~FactoryOption(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+    // None
+
+
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+// -- Methods
+
+    virtual void print(std::ostream&) const; // Change to virtual if base class
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+    FactoryOption(const FactoryOption&);
+    FactoryOption& operator=(const FactoryOption&);
+
+// -- Members
+    // None
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+
+    virtual void set(const std::string& value, Configured&) const;
+    virtual void copy(const Configuration& from, Configured& to) const;
+
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+};
+
+
+} // namespace option
+} // namespace eckit
+
+#include "eckit/option/FactoryOption.cc"
+
+#endif
+
diff --git a/eckit/src/eckit/option/Option.cc b/eckit/src/eckit/option/Option.cc
new file mode 100644
index 0000000..d8d1ce4
--- /dev/null
+++ b/eckit/src/eckit/option/Option.cc
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#include "eckit/option/Option.h"
+#include "eckit/option/SimpleOption.h"
+
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+namespace option {
+
+
+Option::Option(const std::string &name, const std::string &description):
+    name_(name),
+    description_(description) {
+}
+
+
+Option::~Option() {
+}
+
+const std::string &Option::name() const {
+    return name_;
+}
+
+
+bool Option::active() const {
+    return true;
+}
+
+void Option::set(Configured &) const {
+    std::ostringstream os;
+    os << "Option::set() not implemented for " << *this;
+    throw eckit::SeriousBug(os.str());
+}
+
+
+template<>
+const char *Title<size_t>::operator()() const {
+    return "ordinal";
+}
+
+template<>
+const char *Title<long>::operator()() const {
+    return "integer";
+}
+
+template<>
+const char *Title<double>::operator()() const {
+    return "real";
+}
+
+template<>
+const char *Title<bool>::operator()() const {
+    return "0/1";
+}
+
+template<>
+const char *Title<std::string>::operator()() const {
+    return "string";
+}
+
+template<>
+const char *Title<eckit::PathName>::operator()() const {
+    return "path";
+}
+
+template<>
+void SimpleOption<bool>::set(Configured &parametrisation) const {
+    parametrisation.set(name_, true);
+}
+
+template <>
+void SimpleOption<eckit::PathName>::set(const std::string& value, Configured& parametrisation) const {
+    parametrisation.set(name_, value);
+}
+
+template <>
+void SimpleOption<eckit::PathName>::copy(const Configuration &from, Configured &to) const {
+    std::string v;
+    if(from.get(name_, v)) {
+       to.set(name_, v);
+    }
+}
+
+template<>
+void SimpleOption<bool>::print(std::ostream &out) const {
+    out << "   --" << name_ << " (" << description_ << ")";
+}
+
+
+} // namespace option
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/option/Option.h b/eckit/src/eckit/option/Option.h
new file mode 100644
index 0000000..57259b0
--- /dev/null
+++ b/eckit/src/eckit/option/Option.h
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef Option_H
+#define Option_H
+
+#include <iosfwd>
+#include <string>
+
+
+namespace eckit {
+
+class Configuration;
+class Configured;
+
+
+namespace option {
+
+
+class Option {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    Option(const std::string& name, const std::string& description);
+
+// -- Destructor
+
+    virtual ~Option(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+    // None
+
+    const std::string& name() const;
+
+    virtual bool active() const;
+
+    virtual void set(Configured&) const;
+    virtual void set(const std::string& value, Configured&) const = 0;
+    virtual void copy(const Configuration& from, Configured& to) const = 0;
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+    std::string name_;
+    std::string description_;
+
+// -- Methods
+
+    virtual void print(std::ostream&) const = 0; // Change to virtual if base class
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+    Option(const Option&);
+    Option& operator=(const Option&);
+
+// -- Members
+    // None
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+    friend std::ostream& operator<<(std::ostream& s, const Option& p) {
+        p.print(s);
+        return s;
+    }
+
+};
+
+} // namespace option
+
+} // namespace eckit
+#endif
+
diff --git a/eckit/src/eckit/option/Separator.cc b/eckit/src/eckit/option/Separator.cc
new file mode 100644
index 0000000..6ffca07
--- /dev/null
+++ b/eckit/src/eckit/option/Separator.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#include "eckit/option/Separator.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/utils/Translator.h"
+
+#include <iostream>
+
+namespace eckit {
+
+namespace option {
+
+
+Separator::Separator(const std::string &description):
+    Option("", description) {
+}
+
+
+Separator::~Separator() {
+}
+
+
+void Separator::set(const std::string &value, Configured& parametrisation) const {
+    NOTIMP;
+}
+
+void Separator::copy(const Configuration &from, Configured& to) const {;
+}
+
+bool Separator::active() const {
+    return false;
+}
+
+void Separator::print(std::ostream &out) const {
+    out << std::endl << description_ << ":" << std::endl;
+}
+
+} // namespace option
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/option/Separator.h b/eckit/src/eckit/option/Separator.h
new file mode 100644
index 0000000..d46cd1f
--- /dev/null
+++ b/eckit/src/eckit/option/Separator.h
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef Separator_H
+#define Separator_H
+
+#include <iosfwd>
+
+#include "eckit/option/Option.h"
+
+
+namespace eckit {
+
+namespace option {
+
+class Separator : public Option {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    Separator(const std::string& description);
+
+// -- Destructor
+
+    virtual ~Separator(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+    // None
+
+
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+// -- Methods
+
+    virtual void print(std::ostream&) const; // Change to virtual if base class
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+    Separator(const Separator&);
+    Separator& operator=(const Separator&);
+
+// -- Members
+    // None
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+
+    using Option::set;
+    virtual void set(const std::string& value, Configured&) const;
+    virtual bool active() const;
+    virtual void copy(const Configuration& from, Configured& to) const;
+
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+
+};
+
+
+}
+
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/option/SimpleOption.cc b/eckit/src/eckit/option/SimpleOption.cc
new file mode 100644
index 0000000..9c15b6d
--- /dev/null
+++ b/eckit/src/eckit/option/SimpleOption.cc
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+#include <iostream>
+
+#include "eckit/config/Configured.h"
+#include "eckit/config/Configuration.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/option/SimpleOption.h"
+#include "eckit/utils/Translator.h"
+
+namespace eckit {
+
+namespace option {
+
+template<class T> struct Title {
+    const char *operator()() const;
+};
+
+template<>
+const char *Title<size_t>::operator()() const;
+
+template<>
+const char *Title<long>::operator()() const ;
+
+template<>
+const char *Title<double>::operator()() const;
+
+template<>
+const char *Title<bool>::operator()() const ;
+
+template<>
+const char *Title<std::string>::operator()() const;
+
+template<>
+const char *Title<eckit::PathName>::operator()() const;
+
+template<class T>
+SimpleOption<T>::SimpleOption(const std::string &name, const std::string &description):
+    Option(name, description) {
+}
+
+template<class T>
+SimpleOption<T>::~SimpleOption() {
+}
+
+template<class T>
+void SimpleOption<T>::set(const std::string &value, Configured &parametrisation) const {
+    T v = eckit::Translator<std::string, T>()(value);
+    parametrisation.set(name_, v);
+}
+
+template<class T>
+void SimpleOption<T>::set(Configured &parametrisation) const {
+    Option::set(parametrisation);
+}
+
+
+template<class T>
+void SimpleOption<T>::copy(const Configuration &from, Configured &to) const {
+    T v;
+    if(from.get(name_, v)) {
+        to.set(name_, v);
+    }
+}
+
+template <>
+void SimpleOption<eckit::PathName>::set(const std::string& value, Configured& parametrisation) const;
+
+template <>
+void SimpleOption<eckit::PathName>::copy(const Configuration &from, Configured &to) const;
+
+template<>
+void SimpleOption<bool>::print(std::ostream &out) const;
+
+template<class T>
+void SimpleOption<T>::print(std::ostream &out) const {
+    out << "   --" << name_ << "=" << Title<T>()() << " (" << description_ << ")";
+}
+
+template<>
+void SimpleOption<bool>::set(Configured &parametrisation) const;
+
+} // namespace option
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/option/SimpleOption.h b/eckit/src/eckit/option/SimpleOption.h
new file mode 100644
index 0000000..f07509e
--- /dev/null
+++ b/eckit/src/eckit/option/SimpleOption.h
@@ -0,0 +1,120 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef SimpleOption_H
+#define SimpleOption_H
+
+#include <iosfwd>
+
+#include "eckit/option/Option.h"
+
+
+namespace eckit {
+
+namespace option {
+
+
+template<class T>
+class SimpleOption : public Option {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    SimpleOption(const std::string& name, const std::string& description);
+
+// -- Destructor
+
+    virtual ~SimpleOption(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+    // None
+
+
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+// -- Methods
+
+    virtual void print(std::ostream&) const; // Change to virtual if base class
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+    SimpleOption(const SimpleOption&);
+    SimpleOption& operator=(const SimpleOption&);
+
+// -- Members
+    // None
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+
+    virtual void set(Configured&) const;
+    virtual void set(const std::string& value, Configured&) const;
+    virtual void copy(const Configuration& from, Configured& to) const;
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+
+};
+
+
+} // namespace option
+
+} // namespace eckit
+
+#include "eckit/option/SimpleOption.cc"
+
+#endif
+
diff --git a/eckit/src/eckit/option/VectorOption.cc b/eckit/src/eckit/option/VectorOption.cc
new file mode 100644
index 0000000..0aa010e
--- /dev/null
+++ b/eckit/src/eckit/option/VectorOption.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @author Simon Smart
+/// @date March 2016
+
+#include <iostream>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/option/VectorOption.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/types/Types.h"
+
+
+namespace eckit {
+
+namespace option {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+template<class T>
+VectorOption<T>::VectorOption(const std::string &name, const std::string &description, size_t size, const char* separator):
+    Option(name, description), size_(size), separator_(separator) {
+}
+
+
+template<class T>
+VectorOption<T>::~VectorOption() {
+}
+
+template<class T>
+void VectorOption<T>::set(const std::string &value, Configured &parametrisation) const {
+    eckit::Translator<std::string, T> t;
+
+    eckit::Tokenizer parse(separator_);
+    std::vector<std::string> v;
+    parse(value, v);
+
+    std::vector<T> values;
+    for (size_t i = 0; i < v.size(); i++) {
+        values.push_back(t(v[i]));
+    }
+
+    if (size_) {
+        if (values.size() != size_)
+            throw UserError(std::string("Size of supplied vector \"") + name_ + "\" incorrect", Here());
+    }
+
+    parametrisation.set(name_, values);
+}
+
+template<class T>
+void VectorOption<T>::print(std::ostream &out) const {
+    out << "   --" << name_;
+
+    const char *sep = "=";
+    for (size_t i = 0; i < (size_ ? size_ : 2); i++) {
+        out << sep  << Title<T>()();
+        sep = separator_;
+    }
+
+    if(size_ == 0) {
+        out << sep << "...";
+    }
+
+    out << " (" << description_ << ")";
+}
+
+
+template<class T>
+void VectorOption<T>::copy(const Configuration &from, Configured &to) const {
+    std::vector<T> v;
+    if (from.get(name_, v)) {
+        to.set(name_, v);
+    }
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // namespace option
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/option/VectorOption.h b/eckit/src/eckit/option/VectorOption.h
new file mode 100644
index 0000000..444fa2e
--- /dev/null
+++ b/eckit/src/eckit/option/VectorOption.h
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Apr 2015
+
+
+#ifndef VectorOption_H
+#define VectorOption_H
+
+#include <iosfwd>
+
+#include "eckit/option/Option.h"
+#include "eckit/option/SimpleOption.h"
+
+
+namespace eckit {
+
+namespace option {
+
+
+template<class T>
+class VectorOption : public Option {
+  public:
+
+// -- Exceptions
+    // None
+
+// -- Contructors
+
+    VectorOption(const std::string& name, const std::string& description, size_t size, const char* separator="/");
+
+// -- Destructor
+
+    virtual ~VectorOption(); // Change to virtual if base class
+
+// -- Convertors
+    // None
+
+// -- Operators
+    // None
+
+// -- Methods
+    // None
+
+
+
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  protected:
+
+// -- Members
+
+// -- Methods
+
+    virtual void print(std::ostream&) const; // Change to virtual if base class
+
+// -- Overridden methods
+    // None
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+  private:
+
+// No copy allowed
+
+    VectorOption(const VectorOption&);
+    VectorOption& operator=(const VectorOption&);
+
+// -- Members
+
+    size_t size_;
+    const char* separator_;
+
+// -- Methods
+    // None
+
+// -- Overridden methods
+
+    virtual void set(const std::string& value, Configured&) const;
+    virtual void copy(const Configuration& from, Configured& to) const;
+
+// -- Class members
+    // None
+
+// -- Class methods
+    // None
+
+// -- Friends
+
+
+};
+
+
+}
+
+} // namespace eckit
+
+#include "eckit/option/VectorOption.cc"
+
+#endif
+
diff --git a/eckit/src/eckit/os/AutoAlarm.cc b/eckit/src/eckit/os/AutoAlarm.cc
new file mode 100644
index 0000000..8549e9c
--- /dev/null
+++ b/eckit/src/eckit/os/AutoAlarm.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+
+#include "eckit/os/AutoAlarm.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+bool AutoAlarm::caught_ = false;
+bool AutoAlarm::throw_  = false;
+int  AutoAlarm::sec_    = 0;
+
+void AutoAlarm::sigAlarm(int)
+{
+	Log::error() << "Alarm signal received" << std::endl;
+	caught_ = true;
+	if(throw_)
+		throw TimeOut("AutoAlarm",sec_);
+}
+
+AutoAlarm::AutoAlarm(int sec,bool t)
+{
+    /// @todo change this to sigaction
+
+	old_        = ::signal(SIGALRM,sigAlarm);
+	saveThrow_  = throw_;
+	saveSec_    = sec_;
+	throw_      = t;
+	sec_        = sec;
+	caught_     = false;
+
+	::alarm(sec);
+}
+
+AutoAlarm::~AutoAlarm()
+{
+	throw_ = saveThrow_;
+	sec_   = saveSec_;
+	::signal(SIGALRM,old_);
+	::alarm(0);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/AutoAlarm.h b/eckit/src/eckit/os/AutoAlarm.h
new file mode 100644
index 0000000..eb74822
--- /dev/null
+++ b/eckit/src/eckit/os/AutoAlarm.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File AutoAlarm.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_AutoAlarm_h
+#define eckit_AutoAlarm_h
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class AutoAlarm {
+public:
+
+// -- Contructors
+
+	AutoAlarm(int,bool = false);
+
+// -- Destructor
+
+	~AutoAlarm();
+
+// - Class methods
+
+	static bool caught()  { return caught_; }
+
+private:
+
+// No copy allowed
+
+	AutoAlarm(const AutoAlarm&);
+	AutoAlarm& operator=(const AutoAlarm&);
+
+// -- Members
+
+	typedef void (*proc)(int);
+	proc old_;
+	bool saveThrow_;
+	int  saveSec_;
+
+// -- Class members
+
+	static bool caught_;
+	static bool throw_;
+	static int  sec_;
+
+// -- Class methods
+
+	static void sigAlarm(int);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/AutoUmask.h b/eckit/src/eckit/os/AutoUmask.h
new file mode 100644
index 0000000..7648e7b
--- /dev/null
+++ b/eckit/src/eckit/os/AutoUmask.h
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File AutoUmask.h
+// Baudouin Raoult - ECMWF Oct 16
+
+#ifndef eckit_AutoUmask_h
+#define eckit_AutoUmask_h
+
+#include <sys/stat.h>
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class AutoUmask {
+    mode_t umask_;
+
+public:
+    explicit AutoUmask(mode_t u = 0) : umask_(::umask(u)) {}
+    ~AutoUmask() { ::umask(umask_); }
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/BackTrace.cc b/eckit/src/eckit/os/BackTrace.cc
new file mode 100644
index 0000000..778e2ea
--- /dev/null
+++ b/eckit/src/eckit/os/BackTrace.cc
@@ -0,0 +1,129 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+
+#if defined(EC_HAVE_EXECINFO_BACKTRACE) || defined(__FreeBSD__)
+#include <execinfo.h>  // for backtrace
+#endif
+
+#ifdef EC_HAVE_CXXABI_H
+#include <cxxabi.h>
+#endif
+
+#include "eckit/os/BackTrace.h"
+#include "eckit/types/Types.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::string BackTrace::dump()
+{
+    /// @todo implement this using the cxxabi demangle, if CMake detects it
+    
+    std::ostringstream oss;
+    
+#if (defined( EC_HAVE_EXECINFO_BACKTRACE ) || defined(__FreeBSD__)) && !defined( _AIX )
+
+    static Ordinal count = 0;
+    ++count;
+    
+#define BS_BUFF_SIZE 256
+
+    void*   buffer[BS_BUFF_SIZE];
+    char**  strings;
+
+    int addsize = backtrace(buffer, BS_BUFF_SIZE);
+
+    oss << "backtrace [" << count << "] stack has " << addsize << " addresses\n";
+    
+    strings = backtrace_symbols(buffer, addsize);
+    if (strings == NULL)
+        oss << " --- no backtrace_symbols found ---\n";
+    
+#ifndef EC_HAVE_CXXABI_H
+    for (int s = 0; s < addsize; ++s)
+      oss << strings[s] << std::endl;
+#else
+    for (int s = 0; s < addsize; ++s) {
+        int status;
+        char buffer[10240];
+        bool overflow = false;
+
+        char *p = strings[s];
+        size_t i = 0;
+        while(*p) {
+
+            switch(*p) {
+            case ' ':
+            case '(':
+            case ')':
+            case '+':
+            case '\t':
+                oss << *p;
+                if(i) {
+                    buffer[i++] = 0;
+                    char* d = abi::__cxa_demangle(buffer, 0, 0, &status);
+                    if(status == 0) {
+                        oss << d;
+                    }
+                    else {
+                        oss << buffer;
+                    }
+                    if(d) free(d);
+                }
+                i = 0;
+                break;
+
+            default:
+                if(overflow) {
+                    oss << *p;
+                }
+                else {
+                    if(i < sizeof(buffer)) {
+                        buffer[i++] = *p;
+                    }
+                    else {
+                        overflow = true;
+                        for(size_t j = 0; j < i ; j++) {
+                            oss << buffer[j];
+                        }
+                        i = 0;
+                    }
+                }
+            }
+
+            p++;
+        }
+        oss << '\n';
+
+
+    }
+#endif
+    
+    free(strings);
+    
+    oss << "\nexit dumping backtrace ...";
+    
+#else
+    oss << "\ndumping backtrace not supported on this system";
+#endif
+    
+    return oss.str();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/BackTrace.h b/eckit/src/eckit/os/BackTrace.h
new file mode 100644
index 0000000..663ef42
--- /dev/null
+++ b/eckit/src/eckit/os/BackTrace.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_BackTrace_h
+#define eckit_BackTrace_h
+
+#include "eckit/eckit.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class BackTrace {
+public:
+    
+    static std::string dump();
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/Password.cc b/eckit/src/eckit/os/Password.cc
new file mode 100644
index 0000000..0aa4b2c
--- /dev/null
+++ b/eckit/src/eckit/os/Password.cc
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <pwd.h>
+
+#include "eckit/log/Log.h"
+#include "eckit/os/Password.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+bool Password::check(const std::string& user, const std::string& password) {
+    struct passwd p;
+    struct passwd* dummy;
+    char line[1024];
+
+    int n = getpwnam_r(user.c_str(), &p, line, sizeof(line), &dummy);
+
+    if (n != 0) {
+        Log::error() << "User " << user << " is unknown" << std::endl;
+        return false;
+    }
+
+    bool match = password == p.pw_passwd;
+
+    if (match)
+        Log::error() << "User " << user << " gave an valid password" << std::endl;
+    else
+        Log::error() << "User " << user << " gave an invalid password" << std::endl;
+
+    return match;
+}
+
+std::string Password::salt(const std::string& user) {
+    struct passwd p;
+    struct passwd* dummy;
+    char line[1024];
+    int n = getpwnam_r(user.c_str(), &p, line, sizeof(line), &dummy);
+
+    if (n != 0) {
+        Log::error() << "User " << user << " is unknown" << std::endl;
+        return "";
+    }
+
+    char salt[3];
+    strncpy(salt, p.pw_passwd, 2);
+    salt[2] = 0;
+
+    return salt;
+}
+
+//-----------------------------------------------------------------------------
+
+}  // namespace eckit
diff --git a/eckit/src/eckit/os/Password.h b/eckit/src/eckit/os/Password.h
new file mode 100644
index 0000000..8909381
--- /dev/null
+++ b/eckit/src/eckit/os/Password.h
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Password.h
+// Baudouin Raoult - ECMWF Dec 97
+
+#ifndef eckit_Password_h
+#define eckit_Password_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Password {
+public:
+
+	static bool check(const std::string&,const std::string&);
+	static std::string salt(const std::string&);
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/SemLocker.cc b/eckit/src/eckit/os/SemLocker.cc
new file mode 100644
index 0000000..159a8b1
--- /dev/null
+++ b/eckit/src/eckit/os/SemLocker.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <cstdio>
+#include <errno.h>
+#include <sys/sem.h>
+
+#include "eckit/os/SemLocker.h"
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace  {
+
+struct sembuf _lock[] = {
+    { 0, 0,  SEM_UNDO }, /* test */
+    { 0, 1,  SEM_UNDO }, /* lock */
+};
+
+struct sembuf _unlock[] = {
+    { 0, -1, SEM_UNDO }, /* ulck */
+};
+
+}
+
+
+SemLocker::SemLocker(int sem, const PathName& path, int maxWaitLock) :
+    sem_(sem),
+    maxWaitLock_(maxWaitLock),
+    path_(path) {
+
+    int retry = 0;
+    while (retry < maxWaitLock_) {
+        if (semop(sem_, _lock, 2 ) < 0) {
+            int save = errno;
+            retry++;
+
+            if (save == EINTR && retry < maxWaitLock_) {
+                continue;
+            }
+            eckit::Log::warning() << "SharedMemoryLoader: Failed to acquire exclusive lock on " << path_
+                                  << " " << eckit::Log::syserr
+                                  << std::endl;
+
+            // sprintf(message,"ERR: sharedmem:semop:lock(%s)",path);
+            if (retry >= maxWaitLock_) {
+                std::ostringstream os;
+                os << "Failed to acquire semaphore lock for " << path_;
+                throw eckit::FailedSystemCall(os.str());
+            } else {
+                eckit::Log::warning() << "Sleeping for " << SLEEP << " seconds" << std::endl;
+                ::sleep(SLEEP);
+            }
+        } else {
+            break;
+        }
+    }
+}
+
+SemLocker::~SemLocker() {
+
+    int retry = 0;
+    while (retry < maxWaitLock_) {
+        if (semop(sem_, _unlock, 1) < 0) {
+            int save = errno;
+            retry++;
+
+            if (save == EINTR && retry < maxWaitLock_) {
+                continue;
+            }
+
+            eckit::Log::warning() << "SharedMemoryLoader: Failed to realease exclusive lock on " << path_
+                                  << " " << eckit::Log::syserr
+                                  << std::endl;
+
+            if (retry >= maxWaitLock_) {
+                std::ostringstream os;
+                os << "Failed to realease semaphore lock for " << path_;
+                throw eckit::SeriousBug(os.str());
+            } else {
+                eckit::Log::warning() << "Sleeping for " << SLEEP << " seconds" << std::endl;
+                sleep(SLEEP);
+            }
+        } else {
+            break;
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/SemLocker.h b/eckit/src/eckit/os/SemLocker.h
new file mode 100644
index 0000000..734c499
--- /dev/null
+++ b/eckit/src/eckit/os/SemLocker.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Dec 2016
+
+#ifndef eckit_os_SemLocker_h
+#define eckit_os_SemLocker_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/filesystem/PathName.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SemLocker : private NonCopyable {
+
+    static const int SLEEP = 1;
+
+    int sem_;
+    int maxWaitLock_;
+
+    eckit::PathName path_;
+
+public:
+
+    SemLocker(int sem, const eckit::PathName& path, int maxWaitLock = 60);
+
+    ~SemLocker();
+
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/Semaphore.cc b/eckit/src/eckit/os/Semaphore.cc
new file mode 100644
index 0000000..4ab5ca5
--- /dev/null
+++ b/eckit/src/eckit/os/Semaphore.cc
@@ -0,0 +1,132 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstdio>
+#include <errno.h>
+#include <sys/sem.h>
+
+#include "eckit/os/Semaphore.h"
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct sembuf _lock[] = {
+	{ 0, 0,  SEM_UNDO }, /* test */
+	{ 0, 1,  SEM_UNDO }, /* lock */
+};
+
+struct sembuf _unlock[] = {
+	{ 0, -1, SEM_UNDO }, /* ulck */
+};
+
+Semaphore::Semaphore(const PathName& name,int count):
+    semaphore_(-1),
+	count_(count),
+	level_(0)
+{
+	key_t key = ftok(name.localPath(),1);
+
+	if(key == key_t(-1) && errno == ENOENT)
+	{
+		name.touch();
+		key = ftok(name.localPath(),1);
+	}
+
+	if(key == key_t(-1))
+		throw FailedSystemCall(std::string("ftok(") + name + std::string(")"));
+
+    /// @note cannot use Log::debug() of SYSCALL here, because Log may not yet be initialized
+    // std::cout << "Creating semaphore path=" << name << ", count=" << count << ", key=" << hex << key << dec << std::endl;
+	if( (semaphore_ = semget(key, count_, 0666 | IPC_CREAT)) < 0 )
+        perror("semget failed"), throw FailedSystemCall("semget");
+}
+
+Semaphore::~Semaphore()
+{
+	ASSERT(level_ == 0);
+}
+
+void Semaphore::lock(void) 
+{
+	mutex_.lock();
+	if(++level_ == 1)
+		while(semop(semaphore_,_lock,NUMBER(_lock)) < 0)
+		{
+			if(errno != EINTR)
+				throw FailedSystemCall("semop lock");
+		}
+}
+
+void Semaphore::unlock(void) 
+{
+	ASSERT(level_ > 0);
+
+	if(--level_ == 0)
+		while(semop(semaphore_,_unlock,NUMBER(_unlock)) < 0)
+		{
+			if(errno != EINTR)
+				throw FailedSystemCall("semop unlock");
+		}
+
+	mutex_.unlock();
+}
+
+bool Semaphore::test(short unsigned int n)
+{
+	struct sembuf test = { n, 0 , IPC_NOWAIT};
+
+	if(semop(semaphore_,&test,1) == 0)
+		return false;  // Free
+
+	if(errno == EAGAIN)
+		return true;  // in use
+
+	throw FailedSystemCall("semop test");
+}
+
+pid_t  Semaphore::getpid() const
+{
+    int val;
+    SYSCALL(val = semctl(semaphore_,0,GETPID));
+    return val;
+}
+
+void Semaphore::set(int val,int n)
+{
+	SYSCALL(semctl(semaphore_,n,SETVAL,val));
+}
+
+
+int Semaphore::get(int n) const
+{
+	int val;
+	SYSCALL(val = semctl(semaphore_,n,GETVAL,0));
+	return val;
+}
+
+void Semaphore::raise(short unsigned int n)
+{
+	struct sembuf op = { n,1,SEM_UNDO};
+	SYSCALL(semop(semaphore_,&op,1));
+}
+
+void Semaphore::lower(short unsigned int n)
+{
+	struct sembuf op = { n,-1,SEM_UNDO};
+	SYSCALL(semop(semaphore_,&op,1));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/Semaphore.h b/eckit/src/eckit/os/Semaphore.h
new file mode 100644
index 0000000..e1ddbf5
--- /dev/null
+++ b/eckit/src/eckit/os/Semaphore.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date   May 1996
+
+#ifndef eckit_os_Semaphore_h
+#define eckit_os_Semaphore_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/filesystem/PathName.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Semaphore : private NonCopyable {
+
+public: // methods
+
+    Semaphore(const PathName& name,int count = 1);
+
+    ~Semaphore();
+
+    void lock(void);
+    void unlock(void);
+    bool test(unsigned short n = 0);
+
+    int  get(int n = 0) const;
+    void set(int, int n = 0);
+
+    void raise(unsigned short n = 0);
+    void lower(unsigned short n = 0);
+
+    pid_t getpid()  const;
+
+protected: // members
+
+    int semaphore_;
+    int count_;
+    int level_;
+
+    Mutex mutex_;
+
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/SharedInt.cc b/eckit/src/eckit/os/SharedInt.cc
new file mode 100644
index 0000000..5cfcf91
--- /dev/null
+++ b/eckit/src/eckit/os/SharedInt.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include <sys/sem.h>
+
+#include "eckit/os/SharedInt.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+SharedInt::SharedInt(const PathName& path,int count):
+	Semaphore(path,2*count)
+{
+}
+
+SharedInt::~SharedInt()
+{
+}
+
+void SharedInt::use(int n)
+{
+	Semaphore::lower(2*n);
+}
+
+void SharedInt::release(int n)
+{
+	Semaphore::raise(2*n);
+}
+
+void SharedInt::newLimit(short val,unsigned short n)
+{
+	int v;
+	while( (v = semctl(semaphore_,2*n+1,GETVAL,0)) != val)
+	{
+		if(v<0) throw FailedSystemCall("semctl GETVAL");
+        short delta = val - v;
+        typedef unsigned short int U;
+        struct sembuf set[] = {{ U(2*n), delta, 0,},{ U(2*n+1), delta, 0,}};
+		SYSCALL(semop(semaphore_,set,NUMBER(set)));
+	}
+}
+
+int SharedInt::limit(int n) const
+{
+	return Semaphore::get(2*n+1);
+}
+
+int SharedInt::free(int n) const
+{
+	return Semaphore::get(2*n);
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/SharedInt.h b/eckit/src/eckit/os/SharedInt.h
new file mode 100644
index 0000000..89689e1
--- /dev/null
+++ b/eckit/src/eckit/os/SharedInt.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SharedInt.h
+// Baudouin Raoult - ECMWF Jul 97
+
+#ifndef eckit_SharedInt_h
+#define eckit_SharedInt_h
+
+#include "eckit/os/Semaphore.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class SharedInt : public Semaphore {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	SharedInt(const PathName&,int n = 1);
+
+// -- Destructor
+
+	~SharedInt(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	void use(int n = 0);
+	void release(int n = 0);
+
+	int  free(int n = 0) const;
+	int  limit(int n = 0)   const;
+    void newLimit(short val,unsigned short n = 0);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	// void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	SharedInt(const SharedInt&);
+	SharedInt& operator=(const SharedInt&);
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const SharedInt& p)
+	//	{ p.print(s); return s; }
+
+};
+
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/SignalHandler.cc b/eckit/src/eckit/os/SignalHandler.cc
new file mode 100644
index 0000000..1d3faf3
--- /dev/null
+++ b/eckit/src/eckit/os/SignalHandler.cc
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/Log.h"
+#include "eckit/os/SignalHandler.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+SignalHandler* SignalHandler::current_ = 0;
+
+SignalHandler::SignalHandler(void (*handler)(int),
+                             SignalHandler::Signal sig):
+        signal_(sig),
+        next_(current_)
+{
+//	Log::info() << "Installing signal handler " << signal_ << std::endl;
+    current_ = this;
+
+    struct sigaction a;
+
+    a.sa_flags   = 0;
+    a.sa_handler = handler;
+    sigemptyset(&a.sa_mask);
+
+    //volatile int sigtype = sigsetjmp(buf_,1);
+    //if(sigtype == 0)
+    sigaction(signal_,&a,&save_);
+    //else {
+    //Log::warning() << "Got signal " << sigtype << std::endl;
+    //throw Abort("Signal received");
+    //}
+}
+
+SignalHandler::~SignalHandler()
+{
+    //ASSERT(current_ == this);
+    current_ = next_;
+
+    struct sigaction ignore;
+    sigaction(signal_,&save_,&ignore);
+
+//	Log::info() << "Removing signal handler " << signal_ << std::endl;
+}
+
+static bool interrupted_ = false;
+
+void SignalHandler::interrupt(int sig)
+{
+//    printf(" >>> received interrupt %d", sig );
+    
+    if(interrupted_) {
+        ::kill(0, SIGTERM);
+    }
+    //siglongjmp(current_->buf_,sig);
+    interrupted_ = true;
+}
+
+void SignalHandler::checkInterrupt()
+{
+//    printf(".\n");
+    
+    if(interrupted_ && !Exception::throwing())
+    {
+        interrupted_ = false;
+        throw Cancel("Process interrupted");
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/SignalHandler.h b/eckit/src/eckit/os/SignalHandler.h
new file mode 100644
index 0000000..0e6edba
--- /dev/null
+++ b/eckit/src/eckit/os/SignalHandler.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_SignalHandler_h
+#define eckit_SignalHandler_h
+
+#include <signal.h>
+#include <setjmp.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// @warning This class has not been widely tested, and we don't
+//           know if they delete objects properly when the signal is caugth
+
+class SignalHandler : private NonCopyable {
+
+public: // methods
+  
+	enum Signal { 
+			SigInt  = 2, 
+			SigQuit = 3
+	};
+
+// -- Contructors
+
+	SignalHandler(void (*)(int) = interrupt, Signal = SigInt);
+
+// -- Destructor
+
+	~SignalHandler(); 
+	
+	static void checkInterrupt();
+
+private: // methods
+
+	static void interrupt(int);
+
+private: // members
+
+	int         signal_;
+
+// unused // sigjmp_buf       buf_;
+	struct sigaction save_;
+
+	SignalHandler*        next_;
+	static SignalHandler* current_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/Stat.h b/eckit/src/eckit/os/Stat.h
new file mode 100644
index 0000000..1c2f5bb
--- /dev/null
+++ b/eckit/src/eckit/os/Stat.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_os_Stat_h
+#define eckit_os_Stat_h
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// struct to manage differences between stat and stat64 different OS's
+struct Stat
+{
+    /// prefer using stat if supports 64 bit
+
+    typedef struct stat Struct;
+
+    static int stat (const char *path, Struct *s){ return ::stat(path,s); }
+    static int lstat(const char *path, Struct *s){ return ::lstat(path,s); }
+    static int fstat(int fd, Struct *s)          { return ::fstat(fd,s); }
+
+private:
+
+    Stat(); ///< non-instantiable
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/os/System.cc b/eckit/src/eckit/os/System.cc
new file mode 100644
index 0000000..3e5dc06
--- /dev/null
+++ b/eckit/src/eckit/os/System.cc
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/eckit.h"
+
+#include "eckit/os/System.h"
+#include "eckit/types/Types.h"
+
+#if defined(EC_HAVE_DLFCN_H) && defined(EC_HAVE_DLADDR)
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <dlfcn.h>
+
+#define ECKIT_ADDRTOPATH_WITH_DLADDR
+
+#endif
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+std::string System::addrToPath(const void *addr) {
+
+    std::string result = "/UNKNOWN";
+
+#ifdef ECKIT_ADDRTOPATH_WITH_DLADDR
+    Dl_info info;
+    info.dli_fname = result.c_str();
+    dladdr(addr, &info);
+    result = info.dli_fname;
+#endif
+
+    /// @todo: what do we do with archives (.a)
+    ///  * maybe we rely on env variable set?
+    ///  * or rely on etc/path
+
+    return result;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/os/System.h b/eckit/src/eckit/os/System.h
new file mode 100644
index 0000000..38e49a3
--- /dev/null
+++ b/eckit/src/eckit/os/System.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_os_System_h
+#define eckit_os_System_h
+
+#include <string>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class System {
+public:
+
+    static std::string addrToPath(const void *addr);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/parser/JSON.cc b/eckit/src/eckit/parser/JSON.cc
new file mode 100644
index 0000000..71d5349
--- /dev/null
+++ b/eckit/src/eckit/parser/JSON.cc
@@ -0,0 +1,226 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/JSON.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+JSON::JSON(std::ostream& out):
+    out_(out) ,
+    null_(true)
+{
+    sep_.push_back("");
+    state_.push_back(true);
+}
+
+JSON::~JSON()
+{
+    if(null_) out_ << "null";
+}
+
+void JSON::sep()
+{
+    null_ = false;
+    out_ << sep_.back();
+    if(indict() && sep_.back() != ":")
+        sep_.back() = ":";
+    else
+        sep_.back() = ",";
+}
+
+static std::ostream& encode(std::ostream& s,const char *p) {
+    s << '"';
+    while(*p)
+    {
+        switch(*p) {
+
+        case '\\':
+            s << "\\\\";
+            break;
+
+        case '\n':
+            s << "\\n";
+            break;
+
+        case '\t':
+            s << "\\t";
+            break;
+
+        case '\b':
+            s << "\\b";
+            break;
+
+        case '\f':
+            s << "\\f";
+            break;
+
+        case '\r':
+            s << "\\r";
+            break;
+
+        case '"':
+            s << "\\\"";
+            break;
+
+        default:
+            s << *p;
+            break;
+        }
+        p++;
+    }
+
+    s << '"';
+    return s;
+}
+
+
+JSON& JSON::startObject()
+{
+    null_ = false;
+    sep();
+    sep_.push_back("");
+    state_.push_back(true);
+    out_ << "{";
+    return *this;
+}
+
+JSON& JSON::null()
+{
+    null_ = false;
+    sep();
+    out_ << "null";
+    return *this;
+}
+
+JSON& JSON::startList()
+{
+    null_ = false;
+    sep();
+    sep_.push_back("");
+    state_.push_back(false);
+    out_ << "[";
+    return *this;
+}
+
+JSON& JSON::endObject()
+{
+    sep_.pop_back();
+    state_.pop_back();
+    out_ << "}";
+    return *this;
+}
+
+JSON& JSON::endList()
+{
+    sep_.pop_back();
+    state_.pop_back();
+    out_ << "]";
+    return *this;
+}
+
+JSON& JSON::operator<<(const char* s)
+{
+    null_ = false;
+    sep();
+    encode(out_, s);
+    return *this;
+}
+
+JSON& JSON::operator<<(const std::string& s)
+{
+    null_ = false;
+    sep();
+    encode(out_, s.c_str());
+    return *this;
+}
+
+JSON& JSON::operator<<(int n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(unsigned int n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(char n)
+{
+    null_ = false;
+    sep();
+    out_ << '"' << n << '"';
+    return *this;
+}
+
+JSON& JSON::operator<<(bool n)
+{
+    null_ = false;
+    sep();
+    out_ << (n ? "true": "false");
+    return *this;
+}
+
+JSON& JSON::operator<<(long n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(unsigned long n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(long long n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(unsigned long long n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::operator<<(double n)
+{
+    null_ = false;
+    sep();
+    out_ << n;
+    return *this;
+}
+
+JSON& JSON::precision(int n)
+{
+  out_ << std::setprecision(n);
+  return *this;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/parser/JSON.h b/eckit/src/eckit/parser/JSON.h
new file mode 100644
index 0000000..df40173
--- /dev/null
+++ b/eckit/src/eckit/parser/JSON.h
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   JSON.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Aug 2011
+
+#ifndef eckit_JSON_h
+#define eckit_JSON_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class JSON : private NonCopyable {
+
+public: // methods
+
+    /// Contructor
+	JSON(std::ostream&);
+
+    /// Destructor
+	~JSON();
+
+	JSON& operator<<(bool);
+	JSON& operator<<(char);
+	JSON& operator<<(unsigned char);
+	JSON& operator<<(int);
+	JSON& operator<<(unsigned int);
+	JSON& operator<<(long);
+	JSON& operator<<(unsigned long);
+	JSON& operator<<(long long);
+	JSON& operator<<(unsigned long long);
+	JSON& operator<<(float);
+	JSON& operator<<(double);
+
+	JSON& operator<<(const std::string&);
+	JSON& operator<<(const char*);
+
+    template < typename T >
+    JSON& operator<<(const std::vector<T>&);
+
+    template < typename T >
+    JSON& operator<<(const std::set<T>&);
+
+    template < typename T >
+    JSON& operator<<(const std::map<std::string, T>&);
+
+    JSON& null();
+
+    JSON& startObject();
+    JSON& endObject();
+
+    JSON& startList();
+    JSON& endList();
+
+    /// Set the precision for float and double (works like std::setprecision)
+    JSON& precision(int);
+
+private: // members
+
+    std::ostream& out_;
+    std::vector<std::string> sep_;
+    std::vector<bool> state_;
+    bool null_;
+
+private: // methods
+
+    void sep();
+    bool inlist() { return !state_.back(); }
+    bool indict() { return  state_.back(); }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template < typename T >
+JSON& JSON::operator<<(const std::vector<T> &v)
+{
+    startList();
+    for( size_t i = 0; i < v.size(); ++i ) {
+        *this << v[i];
+    }
+    endList();
+    return *this;
+}
+
+template < typename T >
+JSON& JSON::operator<<(const std::set<T>& s)
+{
+    startList();
+    for(typename std::set<T>::const_iterator i = s.begin(); i != s.end(); ++i ) {
+        *this << *i;
+    }
+    endList();
+    return *this;
+}
+
+template < typename T >
+JSON& JSON::operator<<(const std::map<std::string, T> &m)
+{
+    startObject();
+
+    for( typename std::map<std::string, T>::const_iterator it = m.begin(); it != m.end(); ++it )
+        *this << (*it).first << (*it).second;
+
+    endObject();
+    return *this;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/parser/JSONDataBlob.cc b/eckit/src/eckit/parser/JSONDataBlob.cc
new file mode 100644
index 0000000..74a710e
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONDataBlob.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Simon Smart
+/// @date   Jan 2016
+
+#include "eckit/parser/JSONDataBlob.h"
+
+namespace eckit {
+
+// -------------------------------------------------------------------------------------------------
+
+// Construct JSONDataBlob builder object, to self-register this type with the
+// DataBlobFactory
+namespace {
+    DataBlobBuilder<JSONDataBlob> jsonBlobBuilder("json");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+JSONDataBlob::JSONDataBlob(const void * data, size_t length) :
+    DataBlob(data, length),
+    metadata_(buffer_) {
+
+    Log::info() << "[" << *this << "] constructor" << std::endl;
+}
+
+
+JSONDataBlob::JSONDataBlob(DataHandle& dh, size_t length) :
+    DataBlob(dh, length),
+    metadata_(buffer_) {
+
+    Log::info() << "[" << *this << "] constructor" << std::endl;
+}
+
+
+JSONDataBlob::~JSONDataBlob() {}
+
+const eckit::Metadata& JSONDataBlob::metadata() const {
+    return metadata_;
+}
+
+void JSONDataBlob::print(std::ostream& os) const {
+    os << "JSONDataBlob()";
+}
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/parser/JSONDataBlob.h b/eckit/src/eckit/parser/JSONDataBlob.h
new file mode 100644
index 0000000..9aa72b3
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONDataBlob.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Simon Smart
+/// @date   Jan 2016
+
+#ifndef eckit_JSONDataBlob_h
+#define eckit_JSONDataBlob_h
+
+#include "eckit/io/DataBlob.h"
+#include "eckit/parser/JSONMetadata.h"
+
+namespace eckit {
+
+// -------------------------------------------------------------------------------------------------
+
+class JSONDataBlob : public eckit::DataBlob {
+
+public: // methods
+
+    JSONDataBlob(const void* data, size_t length);
+    JSONDataBlob(DataHandle& dh, size_t length);
+
+    virtual ~JSONDataBlob();
+
+    virtual const eckit::Metadata& metadata() const;
+
+private: // methods
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    JSONMetadata metadata_;
+
+};
+
+// -------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_JSONDataBlob_h
diff --git a/eckit/src/eckit/parser/JSONMetadata.cc b/eckit/src/eckit/parser/JSONMetadata.cc
new file mode 100644
index 0000000..f25c63e
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONMetadata.cc
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/parser/JSONMetadata.h"
+#include "eckit/parser/JSONParser.h"
+
+#include <iostream>
+#include <sstream>
+
+using namespace eckit;
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+JSONMetadata::JSONMetadata(const Buffer& buffer) {
+
+    // JSON data is a string, rather than an anonymous blob of bytes...
+    std::string json_str(buffer, buffer.size());
+    std::istringstream iss(json_str);
+
+    // Parse the data, as a value
+    JSONParser parser(iss);
+    root_ = parser.parse();
+}
+
+JSONMetadata::~JSONMetadata() {}
+
+
+std::vector<std::string> JSONMetadata::keywords() const {
+
+    std::vector<std::string> params;
+    if (root_.isMap()) {
+        ValueMap vmap(root_);
+        for (ValueMap::const_iterator it = vmap.begin(); it != vmap.end(); ++it) {
+            params.push_back(std::string(it->first));
+        }
+    }
+    return params;
+}
+
+bool JSONMetadata::has(const std::string& key) const {
+    return root_.isMap() && root_.contains(key);
+}
+
+// If Value had a templated isType<T>() method, we could make this beautifully
+// DRY and templated...
+
+void JSONMetadata::get(const std::string& key, std::string& value) const {
+    ASSERT(root_.isMap());
+
+    if (!has(key))
+        throw OutOfRange(std::string("Element \"") + key + "\" not present in JSON", Here());
+
+    const Value& val(root_[key]);
+    if (!val.isString())
+        throw BadCast(std::string("Element \"") + key + "\" is not a std::string", Here());
+
+    value = std::string(val);
+}
+
+void JSONMetadata::get(const std::string& key, long& value) const {
+    ASSERT(root_.isMap());
+
+    if (!has(key))
+        throw OutOfRange(std::string("Element \"") + key + "\" not present in JSON", Here());
+
+    const Value& val(root_[key]);
+    if (!val.isNumber())
+        throw BadCast(std::string("Element \"") + key + "\" is not a long", Here());
+
+    value = (long)(val);
+}
+
+void JSONMetadata::get(const std::string& key, double& value) const {
+    ASSERT(root_.isMap());
+
+    if (!has(key))
+        throw OutOfRange(std::string("Element \"") + key + "\" not present in JSON", Here());
+
+    const Value& val(root_[key]);
+    if (val.isDouble()) {
+        value = double(val);
+    } else if (val.isNumber()) {
+        long long tmp_long  = (long long)(val);
+        value = tmp_long;
+    } else {
+        throw BadCast(std::string("Element \"") + key + "\" is not a double", Here());
+    }
+}
+
+void JSONMetadata::print(std::ostream& os) const {
+    os << "JSONMetaData[]";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace eckit
+
diff --git a/eckit/src/eckit/parser/JSONMetadata.h b/eckit/src/eckit/parser/JSONMetadata.h
new file mode 100644
index 0000000..f719427
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONMetadata.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+
+#ifndef eckit_JSONMetadata_h
+#define eckit_JSONMetadata_h
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "eckit/types/Metadata.h"
+#include "eckit/value/Value.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Buffer;
+
+
+class JSONMetadata : public Metadata {
+
+public: // methods
+
+    JSONMetadata(const Buffer& buffer);
+    virtual ~JSONMetadata();
+
+    virtual std::vector<std::string> keywords() const;
+
+    virtual bool has(const std::string& name) const;
+
+    virtual void get(const std::string& name, std::string& value) const;
+    virtual void get(const std::string& name, long& value) const;
+    virtual void get(const std::string& name, double& value) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const JSONMetadata& p) {
+        p.print(s);
+        return s;
+    }
+
+protected: // methods
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    Value root_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace eckit
+
+#endif // eckit_JSONMetadata_h
diff --git a/eckit/src/eckit/parser/JSONParser.cc b/eckit/src/eckit/parser/JSONParser.cc
new file mode 100644
index 0000000..0b243bc
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONParser.cc
@@ -0,0 +1,297 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   JSONParser.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Jun 2012
+
+#include "eckit/value/Value.h"
+#include "eckit/parser/JSONParser.h"
+#include "eckit/utils/Translator.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Value JSONParser::parseTrue()
+{
+    consume("true");
+    return Value(true);
+}
+
+Value JSONParser::parseFalse()
+{
+    consume("false");
+    return Value(false);
+}
+
+Value JSONParser::parseNull()
+{
+    consume("null");
+    return Value();
+}
+
+Value JSONParser::parseNumber()
+{
+    bool real = false;
+    std::string s;
+    char c = next();
+    if(c == '-') {
+        s += c;
+        c = next();
+    }
+
+    switch(c) {
+    case '0': s += c; break;
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+        s += c;
+        while(isdigit(peek())) {
+            s += next();
+        }
+        break;
+    default:
+        throw StreamParser::Error(std::string("JSONTokenizer::parseNumber invalid char '") + c + "'");
+        break;
+    }
+
+    if(peek() == '.') {
+        real = true;
+        s += next();
+        c = next();
+        if(!isdigit(c))
+            throw StreamParser::Error(std::string("JSONTokenizer::parseNumber invalid char '") + c + "'");
+        s += c;
+        while(isdigit(peek())) {
+            s += next();
+        }
+    }
+
+
+    c = peek();
+    if(c == 'e' || c == 'E') {
+        real = true;
+        s += next();
+
+        c = next();
+        if(c == '-' || c == '+')
+        {
+            s += c;
+            c = next();
+        }
+
+        if(!isdigit(c))
+            throw StreamParser::Error(std::string("JSONTokenizer::parseNumber invalid char '") + c + "'");
+        s += c;
+        while(isdigit(peek())) {
+            s += next();
+        }
+
+    }
+
+    if(real) {
+        double d = Translator<std::string,double>()(s);
+        return Value(d);
+    }
+    else
+    {
+        long long d = Translator<std::string,long long>()(s);
+        return Value(d);
+    }
+}
+
+Value JSONParser::parseString()
+{
+    consume('"');
+    std::string s;
+    for(;;)
+    {
+        char c = next(true);
+        if(c == '\\')
+        {
+            c = next(true);
+            switch(c) {
+
+            case '"':
+                s += '"';
+                break;
+
+            case '\\':
+                s += '\\';
+                break;
+
+            case '/':
+                s += '/';
+                break;
+
+            case 'b':
+                s += '\b';
+                break;
+
+            case 'f':
+                s += '\f';
+                break;
+
+            case 'n':
+                s += '\n';
+                break;
+
+            case 'r':
+                s += '\r';
+                break;
+
+            case 't':
+                s += '\t';
+                break;
+
+            case 'u':
+                throw StreamParser::Error(std::string("JSONTokenizer::parseString \\uXXXX format not supported"));
+                break;
+            default:
+                throw StreamParser::Error(std::string("JSONTokenizer::parseString invalid \\ char '") + c + "'");
+                break;
+            }
+        }
+        else
+        {
+            if(c == '"')
+            {
+                return Value(s);
+            }
+            s += c;
+        }
+
+    }
+
+}
+
+void JSONParser::parseKeyValue(std::map<Value,Value>& m)
+{
+    Value k = parseString();
+    consume(':');
+    Value v = parseValue();
+
+    m[k] = v;
+}
+
+Value JSONParser::parseObject()
+{
+    consume("{");
+    char c = peek();
+    if(c == '}')
+    {
+        consume(c);
+        return Value::makeMap();
+    }
+
+    std::map<Value,Value> m;
+
+    for(;;) {
+
+        parseKeyValue(m);
+
+        char c = peek();
+        if(c == '}')
+        {
+            consume(c);
+            return Value::makeMap(m);
+        }
+
+        consume(',');
+
+    }
+}
+
+Value JSONParser::parseArray()
+{
+    consume("[");
+    char c = peek();
+    if(c == ']')
+    {
+        consume(c);
+        //cout << "JSONTokenizer::parseArray <== " << std::endl;;
+        return Value::makeList();
+    }
+
+    std::vector<Value> l;
+    for(;;) {
+
+        l.push_back(parseValue());
+
+        char c = peek();
+        if(c == ']')
+        {
+            consume(c);
+            //cout << "JSONTokenizer::parseArray <== " << std::endl;;
+            return Value::makeList(l);
+        }
+
+        consume(',');
+
+    }
+}
+
+
+Value JSONParser::parseValue()
+{
+    char c = peek();
+    switch(c)
+    {
+
+    case 't': return parseTrue(); break;
+    case 'f': return parseFalse(); break;
+    case 'n': return parseNull(); break;
+    case '{': return parseObject(); break;
+    case '[': return parseArray(); break;
+    case '\"': return parseString(); break;
+
+    case '-': return parseNumber(); break;
+    case '0': return parseNumber(); break;
+    case '1': return parseNumber(); break;
+    case '2': return parseNumber(); break;
+    case '3': return parseNumber(); break;
+    case '4': return parseNumber(); break;
+    case '5': return parseNumber(); break;
+    case '6': return parseNumber(); break;
+    case '7': return parseNumber(); break;
+    case '8': return parseNumber(); break;
+    case '9': return parseNumber(); break;
+
+    default:
+        throw StreamParser::Error(std::string("JSONTokenizer::parseValue unexpected char '") + c + "'");
+        break;
+    }
+}
+
+JSONParser::JSONParser(std::istream &in, bool comments ) : StreamParser(in,comments)
+{
+}
+
+Value JSONParser::parse()
+{
+    Value v = parseValue();
+    char c = peek();
+    if(c != 0)
+        throw StreamParser::Error(std::string("JSONTokenizer::parse extra char '") + c + "'");
+    return v;
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/parser/JSONParser.h b/eckit/src/eckit/parser/JSONParser.h
new file mode 100644
index 0000000..cfb623f
--- /dev/null
+++ b/eckit/src/eckit/parser/JSONParser.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Jun 2012
+
+#ifndef eckit_JSONParser_h
+#define eckit_JSONParser_h
+
+#include "eckit/parser/StreamParser.h"
+#include "eckit/types/Types.h"
+#include "eckit/value/Value.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class JSONParser : public StreamParser {
+
+public: // methods
+
+    JSONParser(std::istream& in, bool comments = false);
+
+    Value parse();
+
+private: // methods
+
+    Value parseTrue();
+    Value parseFalse();
+    Value parseNull();
+    Value parseValue();
+    Value parseObject();
+    Value parseArray();
+    Value parseString();
+    Value parseNumber();
+
+    void parseKeyValue(std::map<Value, Value> &);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/parser/StreamParser.cc b/eckit/src/eckit/parser/StreamParser.cc
new file mode 100644
index 0000000..ce5e218
--- /dev/null
+++ b/eckit/src/eckit/parser/StreamParser.cc
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Sep 2012
+
+#include "eckit/os/BackTrace.h"
+#include "eckit/parser/StreamParser.h"
+#include "eckit/utils/Translator.h"
+
+#include "eckit/log/Log.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+StreamParser::StreamParser(std::istream &in, bool comments, const char* comment) :
+    line_(0),
+    in_(in),
+    comments_(comments)
+{
+    while (*comment) {
+        comment_.insert(*comment++);
+    }
+}
+
+char StreamParser::peek(bool spaces)
+{
+    for (;;)
+    {
+        char c = in_.peek();
+
+        if (in_.eof())
+            return 0;
+
+        if (comments_ && comment_.find(c) != comment_.end())
+        {
+            while (in_.peek() != '\n' && !in_.eof()) {
+                in_.get(c);
+            }
+            if (in_.eof()) {
+                return 0;
+            }
+            return peek(spaces);
+        }
+
+        if (spaces || !isspace(c))
+        {
+//            std::cout << "peek(" << c << ")" << std::endl;
+            return c;
+        }
+        else {
+//            std::cout << "skip(" << c << ")" << std::endl;
+            in_.get(c);
+            if (c == '\n') { line_++; }
+        }
+    }
+}
+
+char StreamParser::next(bool spaces)
+{
+    char c;
+    for (;;)
+    {
+        in_.get(c);
+        if (in_.eof())
+            throw StreamParser::Error(std::string("StreamParser::next reached eof"));
+
+        if (c == '\n') { line_++; }
+
+        if (comments_ && comment_.find(c) != comment_.end())
+        {
+            while (in_.peek() != '\n' && !in_.eof()) {
+                in_.get(c);
+            }
+            if (in_.eof()) {
+                throw StreamParser::Error(std::string("StreamParser::next reached eof"));
+            }
+            return next(spaces);
+        }
+
+        if (spaces || !isspace(c))
+        {
+//            std::cout << "next(" << c << ")" << std::endl;
+            return c;
+        }
+    }
+
+}
+
+void StreamParser::consume(char c)
+{
+    char n = next();
+    if (c != n)
+        throw StreamParser::Error(std::string("StreamParser::consume expecting '") + c + "', got '" + n + "'", line_ + 1);
+}
+
+void StreamParser::consume(const char* p)
+{
+    while (*p) consume(*p++);
+}
+
+
+StreamParser::Error::Error(const std::string &what, size_t line) : Exception(what)
+{
+    if (line) {
+        std::ostringstream oss;
+        oss << "Line: " << line << " " << what;
+        reason(oss.str());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/parser/StreamParser.h b/eckit/src/eckit/parser/StreamParser.h
new file mode 100644
index 0000000..92142ce
--- /dev/null
+++ b/eckit/src/eckit/parser/StreamParser.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Sep 2012
+
+#ifndef eckit_StreamParser_h
+#define eckit_StreamParser_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class StreamParser : private NonCopyable  {
+
+public: // types
+
+    class Error : public Exception {
+    public:
+        Error(const std::string& what, size_t line = 0);
+    };
+
+public: // methods
+
+    StreamParser(std::istream& in, bool comments = false, const char* comment = "#");
+
+    char peek(bool spaces = false);
+    char next(bool spaces = false);
+
+    void consume(char);
+    void consume(const char*);
+
+    void expect(const char*);
+
+protected: // members
+
+    size_t line_;
+
+private: // members
+
+    std::istream& in_;
+
+    bool comments_;
+    std::set<char> comment_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/parser/StringTools.cc b/eckit/src/eckit/parser/StringTools.cc
new file mode 100644
index 0000000..662bfd6
--- /dev/null
+++ b/eckit/src/eckit/parser/StringTools.cc
@@ -0,0 +1,229 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstring>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+std::string StringTools::substitute(const std::string& s,const std::map<std::string,std::string>& m)
+{
+    std::string result;
+    size_t len = s.length();
+    bool var = false;
+    std::string word;
+    std::map<std::string,std::string>::const_iterator j;
+
+    for(size_t i = 0; i < len; i++)
+    {
+        switch(s[i])
+        {
+            case '{':
+                if(var) {
+                    std::ostringstream os;
+                    os << "StringTools::substitute: unexpected { found in " <<s << " at position " << i;
+                    throw UserError(os.str());
+                }
+                var = true;
+                word = "";
+                break;
+
+            case '}':
+                if(!var) {
+                    std::ostringstream os;
+                    os << "StringTools::substitute: unexpected } found in " <<s << " at position " << i;
+                    throw UserError(os.str());
+                }
+                var = false;
+
+                j = m.find(word);
+                if(j == m.end()) {
+                    std::ostringstream os;
+                    os << "StringTools::substitute: cannot find a value for '" << word << "' in " <<s << " at position " << i;
+                    throw UserError(os.str());
+                }
+                result += (*j).second;
+                break;
+
+            default:
+                if(var)
+                    word += s[i];
+                else
+                    result += s[i];
+                break;
+
+        }
+    }
+    if(var) {
+        std::ostringstream os;
+        os << "StringTools::substitute: missing } in " << s;
+        throw UserError(os.str());
+    }
+    return result;
+}
+
+std::vector<std::string> StringTools::substituteVariables(const std::string & s)
+{
+    std::vector<std::string> result;
+    size_t len = s.length();
+    bool var = false;
+    std::string word;
+
+    for(size_t i = 0; i < len; i++)
+    {
+        switch(s[i])
+        {
+            case '{':
+                if(var) {
+                    std::ostringstream os;
+                    os << "StringTools::substituteVariables: unexpected { found in " <<s << " at position " << i;
+                    throw UserError(os.str());
+                }
+                var = true;
+                word = "";
+                break;
+
+            case '}':
+                if(!var) {
+                    std::ostringstream os;
+                    os << "StringTools::substituteVariables: unexpected } found in " <<s << " at position " << i;
+                    throw UserError(os.str());
+                }
+                var = false;
+                result.push_back(word);
+                break;
+
+            default:
+                if(var)
+                    word += s[i];
+                break;
+
+        }
+    }
+    if(var) {
+        std::ostringstream os;
+        os << "StringTools::substituteVariables: missing } in " << s;
+        throw UserError(os.str());
+    }
+    return result;
+}
+
+std::string StringTools::upper(const std::string& v)
+{
+	std::string r = v;
+	transform(r.begin(), r.end(), r.begin(), static_cast < int(*)(int) > (toupper));
+	return r;
+}
+
+std::string StringTools::lower(const std::string& v)
+{
+	std::string r = v;
+	transform(r.begin(), r.end(), r.begin(), static_cast < int(*)(int) > (tolower));
+	return r;
+}
+
+std::string StringTools::trim(const std::string& str)
+{
+    return trim(str," \t\n");
+}
+
+std::string StringTools::trim(const std::string& str, const std::string& chars)
+{
+    size_t startpos = str.find_first_not_of(chars);
+    size_t endpos = str.find_last_not_of(chars);
+
+    if((std::string::npos == startpos) || (std::string::npos == endpos))
+        return "";
+    else
+        return str.substr(startpos, endpos - startpos + 1);
+}
+
+std::string StringTools::front_trim(const std::string& str)
+{
+    return front_trim(str," \t");
+}
+
+std::string StringTools::front_trim(const std::string& str, const std::string& chars)
+{
+    size_t startpos = str.find_first_not_of(chars);
+
+    if(std::string::npos == startpos)
+        return "";
+    else
+        return str.substr( startpos );
+}
+
+std::string StringTools::back_trim(const std::string& str)
+{
+    return back_trim(str," \t");
+}
+
+std::string StringTools::back_trim(const std::string& str, const std::string& chars)
+{
+    size_t endpos = str.find_last_not_of(chars);
+
+    if( std::string::npos == endpos )
+        return "";
+    else
+        return str.substr( 0, endpos + 1 );
+}
+
+std::vector<std::string> StringTools::split(const std::string &delim, const std::string &text)
+{
+	std::vector<std::string> ss;
+	Tokenizer tokenizer(delim);
+	tokenizer(text, ss);
+	return ss;
+}
+
+std::string StringTools::join(const std::string &delimiter,
+                             std::vector<std::string>::const_iterator begin,
+                             std::vector<std::string>::const_iterator end) {
+	std::string r;
+    std::vector<std::string>::const_iterator j = begin;
+    size_t i = 0;
+	for (; j != end; ++j, ++i)
+	{
+		if (i > 0)
+			r += delimiter;
+		r += *j;
+	}
+    return r;
+}
+
+std::string StringTools::join(const std::string &delimiter, const std::vector<std::string>& words)
+{
+    return join(delimiter, words.begin(), words.end());
+}
+
+bool StringTools::startsWith(const std::string& str, const std::string& substr)
+{
+    if( ! substr.size() || str.size() < substr.size() )
+        return false;
+
+    for( std::string::size_type i = 0; i < substr.size();  ++i )
+        if( substr[i] != str[i] )
+            return false;
+
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/parser/StringTools.h b/eckit/src/eckit/parser/StringTools.h
new file mode 100644
index 0000000..8d2b261
--- /dev/null
+++ b/eckit/src/eckit/parser/StringTools.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   StringTools.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_StringTools_h
+#define eckit_StringTools_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class StringTools : private NonCopyable {
+public:
+
+    static std::string substitute(const std::string&, const std::map<std::string, std::string>&);
+    static std::vector<std::string>  substituteVariables( const std::string& );
+
+	static std::string upper(const std::string &);
+	static std::string lower(const std::string &);
+
+    static std::string trim(const std::string &);
+    static std::string trim(const std::string &, const std::string &);
+
+    static std::string front_trim(const std::string &);
+    static std::string front_trim(const std::string &, const std::string &);
+
+    static std::string back_trim(const std::string &);
+    static std::string back_trim(const std::string &, const std::string &);
+
+    static std::vector<std::string> split(const std::string& delim, const std::string& text);
+	static std::string join(const std::string &, const std::vector<std::string>&);
+    static std::string join(const std::string &, std::vector<std::string>::const_iterator begin,
+                                                 std::vector<std::string>::const_iterator end);
+
+    static bool startsWith( const std::string& str, const std::string& substr );
+
+private:
+
+    StringTools(); // Non instantiable
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/parser/Tokenizer.cc b/eckit/src/eckit/parser/Tokenizer.cc
new file mode 100644
index 0000000..b03c980
--- /dev/null
+++ b/eckit/src/eckit/parser/Tokenizer.cc
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iterator>
+
+#include "eckit/parser/Tokenizer.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template < class Container >
+void tokenizeInsert(const std::set<char, std::less<char> >& separator,
+                    const std::string& raw,
+                    std::insert_iterator<Container> ins,
+                    bool keepEmpty )
+{
+    int    index  = 0;
+	int    length = raw.length();
+	std::string token  = "";
+
+	while(index < length)
+	{
+		char c = raw[index];
+
+		if( separator.find(c) != separator.end() )
+		{
+            if(token.length()>0 || keepEmpty) {
+				ins = token;
+            }
+            token = "";
+		}
+		else
+			token += c;
+
+		index++;
+	}
+
+    if(token.length()>0 || keepEmpty) {
+		ins = token;
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+Tokenizer::Tokenizer(char c, bool keepEmpty) :
+    keepEmpty_(keepEmpty)
+{
+	separator_.insert(c);
+}
+
+Tokenizer::Tokenizer(const std::string& separators, bool keepEmpty) :
+    keepEmpty_(keepEmpty)
+{
+    for(std::string::size_type i=0; i<separators.length(); i++)
+		separator_.insert(separators[i]);
+}
+
+Tokenizer::~Tokenizer()
+{
+}
+
+void Tokenizer::operator()(const std::string& raw, std::vector<std::string>& v) const
+{
+    tokenizeInsert( separator_, raw, std::inserter(v, v.end()), keepEmpty_);
+}
+
+void Tokenizer::operator()(std::istream& in, std::vector<std::string>& v) const
+{
+	std::string raw;
+	char c;
+
+	while(in.get(c) && c != EOF && c != '\n')
+		raw += c;
+
+    tokenizeInsert( separator_, raw, std::inserter(v, v.end()), keepEmpty_);
+}
+
+void Tokenizer::operator()(const std::string& raw, std::set<std::string>& s) const
+{
+    tokenizeInsert( separator_, raw, std::inserter(s, s.end()), keepEmpty_);
+}
+
+void Tokenizer::operator()(std::istream& in, std::set<std::string>& s) const
+{
+	std::string raw;
+	char c;
+
+	while(in.get(c) && c != EOF && c != '\n')
+		raw += c;
+
+    tokenizeInsert( separator_, raw, std::inserter(s, s.end()), keepEmpty_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/parser/Tokenizer.h b/eckit/src/eckit/parser/Tokenizer.h
new file mode 100644
index 0000000..b245cd9
--- /dev/null
+++ b/eckit/src/eckit/parser/Tokenizer.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Tokenizer.h
+// Manuel Fuentes - ECMWF Jan 97
+
+#ifndef eckit_Tokenizer_h
+#define eckit_Tokenizer_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Tokenizer : private NonCopyable {
+
+public: // methods
+
+    /// Contructor
+
+    Tokenizer(char, bool keepEmpty = false);
+    Tokenizer(const std::string&, bool keepEmpty = false);
+
+    /// Destructor
+
+	~Tokenizer();
+
+    void operator()(const std::string&, std::vector<std::string>&) const;
+    void operator()(std::istream&, std::vector<std::string>&) const;
+
+    void operator()(const std::string&, std::set<std::string>&) const;
+    void operator()(std::istream&, std::set<std::string>&) const;
+
+private: // members
+
+    std::set<char,std::less<char> > separator_;     // To make searching faster
+
+    bool keepEmpty_;
+
+private: // methods
+
+	void print(std::ostream&) const;
+
+	friend std::ostream& operator<<(std::ostream& s,const Tokenizer& p) { p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+
+#endif
diff --git a/eckit/src/eckit/persist/DumpLoad.cc b/eckit/src/eckit/persist/DumpLoad.cc
new file mode 100644
index 0000000..42575b4
--- /dev/null
+++ b/eckit/src/eckit/persist/DumpLoad.cc
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/persist/DumpLoad.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+DumpLoad::DumpLoad()
+{
+}
+
+DumpLoad::~DumpLoad()
+{
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/persist/DumpLoad.h b/eckit/src/eckit/persist/DumpLoad.h
new file mode 100644
index 0000000..db244f2
--- /dev/null
+++ b/eckit/src/eckit/persist/DumpLoad.h
@@ -0,0 +1,214 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DumpLoad.h
+// Baudouin Raoult - ECMWF Sep 99
+
+#ifndef eckit_DumpLoad_h
+#define eckit_DumpLoad_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	DumpLoad();
+
+// -- Destructor
+
+	virtual ~DumpLoad();
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+
+// -- Methods
+
+    virtual void beginObject(const std::string&) = 0;
+	virtual void endObject() = 0;
+
+	virtual void nullObject() = 0;
+
+    virtual std::string nextObject() = 0;
+	virtual void doneObject() = 0;
+
+	virtual void reset() = 0;
+
+
+	// --------------------
+    virtual void load(std::string&) = 0;
+
+	virtual void load(float&) = 0;
+	virtual void load(double&) = 0;
+
+	virtual void load(int&) = 0;
+	virtual void load(unsigned int&) = 0;
+
+	virtual void load(long&) = 0;
+	virtual void load(unsigned long&) = 0;
+
+	virtual void load(long long&) = 0;
+	virtual void load(unsigned long long&) = 0;
+
+	virtual void load(char&) = 0;
+	virtual void load(unsigned char&) = 0;
+
+
+	// -- Dump
+
+
+    virtual void dump(const std::string&) = 0;
+
+	virtual void dump(float) = 0;
+	virtual void dump(double) = 0;
+
+	virtual void dump(int) = 0;
+	virtual void dump(unsigned int) = 0;
+
+	virtual void dump(long) = 0;
+	virtual void dump(unsigned long) = 0;
+
+	virtual void dump(long long) = 0;
+	virtual void dump(unsigned long long) = 0;
+
+	virtual void dump(char) = 0;
+	virtual void dump(unsigned char) = 0;
+
+
+	// --
+
+    virtual void push(const std::string&,const std::string&) = 0;
+    virtual std::string get(const std::string&) = 0;
+    virtual void pop(const std::string&) = 0;
+
+	// -- Overridden methods
+		// None
+
+	// -- Class members
+		// None
+
+
+// -- Class methods
+	// None
+
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+
+	// void print(std::ostream&) const; 
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	DumpLoad(const DumpLoad&);
+	DumpLoad& operator=(const DumpLoad&);
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const DumpLoad& p)
+	//	{ p.print(s); return s; }
+
+};
+
+//-- Load
+
+template<class T>
+inline void load(DumpLoad& a,T& b)                  { b.load(a); }
+
+inline void load(DumpLoad& a,std::string& b)        { a.load(b); }
+
+inline void load(DumpLoad& a,float& b)              { a.load(b); }
+inline void load(DumpLoad& a,double& b)             { a.load(b); }
+
+inline void load(DumpLoad& a,int& b)                { a.load(b); }
+inline void load(DumpLoad& a,unsigned int& b)       { a.load(b); }
+
+inline void load(DumpLoad& a,long& b)               { a.load(b); }
+inline void load(DumpLoad& a,unsigned long& b)      { a.load(b); }
+
+inline void load(DumpLoad& a,long long& b)          { a.load(b); }
+inline void load(DumpLoad& a,unsigned long long& b) { a.load(b); }
+
+inline void load(DumpLoad& a,char& b)               { a.load(b); }
+inline void load(DumpLoad& a,unsigned char& b)      { a.load(b); }
+
+
+// -- Dump
+
+template<class T>
+inline void dump(DumpLoad& a,const T& b)           { b.dump(a); }
+
+inline void dump(DumpLoad& a,const std::string& b)      { a.dump(b); }
+
+inline void dump(DumpLoad& a,float b)              { a.dump(b); }
+inline void dump(DumpLoad& a,double b)             { a.dump(b); }
+
+inline void dump(DumpLoad& a,int b)                { a.dump(b); }
+inline void dump(DumpLoad& a,unsigned int b)       { a.dump(b); }
+
+inline void dump(DumpLoad& a,long b)               { a.dump(b); }
+inline void dump(DumpLoad& a,unsigned long b)      { a.dump(b); }
+
+inline void dump(DumpLoad& a,long long b)          { a.dump(b); }
+inline void dump(DumpLoad& a,unsigned long long b) { a.dump(b); }
+
+inline void dump(DumpLoad& a,char b)               { a.dump(b); }
+inline void dump(DumpLoad& a,unsigned char b)      { a.dump(b); }
+
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/persist/Exporter.cc b/eckit/src/eckit/persist/Exporter.cc
new file mode 100644
index 0000000..054438d
--- /dev/null
+++ b/eckit/src/eckit/persist/Exporter.cc
@@ -0,0 +1,766 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/persist/Exporter.h"
+
+//-----------------------------------------------------------------------------
+
+void *operator new(size_t, void* addr, eckit::Evolve&)
+{
+    return addr;
+}
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void _export(eckit::Exporter& out, unsigned char what) { 
+    out.writeUnsigned(what);
+} 
+
+void _export(eckit::Exporter& out, char what) { 
+    out.writeSigned(what);
+}
+
+void _export(eckit::Exporter& out, unsigned int what) { 
+    out.writeUnsigned(what);
+} 
+void _export(eckit::Exporter& out, int what) { 
+    out.writeSigned(what);
+}
+
+void _export(eckit::Exporter& out, unsigned long what) { 
+    out.writeUnsigned(what);
+} 
+
+void _export(eckit::Exporter& out, long what) { 
+    out.writeSigned(what);
+}
+
+void _export(eckit::Exporter& out, unsigned short what) { 
+    out.writeUnsigned(what);
+} 
+
+void _export(eckit::Exporter& out, short what) { 
+    out.writeSigned(what);
+}
+
+void _export(eckit::Exporter& out, unsigned long long what) { 
+    out.writeUnsigned(what);
+} 
+
+void _export(eckit::Exporter& out, long long what) { 
+    out.writeSigned(what);
+}
+
+void _export(eckit::Exporter& out, double what) { 
+    out.writeDouble(what);
+}
+
+//-----------------------------------------------------------------------------
+
+const int MAX_STRING_LEN = 10240;
+
+enum {
+    
+    TAG_EOF          = 'X',
+
+    TAG_START_CLASS  = 'C',
+    TAG_END_CLASS    = 'c',
+    TAG_START_OBJECT = 'O',
+    TAG_END_OBJECT   = 'o',
+    TAG_START_MEMBER = 'M',
+    TAG_END_MEMBER   = 'm',
+
+    TAG_START_SUBOBJECT = 'L',
+    TAG_END_SUBOBJECT   = 'l',
+
+    TAG_UNSIGNED     = 'u',
+    TAG_SIGNED       = 's',
+    TAG_STRING       = 'S',
+    TAG_DOUBLE       = 'D',
+
+    TAG_START_SCHEMAS   = '{',
+    TAG_END_SCHEMAS   = '}',
+
+    TAG_START_DATABASE  = '[',
+    TAG_END_DATABASE    = ']'
+
+};
+
+template<class T> struct Swap {
+
+    static const int half = sizeof(T) >> 1;
+    static const int last = sizeof(T) - 1;
+    T operator()(T v)
+    {
+        unsigned char *p = (unsigned char*)&v;
+        for(int i = 0; i < half ; i++) std::swap(p[i],p[last-i]);
+        return v;
+    }
+};
+
+// xlc needs this,
+// others will complain about double initialization
+#if defined( __xlC__ )
+template<class T> const int Swap<T>::half = sizeof(T) >> 1;
+template<class T> const int Swap<T>::last = sizeof(T) - 1;
+#endif
+
+class Endian {
+    public:
+
+#ifdef EC_LITTLE_ENDIAN
+        template<class T> static T transform(T x)  { return Swap<T>()(x); }
+#else
+        template<class T> static T transform(T x)  { return x; }
+#endif
+
+};
+
+Exporter::Exporter(DataHandle& handle):
+    handle_(handle),
+    type_(0),
+    location_(0),
+    objectId_(0),
+    objectCount_(0),
+    subCount_(0),
+    inObject_(false)
+{
+}
+
+Exporter::~Exporter()
+{
+}
+
+void Exporter::writeTag(char tag)
+{
+    ASSERT(sizeof(tag) == 1);
+    ASSERT(handle_.write(&tag,1) == 1);
+}
+
+char Exporter::readTag()
+{
+    char tag;
+    ASSERT(sizeof(tag) == 1);
+    ASSERT(handle_.read(&tag,1) == 1);
+    return tag;
+}
+
+
+void Exporter::writeString(const std::string &s)
+{
+    size_t len = s.length();
+    char buffer[MAX_STRING_LEN];
+
+    if(len) {
+        ASSERT(sizeof(s[0]) == 1);
+        ASSERT(len <= sizeof(buffer));
+    }
+
+    writeTag(TAG_STRING);
+    writeUnsigned(len);
+    for(size_t i = 0;  i < len; i++) 
+        buffer[i] = s[i];
+
+    ASSERT( (size_t) handle_.write(buffer,len) == len);
+}
+
+void Exporter::writeString(const char* s)
+{
+    size_t len = strlen(s);
+
+    writeTag(TAG_STRING);
+    writeUnsigned(len);
+	ASSERT(sizeof(s[0]) == 1);
+
+    ASSERT( (size_t) handle_.write(s,len) == len);
+}
+
+std::string Exporter::_readString()
+{
+    try {
+        std::string s;
+
+        size_t len = readUnsigned();
+        for( size_t i = 0;  i < len; i++) {
+            char c;
+            ASSERT(handle_.read(&c,1) == 1);
+            s += c;
+        }
+        return s;
+    }
+    catch(...)
+    {
+        std::cout << "Error reading std::string " << *this << std::endl;
+        throw;
+    }
+}
+
+std::string Exporter::readString()
+{
+    ASSERT(readTag() == TAG_STRING);
+    return _readString();
+}
+
+void Exporter::writeDouble(double d) {
+    writeTag(TAG_DOUBLE);
+    ASSERT(sizeof(double) == 8);
+    d = Endian::transform(d);
+    ASSERT(handle_.write(&d,sizeof(d)) == sizeof(d));
+}
+
+double Exporter::_readDouble() {
+    double d;
+    ASSERT(sizeof(double) == 8);
+    ASSERT(handle_.read(&d,sizeof(d)) == sizeof(d));
+    return Endian::transform(d);
+}
+
+double Exporter::readDouble() {
+    ASSERT(readTag() == TAG_DOUBLE);
+    return _readDouble();
+}
+
+void Exporter::writeSigned(long long d) {
+    writeTag(TAG_SIGNED);
+    ASSERT(sizeof(long long) == 8);
+    d = Endian::transform(d);
+    ASSERT(handle_.write(&d,sizeof(d)) == sizeof(d));
+}
+
+long long Exporter::_readSigned() {
+    long long d;
+    ASSERT(sizeof(long long) == 8);
+    ASSERT(handle_.read(&d,sizeof(d)) == sizeof(d));
+    return Endian::transform(d);
+}
+
+long long Exporter::readSigned() {
+    ASSERT(readTag() == TAG_SIGNED);
+    return _readSigned();
+}
+
+void Exporter::writeUnsigned(unsigned long long d) {
+    writeTag(TAG_UNSIGNED);
+    ASSERT(sizeof(unsigned long long) == 8);
+    d = Endian::transform(d);
+    ASSERT(handle_.write(&d,sizeof(d)) == sizeof(d));
+}
+
+unsigned long long Exporter::_readUnsigned() {
+    unsigned long long d;
+    ASSERT(sizeof(unsigned long long) == 8);
+    ASSERT(handle_.read(&d,sizeof(d)) == sizeof(d));
+    return Endian::transform(d);
+}
+
+unsigned long long Exporter::readUnsigned() {
+    ASSERT(readTag() == TAG_UNSIGNED);
+    return _readUnsigned();
+}
+
+void _startClass(eckit::Exporter& out, const std::string& name) {
+    out.writeTag(TAG_START_CLASS);
+    out.writeString(name);
+}
+
+void _endClass(eckit::Exporter& out, const std::string& name) {
+    out.writeTag(TAG_END_CLASS);
+    //out.writeString(name);
+}
+
+void _startClass(eckit::Exporter& out, const char* name) {
+    out.writeTag(TAG_START_CLASS);
+    out.writeString(name);
+}
+
+void _endClass(eckit::Exporter& out, const char* name) {
+    out.writeTag(TAG_END_CLASS);
+    //out.writeString(name);
+}
+
+void _startMember(eckit::Exporter& out, const char* name) {
+    out.writeTag(TAG_START_MEMBER);
+    out.writeString(name);
+}
+
+void _endMember(eckit::Exporter& out, const char* name) {
+    out.writeTag(TAG_END_MEMBER);
+    //out.writeString(name);
+}
+
+void Exporter::startObject(unsigned long long type, unsigned long long location, unsigned long long id, size_t count) {
+    writeTag(TAG_START_OBJECT);
+    writeUnsigned(type);
+    writeUnsigned(location);
+    writeUnsigned(id);
+    writeUnsigned(count);
+    objectCount_++;
+    ASSERT(!inObject_);
+    inObject_ = true;
+    subCount_ = 0;
+}
+
+void _startObject(eckit::Exporter& e, unsigned long long type, unsigned long long location, unsigned long long id, size_t count) 
+{
+    e.startObject(type, location, id, count);
+}
+
+void Exporter::endObject(unsigned long long type, unsigned long long location, unsigned long long id, size_t count) {
+    writeTag(TAG_END_OBJECT);
+    ASSERT(inObject_);
+    inObject_ = false;
+    ASSERT(subCount_);
+
+
+    /*
+       writeUnsigned(type);
+       writeUnsigned(location);
+       writeUnsigned(id);
+       writeUnsigned(count);
+       */
+}
+
+
+void Exporter::startSubObject()
+{
+    writeTag(TAG_START_SUBOBJECT);
+    subCount_++;
+}
+
+void _startSubObject(eckit::Exporter& e)
+{
+    e.startSubObject();
+}
+
+void Exporter::endSubObject()
+{
+    writeTag(TAG_END_SUBOBJECT);
+}
+
+void _endSubObject(eckit::Exporter& e)
+{
+    e.endSubObject();
+}
+
+void _endObject(eckit::Exporter& e, unsigned long long type, unsigned long long location, unsigned long long id, size_t count) 
+{
+    e.endObject(type, location, id, count);
+}
+
+void Exporter::endObject() {
+    ASSERT(readTag() == TAG_END_OBJECT);
+    ASSERT(subCount_);
+    for(std::map<std::string,Datatype>::iterator j = members_.begin(); j != members_.end(); ++j)
+        if(!(*j).second.used())
+            std::cout << "WARNING NOT USED [" << (*j).first << "]" << std::endl;
+
+    members_.clear();
+    stack_.clear();
+}
+
+bool Exporter::nextDatabase(std::string& name,unsigned long long& id, unsigned long long& count)
+{
+    char tag = readTag();
+    if(tag == TAG_EOF) {
+        return false;
+    }
+
+    if(tag != TAG_START_DATABASE) {
+        std::cout << "tag " << int(tag) << std::endl;
+        std::cout << "tag " << tag << std::endl;
+    }
+
+    ASSERT(tag == TAG_START_DATABASE);
+
+    if(tag == TAG_START_DATABASE) {
+        name  = readString();
+        id    = readUnsigned();
+        count = readUnsigned();
+    }
+    return true; // should only exit with (tag == TAG_START_DATABASE)
+}
+
+size_t Exporter::nextObject() {
+    char tag = readTag();
+
+    if(tag == TAG_END_DATABASE) {
+        unsigned long long objectCount = readUnsigned();
+        std::cout << "objectCount " << objectCount << " " << objectCount_ << std::endl;
+        ASSERT(objectCount == objectCount_);
+        return 0;
+    }
+
+	if(tag != TAG_START_OBJECT) {
+        std::cout << tag << std::endl;
+
+	}
+
+    ASSERT(tag == TAG_START_OBJECT);
+    objectCount_++;
+    subCount_ = 0;
+    stack_.clear();
+
+    try {
+
+        type_     = readUnsigned();
+        location_ = readUnsigned();
+        objectId_ = readUnsigned();
+        return     readUnsigned(); // count
+
+    }
+    catch(...) {
+        std::cout << "ERROR reading start object " <<  *this << std::endl; 
+        throw;
+    }   
+
+}
+
+void _nextSubObject(eckit::Exporter& e)
+{
+    e.nextSubObject();
+}
+
+std::string Exporter::path() const {
+    std::string s;
+    for(std::vector<std::string>::const_iterator j = stack_.begin(); j != stack_.end(); ++j) {
+        if(s.length()) s += ".";
+        s += (*j);
+    }
+    return s;
+}
+
+void Exporter::nextSubObject() {
+    ASSERT(readTag() == TAG_START_SUBOBJECT);
+    subCount_ ++;
+
+    for(std::map<std::string,Datatype>::iterator j = members_.begin(); j != members_.end(); ++j)
+        if(!(*j).second.used())
+            std::cout << "WARNING NOT USED [" << (*j).first << "]" << std::endl;
+
+    members_.clear();
+
+    std::string s;
+    for(;;) {
+        char tag = readTag();
+        switch(tag) {
+
+            case TAG_START_CLASS:
+                s = readString();
+                //cout << s << std::endl;
+                //stack_.push_back(s.substr(0,s.find('<')));
+				stack_.push_back(s);
+                //stack_.push_back(s);
+                break;
+
+            case TAG_START_MEMBER:
+                stack_.push_back(readString());
+                break;
+
+            case TAG_END_MEMBER:
+                stack_.pop_back();
+                break;
+
+            case TAG_END_CLASS:
+                stack_.pop_back();
+                break;
+
+            case TAG_UNSIGNED:
+                {
+                    std::string p = path();
+                    Datatype& x = members_[p];
+                    ASSERT(x.empty());
+                    x = Datatype(_readUnsigned());
+                    //cout << "read [" << p << "] = " << x << std::endl;
+                }
+                break;
+
+            case TAG_SIGNED:
+                {
+                    std::string p = path();
+                    Datatype& x = members_[p];
+                    ASSERT(x.empty());
+                    x = Datatype(_readSigned());
+                    //cout << "read [" << p << "] = " << x << std::endl;
+                }
+                break;
+
+            case TAG_DOUBLE:
+                {
+                    std::string p = path();
+                    Datatype& x = members_[p];
+                    ASSERT(x.empty());
+                    x = Datatype(_readDouble());
+                    //cout << "read [" << p << "] = " << x << std::endl;
+                }
+                break;
+
+            case TAG_END_SUBOBJECT:
+                return;
+
+            default:
+                std::cout << tag << std::endl;
+                ASSERT(1 == 0);
+                break;
+        }
+    }
+}
+
+double Exporter::getDoubleMember(const std::string& name)
+{
+    std::map<std::string,Datatype>::iterator j = members_.find(name);
+    if(j != members_.end())
+    {
+        //cout << "consume [" << name << "] = " << (*j).second << std::endl;
+        return (*j).second;
+    }
+    std::cout << name << " not found" << std::endl;
+    return 0;
+}
+
+long long Exporter::getSignedMember(const std::string& name)
+{
+    std::map<std::string,Datatype>::iterator j = members_.find(name);
+    if(j != members_.end())
+    {
+        //cout << "consume [" << name << "] = " << (*j).second << std::endl;
+        return (*j).second;
+    }
+    std::cout << name << " not found" << std::endl;
+    return 0;
+}
+
+unsigned long long Exporter::getUnsignedMember(const std::string& name)
+{
+    std::map<std::string,Datatype>::iterator j = members_.find(name);
+    if(j != members_.end())
+    {
+        //cout << "consume [" << name << "] = " << (*j).second << std::endl;
+        return (*j).second;
+    }
+    std::cout << name << " not found" << std::endl;
+    return 0;
+}
+
+Evolve::Evolve(eckit::Exporter& e):
+    e_(e),
+    parent_(0)
+{
+}
+
+Evolve::Evolve(Evolve* e, char const* klass, char const* name):
+    e_(e->e_),
+    path_(e->path()),
+    parent_(e)
+{
+    if(path_.length()) path_ += ".";
+    path_ += klass;
+
+    if(name) {
+        path_ += ".";
+        path_ += name;
+    }
+}
+
+
+Evolve Evolve::operator()(char const* klass, char const* name) {
+    return Evolve(this,klass,name);
+}
+
+Evolve::operator unsigned char()
+{
+    return e_.getUnsignedMember(path_);
+}
+
+Evolve::operator unsigned int()
+{
+    return e_.getUnsignedMember(path_);
+}
+
+Evolve::operator int()
+{
+    return e_.getSignedMember(path_);
+}
+
+Evolve::operator char()
+{
+    return e_.getSignedMember(path_);
+}
+
+Evolve::operator unsigned short()
+{
+    return e_.getUnsignedMember(path_);
+}
+
+Evolve::operator unsigned long()
+{
+    return e_.getUnsignedMember(path_);
+}
+
+Evolve::operator long long()
+{
+    return e_.getSignedMember(path_);
+}
+
+Evolve::operator unsigned long long()
+{
+    return e_.getUnsignedMember(path_);
+}
+
+Evolve::operator double()
+{
+    return e_.getDoubleMember(path_);
+}
+
+Evolve::operator long()
+{
+    return e_.getSignedMember(path_);
+}
+
+Exporter::Datatype::Datatype():
+    type_(0),
+    used_(false),
+    double_(0),
+    signed_(0),
+    unsigned_(0)
+{
+}
+
+Exporter::Datatype::Datatype(double d):
+    type_(TAG_DOUBLE),
+    used_(false),
+    double_(d),
+    signed_(0),
+    unsigned_(0)
+{
+}
+
+Exporter::Datatype::Datatype(long long d):
+    type_(TAG_SIGNED),
+    used_(false),
+    double_(0),
+    signed_(d),
+    unsigned_(0)
+{
+}
+
+Exporter::Datatype::Datatype(unsigned long long d):
+    type_(TAG_UNSIGNED),
+    used_(false),
+    double_(0),
+    signed_(0),
+    unsigned_(d)
+{
+}
+
+Exporter::Datatype::operator long long() {
+    ASSERT(type_ == TAG_SIGNED);
+    ASSERT(!used_);
+    used_ = true;
+    return signed_;
+}
+
+Exporter::Datatype::operator unsigned long long() {
+    ASSERT(type_ == TAG_UNSIGNED);
+    ASSERT(!used_);
+    used_ = true;
+    return unsigned_;
+}
+
+Exporter::Datatype::operator double() {
+    ASSERT(type_ == TAG_DOUBLE);
+    ASSERT(!used_);
+    used_ = true;
+    return double_;
+}
+
+
+void Exporter::Datatype::print(std::ostream& out) const
+{
+    switch(type_) {
+        case TAG_SIGNED:   out << "S(" << signed_; break; 
+        case TAG_UNSIGNED: out << "U(" << unsigned_; break; 
+        case TAG_DOUBLE:   out << "D(" << double_; break; 
+        default: out << "X("; break;
+    }
+
+    out <<"," << (used_?"used":"new") << ")";
+}
+
+#define X(a) out << " " << #a << "=" << a;
+
+void Exporter::print(std::ostream& out) const
+{ 
+    out << "Exporter[";
+
+    X(objectCount_);
+    X(subCount_);
+    X(type_);
+    X(location_);
+    X(objectId_);
+    X(inObject_);
+    X(path());
+
+    out << "]";
+}
+
+void Exporter::openForWrite()
+{
+    handle().openForWrite(0);
+}
+
+void Exporter::close()
+{
+    writeTag(TAG_EOF);
+    handle().close();
+}
+
+void Exporter::startSchemas()
+{
+    writeTag(TAG_START_SCHEMAS);
+}
+
+void Exporter::endSchemas()
+{
+    writeTag(TAG_END_SCHEMAS);
+}
+
+void Exporter::startDatabase(const std::string& path, unsigned long id, unsigned long  long count)
+{
+    PathName home("~");
+    std::string p = path;
+
+    if(p.find(home) == 0) {
+         p = std::string("~/") + p.substr(std::string(home).length());
+    }
+
+    writeTag(TAG_START_DATABASE);
+    writeString(p);
+    writeUnsigned(id);
+    writeUnsigned(count);
+
+}
+
+void Exporter::endDatabase(const std::string&, unsigned long id)
+{
+    writeTag(TAG_END_DATABASE);
+    writeUnsigned(objectCount_);
+    objectCount_ = 0;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/persist/Exporter.h b/eckit/src/eckit/persist/Exporter.h
new file mode 100644
index 0000000..c3ab294
--- /dev/null
+++ b/eckit/src/eckit/persist/Exporter.h
@@ -0,0 +1,203 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Exporter.h
+// Baudouin Raoult - (c) ECMWF Oct 10
+
+#ifndef eckit_Exporter_h
+#define eckit_Exporter_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DataHandle;
+
+class Exporter : private NonCopyable {
+
+    class Datatype  {
+        char type_;
+        bool used_;
+        double double_;
+        long long signed_;
+        unsigned long long unsigned_;
+
+    public:
+        Datatype();
+        Datatype(double d);
+        Datatype(long long d);
+        Datatype(unsigned long long d);
+
+        operator double();
+        operator long long();
+        operator unsigned long long();
+
+        bool empty() const { return type_ == 0; }
+        bool used() const { return used_; }
+
+        void print(std::ostream& s) const;
+
+        friend std::ostream& operator<<(std::ostream& s,const Datatype& p) { p.print(s); return s; }
+    };
+
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	Exporter(DataHandle&);
+
+// -- Destructor
+
+	~Exporter(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+    void openForWrite();
+    void close();
+
+    DataHandle& handle() const { return handle_; }
+
+    void startSchemas();
+    void endSchemas();
+
+    void startDatabases();
+    void endDatabases();
+
+    void startDatabase(const std::string&, unsigned long, unsigned long long);
+    void endDatabase(const std::string&, unsigned long);
+    
+    void writeTag(char);
+    char readTag();
+
+    void writeSigned(long long);
+    void writeUnsigned(unsigned long long);
+    void writeString(const std::string&);
+    void writeString(const char*);
+    void writeDouble(double);
+
+    void dataBase(unsigned long long, const std::string&);
+    void typeInfo(unsigned long long, const std::string&);
+
+    long long readSigned();
+    unsigned long long readUnsigned();
+    std::string readString();
+    double readDouble();
+
+    bool nextDatabase(std::string& name,unsigned long long& id, unsigned long long& count);
+
+    size_t nextObject();
+    void endObject();
+    void nextSubObject();
+
+    unsigned long long type() const     { return type_; }
+    void type(unsigned long long  t)    { type_ = t; }
+
+    unsigned long long location() const { return location_; }
+    
+    unsigned long long objectId() const { return objectId_; }
+
+
+    void startObject(unsigned long long, unsigned long long, unsigned long long, size_t);
+    void endObject(unsigned long long, unsigned long long, unsigned long long, size_t);
+
+    void startSubObject();
+    void endSubObject();
+
+    unsigned long long getUnsignedMember(const std::string&);
+    long long          getSignedMember(const std::string&);
+    double             getDoubleMember(const std::string&);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private: // members
+
+    DataHandle& handle_;
+    unsigned long long type_;
+    unsigned long long location_;
+    unsigned long long objectId_;
+
+    unsigned long long objectCount_;
+    unsigned long long subCount_;
+
+    std::vector<std::string> stack_;
+    std::map<std::string,Datatype> members_;
+
+    bool inObject_;
+
+// -- Methods
+    long long _readSigned();
+    unsigned long long _readUnsigned();
+    std::string _readString();
+    double _readDouble();
+
+    std::string path() const;
+	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const Exporter& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/persist/Isa.cc b/eckit/src/eckit/persist/Isa.cc
new file mode 100644
index 0000000..c588f39
--- /dev/null
+++ b/eckit/src/eckit/persist/Isa.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static std::map<std::string,Isa*> *map_ = 0;
+
+void Isa::add(TypeInfo* t,const std::string& s)
+{
+	Isa* i = new Isa(t,get(s));
+//	std::cout << "add isa " << s << " for " << t << std::endl;
+	(*map_)[s] = i;
+}
+
+Isa* Isa::get(const std::string& s)
+{
+	if(map_ == 0) map_ = new std::map<std::string,Isa*>;
+	std::map<std::string,Isa*>::iterator j = map_->find(s);
+	return (j == map_->end()) ? (Isa*)0 : (Isa*)(*j).second;
+}
+
+void _describe(std::ostream& s,int depth,int what)                { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,unsigned int what)       { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,short what)              { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,bool what)               { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,unsigned short what)     { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,long what)               { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,long long what)          { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,unsigned long long what) { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,unsigned long what)      { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,char what)               { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,unsigned char what)      { s << what << std::endl; }
+void _describe(std::ostream& s,int depth,double what)             { s << what << std::endl; }
+
+void _startClass(std::ostream& s,int depth,const std::string& name)
+{
+	for(int i = 0; i < depth; i++) s << " ";
+	s << name;
+	s << "{" << std::endl;
+}
+
+void _endClass(std::ostream& s,int depth,const std::string& name)
+{
+	for(int i = 0; i < depth; i++) s << " ";
+	s << "}" << std::endl;
+}
+
+void _startMember(std::ostream& s,int depth,const std::string& name)
+{
+	for(int i = 0; i < depth; i++) s << " ";
+	s << name << ": ";
+}
+
+void _endMember(std::ostream& s,int depth,const std::string& name)
+{
+	//s << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/runtime/Application.cc b/eckit/src/eckit/runtime/Application.cc
new file mode 100644
index 0000000..fc44eea
--- /dev/null
+++ b/eckit/src/eckit/runtime/Application.cc
@@ -0,0 +1,188 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+#include <unistd.h>
+
+#include "eckit/bases/Loader.h"
+#include "eckit/os/Semaphore.h"
+#include "eckit/config/Resource.h"
+#include "eckit/runtime/Application.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/TimeStampTarget.h"
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+
+static char* reserve_ = 0;
+
+static void end(const char* msg) {
+    static bool in_end = false;
+
+    if (in_end) {
+        std::cout << "terminate called twice" << std::endl;
+        _exit(1);
+    }
+
+    in_end = true;
+
+    delete[] reserve_;
+    reserve_ = 0;
+
+    try {
+        throw;
+    } catch (std::exception& e) {
+        std::cout << "!!!!!!!!!!!!!!!!!! ";
+        std::cout << msg << " with the exception:" << std::endl;
+        std::cout << e.what() << std::endl;
+    }
+    _exit(1);
+}
+
+static void catch_terminate() {
+    end("Terminate was called");
+}
+
+static void catch_unexpected() {
+    end("Unexpected was called");
+}
+
+static void catch_new_handler() {
+    delete[] reserve_;
+    reserve_ = 0;
+    throw OutOfMemory();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Application& Application::instance()
+{
+    return static_cast<Application&>(Main::instance());
+}
+
+Application::Application(int argc, char** argv, const char* homeenv):
+    Main(argc, argv, homeenv),
+    running_(false)
+{
+    reserve_ = new char[20 * 1024]; // In case we runout of memeory
+    std::set_new_handler(&catch_new_handler);
+    std::set_terminate(&catch_terminate);
+    std::set_unexpected(&catch_unexpected);
+
+    Monitor::active(true);
+    Monitor::instance().startup();
+    taskID_ = Monitor::instance().self();
+
+    Loader::callAll(&Loader::execute);
+}
+
+
+Application::~Application() {
+    Monitor::instance().shutdown();
+}
+
+//-----------------------------------------------------------------------------
+
+LogTarget* Application::createInfoLogTarget() const {
+    return new TimeStampTarget("(I)");
+}
+
+LogTarget* Application::createWarningLogTarget() const {
+    return new TimeStampTarget("(W)");
+}
+
+LogTarget* Application::createErrorLogTarget() const {
+    return new TimeStampTarget("(E)");
+}
+
+LogTarget* Application::createDebugLogTarget() const {
+    return new TimeStampTarget("(D)");
+}
+
+//-----------------------------------------------------------------------------
+
+void Application::start() {
+    int status = 0;
+
+    displayName_ = Resource<std::string>("-name", name_);
+
+    Log::info() << "** Start of " << displayName_ << " ** pid is " << getpid() << std::endl;
+
+    try {
+        Log::status() << "Running" << std::endl;
+        running_ = true;
+        run();
+        running_ = false;
+    } catch (std::exception& e) {
+        status = 1;
+        Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+        Log::error() << "** Exception is terminates " << displayName_ << std::endl;
+    }
+
+    Log::info() << "** End of " << displayName_ << " (" << argv(0) << ")  **" << std::endl;
+
+    ::exit(status);
+}
+
+
+
+void Application::stop() {
+    ::exit(0);  // or: throw Stop();
+}
+
+
+
+void Application::kill() {
+    ::exit(1);
+}
+
+
+
+void Application::terminate() {
+    stop();
+}
+
+
+
+void Application::unique() {
+    PathName lockFile("~/locks/" + name_);
+
+    if (!lockFile.exists()) lockFile.touch();
+
+    Semaphore* sem = new Semaphore(lockFile);
+
+    if (sem->test()) {
+        std::ifstream os(lockFile.localPath());
+        std::string s;
+        os >> s;
+        throw SeriousBug("Application " + name_ + " is already running as pid " + s);
+    }
+
+    sem->lock();  // OS should reset the semaphore
+
+    std::ofstream os(lockFile.localPath());
+    os << getpid();
+}
+
+
+time_t Application::uptime() {
+
+    Monitor::TaskArray& info = Monitor::instance().tasks();
+    time_t uptime = info[taskID_].start();
+    time_t now = ::time(0);
+
+    return now - uptime;
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/runtime/Application.h b/eckit/src/eckit/runtime/Application.h
new file mode 100644
index 0000000..01ecd5a
--- /dev/null
+++ b/eckit/src/eckit/runtime/Application.h
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   Application.h
+/// @date   May 1996
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @author Florian Rathgeber
+
+#ifndef eckit_Application_h
+#define eckit_Application_h
+
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Task.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Application : public Task, public Main {
+public:
+
+    static Application& instance();
+
+    // -- Contructors
+
+    Application(int argc, char **argv, const char* homeenv = 0);
+
+    // -- Destructor
+
+    virtual ~Application();
+
+    // -- Methods
+
+    virtual void run() = 0;
+
+    /// call this function once to make this application unique on this host
+    void unique();
+
+    // -- Overridden methods
+
+    // From Task
+
+    virtual void start();
+    virtual void stop();
+    virtual void kill();
+    virtual void wait() {}
+    virtual bool active() { return true; }
+
+    bool running() const { return running_; }
+
+    virtual void terminate();
+
+    time_t uptime();
+
+private: // members
+
+
+    bool   running_;
+
+    /// From Main
+
+    virtual LogTarget* createInfoLogTarget() const;
+    virtual LogTarget* createWarningLogTarget() const;
+    virtual LogTarget* createErrorLogTarget() const;
+    virtual LogTarget* createDebugLogTarget() const;
+
+
+    /// overriden from Configurable
+
+    virtual std::string kind() const  { return "Application"; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Dispatcher.h b/eckit/src/eckit/runtime/Dispatcher.h
new file mode 100644
index 0000000..cb293f7
--- /dev/null
+++ b/eckit/src/eckit/runtime/Dispatcher.h
@@ -0,0 +1,588 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Dispatcher.h
+// Baudouin Raoult - ECMWF Jul 96
+
+#ifndef eckit_Dispatcher_h
+#define eckit_Dispatcher_h
+
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/config/Configurable.h"
+#include "eckit/config/Resource.h"
+#include "eckit/log/Log.h"
+#include "eckit/log/Plural.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/runtime/Dispatcher.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/Monitorable.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------
+
+class JSON;
+
+template<class Request>
+struct DefaultHandler {
+	// Pick first request from queue and put it in result queue
+	void pick(std::list<Request*>&,std::vector<Request*>&);
+	// No-op
+	void idle() {}
+	// Print string representation of request to stream
+	static void print(std::ostream&,const Request&);
+	// Emit JSON representation of request
+	static void json(JSON&,const Request&);
+};
+
+template<class Request>
+struct DequeuePicker {
+	/// Destructor
+	virtual ~DequeuePicker() {}
+	/// Pick a request from the queue
+	virtual void pick(std::list<Request*>&) = 0;
+};
+
+//=======================================
+// A 'T' must have a default constructor a operator()(R*)
+//
+
+template<class Traits>
+class Dispatcher : public Configurable  {
+public:
+
+	typedef typename Traits::Request   Request;
+	typedef typename Traits::Handler   Handler;
+
+// -- Contructors
+
+	Dispatcher(const std::string& name = Traits::name(), int numberOfThreads = 1);
+
+// -- Destructor
+
+	~Dispatcher();
+
+// -- Operators
+
+	// Push a new Request in the queue
+	// (Dispatcher takes ownership of Request)
+	void push(Request*);
+
+	// Push a vector of Requests in the queue
+	void push(const std::vector<Request*>&);
+
+	// Process a request from the queue
+	bool next(Handler&,std::vector<Request*>&,Mutex&);
+
+	// Remove a request from the queue without processing
+	void dequeue(DequeuePicker<Request>&);
+
+	// Sleep for 0 seconds and yield
+	void sleep();
+
+	// Sleep for a given number of seconds and yield
+	void sleep(int);
+
+	// Signal all sleeping threads
+	void awake();
+
+	// Get the queue size
+	int  size() const;
+
+	// Increment the running state by given delta
+	void running(long);
+
+	// Get the running state
+	long running() const;
+
+	// Serialise requests in queue as JSON array
+	void json(JSON&) const;
+
+	// From Configurable
+
+	virtual std::string name() const  { return name_; }
+
+protected:
+
+// -- Members
+
+	// Request queue
+	std::list<Request*>    queue_;
+	// Maximum number of threads (if set to 0, a new thread is
+	// created for each request pushed into the queue)
+	Resource<long>    numberOfThreads_;
+	// Number of currently running threads
+	long              count_;
+	// Counter for thread ids
+	long              next_;
+	// Mutex protecting queue_
+	mutable MutexCond ready_;
+	// Mutex for thread sleep/wake handling
+	MutexCond         wait_;
+	// Dispatcher name
+	std::string       name_;
+	// Do we dynamically grow the number of threads?
+	bool           	  grow_;
+	// Number of active threads (i.e. processing work)
+	long              running_;
+
+	mutable Mutex     lock_;
+
+private:
+
+// No copy allowed
+
+	Dispatcher(const Dispatcher<Traits>&);
+	Dispatcher<Traits>& operator=(const Dispatcher<Traits>&);
+
+	// -- Methods
+
+	void print(std::ostream&) const;
+	void changeThreadCount(int delta);
+	void _push(Request*);
+
+	// From Configurable
+
+	virtual std::string kind() const  { return "Dispatcher"; }
+	virtual void reconfigure();
+
+	friend std::ostream& operator<<(std::ostream& s,const Dispatcher<Traits>& p)
+	{ p.print(s); return s; }
+};
+
+//-----------------------------------------------------------------
+
+// Worker thread (managed by ThreadController)
+
+template<class Traits>
+class DispatchTask : public Thread, public Monitorable {
+public:
+
+	typedef typename Traits::Handler Handler;
+	typedef typename Traits::Request Request;
+
+	// -- Contructors
+
+	DispatchTask(Dispatcher<Traits>&  owner,int id):
+		Thread(false), // Don't delete object when stopped
+		owner_(owner), id_(id) {}
+
+private:
+
+// -- Members
+
+	// Dispatcher owning this thread
+	Dispatcher<Traits>& owner_;
+	// Worker thread id
+	int                 id_;
+	// Requests to be handled by this thread
+	std::vector<Request*>    pick_;
+	// Mutex protecting the queue
+	Mutex               mutex_;
+
+// -- Overridden methods
+
+	// From Thread
+
+	virtual void run();
+
+	// From Monitorable
+
+	virtual void status(std::ostream&) const;
+	virtual void json(JSON&) const;
+
+
+// No copy
+
+	typedef class DispatchTask<Traits> DT;
+	DispatchTask(const DT&);
+	void operator=(const DT&);
+};
+
+//-----------------------------------------------------------------
+
+// Dispatcher thread (managed by ThreadController)
+
+template<class Traits>
+class DispatchInfo : public Thread {
+	// Dispatcher owning this thread
+	Dispatcher<Traits>& owner_;
+	virtual void run();
+public:
+	DispatchInfo(Dispatcher<Traits>& owner):
+		owner_(owner) {}
+};
+
+//=================================================================
+
+template<class Traits>
+void DispatchTask<Traits>::status(std::ostream& s) const
+{
+	AutoLock<Mutex> lock(((DispatchTask<Traits>*)this)->mutex_);
+	s << "Picks for " <<  owner_.name() << " thread " << id_ << std::endl;
+    for(size_t i = 0; i < pick_.size(); i++)
+        if(pick_[i]) Handler::print(s,*pick_[i]);
+}
+
+template<class Traits>
+void DispatchTask<Traits>::json(JSON& s) const
+{
+	AutoLock<Mutex> lock(((DispatchTask<Traits>*)this)->mutex_);
+    int n = 0;
+    for(size_t i = 0; i < pick_.size(); i++)
+        if(pick_[i]) n++;
+
+    if(n) {
+    s.startObject();
+	s <<  "name" << owner_.name();
+    s <<  "id"   << id_ ;
+    s << "picks";
+    s.startList();
+    for(size_t i = 0; i < pick_.size(); i++)
+        if(pick_[i]) Handler::json(s,*pick_[i]);
+    s.endList();
+    s.endObject();
+    }
+}
+
+template<class Traits>
+void DispatchTask<Traits>::run()
+{
+	Log::info() << "Start of " << owner_.name() << " thread " << id_ << std::endl;
+
+	Monitor::instance().name(owner_.name());
+
+	Handler handler;
+
+	while(!stopped())
+	{
+
+		bool stop = owner_.next(handler,pick_,mutex_);
+
+		if(stop) // The thread must stop
+		{
+			Log::info() << "Stopping thread " << id_ << std::endl;
+			break;
+		}
+
+		owner_.running(1);
+		try {
+			if(pick_.size())
+			{
+				handler.handle(pick_);
+				owner_.awake(); // Request is finished
+			}
+			else {
+				handler.idle();
+				owner_.sleep(); // Wait for something else to come...
+			}
+		}
+		catch(std::exception& e)
+		{
+			Log::error() << "** " << e.what() << " Caught in " <<
+				Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+			owner_.awake();
+		}
+		owner_.running(-1);
+
+		AutoLock<Mutex> lock(mutex_);
+
+		for(typename std::vector<Request*>::iterator i = pick_.begin(); i != pick_.end(); ++i)
+		{
+			delete (*i);
+			*i = 0;
+		}
+	}
+
+	owner_.awake();
+	Log::info() << "End of thread " << id_ << std::endl;
+}
+
+//=================================================================
+
+template<class Traits>
+void DispatchInfo<Traits>::run()
+{
+	Monitor::instance().name(owner_.name());
+	for(;;)
+	{
+		int n = owner_.size();
+		Monitor::instance().show(n>0);
+		Log::status() << Plural(n,"request") << " queued" << std::endl;
+		::sleep(1);
+		/* owner_.sleep(3); */
+		/* owner_.awake(); // Awake others */
+	}
+}
+
+//=================================================================
+
+template<class Traits>
+Dispatcher<Traits>::Dispatcher(const std::string& name, int numberOfThreads):
+	// Maximum number of threads defined on the command line or
+	// in config file or default to the argument value
+	numberOfThreads_(this,
+                   "-numberOfThreads;numberOfThreads",
+                   numberOfThreads),
+	count_(0),
+	next_(0),
+    name_(name),
+    // Dynamically grow number of threads if set to 0
+    grow_(numberOfThreads_ == 0),
+    running_(0)
+{
+	// For some reason xlC require that
+	typedef class DispatchInfo<Traits> DI;
+
+	ThreadControler c(new DI(*this));
+	c.start();
+
+	// Spin up appropriate number of threads
+	changeThreadCount(numberOfThreads_);
+}
+
+
+template<class Traits>
+Dispatcher<Traits>::~Dispatcher()
+{
+}
+
+template<class Traits>
+long Dispatcher<Traits>::running() const
+{
+	if(grow_) {
+		AutoLock<Mutex> lock(lock_);
+		return running_;
+	}
+	return 0;
+}
+
+template<class Traits>
+void Dispatcher<Traits>::running(long delta)
+{
+	if(grow_) {
+		AutoLock<Mutex> lock(lock_);
+		running_ += delta;
+		ASSERT(running_ >= 0);
+	}
+}
+
+template<class Traits>
+void Dispatcher<Traits>::push(Request* r)
+{
+	if(!r) return;
+
+	if(grow_)
+	{
+		int cnt = running();
+		if(cnt == count_) changeThreadCount(1);
+	}
+
+	_push(r);
+}
+
+template<class Traits>
+void Dispatcher<Traits>::_push(Request* r)
+{
+
+	{
+		AutoLock<MutexCond> lock(ready_);
+		queue_.push_back(r); // enqueue Request
+		ready_.signal();
+	}
+	awake();
+}
+
+template<class Traits>
+void Dispatcher<Traits>::sleep()
+{
+	//Log::debug() << "Sleeping..." << std::endl;
+	AutoLock<MutexCond> lock(wait_);
+	wait_.wait();
+	//Log::debug() << "Awaken..." << std::endl;
+}
+
+template<class Traits>
+void Dispatcher<Traits>::sleep(int n)
+{
+	AutoLock<MutexCond> lock(wait_);
+	wait_.wait(n);
+}
+
+template<class Traits>
+void Dispatcher<Traits>::awake()
+{
+	Log::debug() << "Awake ..." << std::endl;
+	AutoLock<MutexCond> lock(wait_);
+	wait_.broadcast();
+}
+
+template<class Traits>
+int Dispatcher<Traits>::size() const
+{
+	AutoLock<MutexCond> lock(ready_);
+	return queue_.size();
+}
+
+template<class Traits>
+void Dispatcher<Traits>::print(std::ostream& s) const
+{
+	AutoLock<MutexCond> lock(ready_);
+    for(typename std::list<Request*>::const_iterator i = queue_.begin() ;
+		i != queue_.end(); ++i)
+			if(*i)
+			Handler::print(s,*(*i));
+}
+
+template<class Traits>
+void Dispatcher<Traits>::json(JSON& s) const
+{
+	AutoLock<MutexCond> lock(ready_);
+    s.startList();
+    for(typename std::list<Request*>::const_iterator i = queue_.begin() ;
+		i != queue_.end(); ++i)
+			if(*i)
+			Handler::json(s,*(*i));
+    s.endList();
+}
+
+template<class Traits>
+bool Dispatcher<Traits>::next(Handler& handler,
+	std::vector<Request*>& result,Mutex& mutex)
+{
+	Log::status() << "-" << std::endl;
+
+	AutoLock<MutexCond> lock(ready_);
+
+	bool stop = false;
+
+	while(queue_.empty())
+		ready_.wait();
+
+	AutoLock<Mutex> lock2(mutex); // Lock std::vector
+
+	result.clear();
+
+	// Check for null requests, which serve as thread stop signal
+
+	typename std::list<Request*>::iterator i = std::find(queue_.begin(),queue_.end(),
+		(Request*)0);
+
+	// No null requests found, pick next task from the queue
+	if(i == queue_.end())
+		handler.pick(queue_,result);
+	// A null request was found
+	else {
+		// Clear the null request and stop the thread
+		queue_.erase(i);
+		stop = true;
+	}
+
+	Log::debug() << "Got " << result.size() << " requests from the queue"
+		<< std::endl;
+
+	Log::debug() << "Left " << queue_.size() << " requests in the queue"
+		<< std::endl;
+
+	ready_.signal(); // Let other threads get some more
+
+	if(result.size())
+		Log::status() << "Processing request" << std::endl;
+
+	return stop;
+}
+
+template<class Traits>
+void Dispatcher<Traits>::dequeue(DequeuePicker<Request>& p)
+{
+	AutoLock<MutexCond> lock(ready_);
+	p.pick(queue_);
+	ready_.signal();
+}
+
+template<class Traits>
+void Dispatcher<Traits>::changeThreadCount(int delta)
+{
+	// We do not take a lock, to avoid dead-locks
+	typedef class DispatchTask<Traits> DT;
+
+	// Increase number of threads by starting some more
+	if(delta > 0)
+	{
+		for(int i = 0; i < delta ; i++)
+		{
+			ThreadControler c(new DT(*this,next_++));
+			c.start();
+		}
+	}
+
+	// Decrease number of threads by pushing null requests in
+	// the queue, which will cause threads to stop
+	if(delta < 0)
+	{
+		for(int i = 0; i < -delta ; i++)
+		{
+			_push(0);
+		}
+	}
+
+	if(delta) {
+		AutoLock<Mutex> lock(lock_);
+		count_   += delta;
+	}
+}
+
+template<class Traits>
+void Dispatcher<Traits>::reconfigure()
+{
+	Log::info() << "Reconfiguring maximum thread number to: " << numberOfThreads_ << std::endl;
+	changeThreadCount(numberOfThreads_ - count_);
+	awake();
+}
+
+//=================================================================
+
+template<class Request>
+void DefaultHandler<Request>::pick(std::list<Request*>& queue,
+	std::vector<Request*>& result)
+{
+	Request *r = queue.front();
+	queue.pop_front();
+	result.push_back(r);
+}
+
+template<class Request>
+void DefaultHandler<Request>::print(std::ostream& s,const Request& r)
+{
+	s << r << std::endl;
+}
+
+template<class Request>
+void DefaultHandler<Request>::json(JSON& s,const Request& r)
+{
+	r.json(s);
+}
+
+//-----------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Library.cc b/eckit/src/eckit/runtime/Library.cc
new file mode 100644
index 0000000..e1ec8a2
--- /dev/null
+++ b/eckit/src/eckit/runtime/Library.cc
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// #include <stdlib.h>
+
+#include "eckit/runtime/Library.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Channel.h"
+#include "eckit/log/OStreamTarget.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Library::Library(int argc, char **argv, const char* homeenv) :
+    Main(argc, argv, homeenv)
+{
+}
+
+Library::~Library()
+{
+}
+
+Channel* Library::createChannel() const  {
+    return new Channel(new OStreamTarget(std::cout));
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/Library.h b/eckit/src/eckit/runtime/Library.h
new file mode 100644
index 0000000..f972fca
--- /dev/null
+++ b/eckit/src/eckit/runtime/Library.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Sep 2012
+
+#ifndef eckit_Library_h
+#define eckit_Library_h
+
+#include "eckit/runtime/Main.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Library : public Main {
+
+public: // methods
+
+    /// Contructors
+
+    Library(int argc, char **argv, const char* homeenv = 0);
+
+    /// Destructor
+
+	virtual ~Library();
+
+protected:
+
+    virtual Channel* createChannel() const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Main.cc b/eckit/src/eckit/runtime/Main.cc
new file mode 100644
index 0000000..13ec75e
--- /dev/null
+++ b/eckit/src/eckit/runtime/Main.cc
@@ -0,0 +1,186 @@
+/*
+* (C) Copyright 1996-2017 ECMWF.
+*
+* This software is licensed under the terms of the Apache Licence Version 2.0
+* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+* In applying this licence, ECMWF does not waive the privileges and immunities
+* granted to it by virtue of its status as an intergovernmental organisation nor
+* does it submit to any jurisdiction.
+*/
+
+#include <unistd.h>
+
+#include "eckit/bases/Loader.h"
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/OStreamTarget.h"
+#include "eckit/os/BackTrace.h"
+#include "eckit/runtime/Library.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/system/SystemInfo.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/StaticMutex.h"
+#include "eckit/utils/Translator.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static StaticMutex local_mutex;
+static Main* instance_ = 0;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Main::Main(int argc, char** argv, const char* homeenv) :
+    taskID_(-1),
+    argc_(argc),
+    argv_(argv),
+    home_("/"),
+    debug_(false) {
+
+    if (instance_) {
+        std::cerr << "Attempting to create a new instance of Main()" << std::endl;
+        std::cerr << BackTrace::dump() << std::endl;
+        _exit(1);
+    }
+
+
+    if (::getenv("DEBUG")) {
+        debug_ = eckit::Translator<std::string, bool>()(::getenv("DEBUG"));
+    }
+
+    for (size_t i = 1; i < size_t(argc); ++i) {
+
+        // Old style
+        if (::strcmp(argv[i], "-debug")==0) {
+            debug_ = true;
+        }
+
+        // New style
+        if (::strcmp(argv[i], "--debug")==0) {
+            debug_ = true;
+        }
+
+        // New style with variable
+        if (::strncmp(argv[i], "--debug=", 8)==0) {
+            debug_ = eckit::Translator<std::string, bool>()(argv[i] + 8);
+        }
+
+    }
+
+    ::srand(::getpid() + ::time(0));
+
+    name_ = displayName_ = PathName(argv[0]).baseName(false);
+
+    const char* home = homeenv ? ::getenv(homeenv) : 0;
+
+    if (home) {
+        home_ = home;
+    } else {
+        std::string execHome = eckit::system::SystemInfo::instance().executablePath().dirName().dirName();
+        home_ = execHome;
+    }
+
+    instance_ = this;
+
+    Loader::callAll(&Loader::execute);
+
+}
+
+Main::~Main() {
+    if (instance_ == 0) {
+        std::cerr << "Attempting to delete a non-existent instance of Main()" << std::endl;
+        std::cerr << BackTrace::dump() << std::endl;
+        _exit(1);
+    }
+
+    instance_ = 0;
+}
+
+Main& Main::instance() {
+    if (!instance_) {
+        std::cerr << "Attempting to access a non-existent instance of Main()" << std::endl;
+        std::cerr << BackTrace::dump() << std::endl;
+        _exit(1);
+    }
+    return *instance_;
+}
+
+
+int Main::argc() const {
+    return argc_;
+}
+
+std::string Main::argv(int n) const {
+    ASSERT(argc_ != 0 && argv_ != 0);  // check initialized
+    ASSERT(n < argc_ && n >= 0);       // check bounds
+    // ASSERT(argv_[n] != 0);             // check initialized??
+    return argv_[n] ? argv_[n] : "<undefined>";
+}
+
+char** Main::argv() const {
+    return argv_;
+}
+
+const std::string& Main::home() const {
+    return home_;
+}
+
+long Main::taskID() const {
+    return taskID_;
+}
+
+void Main::taskID(long id) {
+    taskID_ = id;
+}
+
+std::string Main::hostname()
+{
+    char hostname[256];
+    SYSCALL(::gethostname(hostname, sizeof(hostname)));
+    return hostname;
+}
+
+std::string Main::name() const {
+    return name_;
+}
+
+bool Main::ready() {
+    return instance_ != 0;
+}
+
+void Main::terminate() {
+    ::exit(0);
+}
+
+void Main::initialise(int argc, char** argv, const char* homeenv) {
+    AutoLock<StaticMutex> lock(local_mutex);
+    if (instance_ == 0) {
+        new Library(argc, argv, homeenv);
+    }
+}
+
+LogTarget* Main::createInfoLogTarget() const {
+    return createDefaultLogTarget();
+}
+
+LogTarget* Main::createWarningLogTarget() const {
+    return createDefaultLogTarget();
+}
+
+LogTarget* Main::createErrorLogTarget() const {
+    return createDefaultLogTarget();
+}
+
+LogTarget* Main::createDebugLogTarget() const {
+    return createDefaultLogTarget();
+}
+
+LogTarget* Main::createDefaultLogTarget() const  {
+    return new OStreamTarget(std::cout);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/runtime/Main.h b/eckit/src/eckit/runtime/Main.h
new file mode 100644
index 0000000..b8156d2
--- /dev/null
+++ b/eckit/src/eckit/runtime/Main.h
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_Main_h
+#define eckit_Main_h
+
+#include <map>
+
+#include "eckit/log/Channel.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef void (*abort_handler_t)();
+
+class LogStream;
+class PathName;
+
+class Main  : private NonCopyable {
+protected:
+
+    Main(int argc, char** argv, const char* homeenv = 0);
+    virtual ~Main();
+
+public: // methods
+
+    static Main& instance();
+
+    int argc() const;
+    char** argv() const;
+    std::string argv(int n) const;
+
+    const std::string& home() const;
+
+    virtual void terminate();
+
+    // Thread ID where the main is running
+    long taskID() const;
+    void taskID(long);
+
+    /// Platform agnostic hostname
+    static std::string hostname();
+
+    // From Configurable
+
+    virtual std::string name() const;
+
+    /// Ensure that there exits a Main object. This is to be used
+    /// For unit tests and fortran bindinds only
+    static void initialise(int argc, char** argv, const char* homeenv = 0);
+
+    // To be used before main() to check if the instance is ready
+    static bool ready();
+
+    /// Channel handling
+
+    virtual LogTarget* createInfoLogTarget() const;
+    virtual LogTarget* createWarningLogTarget() const;
+    virtual LogTarget* createErrorLogTarget() const;
+    virtual LogTarget* createDebugLogTarget() const;
+
+protected:
+
+    std::string name_;
+    std::string displayName_;  ///< name to be displayed of running application
+    long taskID_;
+
+    // To overrride
+
+    virtual LogTarget* createDefaultLogTarget() const;
+
+private: // members
+
+    int     argc_;
+    char**  argv_;
+
+    std::string  home_; ///< path to the home, may be redefined so not necessarily the same as environment variable HOME
+
+    friend class Log;
+    bool debug_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_Main_h
diff --git a/eckit/src/eckit/runtime/Monitor.cc b/eckit/src/eckit/runtime/Monitor.cc
new file mode 100644
index 0000000..65e05cc
--- /dev/null
+++ b/eckit/src/eckit/runtime/Monitor.cc
@@ -0,0 +1,450 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <pthread.h>
+#include <signal.h>
+
+#include "eckit/config/Resource.h"
+#include "eckit/container/MappedArray.h"
+#include "eckit/container/SharedMemArray.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/os/BackTrace.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/TaskInfo.h"
+#include "eckit/thread/AutoLock.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Monitor::TaskArray::~TaskArray() {}
+
+class MemoryMappedTaskArray : public Monitor::TaskArray {
+
+    virtual void sync() { map_.sync(); }
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual iterator begin()  { return map_.begin(); }
+    virtual iterator end()    { return map_.end(); }
+
+    virtual const_iterator begin() const   { return map_.begin(); }
+    virtual const_iterator end()   const   { return map_.end(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual TaskInfo& operator[](unsigned long n) { return map_[n]; }
+
+    MappedArray<TaskInfo> map_;
+
+public:
+
+    MemoryMappedTaskArray(const PathName& path, unsigned long size) :
+        TaskArray(),
+        map_(path, size)
+    {}
+};
+
+class SharedMemoryTaskArray : public Monitor::TaskArray {
+
+    virtual void sync() { map_.sync(); }
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual iterator begin()  { return map_.begin(); }
+    virtual iterator end()    { return map_.end(); }
+
+    virtual const_iterator begin() const   { return map_.begin(); }
+    virtual const_iterator end()   const   { return map_.end(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual TaskInfo& operator[](unsigned long n) { return map_[n]; }
+
+    SharedMemArray<TaskInfo> map_;
+
+public:
+
+    SharedMemoryTaskArray(const PathName& path, const std::string& name, unsigned long size) :
+        TaskArray(),
+        map_(path, name, size)
+    {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static bool active_ = false;
+
+static Monitor::TaskArray* mapArray = 0;
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+static void taskarray_init(void)
+{
+    std::string  monitor = Resource<std::string>("monitorPath","~/etc/monitor");
+    size_t       size    = Resource<size_t>("monitorSize", 1000);
+
+    std::string monitorArrayType = Resource<std::string>("monitorArrayType","MemoryMapped");
+
+    if(monitorArrayType == "MemoryMapped")
+        mapArray = new MemoryMappedTaskArray(monitor, size);
+    else if(monitorArrayType == "SharedMemory")
+        mapArray = new SharedMemoryTaskArray(monitor, "/etc-monitor", size);
+    else {
+        std::ostringstream oss;
+        oss << "Invalid monitorArrayType : " << monitorArrayType << ", valid types are 'MemoryMapped' and 'SharedMemory'" << std::endl;
+        throw eckit::BadParameter(oss.str(), Here());
+    }
+}
+
+Monitor::TaskArray& Monitor::tasks()
+{
+    pthread_once(&once, taskarray_init);
+	return *mapArray;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template class ThreadSingleton<Monitor>;
+
+Monitor& Monitor::instance()
+{
+    static ThreadSingleton<Monitor> monitor;
+    Monitor& m = monitor.instance();
+	if(!m.ready_) m.init();
+	return m;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Monitor::Monitor():
+   slot_(0),
+	ready_(false),
+	check_(false)
+{
+}
+
+void Monitor::init()
+{
+	if(ready_) return;
+
+    if(!active_) {  ready_ = true;  return; }  // this is for processes that don't use Monitor
+
+	if(check_) return; // safely ignore double initialization
+	check_ = true;
+
+	TaskArray& a = tasks();
+
+	AutoLock<TaskArray> lock(a);
+
+	int slot = hash() % a.size();
+
+	bool found = false;
+
+	for(bool check = false; !check && !found ; check = true)
+	{
+		for( size_t i = 0 ; i < a.size() && !found ; ++i )
+		{
+			slot_ = (slot+i) % a.size();
+			if(!a[slot_].busy(check))
+			{
+				new(&a[slot_]) TaskInfo();
+				found = true;
+			}
+		}
+	}
+
+    if(!found)
+    {
+        std::cout << "No free monitor slots" << std::endl;
+        std::cerr << "No free monitor slots" << std::endl;
+    }
+
+	// No free monitor slots
+
+	PANIC(!found);
+
+	check_ = false;
+	ready_ = true;
+}
+
+Monitor::~Monitor()
+{
+	if(ready_ && active_) {
+		TaskArray& a = tasks();
+		AutoLock<TaskArray> lock(a);
+		a[slot_].TaskInfo::~TaskInfo();
+    }
+}
+
+
+bool Monitor::active()
+{
+    return active_;
+}
+
+void Monitor::active( bool a )
+{
+    active_ = a;
+}
+
+void Monitor::startup()
+{
+    if(!ready_) init();
+}
+
+void Monitor::shutdown()
+{
+	if(!active_) return;
+
+	TaskArray& a = tasks();
+	AutoLock<TaskArray> lock(a);
+
+	pid_t pid = getpid();
+
+	for( size_t i = 0; i < a.size() ; ++i )
+		if(a[i].pid() == pid)
+			a[i].TaskInfo::~TaskInfo();
+
+	// should unmap
+}
+
+unsigned long Monitor::hash()
+{
+	return (((unsigned long)pthread_self() << 16) | (unsigned long)getpid());
+}
+
+TaskInfo* Monitor::task()
+{
+	if( !active_ || !ready_ )
+	{
+		static TaskInfo info;
+		return &info;
+	}
+
+    TaskArray& a = tasks();
+	return &a[slot_];
+}
+
+
+long Monitor::self()
+{
+	if(!active_) {
+        return Main::instance().taskID();
+    }
+	if(!ready_)  return -1;
+	return slot_;
+}
+
+void Monitor::out(char* from,char *to)
+{
+    if(!ready_) return;
+    task()->out(from,to);
+}
+
+void Monitor::name(const std::string& s)
+{
+    if(!ready_) return;
+	task()->name(s);
+}
+
+void Monitor::kind(const std::string& s)
+{
+    if(!ready_) return;
+	task()->kind(s);
+}
+
+void Monitor::progress(const std::string& name, unsigned long long min,unsigned long long max)
+{
+    if(!ready_) return;
+	task()->progressName(name);
+	task()->start(min,max);
+}
+
+void Monitor::progress(unsigned long long value)
+{
+    if(!ready_) return;
+	task()->progress(value);
+}
+
+void Monitor::progress()
+{
+    if(!ready_) return;
+	task()->done();
+}
+
+char Monitor::state(char c)
+{
+	char x = task()->state();
+	task()->state(c);
+	return x;
+}
+
+void Monitor::status(const std::string& msg)
+{
+	if(!ready_) return;
+	task()->status(msg);
+}
+
+std::string Monitor::status()
+{
+	if(!ready_) return std::string();
+	return task()->status();
+}
+
+void Monitor::message(const std::string& msg)
+{
+	if(!ready_) return;
+	task()->message(msg);
+}
+
+std::string Monitor::message()
+{
+	if(!ready_) return std::string();
+	return task()->message();
+}
+
+void Monitor::stoppable(bool b)
+{
+	task()->stoppable(b);
+}
+
+bool Monitor::stopped()
+{
+	return task()->stopped();
+}
+
+void Monitor::abort()
+{
+	task()->abort();
+}
+
+void Monitor::checkAbort()
+{
+	task()->checkAbort();
+}
+
+void Monitor::parent(long p)
+{
+	task()->parent(p);
+}
+
+
+std::string Monitor::statusTree()
+{
+    std::ostringstream os;
+    os << status();
+    int n = self();
+
+	TaskArray& p = tasks();
+
+	for( size_t j = 0 ; j < p.size(); ++j )
+		if((p[j].parent() == n) && p[j].show() && p[j].busy(true))
+		{
+            os << "|" << p[j].status();
+		}
+
+    return os.str();
+}
+
+void Monitor::start(const std::string& app)
+{
+	if(!active_) return;
+
+	TaskArray& p = tasks();
+
+	for( size_t j = 0 ; j < p.size(); ++j )
+		if(p[j].busy(true) && app == p[j].application() &&
+			p[j].depth() == 0)
+		{
+			Log::warning() << app << " is already running with a pid of "
+				<< p[j].pid() << std::endl;
+			Log::warning() << "Please stop it first" << std::endl;
+			return;
+		}
+
+	PathName cmd = Resource<PathName>("startCmd","~/admin/starter");
+
+	std::string s = std::string(cmd) + " " + app;
+
+	Log::info() << "Executing shell command: " << s << std::endl;
+
+	SYSCALL(::system(s.c_str()));
+
+}
+
+void Monitor::port(int p)
+{
+	task()->port(p);
+}
+
+int Monitor::port()
+{
+	return task()->port();
+}
+
+void Monitor::host(const std::string& h)
+{
+	task()->host(h);
+}
+
+std::string Monitor::host()
+{
+	return task()->host();
+}
+
+void Monitor::taskID(const TaskID& id)
+{
+	task()->taskID(id);
+}
+
+TaskID Monitor::taskID()
+{
+	return task()->taskID();
+}
+
+void Monitor::show(bool on)
+{
+	task()->show(on);
+}
+
+int Monitor::kill(const std::string& name, int sig)
+{
+
+	if(!active_) return 0;
+
+    Monitor::TaskArray& info = tasks();
+	pid_t      me   = ::getpid();
+	int        n    = 0;
+
+	// Name. Look for Unix process ID
+	for( size_t i = 0; i < info.size(); ++i )
+		if(info[i].busy(true) && info[i].application() == name)
+		{
+			pid_t pid = info[i].pid();
+			if(pid == me)
+				Log::info() << pid << ": Suicide avoided ;-)" << std::endl;
+			else
+			{
+				if(::kill(pid,sig))
+					Log::info() << Log::syserr << std::endl;
+				else
+				{
+					Log::info() << pid << ": Killed" << std::endl;
+					n++;
+				}
+			}
+		}
+
+	return n;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/Monitor.h b/eckit/src/eckit/runtime/Monitor.h
new file mode 100644
index 0000000..6d37847
--- /dev/null
+++ b/eckit/src/eckit/runtime/Monitor.h
@@ -0,0 +1,171 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @date   Jun 1996
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+#ifndef eckit_Monitor_h
+#define eckit_Monitor_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/runtime/TaskInfo.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/ThreadSingleton.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Monitor : private NonCopyable {
+
+public: // types
+
+    class TaskArray : private eckit::NonCopyable {
+
+    public:
+
+        typedef TaskInfo*       iterator;
+        typedef const TaskInfo* const_iterator;
+
+        virtual ~TaskArray();
+
+        virtual void sync() = 0;
+        virtual void lock() = 0;
+        virtual void unlock() = 0;
+
+        virtual iterator begin() = 0;
+        virtual iterator end() = 0;
+        virtual const_iterator begin() const = 0;
+        virtual const_iterator end()   const = 0;
+
+        virtual unsigned long size() = 0;
+        virtual TaskInfo& operator[](unsigned long n) = 0;
+    };
+
+public: // methods
+    
+    static Monitor& instance();    
+
+    static bool active();
+    static void active( bool a );
+    
+	void startup();
+	void shutdown();
+	void out(char*,char*); // called from Log
+
+    void name(const std::string&);
+	void kind(const std::string&);
+
+	void progress(const std::string&,unsigned long long,unsigned long long);
+	void progress(unsigned long long);
+	void progress();
+
+	char state(char);
+
+	void message(const std::string&);
+	std::string message();
+
+	void status(const std::string&);
+	std::string status();
+	std::string statusTree();
+	void stoppable(bool);
+	bool stopped();
+
+	TaskArray& tasks();
+	long       self();
+	void parent(long);
+
+	void start(const std::string&);
+	int  kill(const std::string& , int = 15);
+
+	void port(int );
+	int  port();
+
+	void    host(const std::string& );
+	std::string  host();
+
+	TaskID taskID();
+	void taskID(const TaskID&);
+
+	void show(bool);
+
+	void cancel(const std::string&);
+	std::string cancelMessage();
+	bool  canceled();
+
+	void abort();
+	void checkAbort();
+
+private: // members
+
+	unsigned long slot_;
+	bool ready_;
+	bool check_;
+
+private: // methods
+
+    /// Contructors
+
+	Monitor();
+
+    /// Destructor
+
+	~Monitor();
+
+	unsigned long hash();
+    
+    TaskInfo* task();
+
+	void init();
+
+    friend class ThreadSingleton<Monitor>;
+    friend struct NewAlloc0<Monitor>;
+    
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Wrap the setting of Monitor::instance().state
+
+class AutoState {
+	char old_;
+public:
+	AutoState(char c): 
+		old_(Monitor::instance().state(c)) {}
+
+	~AutoState()
+		{ Monitor::instance().state(old_); }
+
+	void set(char c) 
+		{ Monitor::instance().state(c); }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+class AutoLockTag {
+	AutoState state_;
+	AutoLock<T> lock_;
+public:
+    AutoLockTag(T& t): state_(t.tag()), lock_(t) 
+		{ state_.set(t.tag() - 'a' + 'A'); }
+    AutoLockTag(T* t): state_(t->tag()), lock_(t) 
+		{ state_.set(t->tag() - 'a' + 'A'); }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Monitorable.cc b/eckit/src/eckit/runtime/Monitorable.cc
new file mode 100644
index 0000000..00f1a48
--- /dev/null
+++ b/eckit/src/eckit/runtime/Monitorable.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/parser/JSON.h"
+#include "eckit/runtime/Monitorable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Monitorable::Monitorable():
+	ClassExtent<Monitorable>(this)
+{
+}
+
+Monitorable::~Monitorable()
+{
+}
+
+void Monitorable::status(std::ostream& s) const 
+{ 
+	print(s); 
+	s << std::endl; 
+}
+
+void Monitorable::print(std::ostream& s) const 
+{ 
+	s << "No print method" << std::endl;
+}
+
+void Monitorable::allStatuses(std::ostream& s)
+{
+	callAll(&Monitorable::status,s);
+}
+
+void Monitorable::allJSON(JSON& s)
+{
+    s.startList();
+	callAll(&Monitorable::json,s);
+    s.endList();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/Monitorable.h b/eckit/src/eckit/runtime/Monitorable.h
new file mode 100644
index 0000000..7a7230f
--- /dev/null
+++ b/eckit/src/eckit/runtime/Monitorable.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Monitorable.h
+// Baudouin Raoult - ECMWF Mar 97
+
+#ifndef eckit_Monitorable_h
+#define eckit_Monitorable_h
+
+#include "eckit/container/ClassExtent.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class JSON;
+
+class Monitorable : public ClassExtent<Monitorable> {
+public:
+
+	Monitorable();
+
+// -- Destructor
+
+	virtual ~Monitorable();
+
+// -- Methods
+
+	virtual void json(JSON&) const = 0;
+	virtual void status(std::ostream&) const;
+
+// -- Class methods
+
+	static void allStatuses(std::ostream&);
+	static void allJSON(JSON&);
+
+protected:
+
+// -- Methods
+	
+	virtual void print(std::ostream&) const;
+
+private:
+
+	friend std::ostream& operator<<(std::ostream& s,const Monitorable& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Pipe.h b/eckit/src/eckit/runtime/Pipe.h
new file mode 100644
index 0000000..283075f
--- /dev/null
+++ b/eckit/src/eckit/runtime/Pipe.h
@@ -0,0 +1,289 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_Pipe_h
+#define eckit_Pipe_h
+
+#include "eckit/eckit.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/CodeLocation.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/thread/ThreadControler.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class PAYLOAD>
+class PipeTask;
+
+template<class PAYLOAD>
+struct OnePayload;
+
+template<class PAYLOAD>
+class Pipe : private NonCopyable {
+public:
+
+    typedef void (*Proc)(Pipe<PAYLOAD>&);
+
+    // -- Contructors
+
+    Pipe(long count = 2);
+
+    // -- Destructor
+
+    ~Pipe();
+
+    // -- Methods
+
+    void spawn(Proc proc);
+
+    bool eof();
+    void close();
+
+    PAYLOAD& message();
+    PAYLOAD& receive();
+    void send();
+
+private:
+
+    bool   error();
+    void   error(const std::string&);
+
+    // -- Methods
+
+    // -- Members
+
+    Mutex  mutex_;
+
+    long   count_;
+
+    int ri_;
+    int wi_;
+    int pi_;
+
+
+    bool   done_;
+    bool   error_;
+    std::string why_;
+
+    OnePayload<PAYLOAD>* payloads_;
+    ThreadControler* controler_;
+
+    // -- Friends
+
+    friend class PipeTask<PAYLOAD>;
+
+};
+
+//-----------------------------------------------------------------------------
+
+template<class PAYLOAD>
+struct OnePayload {
+    MutexCond cond_;
+    bool      ready_;
+    bool       done_;
+    PAYLOAD   payload_;
+    OnePayload(): ready_(false), done_(false), payload_() {}
+};
+
+template<class PAYLOAD>
+class Pipe;
+
+template<class PAYLOAD>
+class PipeTask : public Thread {
+
+    typedef typename Pipe<PAYLOAD>::Proc Proc;
+
+    Pipe<PAYLOAD>&         owner_;
+    Proc&        consumer_;
+    OnePayload<PAYLOAD>*  payloads_;
+public:
+    PipeTask(Proc&,Pipe<PAYLOAD>&,OnePayload<PAYLOAD>*);
+    virtual void run();
+};
+
+
+template<class PAYLOAD>
+Pipe<PAYLOAD>::Pipe(long count):
+    count_(count),
+    ri_(0),
+    wi_(0),
+    pi_(-1),
+    done_(false),
+    error_(false),
+    payloads_(0),
+    controler_(0)
+{
+
+}
+template<class PAYLOAD>
+Pipe<PAYLOAD>::~Pipe()
+{
+    close();
+}
+
+template<class PAYLOAD>
+void Pipe<PAYLOAD>::close()
+{
+    if(controler_) {
+        {
+            AutoLock<Mutex> lock(mutex_);
+            done_ = true;
+        }
+        {
+            AutoLock<MutexCond> lock(payloads_[wi_].cond_);
+            payloads_[wi_].ready_ = true;
+            payloads_[wi_].cond_.signal();
+        }
+        controler_->wait();
+    }
+    delete[] payloads_;
+    controler_ = 0;
+    payloads_ = 0;
+}
+
+
+template<class PAYLOAD>
+inline void Pipe<PAYLOAD>::error(const std::string& why)
+{
+    AutoLock<Mutex> lock(mutex_);
+    error_ = true;
+    why_   = why;
+}
+
+template<class PAYLOAD>
+inline bool Pipe<PAYLOAD>::error()
+{
+    AutoLock<Mutex> lock(mutex_);
+    return error_;
+}
+
+
+template<class PAYLOAD>
+void Pipe<PAYLOAD>::spawn(Proc proc)
+{
+
+    payloads_ = new OnePayload<PAYLOAD>[count_];
+
+    error_   = false;
+
+    controler_ = new ThreadControler(new PipeTask<PAYLOAD>(proc, *this, payloads_), false);
+    controler_->start();
+
+}
+
+template<class PAYLOAD>
+PipeTask<PAYLOAD>::PipeTask(Proc &consumer,
+                            Pipe<PAYLOAD>& owner,
+                            OnePayload<PAYLOAD>* payloads):
+    Thread(false),
+    owner_(owner),
+    consumer_(consumer),
+    payloads_(payloads)
+{
+}
+
+template<class PAYLOAD>
+void PipeTask<PAYLOAD>::run()
+{
+    try {
+        consumer_(owner_);
+    }
+    catch(std::exception& e)
+    {
+        Log::error() << "** " << e.what() << " Caught in " <<
+                        Here() << std::endl;
+        Log::error() << "** Exception is handled" << std::endl;
+        owner_.error(e.what());
+    }
+}
+
+template<class PAYLOAD>
+bool Pipe<PAYLOAD>::eof()
+{
+    AutoLock<Mutex> lock(mutex_);
+    return done_;
+}
+
+template<class PAYLOAD>
+PAYLOAD& Pipe<PAYLOAD>::message()
+{
+    AutoLock<MutexCond> lock(payloads_[wi_].cond_);
+
+    while(payloads_[wi_].ready_)
+        payloads_[wi_].cond_.wait();
+
+    if(error()) {
+        AutoLock<Mutex> lck(mutex_);
+        done_  = true;
+        error_ = false;
+        throw SeriousBug(why_);
+    }
+
+    return payloads_[wi_].payload_;
+}
+
+template<class PAYLOAD>
+void Pipe<PAYLOAD>::send()
+{
+    AutoLock<MutexCond> lock(payloads_[wi_].cond_);
+
+    payloads_[wi_].ready_ = true;
+    payloads_[wi_].cond_.signal();
+
+    wi_++;
+    wi_ %= count_;
+}
+
+template<class PAYLOAD>
+PAYLOAD& Pipe<PAYLOAD>::receive()
+{
+
+    {
+        AutoLock<Mutex> lck(mutex_);
+        if(pi_ >= 0) {
+            AutoLock<MutexCond> lock(payloads_[pi_].cond_);
+            payloads_[pi_].ready_ = false;
+            payloads_[pi_].cond_.signal();
+        }
+    }
+
+    AutoLock<MutexCond> lock(payloads_[ri_].cond_);
+
+    while(!payloads_[ri_].ready_)
+        payloads_[ri_].cond_.wait();
+
+    if(error()) {
+        AutoLock<Mutex> lck(mutex_);
+        throw SeriousBug(why_);
+    }
+
+    pi_ = ri_;
+
+    PAYLOAD& p = payloads_[ri_].payload_;
+    ri_++;
+    ri_ %= count_;
+
+    return p;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/PipeApplication.cc b/eckit/src/eckit/runtime/PipeApplication.cc
new file mode 100644
index 0000000..91fc79f
--- /dev/null
+++ b/eckit/src/eckit/runtime/PipeApplication.cc
@@ -0,0 +1,240 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include "eckit/config/Resource.h"
+#include "eckit/io/Select.h"
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/PipeApplication.h"
+#include "eckit/serialisation/PipeStream.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/log/Seconds.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+PipeApplication::PipeApplication(int argc, char** argv, const char* homeenv):
+    Application(argc, argv, homeenv),
+    in_("-in", -1),
+    out_("-out", -1) {
+    // Establish relationship with 'parent' thread for the monitoring
+
+    long parent = Resource<long>("-parent", 0);
+    Monitor::instance().parent(parent);
+
+    // If we fork, avoid childs having the pipe open
+    SYSCALL(fcntl(in_,  F_SETFD, FD_CLOEXEC));
+    SYSCALL(fcntl(out_, F_SETFD, FD_CLOEXEC));
+}
+
+PipeApplication::~PipeApplication() {
+}
+
+void PipeApplication::run()
+{
+    long timeout = Resource<long>("selectTimeout", 10 * 60); // 10 Minutes
+
+    bool debug = false;
+
+    if (getenv("PIPE_DEBUG")) {
+        debug = true;
+        std::cout << "PIPE_DEBUG[" << name_ << "]" << std::endl;
+        if ( Application::name() == getenv("PIPE_DEBUG") ) {
+            std::cout << "debug me " << getpid() << std::endl;
+            ::sleep(10);
+
+            ::sleep(10);
+        }
+    }
+
+    long maxRequests = Resource<long>("maxRequests", 0);
+    long maxCpu      = Resource<long>("maxCpu", 0);
+    long maxUptime   = Resource<long>("maxUptime", 0);
+    long maxMemory   = Resource<long>("maxMemory", 0); // Given in MB
+    long maxSwaps    = Resource<long>("maxSwaps", 0);
+
+    PipeStream pipe(in_, out_);
+
+    init(pipe);
+
+    int count = 0;
+
+    for (;;) {
+        count++;
+
+        waiting();
+
+        Select select(in_);
+
+        if (!select.ready(timeout)) {
+            if (!debug) {
+                std::cerr << "Timeout, exiting -- PID " << ::getpid() << std::endl;
+                Log::warning() << "Timeout, exiting" << std::endl;
+                return;
+            }
+        }
+
+        try {
+            bool end_;
+            pipe >> end_; // End of batch marker
+            if (end_)
+                endBatch();
+            else
+                process(pipe);
+        } catch (std::exception& e) {
+            Log::error() << "** " << e.what() << " Caught in " <<
+                         Here() << std::endl;
+            Log::error() << "** Exception is re-thrown" << std::endl;
+            pipe << e;
+            throw;
+        }
+
+        if (debug)
+            continue;
+
+        struct rusage usage;
+        getrusage(RUSAGE_SELF, &usage);
+
+        time_t uptime = Application::uptime();
+
+        Log::info() << "End of request " << BigNum(count)
+                    << ", PID: "         << ::getpid()
+                    << ", Uptime: "      << Seconds(uptime)
+                    << ", CPU: "         << Seconds(usage.ru_utime.tv_sec + usage.ru_utime.tv_sec)
+                    << ", Memory: "      << Bytes(usage.ru_maxrss * 1024)
+                    << ", Swaps: "       << BigNum(usage.ru_nswap)
+                    << std::endl;
+
+        if (maxRequests && (count >= maxRequests)) {
+            Log::info() << "Maximum number of requests reached ("
+                        << BigNum(maxRequests)
+                        << "), exiting" << std::endl;
+            return;
+        }
+
+        if (maxUptime && (uptime >= maxUptime)) {
+            Log::info() << "Maximum uptime reached ("
+                        << Seconds(maxUptime)
+                        << "), exiting" << std::endl;
+            return;
+        }
+
+        if (maxCpu && (usage.ru_utime.tv_sec + usage.ru_utime.tv_sec >= maxCpu)) {
+            Log::info() << "Maximum CPU usage reached ("
+                        << Seconds(maxCpu)
+                        << "), exiting" << std::endl;
+            return;
+        }
+
+        if (maxMemory && (usage.ru_maxrss >= (maxMemory * 1024))) {
+            Log::info() << "Maximum memory usage reached ("
+                        << Bytes(usage.ru_maxrss * 1024)
+                        << " > "
+                        << Bytes(maxMemory * 1024 * 1024)
+                        << "), exiting" << std::endl;
+            return;
+        }
+
+        if (maxSwaps && (usage.ru_nswap >= maxSwaps)) {
+            Log::info() << "Maximum memory usage reached ("
+                        << BigNum(maxSwaps)
+                        << "), exiting" << std::endl;
+            return;
+        }
+
+// #define X(a) Log::info() << "Usage " << #a << ": " << usage.a << std::endl;
+#define X(a)
+        X(ru_utime.tv_sec);
+        X(ru_utime.tv_sec);
+        X(ru_maxrss);
+        X(ru_ixrss);
+        X(ru_idrss);
+        X(ru_isrss);
+        X(ru_minflt);
+        X(ru_majflt);
+        X(ru_nswap);
+        X(ru_inblock);
+        X(ru_oublock);
+        X(ru_msgsnd);
+        X(ru_msgrcv);
+        X(ru_nsignals);
+        X(ru_nvcsw);
+        X(ru_nivcsw);
+    }
+}
+
+
+void PipeApplication::endBatch() {
+}
+
+
+void PipeApplication::init(Stream&) {
+}
+
+void PipeApplication::launch(const std::string& name, int input, int output) {
+    char in [20]; snprintf(in, 20, "%d", input);
+    char out[20]; snprintf(out, 20, "%d", output);
+    char par[20]; snprintf(par, 20, "%ld", Monitor::instance().self());
+
+    PathName cmd = std::string("~/bin/") + name;
+
+    Log::debug() << "execlp(" << cmd.localPath() << ','
+                 << cmd.baseName().localPath() << ','
+                 << "-in," << in << ','
+                 << "-out," << out << ','
+                 << "-parent," << par << ")" << std::endl;
+
+#if 0
+    if (getenv("PIPE_DEBUG")) {
+        ::execlp(getenv("PIPE_DEBUG"), getenv("PIPE_DEBUG"),
+                 cmd.c_str(), "-in", in, "-out", out, "-parent", par, 0);
+
+        std::cerr << "Exec failed " << getenv("PIPE_DEBUG") << Log::syserr  << std::endl;
+        ::kill(getpid(), SIGTERM);
+    }
+    else
+#endif
+
+        char command[1024];
+    char basename[1024];
+
+    ASSERT(sizeof(command) - 1 > std::string(cmd).length());
+
+    snprintf(command, 1024, "%s", cmd.localPath());
+    snprintf(basename, 1024, "%s", cmd.baseName().localPath());
+
+    ::execlp(command, basename,
+             "-in", in,
+             "-out", out,
+             "-parent", par,
+             (void*)0);
+
+    std::cerr << "Exec failed " << cmd << Log::syserr << std::endl;
+
+    // exit is not enough: some destructor of ostore objects
+    // are waiting for some locks
+    _exit(1);
+}
+
+void PipeApplication::waiting() {
+    Log::status() << "-" << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/runtime/PipeApplication.h b/eckit/src/eckit/runtime/PipeApplication.h
new file mode 100644
index 0000000..2c8c9da
--- /dev/null
+++ b/eckit/src/eckit/runtime/PipeApplication.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file PipeApplication.h
+/// @author Baudouin Raoult
+/// @author Florian Rathgeber
+
+#ifndef eckit_PipeApplication_h
+#define eckit_PipeApplication_h
+
+#include "eckit/config/Resource.h"
+#include "eckit/runtime/Application.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+class Stream;
+
+//-----------------------------------------------------------------------------
+
+class PipeApplication : public Application {
+
+public: // methods
+
+    PipeApplication(int argc, char **argv, const char* homeenv);
+
+    virtual ~PipeApplication();
+
+    virtual void process(Stream&) = 0;
+    virtual void endBatch();
+    virtual void init(Stream&);
+    virtual void waiting();
+
+    static void launch(const std::string& name, int in, int out);
+
+private: // members
+
+    Resource<long> in_;
+    Resource<long> out_;
+
+    /// overridden from Application
+    virtual void run();
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/PipeHandler.cc b/eckit/src/eckit/runtime/PipeHandler.cc
new file mode 100644
index 0000000..b3eae09
--- /dev/null
+++ b/eckit/src/eckit/runtime/PipeHandler.cc
@@ -0,0 +1,331 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <signal.h>
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/PipeHandler.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/StaticMutex.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class Request>
+PipeHandler<Request>::PipeHandler():
+    ClassExtent<PipeHandler<Request> >(this),
+    pipe_(0),
+    busy_(false),
+    last_(0)
+{
+    Monitor::instance().show(false);
+}
+
+//=========================================================================
+
+template<class Request>
+PipeHandler<Request>::~PipeHandler()
+{
+    stop();
+    delete pipe_;
+}
+
+//=========================================================================
+
+template<class Request>
+void PipeHandler<Request>::send(Request* r)
+{
+
+    int retry = 0;
+    int max   = 10;
+
+    Monitor::instance().show(true);
+
+    for(;;)
+    {
+
+        if(!active())
+            start();
+
+        try {
+            Log::status() << "Sending request" << std::endl;
+            (*pipe_) << bool(false); // endBatch marker
+            r->send(*pipe_);
+            return;
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what()
+                         << " Caught in " << Here() << std::endl;
+            if(++retry >= max)
+            {
+                Log::error() << "** Exception is re-thrown" << std::endl;
+                throw;
+            }
+            else
+            {
+                Log::error() << "** Exception is handled" << std::endl;
+                stop();
+                ::sleep(5);
+            }
+        }
+
+    }
+
+}
+
+//=========================================================================
+
+template<class Request>
+void PipeHandler<Request>::receive(Request* r)
+{
+    Log::status() << "Waiting for " << Request::commandName() << std::endl;
+    r->reply(*pipe_);
+}
+
+//=========================================================================
+
+template<class Request>
+void PipeHandler<Request>::handle(const std::vector<Request*>& v)
+{
+
+    busy_ = true;
+
+    for(typename std::vector<Request*>::const_iterator i = v.begin();
+        i != v.end() ; ++i)
+    {
+        try {
+
+            Request *r = *i;
+
+            bool retry = false;
+            int  cnt   = 0;
+
+            do {
+
+                retry = false;
+
+                try {
+                    send(r);
+                    receive(r);
+                }
+                catch(std::exception& e)
+                {
+                    Log::error() << "** " << e.what() << " Caught in "
+                        << Here() << std::endl;
+                    Log::error() << "** Exception is handled" << std::endl;
+                    retry = r->error(e,++cnt);
+                }
+
+                if(retry)
+                    Log::debug() << "PipeHandler -> retry " << cnt << std::endl;
+
+            } while(retry);
+
+            Log::debug() << "PipeHandler done" << std::endl;
+            r->done();
+
+        }
+        catch(std::exception& e)
+        {
+			Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+        }
+    }
+
+    try {
+        // Send the end batch flag
+        *pipe_ << bool(true);
+    }
+    catch(std::exception& e)
+    {
+		Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+        Log::error() << "** Exception is ignored" << std::endl;
+    }
+
+    last_ = ::time(0);
+    busy_ = false;
+    Log::status() << "-" << std::endl;
+
+
+}
+
+template<class Request>
+void PipeHandler<Request>::idle()
+{
+    Monitor::instance().show(active());
+}
+
+//=========================================================================
+
+static StaticMutex PipeHandler_static_mutex;
+
+template<class Request>
+void PipeHandler<Request>::start()
+{
+
+    // Here we need a mutex, because we don't want
+    // to fork (ProcessControler::start) while an other
+    // thread is creating a pipe. The child process
+    // will then also has a file descriptor for this pipe
+
+    AutoLock<StaticMutex> lock(PipeHandler_static_mutex);
+    Log::debug() << "PipeHandler - Locked..." << std::endl;
+
+
+    delete pipe_;
+    pipe_ = new PipeStream();
+
+    ProcessControler::start();
+
+    // Here, we still are in the parent
+    pipe_->parentProcess();
+    Log::debug() << "PipeHandler - UnLocked..." << std::endl;
+}
+
+//=========================================================================
+
+template<class Request>
+void PipeHandler<Request>::stop()
+{
+    ProcessControler::stop();
+}
+
+//=========================================================================
+
+
+template<class Request>
+void PipeHandler<Request>::run()
+{
+    // Here, we should be in the child
+    pipe_->childProcess();
+
+
+    char in[20];  snprintf(in,20,"%d", pipe_->in());
+    char out[20]; snprintf(out,20,"%d",pipe_->out());
+    char par[20]; snprintf(par,20,"%ld",Monitor::instance().self());
+
+    PathName cmd = std::string("~/bin/") + Request::commandName();
+
+    Log::debug() << "execlp(" << cmd.localPath() << ','
+                 << cmd.baseName().localPath() << ','
+                 << "-in," << in << ','
+                 << "-out," << out << ','
+                 << "-parent," << par << ")" << std::endl;
+
+#if 0
+    if(getenv("PIPE_DEBUG"))
+    {
+        ::execlp(getenv("PIPE_DEBUG"),getenv("PIPE_DEBUG"),
+            cmd.c_str(), "-in",in, "-out",out, "-parent",par, 0);
+
+        std::cerr << "Exec failed " << getenv("PIPE_DEBUG") << Log::syserr  << std::endl;
+        ::kill(getpid(),SIGTERM);
+    }
+    else
+#endif
+
+    char command[1024];
+    char basename[1024];
+
+    ASSERT(sizeof(command)-1 > std::string(cmd).length());
+
+    snprintf(command,1024,"%s", cmd.localPath());
+    snprintf(basename,1024,"%s", cmd.baseName().localPath());
+
+    ::execlp(command, basename,
+        "-in",in,
+        "-out",out,
+        "-parent",par,
+        (void*)0);
+
+    std::cerr << "Exec failed " << cmd << Log::syserr << std::endl;
+
+    // exit is not enough: some destructor of ostore objects
+    // are waiting for some locks
+    ::kill(getpid(),SIGTERM);
+}
+
+template<class Request>
+void PipeHandler<Request>::ready(bool& r)
+{
+    r = r || (active() && !busy());
+}
+
+template<class Request>
+void PipeHandler<Request>::age(time_t& a)
+{
+    if(active() && !busy())
+        if(last_ > a )
+            a = last_;
+}
+
+//=========================================================================
+// Check if an other thread is avialable and as already forked.
+// In this case don't pick the request otherwise, choose it
+
+template<class Request>
+bool PipeHandler<Request>::canPick()
+{
+    time_t a = 0;
+    ClassExtent<PipeHandler<Request> >::callAll(&PipeHandler<Request>::age,a);
+
+    // If someone is more ready me
+    if( a > last_)
+    {
+        Log::debug() << "canPick " << a << " > "
+        << last_ << " size="
+        << ClassExtent<PipeHandler<Request> >::size() << std::endl;
+        return false;
+    }
+
+    bool ok = false;
+    ready(ok);
+
+    // If ready, do it
+
+    if(ok) return true;
+
+    ClassExtent<PipeHandler<Request> >::callAll(&PipeHandler<Request>::ready,ok);
+
+    // If no one else ready, do it
+
+    if(ok)
+    {
+        Log::debug() << "canPick size=" <<
+        ClassExtent<PipeHandler<Request> >::size() << std::endl;
+    }
+
+    return !ok;
+
+}
+
+template<class Request>
+void PipeHandler<Request>::pick(std::list<Request*>& queue,
+    std::vector<Request*>& result)
+{
+
+    if(canPick())
+        DefaultHandler<Request>::pick(queue,result);
+}
+
+template<class Request>
+void PipeHandler<Request>::endBatch(Stream&)
+{
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/PipeHandler.h b/eckit/src/eckit/runtime/PipeHandler.h
new file mode 100644
index 0000000..27f8a32
--- /dev/null
+++ b/eckit/src/eckit/runtime/PipeHandler.h
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File PipeHandler.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_PipeHandler_h
+#define eckit_PipeHandler_h
+
+#include "eckit/container/ClassExtent.h"
+#include "eckit/serialisation/PipeStream.h"
+#include "eckit/runtime/ProcessControler.h"
+#include "eckit/runtime/Dispatcher.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class Request>
+class PipeHandler : public ProcessControler,
+					public ClassExtent<PipeHandler<Request> >,
+					public DefaultHandler<Request> {
+public:
+
+// -- Contructors
+
+	PipeHandler();
+
+// -- Destructor
+
+	~PipeHandler();
+
+// -- Methods
+
+	void handle(const std::vector<Request*>&);
+	void pick(std::list<Request*>&,std::vector<Request*>&);
+	void idle();
+
+	bool busy() const { return busy_; }
+	void ready(bool&) ;
+	void age(time_t&) ;
+
+// -- Overridden methods
+
+	// From ProcessControler
+
+	virtual void start();
+	virtual void stop();
+
+protected:
+
+// -- Methods
+
+	bool canPick();
+
+private:
+
+// -- Members
+
+	PipeStream *pipe_;
+	bool        busy_;
+	time_t      last_;
+
+// -- Methods
+
+	virtual void endBatch(Stream&);
+
+	void send(Request*);
+	void receive(Request*);
+
+	// From ProcessControler
+
+	virtual void run();
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/runtime/PipeHandler.cc"
+
+#endif
diff --git a/eckit/src/eckit/runtime/ProcessControler.cc b/eckit/src/eckit/runtime/ProcessControler.cc
new file mode 100644
index 0000000..2c004ab
--- /dev/null
+++ b/eckit/src/eckit/runtime/ProcessControler.cc
@@ -0,0 +1,263 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/ProcessControler.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+ProcessControler::ProcessControler(bool forget):
+	ClassExtent<ProcessControler>(this),
+	pid_(-1),
+	child_(false),
+	status_(0),
+	forget_(forget)
+{
+}
+
+ProcessControler::~ProcessControler()
+{
+	if(!forget_ && active())
+		Log::warning() << "~ProcessControler called while process still active" << std::endl;
+}
+
+void ProcessControler::printStatus(pid_t pid,int status)
+{
+	Log::info() << "-------- End of " << pid;
+
+	if(WIFEXITED(status))
+		Log::info() << " exited ";
+
+	if(WEXITSTATUS(status))
+		Log::info() << " status " << WEXITSTATUS(status) << ' ';
+
+	if(WIFSIGNALED(status))
+		Log::info() << " with signal " << WTERMSIG(status);
+
+	Log::info() << std::endl;
+}
+
+void ProcessControler::end(result& r)
+{
+	if(r.pid_ == pid_)
+	{
+		r.found_ = true;
+		status_  = r.status_;
+		pid_     = -pid_;
+	}
+}
+
+// This thread should receive the signals
+
+class ChildReaper : public Thread {
+	virtual void run();
+};
+
+void ChildReaper::run()
+{
+
+	sigset_t newmask, oldmask;
+	sigemptyset(&newmask);
+	sigaddset(&newmask, SIGCHLD);
+	pthread_sigmask(SIG_UNBLOCK,&newmask,&oldmask);
+
+	Monitor::instance().name("reaper");
+
+	for(;;)
+	{
+		ProcessControler::result r;
+		r.found_ = false;
+		Log::status() << "Waiting" << std::endl;
+
+        Monitor::instance().show(false);
+		r.pid_ = ::waitpid(-1, &r.status_, 0);
+        Monitor::instance().show(true);
+
+		if(r.pid_ == -1) {
+			// Todo: use mutex cond....
+            if(errno != ECHILD)
+                Log::error() << "Wait pid " << Log::syserr << std::endl;
+			::sleep(5);
+		}
+		else
+		{
+			Log::status() << "End of " << r.pid_ << std::endl;
+			ProcessControler::printStatus(r.pid_,r.status_);
+			ProcessControler::callAll(&ProcessControler::end,r);
+		}
+	}
+
+}
+
+static void init()
+{
+	ThreadControler ctl(new ChildReaper());
+	ctl.start();
+
+/*
+	// For now, ignore the sig childs...
+	// should use sigaction instead of signal
+    signal(SIGCHLD, SIG_IGN);
+*/
+}
+
+void ProcessControler::start()
+{
+	pthread_once(&once,init);
+
+	sigset_t newmask, oldmask;
+	sigemptyset(&newmask);
+	sigaddset(&newmask, SIGCHLD);
+	pthread_sigmask(SIG_BLOCK,&newmask,&oldmask);
+
+	switch(pid_ = ::fork())
+	{
+		case 0:
+
+			pid_ = ::getpid();
+			child_ = true;
+
+			//Log::info() << "Starting new process with a pid of " << pid_ << std::endl;
+
+			try {
+				run();
+			}
+			catch(std::exception& e){
+
+				Log::error() << "** " << e.what() << " Caught in "
+					<< Here() << std::endl;
+				Log::error() << "** Exception is terminate process "
+							 << pid_ << std::endl;
+			}
+
+
+			::exit(0);
+			break;
+
+		case -1:
+			Log::error() << "Cannot fork " << Log::syserr << std::endl;
+			throw FailedSystemCall("fork");
+			break;
+
+		default:
+			break;
+
+	}
+
+	sigemptyset(&newmask);
+	sigaddset(&newmask, SIGCHLD);
+	pthread_sigmask(SIG_UNBLOCK,&newmask,&oldmask);
+
+}
+
+void ProcessControler::stop()
+{
+	if(!active()) return;
+
+	Log::info() << "ProcessControler::stop " << child_ << '-' << pid_ << std::endl;
+	if(child_)
+		::exit(0);
+	else
+		::kill(pid_,SIGTERM);
+
+	//pid_ = -1;
+}
+
+void ProcessControler::kill()
+{
+	stop(); //For the time being....
+}
+
+void ProcessControler::wait()
+{
+	int status;
+
+	if(!active())
+		return;
+
+	Log::info() << "ProcessControler::wait " << pid_ << " " << child_ << std::endl;
+	if(pid_ != -1 && !child_)
+	{
+		pid_t pid = ::waitpid(pid_,&status,0);
+
+		if(pid != pid_)
+		{
+			Log::error() << "Wait pid returns " << errno << ' ' <<pid << std::endl;
+			Log::error() << Log::syserr << std::endl;
+		}
+		else
+		{
+
+			printStatus(pid,status);
+
+			pid_    = -1;
+			status_ = status;
+		}
+	}
+}
+
+bool ProcessControler::active()
+{
+	if(pid_ < 0 && pid_ != -1)
+	{
+		printStatus(-pid_,status_);
+		pid_ = -1;
+	}
+	return pid_ != -1;
+}
+
+
+bool ProcessControler::isRunning(pid_t pid)
+{
+#ifdef IBM
+	pid_t p = ::getsid(pid);
+	if(p == -1)
+	{
+		if(errno == EPERM)
+			return true;
+
+		if(errno == ESRCH)
+			return false;
+
+		throw FailedSystemCall("getsid");
+	}
+	return true;
+#else
+#ifdef __APPLE__
+	return ::kill(pid, 0) == 0;
+#else
+	char buf[1024];
+	snprintf(buf,1024,"/proc/%d",pid);
+	return access(buf,F_OK) == 0;
+#endif
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/ProcessControler.h b/eckit/src/eckit/runtime/ProcessControler.h
new file mode 100644
index 0000000..61cdf6c
--- /dev/null
+++ b/eckit/src/eckit/runtime/ProcessControler.h
@@ -0,0 +1,88 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ProcessControler.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_ProcessControler_h
+#define eckit_ProcessControler_h
+
+#include "eckit/container/ClassExtent.h"
+#include "eckit/runtime/Task.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ProcessControler : public Task, 
+                         public ClassExtent<ProcessControler> {
+public:
+
+	struct result { bool found_; pid_t pid_; int status_; };
+
+// -- Contructors
+
+	ProcessControler(bool forget = false);
+
+// -- Destructor
+
+	~ProcessControler();
+
+// -- Methods
+
+	virtual void run() = 0;
+
+// -- Overridden methods
+
+	// From Task
+
+	virtual void start();
+	virtual void stop();
+	virtual void kill();
+	virtual void wait();
+	virtual bool active();
+
+	// Class-methods
+
+	static bool isRunning(pid_t);
+    
+
+private:
+
+// -- Members
+
+	pid_t pid_;
+	bool  child_;
+	int   status_;
+	bool forget_;
+
+// -- Methods
+
+	void end(result&);
+
+// -- Class methods
+
+	static void* waitChild(void*);
+	static void sigChild(int);
+	static void printStatus(pid_t, int);
+
+	friend class ChildReaper;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/runtime/ProducerConsumer.h b/eckit/src/eckit/runtime/ProducerConsumer.h
new file mode 100644
index 0000000..9315b7f
--- /dev/null
+++ b/eckit/src/eckit/runtime/ProducerConsumer.h
@@ -0,0 +1,279 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_ProducerConsumer_h
+#define eckit_ProducerConsumer_h
+
+#include "eckit/eckit.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/thread/MutexCond.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class PAYLOAD>
+class Producer {
+public:
+    virtual ~Producer() {}
+    virtual bool done() = 0;
+    virtual void produce(PAYLOAD&) = 0;
+};
+
+template<class PAYLOAD>
+class Consumer {
+public:
+    virtual ~Consumer() {}
+    virtual void consume(PAYLOAD&) = 0;
+};
+
+template<class PAYLOAD>
+class ProducerConsumerTask;
+
+template<class PAYLOAD>
+class ProducerConsumer {
+public:
+
+    // -- Contructors
+
+    ProducerConsumer(long count = 2);
+
+    // -- Destructor
+
+    ~ProducerConsumer();
+
+    // -- Methods
+
+    void execute(Producer<PAYLOAD>&, Consumer<PAYLOAD>&);
+
+
+    bool   error();
+    void   error(const std::string&);
+
+private:
+
+    // No copy allowed
+
+    ProducerConsumer(const ProducerConsumer&);
+    ProducerConsumer& operator=(const ProducerConsumer&);
+
+    // -- Methods
+
+    // -- Members
+
+    Mutex  mutex_;
+
+    long   count_;
+
+
+    bool   error_;
+    std::string why_;
+
+
+    // -- Friends
+
+    friend class ProducerConsumerTask<PAYLOAD>;
+
+};
+
+//-----------------------------------------------------------------------------
+
+template<class PAYLOAD>
+struct OnePayload {
+    MutexCond cond_;
+    bool      ready_;
+    bool       done_;
+    PAYLOAD   payload_;
+    OnePayload(): ready_(false), done_(false), payload_() {}
+};
+
+template<class PAYLOAD>
+class ProducerConsumer;
+
+template<class PAYLOAD>
+class ProducerConsumerTask : public Thread {
+    ProducerConsumer<PAYLOAD>&         owner_;
+    Consumer<PAYLOAD>&        consumer_;
+    OnePayload<PAYLOAD>*  payloads_;
+public:
+    ProducerConsumerTask(Consumer<PAYLOAD>&,ProducerConsumer<PAYLOAD>&,OnePayload<PAYLOAD>*);
+    virtual void run();
+};
+
+
+template<class PAYLOAD>
+ProducerConsumer<PAYLOAD>::ProducerConsumer(long count):
+    count_(count),
+    error_(false)
+{
+
+}
+template<class PAYLOAD>
+ProducerConsumer<PAYLOAD>::~ProducerConsumer()
+{
+}
+
+template<class PAYLOAD>
+inline void ProducerConsumer<PAYLOAD>::error(const std::string& why)
+{
+    AutoLock<Mutex> lock(mutex_);
+    error_ = true;
+    why_   = why;
+}
+
+template<class PAYLOAD>
+inline bool ProducerConsumer<PAYLOAD>::error()
+{
+    AutoLock<Mutex> lock(mutex_);
+    return error_;
+}
+
+
+template<class PAYLOAD>
+void ProducerConsumer<PAYLOAD>::execute(Producer<PAYLOAD>& producer,Consumer<PAYLOAD>& consumer)
+{
+
+    OnePayload<PAYLOAD>* payloads = new OnePayload<PAYLOAD>[count_];
+
+    error_   = false;
+
+    ThreadControler thread(new ProducerConsumerTask<PAYLOAD>(consumer,*this,payloads),false);
+
+    thread.start();
+
+    int i = 0;
+
+    while(!error())
+    {
+        AutoLock<MutexCond> lock(payloads[i].cond_);
+
+        while(payloads[i].ready_)
+            payloads[i].cond_.wait();
+
+        if(error())
+            break;
+
+        if(producer.done())
+        {
+            payloads[i].done_ = true;
+            payloads[i].ready_ = true;
+            payloads[i].cond_.signal();
+            break;
+        }
+
+        try {
+            producer.produce(payloads[i].payload_);
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " <<
+                            Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+            error(e.what());
+        }
+
+        payloads[i].ready_ = true;
+        payloads[i].cond_.signal();
+
+        i++;
+        i %= count_;
+
+    }
+
+
+
+    thread.wait();
+    delete[] payloads;
+
+    if(error_) {
+        throw SeriousBug(why_);
+    }
+
+
+}
+
+template<class PAYLOAD>
+ProducerConsumerTask<PAYLOAD>::ProducerConsumerTask(Consumer<PAYLOAD>& consumer,
+                                                    ProducerConsumer<PAYLOAD>& owner,
+                                                    OnePayload<PAYLOAD>* payloads):
+    Thread(),
+    owner_(owner),
+    consumer_(consumer),
+    payloads_(payloads)
+{
+}
+
+template<class PAYLOAD>
+void ProducerConsumerTask<PAYLOAD>::run()
+{
+
+    int i = 0;
+
+    while(!owner_.error())
+    {
+
+        AutoLock<MutexCond> lock(payloads_[i].cond_);
+
+        while(!payloads_[i].ready_)
+            payloads_[i].cond_.wait();
+
+        if(owner_.error())
+            break;
+
+        if(payloads_[i].done_) {
+            payloads_[i].ready_ = false;
+            payloads_[i].cond_.signal();
+            break;
+        }
+
+        bool error = false;
+
+        try {
+            consumer_.consume(payloads_[i].payload_);
+        }
+        catch(std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " <<
+                            Here() << std::endl;
+            Log::error() << "** Exception is handled" << std::endl;
+            owner_.error(e.what());
+            error = true;
+        }
+
+        payloads_[i].ready_ = false;
+
+        if(error)
+        {
+            ASSERT(owner_.error());
+            payloads_[i].cond_.signal();
+            break;
+        }
+
+
+        payloads_[i].cond_.signal();
+
+        i++;
+        i %= owner_.count_;
+    }
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Task.cc b/eckit/src/eckit/runtime/Task.cc
new file mode 100644
index 0000000..2885123
--- /dev/null
+++ b/eckit/src/eckit/runtime/Task.cc
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/runtime/Task.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Task::Task()
+{
+}
+
+Task::~Task()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/Task.h b/eckit/src/eckit/runtime/Task.h
new file mode 100644
index 0000000..926acf3
--- /dev/null
+++ b/eckit/src/eckit/runtime/Task.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Task.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_Task_h
+#define eckit_Task_h
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Task : private NonCopyable {
+public:
+
+// -- Contructors
+
+	Task();
+
+// -- Destructor
+
+	virtual ~Task();
+
+// -- Methods
+
+	virtual void start()  = 0;
+	virtual void stop()   = 0;
+	virtual void wait()   = 0;
+	virtual bool active() = 0;
+	virtual void kill()   = 0;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/TaskID.h b/eckit/src/eckit/runtime/TaskID.h
new file mode 100644
index 0000000..4982331
--- /dev/null
+++ b/eckit/src/eckit/runtime/TaskID.h
@@ -0,0 +1,22 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+
+#ifndef eckit_TaskID_h
+#define eckit_TaskID_h
+
+namespace eckit {
+
+  typedef unsigned long long TaskID;
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/TaskInfo.cc b/eckit/src/eckit/runtime/TaskInfo.cc
new file mode 100644
index 0000000..595da46
--- /dev/null
+++ b/eckit/src/eckit/runtime/TaskInfo.cc
@@ -0,0 +1,212 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "eckit/runtime/Main.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/runtime/ProcessControler.h"
+#include "eckit/runtime/TaskInfo.h"
+#include "eckit/log/Timer.h"
+#include "eckit/os/SignalHandler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TaskInfo::TaskInfo()
+{
+    ASSERT(busy_ == false);
+
+    eckit::zero(*this);
+
+    pid_    = getpid();
+    thread_ = pthread_self();
+    pos_    = 0;
+    start_  = ::time(0);
+    strncpy(name_, Main::instance().name().c_str(), sizeof(name_) - 1);
+    strcpy(kind_, name_);
+    strcpy(application_, name_);
+    strcpy(status_, "Starting");
+    show_ = true;
+    start(0, 0);
+    busy_   = true;
+    stoppable_ = true;
+    parent_  = -1;
+    state_ = ' ';
+}
+
+
+TaskInfo::~TaskInfo()
+{
+    busy_ = false;
+}
+
+void TaskInfo::kind(const std::string& s)
+{
+    touch();
+    strncpy(kind_, s.c_str(), sizeof(kind_) - 1);
+}
+
+void TaskInfo::name(const std::string& s)
+{
+    touch();
+    strncpy(name_, s.c_str(), sizeof(name_) - 1);
+}
+
+void TaskInfo::status(const std::string& s)
+{
+    touch();
+    strncpy(status_, s.c_str(), sizeof(status_) - 1);
+}
+
+void TaskInfo::message(const std::string& s)
+{
+    touch();
+    zero(message_);
+    strncpy(message_, s.c_str(), sizeof(message_) - 1);
+}
+
+void TaskInfo::progressName(const std::string& s)
+{
+    touch();
+    strncpy(progress_.name_, s.c_str(), sizeof(progress_.name_) - 1);
+}
+
+void TaskInfo::out(char* from, char *to)
+{
+    touch();
+    for (char *p = from; p != to ; p++)
+        buffer_[(pos_++) % size_] = *p;
+}
+
+unsigned long TaskInfo::text(char *buf, unsigned long max, unsigned long& pos) const
+{
+    unsigned long len = pos_ - pos;
+    if (len > size_) len = size_;
+    if (len > max  ) len = max;
+
+    unsigned long p = pos_ - len + size_;
+    unsigned long n = len;
+
+    while (n-- > 0) *buf++ = buffer_[(p++) % size_];
+
+    pos = pos_;
+
+    return len;
+}
+
+bool TaskInfo::busy(bool check)
+{
+    if (!busy_)
+        return false;
+
+    time_t now = ::time(0);
+
+    // After 2 minutes, force the check
+    if ( (now - check_) > 120)
+        check = true;
+
+    check_ = now;
+
+    if (!check)
+        return busy_;
+
+    // Check first
+
+    if (!ProcessControler::isRunning(pid_))
+    {
+        this->TaskInfo::~TaskInfo();
+        return false;
+    }
+
+    return true;
+}
+
+void TaskInfo::start(unsigned long long min, unsigned long long max)
+{
+    progress_.rate_     = progress_.speed_ = 0;
+    progress_.min_      = min;
+    progress_.max_      = max;
+    progress_.val_      = min;
+    gettimeofday(&progress_.start_, 0);
+    gettimeofday(&progress_.last_, 0);
+
+    touch();
+}
+
+void TaskInfo::progress(unsigned long long val)
+{
+    ::timeval now;
+    gettimeofday(&now, 0);
+
+    ::timeval diff = now - progress_.last_;
+
+    progress_.rate_ = (val - progress_.val_) /
+                      ((double)diff.tv_sec + ((double)diff.tv_usec / 1000000.));
+
+    diff = now - progress_.start_;
+
+    progress_.speed_ = (val - progress_.min_) /
+                       ((double)diff.tv_sec + ((double)diff.tv_usec / 1000000.));
+
+    progress_.val_ = val;
+
+    gettimeofday(&progress_.last_, 0);
+    touch();
+}
+
+void TaskInfo::done()
+{
+    start(0, 0);
+}
+
+void TaskInfo::touch()
+{
+    checkAbort();
+
+    // FIXME: potential race condition (reported by Clang ThreadSanitizer)
+    check_ = last_ = ::time(0);
+    busy_  = true;
+
+    SignalHandler::checkInterrupt();
+
+    if (stop_ && stoppable_)
+    {
+        stopped_ = true;
+        stop_ = false;
+        exit(1);
+    }
+}
+
+void TaskInfo::checkAbort()
+{
+    if (abort_)
+    {
+        abort_   = false;
+        throw Abort("ThreadControler aborted by request");
+    }
+}
+
+void TaskInfo::parent(long p)
+{
+    parent_ = p;
+    depth_  = 0;
+    if (p != -1) depth_ = Monitor::instance().tasks()[p].depth() + 1;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/TaskInfo.h b/eckit/src/eckit/runtime/TaskInfo.h
new file mode 100644
index 0000000..0ccab4b
--- /dev/null
+++ b/eckit/src/eckit/runtime/TaskInfo.h
@@ -0,0 +1,206 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TaskInfo.h
+// Baudouin Raoult - ECMWF Nov 96
+
+#ifndef eckit_TaskInfo_h
+#define eckit_TaskInfo_h
+
+#include "eckit/memory/Padded.h"
+#include "eckit/types/Types.h"
+#include "eckit/runtime/TaskID.h"
+
+#include <sys/time.h>
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct Info {
+protected:
+
+	bool           busy_;
+	pthread_t      thread_;
+	pid_t          pid_;
+
+	time_t         start_;
+	time_t         last_;
+	time_t         check_;
+
+	bool           show_;
+
+	unsigned long  late_;
+
+	// Logging
+	enum { size_ = 10240 };
+	char           buffer_[size_];
+	unsigned long  pos_;
+
+	char           name_[80];
+	char           kind_[80];
+	char           status_[256];
+	char           application_[80];
+
+	// Progress
+
+	struct Progress {
+		unsigned long long min_;
+		unsigned long long max_;
+		unsigned long long val_;
+		char               name_[80];
+		double             rate_;
+		double             speed_;
+		::timeval            start_;
+		::timeval            last_;
+	};
+
+	Progress  progress_;
+
+	TaskID  taskID_;
+
+	bool           stop_;
+	bool           abort_;
+	bool           stoppable_;
+	bool           stopped_;
+	bool           canceled_;
+	bool           exception_;
+	char           cancelMsg_[80];
+
+	int            config_;
+	char           resource_;
+
+	long           parent_;
+	long           depth_;
+
+	char           state_;
+
+	int            port_;
+	char           host_[80];
+
+	char           message_[80];
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TaskInfo : public Padded<Info,4096>,
+                 public NonCopyable {
+public:
+
+// -- Contructors
+
+	TaskInfo();
+
+// -- Destructor
+
+	~TaskInfo();
+
+// -- Methods
+
+	void  out(char*,char*);
+	bool  busy(bool = false);
+	const char *name()   const { return name_;  }
+	const char *kind()   const { return kind_;  }
+	const char *status() const { return status_;}
+	const char *message() const { return message_;}
+	const char *application() const { return application_;}
+	pid_t  pid()         const { return pid_;   }
+	time_t last()        const { return last_;  }
+	time_t start()       const { return start_;  }
+
+	unsigned long   late()       const  { return late_;  }
+	void   late(unsigned long n)        { touch();late_ = n;     }
+
+	const TaskID& taskID()       { return taskID_; }
+	void taskID(const TaskID& n) { taskID_ = n; }
+
+	void stop()                         { stop_ = true; }
+	bool stopped()        const         { return stopped_; }
+	void abort()                        { abort_ = true; }
+	void checkAbort();
+	void kill(int);
+
+	bool exception() const  { return exception_; }
+	void exception(bool on) { exception_ = on;   }
+
+	// ---------------------------------------------------------
+	// Progress
+
+	void   start(unsigned long long,unsigned long long) ;
+	void   progress(unsigned long long);
+	void   done();
+
+	unsigned long long max() const   { return progress_.max_;   }
+	unsigned long long min() const   { return progress_.min_;   }
+	unsigned long long val() const   { return progress_.val_;   }
+	double rate()  const             { return progress_.rate_;  }
+	double speed() const             { return progress_.speed_; }
+	const char *progressName() const { return progress_.name_;}
+  const ::timeval& progressStart() const { return progress_.start_;}
+  const ::timeval& progressLast() const  { return progress_.last_;}
+
+	// ---------------------------------------------------------
+
+	void kind(const std::string&);
+	void name(const std::string&);
+	void status(const std::string&);
+	void message(const std::string&);
+	void progressName(const std::string&);
+
+	// FIXME: potential race condition (reported by Clang ThreadSanitizer)
+	void show(bool s) { touch(); show_ = s; }
+	bool show() const { return show_; }
+
+	void stoppable(bool s) { stoppable_ = s;touch(); }
+	bool stoppable() const { return stoppable_; }
+
+	void touch();
+
+	void resource(const std::string&);
+
+	unsigned long text(char*,unsigned long, unsigned long&) const;
+
+
+	void parent(long p);
+	long parent() const { return parent_; }
+	long depth() const  { return depth_; }
+
+	void state(char c)  { touch(); state_ = c; }
+	char state() const  { return state_;       }
+
+	void  port(int p)    { touch(); port_ = p; }
+	int   port() const   { return port_; }
+
+	void     host(const std::string& h)    { touch();
+			 strncpy(host_,h.c_str(),sizeof(host_)); }
+	std::string   host() const             { return host_; }
+
+private:
+
+// -- Methods
+
+	void print(std::ostream&) const; 
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const TaskInfo& p)
+		{ p.print(s); return s; }
+
+};
+
+// Used by MappedArray
+
+inline unsigned long version(TaskInfo*) { return 1; }
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/runtime/Tool.cc b/eckit/src/eckit/runtime/Tool.cc
new file mode 100644
index 0000000..d3c3a22
--- /dev/null
+++ b/eckit/src/eckit/runtime/Tool.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// #include <stdlib.h>
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Tool::Tool(int argc, char **argv, const char* homeenv) :
+    Main(argc, argv, homeenv)
+{
+}
+
+Tool::~Tool()
+{
+}
+
+int Tool::start()
+{
+    int status = 0;
+
+    try {
+        run();
+    }
+    catch ( Exception& e ) {
+        status = 1;
+        Log::error() << "** " << e.what() << " Caught in "  << Here() << std::endl;
+        Log::error() << "** Exception terminates " << name() << std::endl;
+    }
+    catch ( std::exception& e ) {
+        status = 1;
+        Log::error() << "** " << e.what() << " Caught in "  << Here() << std::endl;
+        Log::error() << "** Exception terminates " << name() << std::endl;
+    }
+
+    return status;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/runtime/Tool.h b/eckit/src/eckit/runtime/Tool.h
new file mode 100644
index 0000000..41f758d
--- /dev/null
+++ b/eckit/src/eckit/runtime/Tool.h
@@ -0,0 +1,46 @@
+
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Sep 2012
+
+#ifndef eckit_Tool_h
+#define eckit_Tool_h
+
+#include "eckit/runtime/Main.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Tool : public Main {
+
+public: // methods
+
+    /// Contructors
+
+    Tool(int argc, char **argv, const char* homeenv = 0);
+
+    /// Destructor
+
+	virtual ~Tool();
+
+    int start();
+
+	virtual void run() = 0;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/serialisation/FileStream.cc b/eckit/src/eckit/serialisation/FileStream.cc
new file mode 100644
index 0000000..326c356
--- /dev/null
+++ b/eckit/src/eckit/serialisation/FileStream.cc
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <dirent.h>
+
+#include "eckit/eckit.h"
+
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/log/Log.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+FileStream::FileStream(const PathName& name, const char *mode):
+    file_(::fopen(name.localPath(), mode)),
+    read_(std::string(mode) == "r"),
+    name_(name)
+{
+    if (file_ == 0)
+        throw CantOpenFile(name);
+    //setbuf(file_,0);
+}
+
+FileStream::~FileStream()
+{
+    ASSERT(file_);
+
+    if (!read_)
+    {
+        if (::fflush(file_))
+            throw WriteError(std::string("FileStream::~FileStream(fflush(") + name_ + "))");
+
+        // Because AIX has large system buffers,
+        // the close may be successful without the
+        // data being physicaly on disk. If there is
+        // a power failure, we lose some data. So we
+        // need to fsync
+
+        int ret = fsync(fileno(file_));
+
+        while (ret < 0 && errno == EINTR)
+            ret = fsync(fileno(file_));
+        if (ret < 0) {
+            Log::error() << "Cannot fsync(" << name_ << ") " << fileno(file_) <<  Log::syserr << std::endl;
+        }
+        //if(ret<0)
+        //throw FailedSystemCall(std::string("fsync(") + name_ + ")");
+
+        // On Linux, you must also flush the directory
+
+#ifdef EC_HAVE_DIRFD
+        PathName directory = PathName(name_).dirName();
+        DIR *d = ::opendir(directory.localPath());
+        if (!d) SYSCALL(-1);
+
+        int dir;
+        SYSCALL( dir = dirfd(d)  );
+        ret = ::fsync(dir);
+
+        while (ret < 0 && errno == EINTR)
+            ret = fsync(dir);
+
+        if (ret < 0) {
+            Log::error() << "Cannot fsync(" << directory << ")" << Log::syserr << std::endl;
+        }
+        ::closedir(d);
+#endif
+
+    }
+
+    if (::fclose(file_))
+        throw WriteError(std::string("FileStream::~FileStream(fclose(") + name_ + "))");
+    file_ = 0;
+}
+
+long FileStream::read(void* buf, long length)
+{
+    long n = fread(buf, 1, length, file_);
+    ASSERT(n >= 0);
+
+    return n;
+}
+
+long FileStream::write(const void* buf, long length)
+{
+    return fwrite(buf, 1, length, file_);
+}
+
+std::string FileStream::name() const
+{
+    return "FileStream";
+}
+
+void FileStream::rewind()
+{
+    ::fflush(file_);
+    fseeko(file_, 0, SEEK_SET);
+    resetBytesWritten();
+}
+
+void FileStream::print(std::ostream& s) const {
+    s << "FileStream[path=" << name_ << "]";
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/serialisation/FileStream.h b/eckit/src/eckit/serialisation/FileStream.h
new file mode 100644
index 0000000..0bb6d9a
--- /dev/null
+++ b/eckit/src/eckit/serialisation/FileStream.h
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FileStream.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_FileStream_h
+#define eckit_FileStream_h
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileStream : public Stream {
+
+public: // methods
+
+    /// Contructor
+
+	FileStream(const PathName& name,const char *mode);
+
+    /// Destructor
+
+	~FileStream();
+
+    // Overriden from Stream
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void rewind();
+
+protected: // members
+
+    FILE* 		file_;
+    bool        read_;
+    PathName    name_;
+
+protected: // methods
+
+    // Overriden from Stream
+
+	virtual std::string name() const;
+    virtual void print(std::ostream& s) const;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/serialisation/HandleStream.h b/eckit/src/eckit/serialisation/HandleStream.h
new file mode 100644
index 0000000..bd60d07
--- /dev/null
+++ b/eckit/src/eckit/serialisation/HandleStream.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HandleStream.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef eckit_HandleStream_h
+#define eckit_HandleStream_h
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+// Adaptor: allow Stream operations on a DataHandle;
+
+class HandleStream : public Stream {
+public:
+
+// -- Contructors
+
+	HandleStream(DataHandle& h) : handle_(h) {}
+
+// -- Destructor
+
+	~HandleStream() {}
+
+private:
+
+// -- Members
+	
+	DataHandle& handle_;
+
+// -- Overridden methods
+
+	// From Stream
+
+    virtual long write(const void* buf,long len)
+		{ return handle_.write(buf,len); }
+
+	virtual long read(void* buf,long len)
+		{ return handle_.read(buf,len); }
+
+	virtual std::string name() const
+		{ return "HandleStream"; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/serialisation/MemoryStream.cc b/eckit/src/eckit/serialisation/MemoryStream.cc
new file mode 100644
index 0000000..c3f91f0
--- /dev/null
+++ b/eckit/src/eckit/serialisation/MemoryStream.cc
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/serialisation/MemoryStream.h"
+#include "eckit/io/Buffer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+#if 0
+ClassSpec MemoryStream::classSpec_ = {&DataHandle::classSpec(),"MemoryStream",};
+Reanimator<MemoryStream> MemoryStream::reanimator_;
+#endif
+
+
+MemoryStream::MemoryStream(const Buffer& buffer):
+	address_(const_cast<Buffer&>(buffer)),
+    size_(buffer.size()),
+    read_(false),
+    position_(0)
+{
+}
+
+
+MemoryStream::MemoryStream(Buffer& buffer):
+    address_(buffer),
+    size_(buffer.size()),
+    read_(false),
+    position_(0)
+{
+}
+
+MemoryStream::MemoryStream(const void* address,size_t size):
+	address_(const_cast<char*>(reinterpret_cast<const char*>(address))),
+    size_(size),
+    read_(false),
+    position_(0)
+{
+}
+
+
+MemoryStream::MemoryStream(void* address,size_t size):
+    address_(reinterpret_cast<char*>(address)),
+    size_(size),
+    read_(false),
+    position_(0)
+{
+}
+
+MemoryStream::~MemoryStream()
+{
+}
+
+long MemoryStream::read(void* buffer,long length)
+{
+    size_t left = size_ - position_;
+    size_t size = std::min(left, size_t(length));
+    ::memcpy(buffer, address_ + position_, size);
+    position_ += size;
+
+	return size;
+}
+
+long MemoryStream::write(const void* buffer,long length)
+{
+    size_t left = size_ - position_;
+    size_t size = std::min(left, size_t(length));
+    ::memcpy(address_ + position_, buffer, size);
+    position_ += size;
+
+    return size;
+}
+
+void MemoryStream::rewind()
+{
+	position_ = 0;
+}
+
+std::string MemoryStream::name() const {
+	return "MemoryStream";
+}
+
+size_t MemoryStream::position() const {
+    return position_;
+}
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/MemoryStream.h b/eckit/src/eckit/serialisation/MemoryStream.h
new file mode 100644
index 0000000..39586c7
--- /dev/null
+++ b/eckit/src/eckit/serialisation/MemoryStream.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File serialisation/MemoryStream.h
+// Manuel Fuentes - ECMWF Apr 16
+
+#ifndef eckit_serialisation_MemoryStream_H
+#define eckit_serialisation_MemoryStream_H
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+class Buffer;
+
+//-----------------------------------------------------------------------------
+
+class MemoryStream : public Stream {
+public:
+
+
+
+	MemoryStream(const Buffer&);
+    MemoryStream(Buffer&);
+
+	MemoryStream(const void* address,size_t size);
+    MemoryStream(void* address,size_t size);
+
+    /// Destructor
+
+	~MemoryStream();
+
+// -- Operators
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void rewind();
+	virtual std::string name() const;
+
+    size_t position() const;
+
+
+// -- Overridden methods
+
+
+private: // members
+
+	char*          address_;
+    const size_t   size_;
+
+    bool           read_;
+    size_t         position_;
+
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/serialisation/PipeStream.cc b/eckit/src/eckit/serialisation/PipeStream.cc
new file mode 100644
index 0000000..a7ec9bf
--- /dev/null
+++ b/eckit/src/eckit/serialisation/PipeStream.cc
@@ -0,0 +1,146 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "eckit/thread/Mutex.h"
+#include "eckit/serialisation/PipeStream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+PipeStream::PipeStream():
+	in_(-1),
+	out_(-1)
+{
+	toChild_[0] = toChild_[1] = toParent_[0] = toParent_[1] = -1;
+
+	SYSCALL(::pipe(toChild_));
+	SYSCALL(::pipe(toParent_));
+
+}
+
+PipeStream::PipeStream(int in,int out):
+	in_(in),
+	out_(out)
+{
+	toChild_[0] = toChild_[1] = toParent_[0] = toParent_[1] = -1;
+}
+
+PipeStream::~PipeStream()
+{
+	::close(in_);
+	::close(out_);
+	::close(toChild_[0]);
+	::close(toChild_[1]);
+	::close(toParent_[0]);
+	::close(toParent_[1]);
+}
+
+void PipeStream::parentProcess()
+{
+    /// @todo change this to sigaction
+
+	::signal(SIGPIPE,SIG_IGN);
+
+	in_  = toParent_[0];
+	out_ = toChild_[1];
+
+	::close(toChild_[0]);
+	::close(toParent_[1]);
+
+	// Avoid childs from other threads having opened file descriptors
+	SYSCALL(fcntl(in_,F_SETFD,FD_CLOEXEC));
+	SYSCALL(fcntl(out_,F_SETFD,FD_CLOEXEC));
+
+	Log::debug() << "parentProcess : "<<
+		in_ << '/' << out_ << " closing " << toChild_[0] << '/' << toParent_[1] << std::endl;
+
+	toChild_[0] = toChild_[1] = toParent_[0] = toParent_[1] = -1;
+
+}
+
+void PipeStream::childProcess()
+{
+    /// @todo change this to sigaction
+
+    ::signal(SIGPIPE,SIG_IGN);
+
+	in_  = toChild_[0];
+	out_ = toParent_[1];
+
+	::close(toChild_[1]);
+	::close(toParent_[0]);
+
+	Log::debug() << "childProcess : "<<
+		in_ << '/' << out_ << " closing " << toChild_[1] << '/' << toParent_[0] << std::endl;
+
+	toChild_[0] = toChild_[1] = toParent_[0] = toParent_[1] = -1;
+
+}
+
+long PipeStream::write(const void *buf, long length)
+{
+    long sent     = 0;
+    const char *p = (const char*)buf;
+
+    while(length > 0)
+    {
+        long len = ::write(out_, p, length);
+
+        if(len <  0) {
+            Log::error() << "PipeStream::write " << Log::syserr << std::endl;
+            return len;
+        }
+
+        if(len == 0) return sent;
+
+        sent   += len;
+        length -= len;
+        p      += len;
+
+    }
+
+    return sent;
+}
+
+long PipeStream::read(void *buf, long length)
+{
+    long received = 0;
+    char *p = (char*)buf;
+
+    while(length > 0)
+    {
+        long len = ::read(in_, p, length);
+
+		if(len <  0) {
+			Log::error() << "PipeStream::read " << Log::syserr << std::endl;
+			return len;
+		}
+
+		if(len == 0) return received;
+
+		received  += len;
+		length    -= len;
+		p         += len;
+    }
+
+    return received;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/PipeStream.h b/eckit/src/eckit/serialisation/PipeStream.h
new file mode 100644
index 0000000..892eded
--- /dev/null
+++ b/eckit/src/eckit/serialisation/PipeStream.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File PipeStream.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_PipeStream_h
+#define eckit_PipeStream_h
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Stream using pipes
+
+class PipeStream : public Stream {
+public:
+
+// -- Contructors
+
+	PipeStream();
+	PipeStream(int,int);
+
+// -- Destructor
+
+	~PipeStream();
+
+// -- Methods
+
+	// After a fork:
+
+	void parentProcess(); // Notify that we are in the parent
+	void childProcess();  // Notify that we are in the child 
+
+	int in()  { return in_;  }
+	int out() { return out_; }
+
+private:
+
+// -- Members
+
+	int toChild_[2];
+	int toParent_[2];
+
+	int in_;
+	int out_;
+
+// -- Overridden methods
+
+	// From Stream
+
+	virtual long write(const void*,long);
+	virtual long read(void*,long);
+	virtual std::string name() const { return "pipe"; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/serialisation/Reanimator.cc b/eckit/src/eckit/serialisation/Reanimator.cc
new file mode 100644
index 0000000..2258457
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Reanimator.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/serialisation/Streamable.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T> 
+Reanimator<T>::Reanimator():
+	 ReanimatorBase(T::classSpec())
+{
+}
+
+template<class T> 
+Streamable* Reanimator<T>::ressucitate(Stream& s) const
+{
+	return new T(s);
+}
+
+
+template<class T>
+T*  Reanimator<T>::reanimate(Stream& s) 
+{ 
+	return static_cast<T*>(ReanimatorBase::reanimate(s,&T::classSpec()));
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/Reanimator.h b/eckit/src/eckit/serialisation/Reanimator.h
new file mode 100644
index 0000000..db3bc7d
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Reanimator.h
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Reanimator.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_Reanimator_h
+#define eckit_Reanimator_h
+
+#include "eckit/serialisation/Stream.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Streamable;
+
+// -----------------------------------------------------------------------------
+// The class Streamable is a abstract class for encoding/decoding objects to/from
+// a Stream.
+//
+// For a class to be "stream-able", it must:
+//
+//   1- Inherit from class Streamable
+//   2- Have a constructor from a Stream&
+//   3- Support a encode(Stream&) const method
+//
+//   When encoded, it must first encode its superclass, then all its members
+//   objects, and write is simple members ...
+//
+// For a class 'Foo' to be "reanimate-able", i.e. to be recreated from a stream
+// as a first class object, it must be "stream-able" and it must have the two following
+// private members:
+//
+//    static  ClassSpec        classSpec_;
+//    static  Reanimator<Foo> reanimator_;
+//
+// and the two public methods to access those members:
+//
+//	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+//	static  const ClassSpec&  classSpec()        { return classSpec_; }
+//
+// -----------------------------------------------------------------------------
+// A ClassSpec decribes the class:
+//
+
+struct ClassSpec {
+	const ClassSpec	*superClass_; // Pointer to superclass ClassSpec  (Note that
+	                              // multiple inheritance is not supported.)
+	const char      *name_;		  // class name
+};
+
+// in the file Foo.cc, if Foo is a direct subclass of Bar, its ClassSpec can be
+// defined as:
+//
+// ClassSpec Foo::classSpec_ = { &Bar::ClassSpec(), "Foo", 1, };
+//
+// and the reanimator as
+//
+// Reanimator<Foo> Foo::reanimator_;
+//
+// -----------------------------------------------------------------------------
+// A ReanimatorBase is an 'factory' used to create objects from a Stream
+//
+
+class ReanimatorBase {
+
+	const ClassSpec&  spec_;
+
+	virtual Streamable*  ressucitate(Stream&) const = 0;
+
+	class NotSubClass: public Exception
+    { public: NotSubClass(const std::string&, const std::string&); };
+
+	class UnknowClass: public Exception
+    { public: UnknowClass(const std::string&); };
+
+public:
+
+	ReanimatorBase(const ClassSpec&);
+	virtual ~ReanimatorBase();
+	const ClassSpec& spec() const  { return spec_; }
+
+static Streamable*  reanimate(Stream&,const ClassSpec* = 0);
+};
+
+
+// -----------------------------------------------------------------------------
+
+template<class T> class Reanimator : public ReanimatorBase {
+	Streamable*  ressucitate(Stream& s) const;
+public:
+	Reanimator();
+static T*  reanimate(Stream& s);
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/serialisation/Reanimator.cc"
+
+#endif
diff --git a/eckit/src/eckit/serialisation/ReanimatorBase.cc b/eckit/src/eckit/serialisation/ReanimatorBase.cc
new file mode 100644
index 0000000..5e62091
--- /dev/null
+++ b/eckit/src/eckit/serialisation/ReanimatorBase.cc
@@ -0,0 +1,94 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/serialisation/Streamable.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Should protected with a mutex...
+
+typedef std::map<std::string,ReanimatorBase*,std::less<std::string> > Map;
+
+// This trick garanty than a std::map is created 
+
+static Map& theMap()
+{
+	static Map m;
+	return m;
+}
+
+ReanimatorBase::ReanimatorBase(const ClassSpec& spec):
+	spec_(spec)
+{
+	theMap()[std::string(spec_.name_)] = this;
+//	std::cout << "ReanimatorBase::ReanimatorBase " << spec_.name_ << std::endl;
+}
+
+ReanimatorBase::~ReanimatorBase()
+{
+	// Should not be there
+	// remove ReanimatorBase form list
+}
+
+
+ReanimatorBase::UnknowClass::UnknowClass(const std::string& w):
+	Exception(std::string("Unknow class: ") + w)
+{
+}
+
+ReanimatorBase::NotSubClass::NotSubClass(const std::string& found, 
+	const std::string& clss):
+	Exception(std::string("Not a sub class: object ") + found + 
+		std::string(" found, but it is not subclass of ") + clss)
+{
+}
+
+
+Streamable* ReanimatorBase::reanimate(Stream& s,const ClassSpec *c)
+{
+
+	if(!s.next()) return 0;	
+	
+	std::string name;
+
+	s >> name;
+
+	Map::iterator i = theMap().find(name);
+	if(i == theMap().end())
+		throw UnknowClass(name);
+
+	ReanimatorBase *r = (*i).second;
+
+	// Check for the class
+
+	if(c)
+	{
+		const ClassSpec *a = &r->spec_;
+		while(a != 0 && a != c)
+			a = a->superClass_;
+					
+		if(a == 0) throw NotSubClass(name,c->name_);
+	}
+
+	Streamable* x = r->ressucitate(s);
+	s.skipEndObject();
+	return x;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/Stream.cc b/eckit/src/eckit/serialisation/Stream.cc
new file mode 100644
index 0000000..260b5b4
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Stream.cc
@@ -0,0 +1,785 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Log.h"
+#include "eckit/serialisation/Stream.h"
+
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if 0
+#define T(a,x) do { std::cout << "Stream: " << a << " ->  "  << x << std::endl;} while(0)
+#else
+#define T(a,x) /**/
+#endif
+
+static const char *tag_names[] = {
+    "0",
+    "start of object",
+    "end of object",
+    "char",
+    "unsigned char",
+    "int",
+    "unsigned int",
+    "short",
+    "unsigned short",
+    "long",
+    "unsigned long",
+    "long long",
+    "unsigned long long",
+    "float",
+    "double",
+    "string",
+    "blob",
+    "exception",
+    "start of record",
+    "end of record",
+    "end of file",
+    "large blob"
+};
+
+const int tag_count = sizeof(tag_names) / sizeof(tag_names[0]);
+
+
+Stream::Stream():
+    lastTag_(tag_zero),
+    writeCount_(0) {
+}
+
+Stream::~Stream() {
+
+}
+
+void Stream::putBytes(const void *buf, long len) {
+    writeCount_ += len;
+    if (write(buf, len) != len)
+        throw WriteError(name());
+}
+
+void Stream::getBytes(void *buf, long len) {
+    if (read(buf, len) != len)
+        throw ReadError(name());
+}
+
+
+void Stream::putChar(unsigned char p) {
+    assert(sizeof(unsigned char) == 1);
+    putBytes(&p, 1);
+}
+
+unsigned char Stream::getChar() {
+    assert(sizeof(unsigned char) == 1);
+
+    char p;
+    getBytes(&p, 1);
+    return p;
+}
+
+void Stream::putLong(unsigned long p) {
+
+    if (sizeof(unsigned long) != 4) {
+        if ( sizeof(unsigned int) == 4 ) {
+            unsigned int n = htonl(p);
+            putBytes(&n, sizeof(n));
+        } else if (sizeof(unsigned short) == 4) {
+            unsigned short s = htons(p);
+            putBytes(&s, sizeof(s));
+        } else NOTIMP;
+    } else {
+        p = htonl(p);
+        putBytes(&p, sizeof(p));
+    }
+}
+
+unsigned long Stream::getLong() {
+    if (sizeof(unsigned long) != 4) {
+        if ( sizeof(unsigned int) == 4 ) {
+            unsigned int n;
+            getBytes(&n, sizeof(n));
+            return ntohl(n);
+        } else if (sizeof(unsigned short) == 4) {
+            unsigned short s;
+            getBytes(&s, sizeof(s));
+            return ntohs(s);
+        } else NOTIMP;
+    } else {
+        long p;
+        getBytes(&p, sizeof(p));
+        return ntohl(p);
+    }
+}
+
+std::ostream &operator<<(std::ostream &s, Stream::tag t) {
+    if (t >= 0 && t < tag_count)
+        return s << '\'' << tag_names[t] << '\'';
+    else
+        return s << '\'' << long(t) << '\'';
+}
+
+void Stream::badTag(Stream::tag need, Stream::tag got) {
+    std::ostringstream os;
+
+    os << "Bad tag found in stream ";
+    os << *this;
+    os << ". Expecting a " << need << ", got a " << got;
+
+    Log::error() << os.str() << std::endl;
+
+    if (got == tag_string) {
+        long length = getLong();
+        std::string s;
+        s.resize(length);
+        for (long i = 0; i < length; i++) s[i] = getChar();
+        Log::error() << "String is " << s << std::endl;
+    }
+
+    throw BadTag(os.str());
+}
+
+Stream::tag Stream::nextTag() {
+    if (lastTag_ != tag_zero) {
+        tag t    = lastTag_;
+        lastTag_ = tag_zero;
+        //      Log::debug() << "Found tag " << t << std::endl;
+        return t;
+    }
+
+    unsigned char c = 0;
+    int len;
+
+    if ( (len = read(&c, 1)) == 0) {
+        //      Log::debug() << "End of stream" << tag_zero << std::endl;
+        return tag_eof;
+    }
+
+    if (len == EOF)
+        throw ShortFile(name());
+
+    tag t = static_cast<tag>(c);
+    //  Log::debug() << "Next tag " << t << std::endl;
+    return t;
+
+}
+
+Stream::tag Stream::readTag(Stream::tag need) {
+    tag t;
+
+    // Skip any end-of-object lingering
+
+    while ((t = nextTag())  == tag_end_obj)
+        ;
+
+    if (t == tag_exception) {
+        std::string s;
+        (*this) >> s;
+        throw RemoteException(s, name());
+    }
+
+    if (need != t)
+        badTag(need, t);
+
+
+    return t;
+}
+
+void Stream::writeTag(Stream::tag t) {
+    // Log::info() << "Stream::writeTag(" << t << ")" << std::endl;
+    unsigned char c = static_cast<unsigned char>(t);
+    putBytes(&c, 1);
+}
+
+
+Stream &Stream::operator<<(char x) {
+    union {
+        uint8_t u;
+        int8_t s;
+    } u;
+    T("w char", x);
+    u.s = x;
+    writeTag(tag_char);
+    putChar(u.u);
+    return *this;
+}
+
+Stream &Stream::operator<<(unsigned char x) {
+    T("w unsigned char", x);
+    writeTag(tag_unsigned_char);
+    putChar(x);
+    return *this;
+}
+
+Stream &Stream::operator<<(int x) {
+    union {
+        uint32_t u;
+        int32_t s;
+    } u;
+    T("w int", x);
+    u.s = x;
+    writeTag(tag_int);
+    putLong(u.u);
+    return *this;
+}
+
+Stream &Stream::operator<<(bool x) {
+    T("w bool", x);
+    return (*this) << int(x);
+}
+
+Stream &Stream::operator<<(unsigned int x) {
+    T("w unsigned int", x);
+    writeTag(tag_unsigned_int);
+    putLong(x);
+    return *this;
+}
+
+Stream &Stream::operator<<(long x) {
+    union {
+        uint32_t u;
+        int32_t s;
+    } u;
+    T("w long", x);
+    u.s = x;
+    writeTag(tag_long);
+    putLong(u.u);
+    return *this;
+}
+
+Stream &Stream::operator<<(unsigned long x) {
+    T("w unsigned long", x);
+    writeTag(tag_unsigned_long);
+    putLong(x);
+    return *this;
+}
+
+Stream &Stream::operator<<(long long x) {
+    union {
+        uint64_t u;
+        int64_t s;
+    } u;
+    T("w long long", x);
+    u.s = x;
+    writeTag(tag_long_long);
+    putLong(u.u >> 32);
+    putLong(u.u & 0xffffffff);
+    return *this;
+}
+
+Stream &Stream::operator<<(unsigned long long x) {
+    T("w unsigned long long", x);
+    writeTag(tag_unsigned_long_long);
+    putLong(x >> 32);
+    putLong(x & 0xffffffff);
+    return *this;
+}
+
+Stream &Stream::operator<<(short x) {
+    union {
+        uint16_t u;
+        int16_t s;
+    } u;
+    T("w short", x);
+    u.s = x;
+    writeTag(tag_short);
+    putLong(u.u);
+    return *this;
+}
+
+Stream &Stream::operator<<(unsigned short x) {
+    T("w unsigned short", x);
+    writeTag(tag_unsigned_short);
+    putLong(x);
+    return *this;
+}
+
+Stream &Stream::operator<<(float x) {
+    T("w float", x);
+    writeTag(tag_float);
+    NOTIMP;
+    return *this;
+}
+
+union Double {
+    double d;
+#if __SIZEOF_LONG__ == 4
+    struct {
+        unsigned long hi;
+        unsigned long lo;
+    } s;
+#else
+    struct {
+        unsigned int hi;
+        unsigned int lo;
+    } s;
+#endif
+};
+
+Stream &Stream::operator<<(double x) {
+    T("w double", x);
+    writeTag(tag_double);
+    Double d;
+    ASSERT(sizeof(d.d) == 2 * sizeof(d.s.hi));
+    d.d = x;
+    putLong(d.s.hi);
+    putLong(d.s.lo);
+    return *this;
+}
+
+Stream &Stream::operator<<(const char *x) {
+    T("w std::string", x);
+    writeTag(tag_string);
+    ASSERT(x);
+    long len = strlen(x);
+    putLong(len);
+    assert(sizeof(unsigned char) == 1);
+    putBytes(x, len);
+    return *this;
+}
+
+Stream &Stream::operator<<(const std::string &x) {
+    T("w std::string", x);
+    writeTag(tag_string);
+    long len = x.length();
+    putLong(len);
+
+    char buf[len];
+    assert(sizeof(unsigned char) == 1);
+    for (long i = 0; i < len; i++)
+        buf[i] = x[i];
+
+    putBytes(buf, len);
+    return *this;
+}
+
+void Stream::writeLargeBlob(const void *buffer, size_t size) {
+    T("w blob", x);
+    writeTag(tag_large_blob);
+
+    unsigned long long len = size;
+    ASSERT(size_t(len) == size);
+
+    // std::cout << "Stream::writeLargeBlob " << size << std::endl;
+
+    putLong(len >> 32);
+    putLong(len & 0xffffffff);
+
+    long n = 0x80000000;
+    const char *p = static_cast<const char *>(buffer);
+    while (size > 0) {
+        long l = size > size_t(n) ? n : size;
+        putBytes(p, l);
+        p += l;
+        size -= l;
+    }
+}
+
+void Stream::writeBlob(const void *buffer, size_t size) {
+    T("w blob", x);
+    writeTag(tag_blob);
+
+    long len = size;
+    ASSERT(size_t(len) == size);
+    ASSERT(len >= 0);
+
+    putLong(len);
+    putBytes(buffer, len);
+}
+
+Stream &Stream::operator<<(const Buffer &x) {
+    writeBlob(x, x.size());
+    return *this;
+}
+
+Stream &Stream::operator<<(const std::exception &e) {
+    T("w exception", e.what());
+    writeTag(tag_exception);
+    return *this << std::string(e.what());
+}
+
+Stream &Stream::operator>>(char &x) {
+    union {
+        uint8_t u;
+        int8_t s;
+    } u;
+    readTag(tag_char);
+    u.u = getChar();
+    x = u.s;
+    T("r char", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(unsigned char &x) {
+    readTag(tag_unsigned_char);
+    x = getChar();
+    T("r unsigned char", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(int &x) {
+    union {
+        uint32_t u;
+        int32_t s;
+    } u;
+    readTag(tag_int);
+    u.u = getLong();
+    x = u.s;
+    T("r int", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(bool &x) {
+    int n; (*this) >> n; x = n;
+    T("r bool", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(unsigned int &x) {
+    readTag(tag_unsigned_int);
+    x = getLong();
+    T("r unsigned int", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(short &x) {
+    union {
+        uint16_t u;
+        int16_t s;
+    } u;
+    readTag(tag_short);
+    u.u = getLong();
+    x = u.s;
+    T("r short", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(unsigned short &x) {
+    readTag(tag_unsigned_short);
+    x = getLong();
+    T("r unsigned short", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(long &x) {
+    union {
+        uint32_t u;
+        int32_t s;
+    } u;
+    readTag(tag_long);
+    u.u = getLong();
+    x = u.s;
+    T("r long", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(unsigned long &x) {
+    readTag(tag_unsigned_long);
+    x = getLong();
+    T("r unsigned long", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(long long &x) {
+    union {
+        uint64_t u;
+        int64_t s;
+    } u;
+    readTag(tag_long_long);
+    uint64_t u1 = getLong();;
+    uint64_t u2 = getLong();
+    u.u = (u1 << 32) | u2;
+    x = u.s;
+    T("r long long", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(unsigned long long &x) {
+    readTag(tag_unsigned_long_long);
+    unsigned long long u1 = getLong();;
+    unsigned long long u2 = getLong();
+    x = (u1 << 32) | u2;
+    T("r unsigned long long", x);
+    return *this;
+}
+
+
+Stream &Stream::operator>>(float &x) {
+    readTag(tag_float);
+    NOTIMP;
+    T("r float", x);
+    return *this;
+}
+
+void Stream::readLargeBlob(void *buffer, size_t size) {
+    readTag(tag_large_blob);
+
+    unsigned long long u1 = getLong();;
+    unsigned long long u2 = getLong();
+    unsigned long long len = (u1 << 32) | u2;
+
+    ASSERT(size_t(len) == size);
+
+    // std::cout << "Stream::readLargeBlob " << size << std::endl;
+
+    long n = 0x80000000;
+    char *p = static_cast< char *>(buffer);
+    while (size > 0) {
+        long l = size > size_t(n) ? n : size;
+        getBytes(p, l);
+        p += l;
+        size -= l;
+    }
+}
+
+void Stream::readBlob(void *buffer, size_t size) {
+    readTag(tag_blob);
+    long len = getLong();
+    ASSERT(len >= 0);
+    ASSERT(size_t(len) == size);
+    getBytes(buffer, len);
+    T("r blob", x);
+}
+
+
+size_t Stream::blobSize() {
+    readTag(tag_blob);
+    long len = getLong();
+    ASSERT(len >= 0);
+    return len;
+}
+
+Stream &Stream::operator>>(Buffer &x) {
+    readBlob(x, x.size());
+    return *this;
+}
+
+Stream &Stream::operator>>(double &x) {
+    readTag(tag_double);
+    Double d;
+    d.s.hi = getLong();
+    d.s.lo = getLong();
+    x = d.d;
+    T("r double", x);
+    return *this;
+}
+
+Stream &Stream::operator>>(std::string &s) {
+    readTag(tag_string);
+    long length = getLong();
+    char buf[length];
+    getBytes(buf, length);
+
+    s.resize(length);
+
+    for (long i = 0; i < length; i++)
+        s[i] = buf[i];
+
+    T("r std::string", s);
+    return *this;
+}
+
+bool Stream::next(std::string &s) {
+    tag t = nextTag();
+    if (t == tag_eof)
+        return false;
+
+    if (t != tag_string)
+        badTag(tag_string, t);
+
+    long length = getLong();
+    char buf[length];
+    getBytes(buf, length);
+
+    s.resize(length);
+
+    for (long i = 0; i < length; i++)
+        s[i] = buf[i];
+
+    T("r std::string", s);
+
+    return true;
+}
+
+void Stream::startObject() {
+    T("w start", 0);
+    writeTag(tag_start_obj);
+}
+
+void Stream::endObject() {
+    T("w end", 0);
+    writeTag(tag_end_obj);
+}
+
+bool Stream::endObjectFound() {
+    lastTag_ = nextTag();
+    return (lastTag_ == tag_end_obj) || (lastTag_ == tag_zero);
+}
+
+void Stream::skipEndObject() {
+    if (endObjectFound()) lastTag_ = tag_zero;
+}
+
+#if 0
+
+void Stream::startRecord(unsigned long type) {
+    writeTag(tag_start_rec);
+    putLong(type);
+}
+
+void Stream::endRecord() {
+    writeTag(tag_end_obj);
+}
+
+bool Stream::nextRecord(unsigned long &type, bool sync) {
+    int first = 0;
+
+    for (;;) {
+        unsigned char c = 0;
+        int len;
+
+        if ( (len = read(&c, 1)) == 0)
+            return false;
+
+        if (len == EOF)
+            throw ShortFile(name());
+
+        tag t =  static_cast<tag>(c);
+
+        if (t == tag_start_rec) {
+            type = getLong();
+            return true;
+        }
+
+        if (t != tag_end_rec) {
+            if (!sync) badTag(tag_start_rec, t);
+            if (first) {
+                Log::error() << "Bad tag found in stream " << t << std::endl;
+                Log::error() << "Trying to synchronise" << std::endl;
+                first = 0;
+            }
+        }
+    }
+}
+
+#endif
+
+bool Stream::next() {
+    for (;;) {
+        tag t = nextTag();
+
+        if (t == tag_start_obj)
+            return true;
+
+        if (t == tag_eof)
+            return false;
+
+        if (t != tag_end_obj)
+            badTag(tag_start_obj, t);
+    }
+}
+
+class StreamDecoder : public Stream {
+    const char *buffer_;
+    size_t pos_;
+    size_t len_;
+
+    virtual long write(const void *buf, long len) {
+        NOTIMP;
+    }
+
+    virtual long read(void *buf, long len) {
+        ::memcpy(buf, buffer_ + pos_, len);
+        len_ += len;
+        pos_ += len;
+        return len;
+    }
+
+    virtual std::string name() const {
+        return "StreamDecoder";
+    }
+
+  public:
+    StreamDecoder(const char *buffer): buffer_(buffer), pos_(0), len_(0) {}
+    size_t len() const {
+        return len_;
+    }
+
+};
+
+void Stream::dump(std::ostream &out, const char *p, size_t len) {
+    size_t i = 0;
+    while (i < len) {
+        if (p[i] < last_tag) {
+            out << "<" << tag_names[static_cast<size_t>(p[i])] << ">";
+
+            StreamDecoder d(&p[i]);
+
+            switch (p[i]) {
+            case tag_zero: NOTIMP; break;
+            case tag_start_obj: NOTIMP; break;
+            case tag_end_obj: NOTIMP; break;
+            case tag_char: NOTIMP; break;
+            case tag_unsigned_char: NOTIMP; break;
+            case tag_int: {
+                int s;
+                d >> s;
+                out << s;
+            };
+            break;
+            case tag_unsigned_int: NOTIMP; break;
+            case tag_short: NOTIMP; break;
+            case tag_unsigned_short: NOTIMP; break;
+            case tag_long: NOTIMP; break;
+            case tag_unsigned_long: NOTIMP; break;
+            case tag_long_long: NOTIMP; break;
+            case tag_unsigned_long_long: {
+                unsigned long long s;
+                d >> s;
+                out << s;
+            };
+            break;
+            case tag_float: NOTIMP; break;
+            case tag_double: NOTIMP; break;
+
+            case tag_string: {
+                std::string s;
+                d >> s;
+                out << s;
+            };
+            break;
+
+            case tag_blob: NOTIMP; break;
+            case tag_exception: NOTIMP; break;
+            case tag_start_rec: NOTIMP; break;
+            case tag_end_rec: NOTIMP; break;
+            case tag_eof: NOTIMP; break;
+            default: NOTIMP; break;
+            }
+
+            i +=  d.len();
+        } else {
+            out << "<invalid>";
+            i++;
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/Stream.h b/eckit/src/eckit/serialisation/Stream.h
new file mode 100644
index 0000000..8da314b
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Stream.h
@@ -0,0 +1,227 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Stream.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_Stream_h
+#define eckit_Stream_h
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+template<class T> class BufferedWriter;
+template<class T> class BufferedReader;
+template<class T> class IOBuffer;
+
+class Buffer;
+
+class Stream : private NonCopyable {
+public:
+
+// -- Exceptions
+
+    class BadTag : public Exception {
+    public:
+        BadTag(const std::string& what): Exception(what) {}
+    };
+
+// -- Destructor
+
+    virtual ~Stream();
+
+// -- Operators
+
+    // Output
+
+    Stream& operator<<(char);
+    Stream& operator<<(unsigned char);
+
+    Stream& operator<<(bool);
+
+    Stream& operator<<(int);
+    Stream& operator<<(unsigned int);
+
+    Stream& operator<<(short);
+    Stream& operator<<(unsigned short);
+
+    Stream& operator<<(long);
+    Stream& operator<<(unsigned long);
+
+    Stream& operator<<(long long);
+    Stream& operator<<(unsigned long long);
+
+    Stream& operator<<(float);
+    Stream& operator<<(double);
+
+    Stream& operator<<(const std::string&);
+    Stream& operator<<(const char*);
+
+
+    Stream& operator<<(const std::exception&);
+
+    // Blobs
+    Stream& operator<<(const Buffer&);
+
+    // Input
+
+    Stream& operator>>(char&);
+    Stream& operator>>(unsigned char&);
+
+    Stream& operator>>(int&);
+    Stream& operator>>(unsigned int&);
+
+    Stream& operator>>(bool&);
+
+    Stream& operator>>(long&);
+    Stream& operator>>(unsigned long&);
+
+    Stream& operator>>(long long&);
+    Stream& operator>>(unsigned long long&);
+
+    Stream& operator>>(short&);
+    Stream& operator>>(unsigned short&);
+
+    Stream& operator>>(float&);
+    Stream& operator>>(double&);
+
+    Stream& operator>>(std::string&);
+
+    // Blobs
+    Stream& operator>>(Buffer&);
+
+// -- Methods
+
+    bool next(std::string&);
+
+    bool endObjectFound();
+    bool next();
+    void skipEndObject();
+    void startObject();
+    void endObject();
+
+    void writeBlob(const void*, size_t);
+    void readBlob(void*, size_t);
+
+    void writeLargeBlob(const void*, size_t);
+    void readLargeBlob(void*, size_t);
+
+    virtual void rewind()      { NOTIMP; }
+    virtual void closeOutput() { NOTIMP; }
+    virtual void closeInput()  { NOTIMP; }
+
+    long long bytesWritten() { return writeCount_; }
+    void resetBytesWritten() { writeCount_ = 0; }
+
+    void startRecord(unsigned long);
+    void endRecord();
+    bool nextRecord(unsigned long&,bool sync = false);
+
+    void lock()   { mutex_.lock(); }
+    void unlock() { mutex_.unlock(); }
+
+
+    static void dump(std::ostream&,const char*, size_t);
+
+
+protected:
+
+// -- Contructors
+
+    Stream();
+
+// -- Methods
+
+    virtual std::string name() const 			= 0;
+    virtual void print(std::ostream& s) const  	{ s << name();  }
+
+    size_t blobSize();
+
+private:
+
+    enum tag {
+        tag_zero,
+        tag_start_obj,
+        tag_end_obj,
+        tag_char,
+        tag_unsigned_char,
+        tag_int,
+        tag_unsigned_int,
+        tag_short,
+        tag_unsigned_short,
+        tag_long,
+        tag_unsigned_long,
+        tag_long_long,
+        tag_unsigned_long_long,
+        tag_float,
+        tag_double,
+        tag_string,
+        tag_blob,
+        tag_exception,
+        tag_start_rec,
+        tag_end_rec,
+        tag_eof,
+        tag_large_blob, // For blobs >= 2Gb
+        last_tag
+    };
+
+// -- Members
+
+    tag			lastTag_;
+    Mutex		mutex_;
+    long		writeCount_;
+
+// -- Methods
+
+    // These are the two methods to override
+
+    virtual long write(const void*,long) = 0;
+    virtual long read(void*,long) = 0;
+
+    unsigned char getChar();
+    unsigned long getLong();
+
+    void putChar(unsigned char);
+    void putLong(unsigned long);
+
+    void badTag(tag,tag);
+    tag  nextTag();
+    tag  readTag(tag = tag_zero);
+    void writeTag(tag);
+
+    void getBytes(void*,long);
+    void putBytes(const void*,long);
+
+    friend std::ostream& operator<<(std::ostream&,tag);
+
+    friend class BufferedWriter<Stream>;
+    friend class BufferedReader<Stream>;
+    friend class IOBuffer<Stream>;
+
+    friend std::ostream& operator<<(std::ostream& out, const Stream& s) {
+        s.print(out);
+        return out;
+    }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/serialisation/Streamable.cc b/eckit/src/eckit/serialisation/Streamable.cc
new file mode 100644
index 0000000..ab99341
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Streamable.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/serialisation/Streamable.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec Streamable::classSpec_ = {0,"Streamable",};
+Reanimator<Streamable> Streamable::reanimator_;
+
+Streamable::Streamable(Stream&)
+{
+}
+
+void Streamable::encode(Stream&) const
+{
+}
+
+std::string Streamable::className() const
+{
+	return reanimator().spec().name_;
+}
+
+Stream& operator<<(Stream& s,const Streamable& x)
+{
+	s.startObject();
+	s << x.className();
+	x.encode(s);
+	s.endObject();
+	return s;
+}
+
+bool Streamable::sameClass(const Streamable& other) const
+{
+	return &reanimator() == &other.reanimator();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/serialisation/Streamable.h b/eckit/src/eckit/serialisation/Streamable.h
new file mode 100644
index 0000000..facce2f
--- /dev/null
+++ b/eckit/src/eckit/serialisation/Streamable.h
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Streamable.h
+// B.Raoult - ECMWF May-1996
+
+#ifndef eckit_Streamable_h
+#define eckit_Streamable_h
+
+#include "eckit/memory/MemoryPool.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/serialisation/Reanimator.h"
+#include "eckit/serialisation/Stream.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Streamable : private NonCopyable {
+public:
+
+	friend Stream& operator<<(Stream&,const Streamable&);
+
+// -- Contructors
+
+	Streamable() 			{}
+	Streamable(Stream&);
+
+// -- Destructor
+
+	virtual ~Streamable()   {}
+
+// -- Methods
+	
+    virtual std::string className() const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()        { return classSpec_; }
+
+// -- Operators
+
+	void *operator new(size_t s)          { return MemoryPool::fastAllocate(s);}
+	void *operator new(size_t s,void *p)  { return p;                          }
+	void operator delete(void* p)         { MemoryPool::fastDeallocate(p);     } 
+
+protected:
+
+// -- Methods
+	
+	virtual void encode(Stream&) const;
+
+	bool sameClass(const Streamable&) const;
+
+private:
+
+// -- Class members
+	
+	static  ClassSpec classSpec_;
+	static  Reanimator<Streamable> reanimator_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/system/Library.cc b/eckit/src/eckit/system/Library.cc
new file mode 100644
index 0000000..9c60030
--- /dev/null
+++ b/eckit/src/eckit/system/Library.cc
@@ -0,0 +1,267 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#include <map>
+#include <algorithm>
+#include <cctype>
+
+#include "eckit/system/Library.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "eckit/log/OStreamTarget.h"
+#include "eckit/log/PrefixTarget.h"
+#include "eckit/os/System.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef std::map<std::string, Library*> LibraryMap;
+
+struct LibraryRegistry {
+
+    static LibraryRegistry& instance() {
+        static LibraryRegistry reg;
+        return reg;
+    }
+
+    LibraryMap map_;
+
+    LibraryMap& map() { return map_; }
+};
+
+
+static pthread_once_t once  = PTHREAD_ONCE_INIT;
+static eckit::Mutex* local_mutex = 0;
+
+static void init() {
+    local_mutex = new eckit::Mutex();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::vector<std::string> Library::list() {
+
+    std::vector<std::string> result;
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    LibraryMap& m = LibraryRegistry::instance().map();
+
+    for (LibraryMap::const_iterator j = m.begin() ; j != m.end() ; ++j) {
+        result.push_back(j->first);
+    }
+    return result;
+}
+
+void Library::list(std::ostream& out) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    LibraryMap& m = LibraryRegistry::instance().map();
+
+    const char* sep = "";
+    for (LibraryMap::const_iterator j = m.begin() ; j != m.end() ; ++j) {
+        out << sep << (*j).first;
+        sep = ", ";
+    }
+}
+
+bool Library::exists(const std::string& name) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    LibraryMap& m = LibraryRegistry::instance().map();
+
+    LibraryMap::const_iterator j = m.find(name);
+
+    return (j != m.end());
+}
+
+const Library& Library::lookup(const std::string& name) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    LibraryMap& m = LibraryRegistry::instance().map();
+
+    LibraryMap::const_iterator j = m.find(name);
+
+    // eckit::Log::info() << "Looking for Library '" << name << "'" << std::endl;
+
+    if (j == m.end()) {
+        eckit::Log::error() << "No Library found with name '" << name << "'" << std::endl;
+        eckit::Log::error() << "Registered libraries are:" << std::endl;
+        for (j = m.begin() ; j != m.end() ; ++j)
+            eckit::Log::error() << "   " << (*j).first << std::endl;
+        throw eckit::SeriousBug(std::string("No Library found with name ") + name);
+    }
+
+    ASSERT(j->second);
+
+    return *(j->second);
+}
+
+Library::Library(const std::string& name) :
+    name_(name),
+    prefix_(name),
+    debug_(false) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    LibraryMap& m = LibraryRegistry::instance().map();
+
+    ASSERT(m.find(name) == m.end());
+    m[name] = this;
+
+    std::transform(prefix_.begin(), prefix_.end(), prefix_.begin(), ::toupper);
+
+    std::string s = prefix_ + "_DEBUG";
+    const char* e = ::getenv(s.c_str());
+    if (e) {
+        debug_ = eckit::Translator<std::string, bool>()(e);
+    }
+
+    if (!debug_) {
+        e = ::getenv("DEBUG");
+        if (e) {
+            debug_ = eckit::Translator<std::string, bool>()(e);
+        }
+    }
+}
+
+Library::~Library() {
+}
+
+const std::string& Library::name() const {
+    return name_;
+}
+
+std::string Library::prefixDirectory() const {
+    eckit::AutoLock<Mutex> lock(mutex_);
+
+    if (prefixDirectory_.empty()) {
+        prefixDirectory_ = LocalPathName(libraryPath()).dirName().dirName().realName();
+    }
+
+    return prefixDirectory_;
+}
+
+std::string Library::home() const
+{
+    eckit::AutoLock<Mutex> lock(mutex_);
+
+    std::string libhome = prefix_ + "_HOME";
+    char* home = ::getenv(libhome.c_str());
+    if(home) { return home; }
+
+    return home_; // may return empty string (meaning not set)
+}
+
+void Library::libraryHome(const std::string& home)
+{
+    eckit::AutoLock<Mutex> lock(mutex_);
+    home_ = home;
+}
+
+std::string Library::libraryPath() const {
+    eckit::AutoLock<Mutex> lock(mutex_);
+
+    if (libraryPath_.empty()) {
+        std::string p = eckit::System::addrToPath(addr());
+        libraryPath_ = LocalPathName(p).realName();
+    }
+    return libraryPath_;
+}
+
+Channel& Library::debugChannel() const
+{
+    eckit::AutoLock<Mutex> lock(mutex_);
+
+    if (debugChannel_) { return *debugChannel_; }
+
+    std::string s = prefix_ + "_DEBUG";
+
+    if (debug_) {
+        debugChannel_.reset(new Channel(new PrefixTarget(s)));
+    }
+    else {
+        debugChannel_.reset(new Channel());
+    }
+
+    return *debugChannel_;
+}
+
+std::string Library::expandPath(const std::string& p) const {
+
+    std::string s = "~" + name_;
+
+    ASSERT(p.substr(0, s.size()) == s);
+    ASSERT(p.size() == s.size() || p[s.size()] == '/');
+
+    // 1. if HOME is set for this library, either via env variable LIBNAME_HOME exists
+    //    or set in code expand ~lib/ to its content
+
+    const std::string h = home();
+    if(!h.empty()) {
+        std::string result = h + "/" + p.substr(s.size());
+        return result;
+    }
+
+    // 2. try to walk up the path and check for paths that exist
+
+    const std::string extra = "/" + p.substr(s.size());
+
+    eckit::LocalPathName path = prefixDirectory();
+    eckit::LocalPathName root("/");
+
+    while(true) {
+
+        LocalPathName tmp = path + extra;
+
+        if(tmp.exists()) return tmp;
+
+        if(path == root) break;
+
+        path = path.dirName();
+    }
+
+    // 3. as a last resort expand with prefix directory although we know the path doesn't exist
+
+    return prefixDirectory() + extra;
+}
+
+void Library::print(std::ostream &out) const {
+    out << "Library("
+        << "name=" << name_
+        << ", path=" << libraryPath()
+        << ", prefix=" << prefixDirectory()
+        << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/Library.h b/eckit/src/eckit/system/Library.h
new file mode 100644
index 0000000..ed6b67c
--- /dev/null
+++ b/eckit/src/eckit/system/Library.h
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef eckit_system_Library_H
+#define eckit_system_Library_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+
+class Channel;
+
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Library : private eckit::NonCopyable {
+
+public: // methods
+
+
+    Library(const std::string& name);
+
+    virtual ~Library();
+
+    const std::string& name() const;
+
+    virtual std::string prefixDirectory() const;
+
+    virtual void libraryHome(const std::string&);
+
+    virtual std::string expandPath(const std::string& path) const;
+
+//    virtual LocalPathName bin() const;
+//    virtual LocalPathName lib() const;
+//    virtual LocalPathName share() const;
+//    virtual LocalPathName etc() const;
+
+    std::string libraryPath() const;
+
+    virtual std::string version() const = 0;
+    virtual std::string gitsha1(unsigned int count = 40) const = 0;
+
+    virtual Channel& debugChannel() const;
+
+public: // class methods
+
+    static std::vector<std::string> list();
+    static void list(std::ostream&);
+
+    static bool exists(const std::string& name);
+    static const Library& lookup(const std::string& name);
+
+protected: // methods
+
+    virtual std::string home() const;
+
+    virtual const void* addr() const = 0;
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const Library& p) { p.print(s); return s; }
+
+private: // methods
+
+    std::string location() const;
+
+private: // members
+
+    std::string name_;
+    std::string prefix_;
+    std::string home_;    // if not set explicitly, will be empty
+
+    bool debug_;
+
+    mutable eckit::Mutex mutex_;
+
+    mutable std::string libraryPath_;
+    mutable std::string prefixDirectory_;
+
+    mutable eckit::ScopedPtr<eckit::Channel> debugChannel_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/system/ResourceUsage.cc b/eckit/src/eckit/system/ResourceUsage.cc
new file mode 100644
index 0000000..719be38
--- /dev/null
+++ b/eckit/src/eckit/system/ResourceUsage.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#include "eckit/system/ResourceUsage.h"
+
+#include "eckit/eckit_ecbuild_config.h"
+#include "eckit/parser/StringTools.h"
+
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ResourceUsage::ResourceUsage() {
+    ::getrusage(RUSAGE_SELF, &usage_);
+
+    if (StringTools::startsWith(ECKIT_OS_NAME, "Darwin")) {
+        factor_ = 1; // ru_masrss is in bytes
+    } else {
+        factor_ = 1024; // assume ru_masrss is in Kilobytes
+    }
+}
+
+void ResourceUsage::print(std::ostream &out) const {
+    out << "CPU: " << cpuTime() << " ("
+        << eckit::Seconds(cpuTime())
+        << "), memory: "
+        << maxResidentSetSize()
+        << " ("
+        << eckit::Bytes(maxResidentSetSize())
+        << "), swaps: "
+        << eckit::BigNum(numberOfSwaps());
+}
+
+size_t ResourceUsage::maxResidentSetSize() const {
+    return usage_.ru_maxrss * factor_;
+}
+
+double ResourceUsage::cpuTime() const {
+    return usage_.ru_utime.tv_sec + usage_.ru_utime.tv_usec / 1000000.0;
+}
+
+size_t ResourceUsage::numberOfSwaps() const {
+    return usage_.ru_nswap;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/ResourceUsage.h b/eckit/src/eckit/system/ResourceUsage.h
new file mode 100644
index 0000000..2820df0
--- /dev/null
+++ b/eckit/src/eckit/system/ResourceUsage.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#ifndef eckit_system_ResourceUsage_H
+#define eckit_system_ResourceUsage_H
+
+#include <iosfwd>
+#include <sys/resource.h>
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ResourceUsage {
+
+public: // methods
+
+    ResourceUsage();
+
+    size_t maxResidentSetSize() const;
+    double cpuTime() const;
+    size_t numberOfSwaps() const;
+
+protected: // methods
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const ResourceUsage& p) { p.print(s); return s; }
+
+private: // members
+
+    size_t factor_;
+    struct rusage usage_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/system/SystemInfo.cc b/eckit/src/eckit/system/SystemInfo.cc
new file mode 100644
index 0000000..00bd07a
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfo.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#include "eckit/system/SystemInfo.h"
+
+#include "eckit/eckit_ecbuild_config.h"
+#include "eckit/parser/StringTools.h"
+
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/ScopedPtr.h"
+
+#if defined(__APPLE__) && defined(__MACH__)
+#include "eckit/system/SystemInfoMacOSX.h"
+#endif
+
+#if defined(__linux__)
+#include "eckit/system/SystemInfoLinux.h"
+#endif
+
+#if defined(__FreeBSD__)
+#include "eckit/system/SystemInfoFreeBSD.h"
+#endif
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SystemInfo* makeSystemInfo(const std::string& system)
+{
+    ///< @todo add a factory?
+
+#if defined(__APPLE__) && defined(__MACH__)
+    if (StringTools::startsWith(ECKIT_OS_NAME, "Darwin")) { // double check with ecbuild name
+        return new SystemInfoMacOSX();
+    }
+#endif
+
+#if defined(__linux__)
+    if (StringTools::startsWith(ECKIT_OS_NAME, "Linux")) {
+        return new SystemInfoLinux();
+    }
+#endif
+
+#if defined(__FreeBSD__)
+    if (StringTools::startsWith(ECKIT_OS_NAME, "FreeBSD")) {
+        return new SystemInfoFreeBSD();
+    }
+#endif
+
+    NOTIMP;
+}
+
+static pthread_once_t once  = PTHREAD_ONCE_INIT;
+static eckit::ScopedPtr<SystemInfo> systemInfoPtr;
+
+static void createInstance() {
+    ASSERT(!systemInfoPtr);
+    systemInfoPtr.reset( makeSystemInfo(ECKIT_OS_NAME) );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+const SystemInfo& SystemInfo::instance() {
+    pthread_once(&once, createInstance);
+    ASSERT(systemInfoPtr);
+    return *systemInfoPtr;
+}
+
+SystemInfo::~SystemInfo() {
+}
+
+void SystemInfo::print(std::ostream &out) const {
+    out << "SystemInfo("
+        << "executablePath=" << executablePath()
+        << ")";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/SystemInfo.h b/eckit/src/eckit/system/SystemInfo.h
new file mode 100644
index 0000000..12e4bff
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfo.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#ifndef eckit_system_SystemInfo_H
+#define eckit_system_SystemInfo_H
+
+#include <iosfwd>
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct Mem {
+    size_t resident_size_;
+    size_t virtual_size_;
+    Mem(size_t resident_size, size_t virtual_size):
+        resident_size_(resident_size),
+        virtual_size_(virtual_size) {}
+};
+
+class SystemInfo : private eckit::NonCopyable {
+
+public: // methods
+
+    virtual ~SystemInfo();
+
+    static const SystemInfo& instance();
+
+    virtual eckit::LocalPathName executablePath() const = 0;
+
+    virtual size_t memoryAllocated() const = 0;
+    virtual Mem memoryUsage() const = 0;
+
+protected: // methods
+
+    void print(std::ostream&) const;
+
+    friend std::ostream& operator<<(std::ostream& s, const SystemInfo& p) { p.print(s); return s; }
+
+private: // members
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/system/SystemInfoFreeBSD.cc b/eckit/src/eckit/system/SystemInfoFreeBSD.cc
new file mode 100644
index 0000000..5e7f441
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoFreeBSD.cc
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @author Simon Smart
+/// @date   March 2017
+
+#include <stdlib.h>
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+
+#include <climits>
+
+#include "eckit/system/SystemInfoFreeBSD.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SystemInfoFreeBSD::~SystemInfoFreeBSD() {
+}
+
+LocalPathName SystemInfoFreeBSD::executablePath() const
+{
+    Buffer buffer(PATH_MAX);
+
+    int mib[4];
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PATHNAME;
+    mib[3] = -1;
+
+    size_t size = buffer.size();
+    SYSCALL(::sysctl(mib, 4, buffer, &size, NULL, 0));
+    std::string path(buffer, size);
+
+    return LocalPathName(path).realName();
+}
+
+Mem SystemInfoFreeBSD::memoryUsage() const {
+    struct rusage usage;
+    SYSCALL(getrusage(RUSAGE_SELF, &usage));
+    return Mem(usage.ru_maxrss * 1024, 0) ;
+}
+
+size_t SystemInfoFreeBSD::memoryAllocated() const {
+    NOTIMP;
+    // return ::mallinfo().uordblks;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/SystemInfoFreeBSD.h b/eckit/src/eckit/system/SystemInfoFreeBSD.h
new file mode 100644
index 0000000..03936f8
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoFreeBSD.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @author Simon Smart
+/// @date   March 2017
+
+#ifndef eckit_system_SystemInfoFreeBSD_H
+#define eckit_system_SystemInfoFreeBSD_H
+
+#include <iosfwd>
+
+#include "eckit/system/SystemInfo.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SystemInfoFreeBSD : public SystemInfo {
+
+public: // methods
+
+    virtual ~SystemInfoFreeBSD();
+
+    virtual eckit::LocalPathName executablePath() const;
+
+    virtual size_t memoryAllocated() const;
+    virtual Mem memoryUsage() const;
+
+protected: // methods
+
+private: // members
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/system/SystemInfoLinux.cc b/eckit/src/eckit/system/SystemInfoLinux.cc
new file mode 100644
index 0000000..e9a0d21
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoLinux.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#include <unistd.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <climits>
+#include <cstdlib>
+
+#include "eckit/system/SystemInfoLinux.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SystemInfoLinux::~SystemInfoLinux() {
+}
+
+LocalPathName SystemInfoLinux::executablePath() const
+{
+    Buffer buffer(PATH_MAX);
+	ssize_t size = SYSCALL(::readlink("/proc/self/exe", buffer, buffer.size()));
+    std::string path(buffer, size);
+    return LocalPathName(path).realName();
+}
+
+Mem SystemInfoLinux::memoryUsage() const {
+    struct rusage usage;
+    SYSCALL(getrusage(RUSAGE_SELF, &usage));
+    return Mem(usage.ru_maxrss * 1024, 0) ;
+}
+
+size_t SystemInfoLinux::memoryAllocated() const {
+    return mallinfo().uordblks;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/SystemInfoLinux.h b/eckit/src/eckit/system/SystemInfoLinux.h
new file mode 100644
index 0000000..200b3d4
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoLinux.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#ifndef eckit_system_SystemInfoLinux_H
+#define eckit_system_SystemInfoLinux_H
+
+#include <iosfwd>
+
+#include "eckit/system/SystemInfo.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SystemInfoLinux : public SystemInfo {
+
+public: // methods
+
+    virtual ~SystemInfoLinux();
+
+    virtual eckit::LocalPathName executablePath() const;
+
+    virtual size_t memoryAllocated() const;
+    virtual Mem memoryUsage() const;
+
+protected: // methods
+
+private: // members
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/system/SystemInfoMacOSX.cc b/eckit/src/eckit/system/SystemInfoMacOSX.cc
new file mode 100644
index 0000000..a5495a8
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoMacOSX.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#include <sys/param.h>
+#include <mach-o/dyld.h>
+#include <mach/mach.h>
+#include <malloc/malloc.h>
+
+#include "eckit/system/SystemInfoMacOSX.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SystemInfoMacOSX::~SystemInfoMacOSX() {
+}
+
+LocalPathName SystemInfoMacOSX::executablePath() const
+{
+    Buffer buffer(MAXPATHLEN);
+
+    int err = 0;
+    uint32_t actual = uint32_t(buffer.size());
+    if( (err = _NSGetExecutablePath(buffer, &actual)) == -1 ) {
+        buffer.resize(actual);
+        err = _NSGetExecutablePath(buffer, &actual);
+    }
+
+    if(err != 0) {
+        std::ostringstream oss;
+        oss << "_NSGetExecutablePath when called with buffer sized " << buffer.size();
+        throw FailedSystemCall(oss.str(), Here());
+    }
+
+    std::string path(buffer);
+
+    return LocalPathName(path).realName();
+}
+
+Mem SystemInfoMacOSX::memoryUsage() const {
+    struct task_basic_info info;
+    mach_msg_type_number_t size = sizeof(info);
+
+    kern_return_t err = task_info(mach_task_self(),
+                                  TASK_BASIC_INFO,
+                                  (task_info_t)&info,
+                                  &size);
+
+    if ( err != KERN_SUCCESS ) {
+        throw eckit::FailedSystemCall(mach_error_string(err), Here());
+    }
+
+    return Mem(info.resident_size, info.virtual_size);}
+
+size_t SystemInfoMacOSX::memoryAllocated() const {
+    return mstats().bytes_used;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
diff --git a/eckit/src/eckit/system/SystemInfoMacOSX.h b/eckit/src/eckit/system/SystemInfoMacOSX.h
new file mode 100644
index 0000000..eb85528
--- /dev/null
+++ b/eckit/src/eckit/system/SystemInfoMacOSX.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   May 2016
+
+#ifndef eckit_system_SystemInfoMacOSX_H
+#define eckit_system_SystemInfoMacOSX_H
+
+#include <iosfwd>
+
+#include "eckit/system/SystemInfo.h"
+
+namespace eckit {
+namespace system {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class SystemInfoMacOSX : public SystemInfo {
+
+public: // methods
+
+    virtual ~SystemInfoMacOSX();
+
+    virtual eckit::LocalPathName executablePath() const;
+
+    virtual size_t memoryAllocated() const;
+    virtual Mem memoryUsage() const;
+
+protected: // methods
+
+private: // members
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace system
+} // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/testing/Setup.h b/eckit/src/eckit/testing/Setup.h
new file mode 100644
index 0000000..f7c69ec
--- /dev/null
+++ b/eckit/src/eckit/testing/Setup.h
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   Setup.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Aug 2016
+
+#ifndef eckit_testing_Setup_h
+#define eckit_testing_Setup_h
+
+#include "eckit/runtime/Main.h"
+
+namespace eckit {
+namespace testing {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct Setup {
+    Setup() {
+        eckit::Main::initialise(boost::unit_test::framework::master_test_suite().argc,
+                                boost::unit_test::framework::master_test_suite().argv);
+
+//        eckit::Main::instance().assertDumps(false);
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace testing
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/AutoLock.h b/eckit/src/eckit/thread/AutoLock.h
new file mode 100644
index 0000000..02732d4
--- /dev/null
+++ b/eckit/src/eckit/thread/AutoLock.h
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_AutoLock_h
+#define eckit_AutoLock_h
+
+#include "eckit/log/Seconds.h"
+#include "eckit/log/Timer.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// The class AutoLock is used to AutoLock a mutex in a multi-threaded
+// environment. AutoLocks are exception safe.
+
+template<class T> 
+class AutoLock : private NonCopyable {
+
+public:
+
+// -- Contructors
+	
+    AutoLock(T& resource) :
+        resource_(resource) {
+        resource_.lock();
+    }
+
+    AutoLock(T* resource) :
+        resource_(*resource) {
+        resource_.lock();
+    }
+
+// -- Destructor
+
+    ~AutoLock() { resource_.unlock(); }
+
+private: // members
+	
+    T& resource_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T> 
+class AutoSharedLock : private NonCopyable {
+public:
+
+// -- Contructors
+	
+    AutoSharedLock(T& resource) : resource_(resource) 
+							{ resource_.lockShared(); }
+    AutoSharedLock(T* resource) : resource_(*resource)
+							{ resource_.lockShared(); }
+
+// -- Destructor
+
+    ~AutoSharedLock() { resource_.unlock(); }
+
+private: // members
+	
+    T& resource_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+class TimedAutoLock : private NonCopyable {
+public:
+
+    // -- Constructors
+
+    TimedAutoLock(T& resource, const std::string& message)
+        : resource_(resource), timer_(message + " (release)") {
+        resource_.lock();
+        timer_.report(message + " (acquire)");
+    }
+
+    // -- Destructor
+
+    ~TimedAutoLock() { resource_.unlock(); }
+
+private: // members
+
+    T& resource_;
+    Timer timer_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T, class U>
+class TraceAutoLock : private NonCopyable {
+public:
+
+    // -- Constructors
+
+    TraceAutoLock(T& resource, const std::string& message)
+        : resource_(resource), timer_(message + " (release)") {
+        resource_.lock();
+        timer_.report(message + " (acquire)");
+    }
+
+    // -- Destructor
+
+    ~TraceAutoLock() { resource_.unlock(); }
+
+private: // members
+
+    T& resource_;
+    TraceTimer<U> timer_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/Mutex.cc b/eckit/src/eckit/thread/Mutex.cc
new file mode 100644
index 0000000..4651896
--- /dev/null
+++ b/eckit/src/eckit/thread/Mutex.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/Mutex.h"
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Mutex::Mutex(char tag) :
+	exists_(false),
+	tag_(tag)
+{
+	pthread_mutexattr_t attr;
+	THRCALL(::pthread_mutexattr_init(&attr));
+	THRCALL(::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE));
+
+	THRCALL(::pthread_mutex_init(&mutex_,&attr));
+
+	exists_ = true;
+	THRCALL(::pthread_mutexattr_destroy(&attr));
+}
+
+Mutex::~Mutex()
+{
+	THRCALL(::pthread_mutex_destroy(&mutex_));
+}
+
+void Mutex::lock(void)
+{
+	if(!exists_)
+	{
+        std::cerr << "Mutex used before being constructed" << std::endl;
+        ::abort();
+	}
+	THRCALL(::pthread_mutex_lock(&mutex_));
+}
+
+void Mutex::unlock(void)
+{
+	if(!exists_)
+	{
+        std::cerr << "Mutex used before being constructed" << std::endl;
+        ::abort();
+	}
+	THRCALL(::pthread_mutex_unlock(&mutex_));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/Mutex.h b/eckit/src/eckit/thread/Mutex.h
new file mode 100644
index 0000000..2827c55
--- /dev/null
+++ b/eckit/src/eckit/thread/Mutex.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Mutex.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_Mutex_h
+#define eckit_Mutex_h
+
+#include <pthread.h>
+
+#include "eckit/eckit.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Mutex : private NonCopyable {
+
+public: // methods
+
+// -- Contructors
+
+	Mutex(char tag = ' ');
+
+// -- Destructor
+
+	~Mutex();
+
+// -- Methods
+
+	void lock();
+	void unlock();
+	char tag() const { return tag_; }
+
+protected: // members
+
+	pthread_mutex_t mutex_;
+	bool            exists_;
+	char            tag_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/MutexCond.cc b/eckit/src/eckit/thread/MutexCond.cc
new file mode 100644
index 0000000..143a81b
--- /dev/null
+++ b/eckit/src/eckit/thread/MutexCond.cc
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/MutexCond.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+MutexCond::MutexCond(char tag):
+	tag_(tag)
+{
+	pthread_mutexattr_t attr;
+	pthread_condattr_t  cattr;
+
+    THRCALL(::pthread_mutexattr_init(&attr));
+	THRCALL(::pthread_condattr_init(&cattr));
+
+	THRCALL(::pthread_mutex_init(&mutex_,&attr));
+	THRCALL(::pthread_cond_init(&cond_,&cattr));
+
+	inited_ = true;
+
+    THRCALL(::pthread_mutexattr_destroy(&attr));
+	THRCALL(::pthread_condattr_destroy(&cattr));
+}
+
+MutexCond::~MutexCond()
+{
+	THRCALL(::pthread_mutex_destroy(&mutex_));
+
+	pthread_cond_destroy(&cond_); // Don't use THRCALL as some thread may be waiting for that condition
+	inited_ = false;
+}
+
+void MutexCond::lock()
+{
+	ASSERT(inited_);
+	THRCALL(::pthread_mutex_lock(&mutex_));
+}
+
+void MutexCond::unlock()
+{
+	ASSERT(inited_);
+	THRCALL(::pthread_mutex_unlock(&mutex_));
+}
+
+void MutexCond::wait()
+{
+	ASSERT(inited_);
+//	AutoState x('.');
+	THRCALL(::pthread_cond_wait(&cond_,&mutex_));
+}
+
+bool MutexCond::wait(int sec)
+{
+	ASSERT(inited_);
+//	AutoState x(':');
+	timespec timeout = { ::time(0) + sec ,0};
+	int n = pthread_cond_timedwait(&cond_,&mutex_,&timeout);
+	if(n && n != ETIMEDOUT) THRCALL(n);
+	return n == ETIMEDOUT;
+}
+
+void MutexCond::signal()
+{
+	ASSERT(inited_);
+	pthread_cond_signal(&cond_);
+}
+
+void MutexCond::broadcast()
+{
+	ASSERT(inited_);
+	pthread_cond_broadcast(&cond_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/MutexCond.h b/eckit/src/eckit/thread/MutexCond.h
new file mode 100644
index 0000000..f7d4d43
--- /dev/null
+++ b/eckit/src/eckit/thread/MutexCond.h
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MutexCond.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_MutexCond_h
+#define eckit_MutexCond_h
+
+#include <pthread.h>
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// A mutex and a condition variable
+// for Producer/Consumer architectures
+
+class MutexCond : private NonCopyable {
+public:
+
+// -- Contructors
+
+	MutexCond(char tag = ' ');
+
+// -- Destructor
+
+	~MutexCond();
+
+// -- Methods
+
+	void lock();
+	void unlock();
+	void wait();
+	void signal();
+	void broadcast();
+	bool wait(int);
+	char tag() const { return tag_; }
+
+private:
+
+// -- Members
+
+	pthread_mutex_t mutex_;
+	pthread_cond_t  cond_;
+	char tag_;
+	bool inited_;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/Once.h b/eckit/src/eckit/thread/Once.h
new file mode 100644
index 0000000..3b0cfa3
--- /dev/null
+++ b/eckit/src/eckit/thread/Once.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_Once_h
+#define eckit_Once_h
+
+#include <pthread.h>
+
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T> class Once : private NonCopyable {
+public:
+
+// -- Contructors
+
+	Once();
+
+// -- Destructor
+
+	~Once();
+
+// -- Class methods
+
+	operator T&();
+
+private:
+
+// -- Members
+    T* value_;
+
+// -- Class members
+
+	static pthread_once_t once_;
+	static pthread_mutex_t mutex_;
+
+// -- Class methods
+
+	static void init(void);
+
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T> pthread_once_t Once<T>::once_ = PTHREAD_ONCE_INIT;
+template<class T> pthread_mutex_t Once<T>::mutex_;
+
+template<class T> Once<T>::Once() : value_(0)
+{
+}
+
+template<class T> Once<T>::~Once()
+{
+}
+
+template<class T> 
+Once<T>::operator T&()
+{
+	::pthread_once(&once_,init);
+
+	::pthread_mutex_lock(&mutex_); 
+
+	if(!value_) value_ = new T();
+
+	::pthread_mutex_unlock(&mutex_);
+
+	return *value_;
+}
+
+
+template<class T> void Once<T>::init()
+{
+	pthread_mutexattr_t attr;
+
+    ::pthread_mutexattr_init(&attr);
+
+	//::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
+
+	pthread_mutex_init(&mutex_,&attr);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/StaticMutex.cc b/eckit/src/eckit/thread/StaticMutex.cc
new file mode 100644
index 0000000..16d463b
--- /dev/null
+++ b/eckit/src/eckit/thread/StaticMutex.cc
@@ -0,0 +1,142 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/thread/StaticMutex.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static void init_mutex_attr(pthread_mutex_t* mutex) {
+
+    pthread_mutexattr_t attr;
+
+    CHECK_CALL_NOLOG( ::pthread_mutexattr_init(&attr) );
+    CHECK_CALL_NOLOG( ::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) );
+
+    CHECK_CALL_NOLOG( ::pthread_mutex_init(mutex, &attr) );
+
+    CHECK_CALL_NOLOG(::pthread_mutexattr_destroy(&attr));
+}
+
+struct StaticMutexRegister {
+
+    // ensures creation on first call as in pthread_once_t
+    // but without being dependent on order of static initialization
+    static StaticMutexRegister& instance();
+
+    StaticMutexRegister();
+
+    void add(pthread_mutex_t* mutex) {
+        CHECK_CALL_NOLOG(::pthread_mutex_lock(&register_lock));
+        set_.insert(mutex);
+        CHECK_CALL_NOLOG(::pthread_mutex_unlock(&register_lock));
+    }
+
+    void remove(pthread_mutex_t* mutex) {
+        CHECK_CALL_NOLOG(::pthread_mutex_lock(&register_lock));
+        set_.erase(mutex);
+        CHECK_CALL_NOLOG(::pthread_mutex_unlock(&register_lock));
+    }
+
+    pthread_mutex_t register_lock;
+    std::set<pthread_mutex_t*> set_;
+};
+
+static void get_locks()
+{
+    std::set<pthread_mutex_t*>& reg = StaticMutexRegister::instance().set_;
+    for(std::set<pthread_mutex_t*>::iterator i = reg.begin(); i != reg.end(); ++i) {
+        CHECK_CALL_NOLOG(::pthread_mutex_lock(*i));
+    }
+}
+
+static void release_locks_parent()
+{
+    std::set<pthread_mutex_t*>& reg = StaticMutexRegister::instance().set_;
+    for(std::set<pthread_mutex_t*>::reverse_iterator i = reg.rbegin(); i != reg.rend(); ++i) {
+        CHECK_CALL_NOLOG(::pthread_mutex_unlock(*i));
+    }
+}
+
+static void release_locks_child()
+{
+    std::set<pthread_mutex_t*>& reg = StaticMutexRegister::instance().set_;
+    for(std::set<pthread_mutex_t*>::reverse_iterator i = reg.rbegin(); i != reg.rend(); ++i) {
+        init_mutex_attr(*i);
+    }
+}
+
+StaticMutexRegister& StaticMutexRegister::instance()
+{
+    static StaticMutexRegister reg;
+    return reg;
+}
+
+StaticMutexRegister::StaticMutexRegister() {
+
+    init_mutex_attr(&register_lock);
+
+    // see ECKIT-140
+    CHECK_CALL_NOLOG(::pthread_atfork(get_locks, release_locks_parent, release_locks_child));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+StaticMutex::StaticMutex()
+// note the absence of the initializer list
+// this class is for static only objects, which are default initialized by the OS to zero
+// we use that fact in case lock() / unlock() are called before the constructor
+{
+    init();
+}
+
+StaticMutex::~StaticMutex()
+{
+    if(exists_) {
+        StaticMutexRegister::instance().remove(&mutex_);
+        CHECK_CALL_NOLOG(::pthread_mutex_destroy(&mutex_));
+    }
+}
+
+// if lock() is called before the constructor, we run init() which ensures the construction
+void StaticMutex::lock()
+{
+    init();
+    CHECK_CALL_NOLOG(::pthread_mutex_lock(&mutex_));
+}
+
+void StaticMutex::unlock()
+{
+    if(!exists_)
+    {
+        std::cerr << "StaticMutex::unlock() called before being constructed or locked" << std::endl;
+        ::abort();
+    }
+
+    CHECK_CALL_NOLOG(::pthread_mutex_unlock(&mutex_));
+}
+
+void StaticMutex::init()
+{
+    if(!exists_)
+    {
+        init_mutex_attr(&mutex_);
+        exists_ = true;
+
+        StaticMutexRegister::instance().add(&mutex_);
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/StaticMutex.h b/eckit/src/eckit/thread/StaticMutex.h
new file mode 100644
index 0000000..16cfbc4
--- /dev/null
+++ b/eckit/src/eckit/thread/StaticMutex.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Nov 2016
+
+
+#ifndef eckit_StaticMutex_h
+#define eckit_StaticMutex_h
+
+#include <pthread.h>
+
+#include "eckit/eckit.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Class meant to be used only for static mutexes protecting local resources inside a single compilation unit
+
+class StaticMutex : private NonCopyable {
+
+public: // methods
+
+    StaticMutex();
+
+	~StaticMutex();
+
+	void lock();
+	void unlock();
+
+protected: // members
+
+    void init();
+
+    /// since this will be static memory, it should be initialized to zero by the system
+
+    pthread_mutex_t mutex_;
+    bool            exists_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/Thread.cc b/eckit/src/eckit/thread/Thread.cc
new file mode 100644
index 0000000..0ef803c
--- /dev/null
+++ b/eckit/src/eckit/thread/Thread.cc
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Thread.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+Thread::Thread(bool autodel):
+	stop_(false),
+	autodel_(autodel)
+{
+}
+
+Thread::~Thread()
+{
+}
+
+void Thread::stop()
+{
+	AutoLock<Mutex> lock(mutex_);
+	stop_ = true;	
+}
+
+bool Thread::stopped()
+{
+	AutoLock<Mutex> lock(mutex_);
+	return stop_;	
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/Thread.h b/eckit/src/eckit/thread/Thread.h
new file mode 100644
index 0000000..621b948
--- /dev/null
+++ b/eckit/src/eckit/thread/Thread.h
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File thread/Thread.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_Thread_h
+#define eckit_Thread_h
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// This should be a Task
+
+class Thread : private NonCopyable {
+public:
+	friend class ThreadControler;
+
+// -- Contructors
+	
+	Thread(bool autodel = true); // 
+
+// -- Destructor
+
+	virtual ~Thread();
+
+// -- Methods
+
+	void stop();
+
+protected:
+
+// -- Members
+
+	Mutex mutex_;
+
+// -- Methods
+
+	bool  stopped();
+
+private:
+
+// -- Members
+
+	bool    stop_;
+	bool    autodel_;
+
+// -- Methods
+	
+	virtual void run() = 0;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/ThreadControler.cc b/eckit/src/eckit/thread/ThreadControler.cc
new file mode 100644
index 0000000..3458ff6
--- /dev/null
+++ b/eckit/src/eckit/thread/ThreadControler.cc
@@ -0,0 +1,196 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <signal.h>
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/runtime/Main.h"
+#include "eckit/log/Log.h"
+#include "eckit/memory/MemoryPool.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+ThreadControler::ThreadControler(Thread* proc,bool detached,size_t stack):
+    detached_(detached),
+    thread_(0),
+    proc_(proc),
+    stack_(stack),
+    running_(false)
+{
+    //cout << "ThreadControler::ThreadControler(" << this << ")" << " " << hex << pthread_self() << std::endl;
+}
+
+ThreadControler::~ThreadControler()
+{
+    //cout << "ThreadControler::~ThreadControler(" << this << ")" << " " <<hex <<  pthread_self() << std::endl;
+    AutoLock<MutexCond> lock(cond_);
+
+    if(running_)
+    {
+        // The Thread will delete itself
+        // so there is no need for:
+        // delete proc_;
+    }
+    else
+    {
+        //Log::warning() << "Deleting Thread in ThreadControler::~ThreadControler()" << " " << hex << pthread_self() << std::endl;
+        delete proc_;
+        proc_ = 0;
+    }
+}
+
+//------------------------------------------------------
+
+void ThreadControler::execute()
+{
+    // Make a copy, because "this" will desappear
+    Thread* proc = proc_;
+    if (detached_)
+        proc_ = 0;
+
+    //cout << "ThreadControler::execute(" << this << ")" <<  " " << hex << pthread_self() << std::endl;
+    //=================
+    // Make sure the logs are created...
+
+    Monitor::instance().startup();
+    Monitor::instance().parent(Main::instance().taskID());
+
+    //============
+
+
+    { // Signal that we are running
+
+        AutoLock<MutexCond> lock(cond_);
+        running_ = true;
+        cond_.signal();
+
+        // NOTE: "this" is not valid any more after this point
+
+    }
+
+    //=============
+
+    // We don't want to receive reconfigure events
+
+    sigset_t set,old_set;
+
+    sigemptyset(&set);
+
+    sigaddset(&set, SIGHUP);
+    sigaddset(&set, SIGCHLD);
+    sigaddset(&set, SIGPIPE);
+
+    THRCALL(::pthread_sigmask(SIG_BLOCK, &set, &old_set));
+
+    //=============
+
+    ASSERT(proc);
+
+    try {
+        proc->run();
+    }
+    catch(std::exception& e) {
+        Log::error() << "** " << e.what() << " Caught in "   << Here() << std::endl;
+        Log::error() << "** Exception is terminates thread " << pthread_self() << std::endl;
+    }
+
+    if(proc->autodel_)
+    {
+        delete proc;
+    }
+}
+
+void* ThreadControler::startThread (void* data)
+{
+    //cout << "ThreadControler::startThread(" << data << ")" << " " << hex << pthread_self() << std::endl;
+    reinterpret_cast<ThreadControler*>(data)->execute(); // static_cast or dynamic_cast ??
+    return 0;
+}
+
+void ThreadControler::start()
+{
+    //cout << "ThreadControler::start(" << this << ")" << " " << hex << pthread_self() << std::endl;
+    ASSERT(thread_ == 0);
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+
+    if(stack_)
+    {
+        THRCALL(::pthread_attr_setstacksize(&attr, stack_));
+    }
+    if(detached_)
+        THRCALL(::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
+    else
+        THRCALL(::pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
+
+    AutoLock<MutexCond> lock(cond_);
+
+    THRCALL(::pthread_create(&thread_,&attr,startThread,this));
+
+    pthread_attr_destroy(&attr);
+
+    while(!running_)
+        cond_.wait();
+}
+
+void ThreadControler::kill()
+{
+    pthread_cancel(thread_);
+    //pthread_kill(thread_,sig);
+}
+
+void ThreadControler::stop()
+{
+    // Due to legacy code, this stop routine may be called on detached threads. Don't
+    // stress about it!
+    if (!detached_ && proc_ != NULL) {
+        proc_->stop();
+    }
+}
+
+void ThreadControler::wait()
+{
+    ASSERT(!detached_);
+    // if(running_)
+    THRCALL(::pthread_join(thread_,0));
+}
+
+bool ThreadControler::active()
+{
+    if(thread_ != 0)
+    {
+        // Try see if it exists
+
+        int policy;
+        sched_param param;
+
+        int n = pthread_getschedparam(thread_, &policy, &param);
+
+        // The thread does not exist
+        if(n != 0)
+            thread_ = 0;
+
+    }
+    return thread_ != 0;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/ThreadControler.h b/eckit/src/eckit/thread/ThreadControler.h
new file mode 100644
index 0000000..8219423
--- /dev/null
+++ b/eckit/src/eckit/thread/ThreadControler.h
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File thread/ThreadControler.h
+// Baudouin Raoult - ECMWF May 96
+
+#ifndef eckit_ThreadControler_h
+#define eckit_ThreadControler_h
+
+#include "eckit/thread/MutexCond.h"
+#include "eckit/runtime/Task.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class Thread;
+
+/// @note Don't subclass from ThreadControler but from Thread
+class ThreadControler : public Task {
+public:
+
+// -- Contructors
+	
+	/// @note ThreadControler takes ownership of Thread
+    ThreadControler(Thread*, bool detached = true, size_t stack = 0);
+
+// -- Destructor
+	
+	~ThreadControler();
+
+// -- Overridden methods
+
+	// From Task
+
+	virtual void start();
+	virtual void stop();
+	virtual void kill();
+	virtual void wait();
+	virtual bool active();
+
+protected:
+
+// -- Members
+	
+	MutexCond  cond_;
+	bool       detached_;
+
+private:
+
+// -- Members
+	
+    pthread_t   thread_;
+    Thread*     proc_;
+    size_t      stack_;
+    bool        running_;
+
+// -- Methods
+	
+	void execute();
+
+    static void* startThread (void *data);
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/ThreadPool.cc b/eckit/src/eckit/thread/ThreadPool.cc
new file mode 100644
index 0000000..1897880
--- /dev/null
+++ b/eckit/src/eckit/thread/ThreadPool.cc
@@ -0,0 +1,237 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ThreadPool.cc
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/thread/ThreadPool.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ThreadPoolThread : public Thread {
+    ThreadPool& owner_;
+    void run();
+public:
+    ThreadPoolThread(ThreadPool& owner):
+        owner_(owner) {}
+};
+
+void ThreadPoolThread::run()
+{
+    owner_.notifyStart();
+
+    Monitor::instance().name(owner_.name());
+
+    //Log::info() << "Start of ThreadPoolThread " << std::endl;
+
+
+    for (;;)
+    {
+        Monitor::instance().show(false);
+        Log::status() << "-" << std::endl;
+        ThreadPoolTask* r = owner_.next();
+        if (!r) break;
+        Monitor::instance().show(true);
+
+        r->pool_ = &owner_;
+
+
+        try {
+            r->execute();
+        }
+        catch (std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is reported" << std::endl;
+            owner_.error(e.what());
+        }
+
+        try {
+            delete r;
+        }
+        catch (std::exception& e)
+        {
+            Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+            Log::error() << "** Exception is reported" << std::endl;
+            owner_.error(e.what());
+        }
+
+        owner_.endTask();
+
+    }
+
+
+    // Log::info() << "End of ThreadPoolThread " << std::endl;
+
+    owner_.notifyEnd();
+}
+
+ThreadPool::ThreadPool(const std::string& name, size_t count, size_t stack):
+    count_(0),
+    stack_(stack),
+    running_(0),
+    tasks_(0),
+    name_(name),
+    error_(false)
+{
+    //Log::info() << "ThreadPool::ThreadPool " << nme_ << " " << count << std::endl;
+    resize(count);
+
+}
+
+ThreadPool::~ThreadPool()
+{
+    //Log::info() << "ThreadPool::~ThreadPool " << name_ << std::endl;
+
+    try {
+        waitForThreads();
+    }
+    catch (std::exception& e)
+    {
+        Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+        Log::error() << "** Exception is ignored" << std::endl;
+    }
+}
+
+void ThreadPool::waitForThreads()
+{
+
+    for (size_t i = 0; i < count_ ; i++) {
+        push(0);
+    }
+
+
+    AutoLock<MutexCond> lock(done_);
+    //Log::info() << "ThreadPool::waitForThreads " << name_ << " running: " << running_ << std::endl;
+    while (running_)
+    {
+        //Log::info() << "ThreadPool::waitForThreads " << name_ << " running: " << running_ << std::endl;
+        done_.wait();
+    }
+
+    if (error_)
+    {
+        error_ = false;
+        throw SeriousBug(std::string("ThreadPool::waitForThreads: ") + errorMessage_);
+    }
+}
+
+void ThreadPool::notifyStart()
+{
+    AutoLock<MutexCond> lock(done_);
+    running_++;
+    done_.signal();
+    // Log::info() << "ThreadPool::notifyStart " << name_ << " running: " << running_ << std::endl;
+}
+
+void ThreadPool::notifyEnd()
+{
+    AutoLock<MutexCond> lock(done_);
+    running_--;
+    done_.signal();
+    // Log::info() << "ThreadPool::notifyEnd " << name_ << " running: " << running_ << std::endl;
+}
+
+
+void ThreadPool::startTask()
+{
+    AutoLock<MutexCond> lock(active_);
+    tasks_++;
+    active_.signal();
+    // Log::info() << "ThreadPool::notifyStart " << name_ << " running: " << running_ << std::endl;
+}
+
+void ThreadPool::endTask()
+{
+    AutoLock<MutexCond> lock(active_);
+    tasks_--;
+    active_.signal();
+    // Log::info() << "ThreadPool::notifyStart " << name_ << " running: " << running_ << std::endl;
+}
+
+void ThreadPool::error(const std::string& msg)
+{
+    AutoLock<MutexCond> lock(done_);
+    if (error_) errorMessage_ += " | ";
+    error_ = true;
+    errorMessage_ += msg;
+
+}
+
+void ThreadPool::push(ThreadPoolTask* r)
+{
+    if (r) {
+        startTask();
+    }
+
+    AutoLock<MutexCond> lock(ready_);
+    queue_.push_back(r);
+    ready_.signal();
+}
+
+void ThreadPool::push(std::list<ThreadPoolTask*>& l)
+{
+    AutoLock<MutexCond> lock(ready_);
+
+    for (std::list<ThreadPoolTask*>::iterator j = l.begin(); j != l.end(); ++j)
+        queue_.push_back((*j));
+
+    l.clear();
+    ready_.signal();
+}
+
+ThreadPoolTask* ThreadPool::next()
+{
+    AutoLock<MutexCond> lock(ready_);
+    while (queue_.empty())
+        ready_.wait();
+
+    ThreadPoolTask* r = queue_.front();
+    queue_.pop_front();
+
+    if (!queue_.empty())
+        ready_.signal();
+
+    return r;
+}
+
+void ThreadPool::wait() {
+    AutoLock<MutexCond> lock(active_);
+
+    while (tasks_) {
+        active_.wait();
+    }
+}
+
+void ThreadPool::resize(size_t size) {
+
+    while (count_ > size) {
+        push(0);
+        count_--;
+    }
+
+    while (count_ < size) {
+        ThreadControler c(new ThreadPoolThread(*this), true, stack_);
+        c.start();
+        count_++;
+    }
+}
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/thread/ThreadPool.h b/eckit/src/eckit/thread/ThreadPool.h
new file mode 100644
index 0000000..da9298d
--- /dev/null
+++ b/eckit/src/eckit/thread/ThreadPool.h
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_ThreadPool_h
+#define eckit_ThreadPool_h
+
+#include "eckit/eckit.h"
+#include "eckit/thread/MutexCond.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ThreadPool;
+
+//-----------------------------------------------------------------------------
+
+class ThreadPoolTask {
+public:
+
+    virtual ~ThreadPoolTask() {}
+    virtual void execute() = 0;
+
+    friend class ThreadPoolThread;
+
+protected:
+
+    ThreadPool& pool() { return *pool_; }
+
+private:
+
+    ThreadPool* pool_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+class ThreadPool : private NonCopyable {
+
+public: // methods
+
+    ThreadPool(const std::string& name, size_t count, size_t stack = 0);
+
+	~ThreadPool();
+
+    void push(ThreadPoolTask*);
+    void push(std::list<ThreadPoolTask*>&);
+    ThreadPoolTask* next();
+    void notifyStart();
+    void notifyEnd();
+    void waitForThreads();
+    const std::string& name() const { return name_; }
+    void error(const std::string&);
+
+    void wait();
+    void resize(size_t);
+
+    void startTask();
+    void endTask();
+
+private: // members
+
+    MutexCond ready_;
+    MutexCond done_;
+    MutexCond active_;
+
+    size_t  count_;
+    size_t  stack_;
+    size_t  running_;
+    size_t  tasks_;
+
+    std::string errorMessage_;
+    std::string name_;
+    std::list<ThreadPoolTask*> queue_;
+
+    bool error_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/thread/ThreadSingleton.h b/eckit/src/eckit/thread/ThreadSingleton.h
new file mode 100644
index 0000000..713d620
--- /dev/null
+++ b/eckit/src/eckit/thread/ThreadSingleton.h
@@ -0,0 +1,129 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file ThreadSingleton.h
+/// @author Baudouin Raoult
+
+#ifndef eckit_ThreadSingleton_h
+#define eckit_ThreadSingleton_h
+
+#include <pthread.h>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template< typename TYPE >
+struct NewAlloc0 {
+    TYPE* operator() () { return new TYPE(); }
+};
+
+template< typename TYPE, typename P1 >
+struct NewAlloc1 {
+    NewAlloc1( const P1& p1 ) : p1_(p1) {}
+    TYPE* operator() () { return new TYPE( p1_ ); }
+    P1 p1_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template< typename T, typename A = NewAlloc0<T> >
+class ThreadSingleton : private NonCopyable {
+public:
+
+    ThreadSingleton();
+    ThreadSingleton( const A& alloc );
+
+	~ThreadSingleton();
+
+    T& instance();
+
+private: // members
+
+    A alloc_;
+
+private: // class members
+
+	static pthread_once_t once_;
+	static pthread_key_t  key_;
+
+private: // class methods
+
+	static void init(void);
+	static void cleanUp(void*);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template< typename T, typename A> pthread_once_t ThreadSingleton<T,A>::once_ = PTHREAD_ONCE_INIT;
+template< typename T, typename A> pthread_key_t ThreadSingleton<T,A>::key_;
+
+template< typename T, typename A>
+ThreadSingleton<T,A>::ThreadSingleton() : alloc_( A() )
+{
+}
+
+template< typename T, typename A>
+ThreadSingleton<T,A>::ThreadSingleton( const A& alloc ) : alloc_( alloc )
+{
+
+}
+
+template< typename T, typename A>
+ThreadSingleton<T,A>::~ThreadSingleton()
+{
+    ::pthread_once(&once_,init);
+
+    T* value = (T*) ::pthread_getspecific(key_);
+    if(value) {
+        ::pthread_key_delete(key_);
+        delete value;
+    }
+}
+
+template< typename T, typename A>
+T& ThreadSingleton<T,A>::instance()
+{
+    ::pthread_once(&once_,init);
+
+    T* value = (T*) ::pthread_getspecific(key_);
+	if(!value)
+	{
+        value = alloc_();
+        THRCALL(::pthread_setspecific(key_,value));
+	}
+
+    return *value;
+}
+
+template< typename T, typename A>
+void ThreadSingleton<T,A>::cleanUp(void* data)
+{
+    delete (T*) data;
+    ::pthread_setspecific(key_,0);
+}
+
+template< typename T, typename A>
+void ThreadSingleton<T,A>::init()
+{
+    ::pthread_key_create(&key_,cleanUp);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/transaction/TxnEvent.cc b/eckit/src/eckit/transaction/TxnEvent.cc
new file mode 100644
index 0000000..0108044
--- /dev/null
+++ b/eckit/src/eckit/transaction/TxnEvent.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/transaction/TxnEvent.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+ClassSpec TxnEvent::classSpec_ = {&Streamable::classSpec(),"TxnEvent",};
+Reanimator<TxnEvent> TxnEvent::reanimator_;
+
+TxnEvent::TxnEvent():
+	txnID_(0)
+{
+}
+
+TxnEvent::TxnEvent(Stream& s):
+	txnID_(0)
+{
+	s >> txnID_;	
+}
+
+void TxnEvent::encode(Stream& s) const
+{
+	s << txnID_;
+}
+
+TxnEvent::~TxnEvent()
+{
+}
+
+void TxnEvent::print(std::ostream& s) const
+{
+	s << "TxnEvent " << txnID_;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/transaction/TxnEvent.h b/eckit/src/eckit/transaction/TxnEvent.h
new file mode 100644
index 0000000..0035283
--- /dev/null
+++ b/eckit/src/eckit/transaction/TxnEvent.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Jan 97
+
+#ifndef eckit_TxnEvent_h
+#define eckit_TxnEvent_h
+
+#include "eckit/serialisation/Streamable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef unsigned long long TxnID;
+
+class TxnEvent : public Streamable {
+public:
+
+// -- Contructors
+
+	TxnEvent();
+	TxnEvent(Stream&);
+
+// -- Destructor
+
+	virtual ~TxnEvent();
+
+// -- Methods
+
+	const TxnID& transactionID() const { return txnID_; }
+	void transactionID(const TxnID& id){ txnID_ = id; }
+
+// -- Overridden methods
+
+    // From Streamble
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+
+// -- Class members
+
+	static const ClassSpec&  classSpec()        { return classSpec_;}
+
+protected:
+
+// -- Methods
+	
+	virtual void print(std::ostream&) const;
+
+private:
+
+// No copy allowed
+
+	TxnEvent(const TxnEvent&);
+	TxnEvent& operator=(const TxnEvent&);
+
+// -- Members
+
+	TxnID txnID_;
+
+// -- Class members
+
+	static ClassSpec              classSpec_;
+	static Reanimator<TxnEvent>   reanimator_;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const TxnEvent& p)
+		{ p.print(s); return s; }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/transaction/TxnLog.cc b/eckit/src/eckit/transaction/TxnLog.cc
new file mode 100644
index 0000000..5511d21
--- /dev/null
+++ b/eckit/src/eckit/transaction/TxnLog.cc
@@ -0,0 +1,352 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/container/MappedArray.h"
+#include "eckit/container/SharedMemArray.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Seconds.h"
+#include "eckit/log/TimeStamp.h"
+#include "eckit/runtime/Monitor.h"
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/transaction/TxnLog.h"
+#include "eckit/utils/Translator.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class TxnArray : private eckit::NonCopyable {
+
+public:
+
+    typedef TxnID*       iterator;
+    typedef const TxnID* const_iterator;
+
+    virtual ~TxnArray() {}
+
+    virtual void lock() = 0;
+    virtual void unlock() = 0;
+
+    virtual unsigned long size() = 0;
+    virtual TxnID& operator[](unsigned long n) = 0;
+};
+
+class MemoryMappedTxnArray : public TxnArray {
+
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual TxnID& operator[](unsigned long n) { return map_[n]; }
+
+    MappedArray<TxnID> map_;
+
+public:
+
+    MemoryMappedTxnArray(const PathName& path, unsigned long size) :
+        TxnArray(),
+        map_(path, size)
+    {}
+};
+
+class SharedMemoryTxnArray : public TxnArray {
+
+    virtual void lock() { map_.lock(); }
+    virtual void unlock()  { map_.unlock(); }
+
+    virtual unsigned long size()   { return map_.size(); }
+    virtual TxnID& operator[](unsigned long n) { return map_[n]; }
+
+    SharedMemArray<TxnID> map_;
+
+public:
+
+    SharedMemoryTxnArray(const PathName& path, const std::string& name, unsigned long size) :
+        TxnArray(),
+        map_(path, name, size)
+    {}
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static PathName pathName(const std::string& name)
+{
+	PathName path = std::string("~/txn/") + name;
+	path.mkdir();
+	return path;
+}
+
+#if 0 // unused
+static PathName lockName(const std::string& name)
+{
+	PathName path = std::string("~/txn/") + name + "/lock";
+	path.touch();
+	return path;
+}
+#endif
+
+template<class T>
+TxnLog<T>::TxnLog(const std::string& name):
+	path_(pathName(name)),
+    next_(path_ +  "/next")
+{
+    std::string txnArrayType = Resource<std::string>("txnArrayType", "MemoryMapped");
+
+    if(txnArrayType == "MemoryMapped") {
+        nextID_ = new MemoryMappedTxnArray(next_, 1);
+    }
+    else if(txnArrayType == "SharedMemory") {
+        std::string s = path_;
+        std::replace(s.begin(), s.end(), '/', '-');
+        nextID_ = new SharedMemoryTxnArray(next_, s, 1);
+    }
+    else {
+        std::ostringstream oss;
+        oss << "Invalid txnArrayType : " << txnArrayType << ", valid types are 'MemoryMapped' and 'SharedMemory'" << std::endl;
+        throw eckit::BadParameter(oss.str(), Here());
+    }
+
+
+
+    AutoLock<TxnArray > lock(*nextID_);
+	Log::debug() << "TxnLog file is " << path_ << std::endl;
+
+	PathName done = path_ + "/done";
+	done.mkdir();
+}
+
+template<class T>
+TxnLog<T>::~TxnLog()
+{
+}
+
+template<class T>
+PathName TxnLog<T>::name(T& event)
+{
+    std::ostringstream s;
+    s << std::setfill('0') << std::setw(10) << event.transactionID();
+    return path_ + "/" + s.str();
+}
+
+
+template<class T>
+void TxnLog<T>::begin(T& event)
+{
+    AutoLock<TxnArray > lock(*nextID_);
+
+	if(event.transactionID() == 0)
+        event.transactionID(++(*nextID_)[0]);
+
+	PathName path = name(event);
+	ASSERT(!path.exists());
+
+	FileStream log(path,"w");
+	log << event;
+}
+
+template<class T>
+void TxnLog<T>::end(T& event,bool backup)
+{
+    AutoLock<TxnArray > lock(*nextID_);
+
+	PathName path = name(event);
+
+	if(backup)
+	{
+        std::ostringstream s;
+        s << path.dirName() << "/done/" << TimeStamp(time(0),"%Y%m%d");
+
+		// Append to current day's backup
+
+        FileStream log(s.str(), "a");
+		log << event;
+	}
+
+	// Remove file
+
+	path.unlink();
+}
+
+template<class T>
+class RecoverThread : public Thread {
+    TxnArray& nextID_;
+	TxnRecoverer<T>&    client_;
+	std::vector<PathName>    result_;
+	long                age_;
+	time_t              now_;
+	virtual void run();
+public:
+    RecoverThread(const PathName&,TxnArray&,
+		TxnRecoverer<T>&,long);
+	void recover();
+};
+
+template<class T>
+RecoverThread<T>::RecoverThread(const PathName& path,
+        TxnArray& nextID,
+		TxnRecoverer<T>& client,
+		long age):
+    nextID_(nextID),
+	client_(client),
+	age_(age),
+	now_(::time(0))
+{
+    AutoLock<TxnArray > lock(nextID_);
+	PathName::match(path+"/[0-9]*",result_);
+	
+	// Sort by ID to preserve order
+	std::sort(result_.begin(),result_.end());
+	Log::info() << result_.size() << " task(s) found in log files" << std::endl;
+
+	if(result_.size())
+	{
+		PathName last = result_[result_.size()-1];
+		std::string name = last.baseName();
+		TxnID id = Translator<std::string,unsigned long long>()(name);
+		if(id >= nextID_[0])
+			nextID_[0] = id + 1;
+	}
+}
+
+template<class T>
+void RecoverThread<T>::run()
+{
+	Monitor::instance().name("recover");
+	recover();
+}
+
+template<class T>
+void RecoverThread<T>::recover()
+{
+	for(Ordinal i = 0 ; i < result_.size() ; i++)
+	{
+
+		if(now_ - result_[i].created() < age_)
+			Log::info() << "Skipping " << result_[i] << ", created " << 
+				Seconds(now_ - result_[i].created()) << " ago." << std::endl;
+		else
+			try {
+				FileStream log(result_[i],"r");
+				T *task = Reanimator<T>::reanimate(log);
+				if(task) {
+					ASSERT(task->transactionID() < nextID_[0]);
+					client_.push(task);
+				}
+			}
+			catch(std::exception& e)
+			{
+				Log::error() << "** " << e.what() << " Caught in " <<
+					Here() << std::endl;
+				Log::error() << "** Exception is ignored" << std::endl;
+			}
+	}
+}
+
+template<class T>
+void TxnLog<T>::recover(TxnRecoverer<T>& client,bool inThread,long age)
+{
+	if(inThread)
+	{
+        ThreadControler c(new RecoverThread<T>(path_,*nextID_,client,age));
+		c.start();
+	}
+	else
+	{
+         RecoverThread<T> r(path_,*nextID_,client,age);
+		 r.recover();
+	}
+}
+
+template <class T>
+void TxnLog<T>::find(TxnFinder<T>& r)
+{
+
+	PathName path = path_ + "/[0-9]*";
+	std::vector<PathName>  active;
+
+	PathName::match(path,active);
+
+	for(Ordinal j = 0; (j < active.size()); ++j)
+	{
+		try {
+			 FileStream log(active[j],"r");
+			 std::auto_ptr<T> task(Reanimator<T>::reanimate(log));
+			 if(task.get()) 
+				if(!r.found(*task))
+				return;
+		}
+		catch(Abort& e)
+		{
+			Log::error() << "** " << e.what() << " Caught in " <<
+				Here() << std::endl;
+			Log::error() << "** Exception is re-thrown" << std::endl;
+			throw;
+		}
+		catch(std::exception& e)
+		{
+			Log::error() << "** " << e.what() << " Caught in " <<
+							Here() << std::endl;
+			Log::error() << "** Exception is ignored" << std::endl;
+		}
+	}
+
+
+	if(r.old())
+	{
+
+		// Look for non active transactions
+
+		path = path_ + "/done/[0-9]*";
+		std::vector<PathName> dates;
+
+		PathName::match(path,dates);
+
+		// Sort by date in reverse order
+        std::sort(dates.begin(),dates.end(),std::greater<PathName>());
+
+
+		for(Ordinal k = 0 ; k < dates.size() ; k++)
+		{
+			Log::info() << "Searching " << dates[k] << std::endl;
+			try {
+				FileStream log(dates[k],"r");
+				T *task = 0;
+                while( (task = Reanimator<T>::reanimate(log)) )
+				{
+                    if(!r.found(*(std::auto_ptr<T>(task))))
+						return;
+				}
+			}
+			catch(Abort& e)
+			{
+				Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+				Log::error() << "** Exception is re-thrown" << std::endl;
+				throw;
+			}
+			catch(std::exception& e)
+			{
+				Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+				Log::error() << "** Exception is ignored" << std::endl;
+			}
+		}
+	}
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/transaction/TxnLog.h b/eckit/src/eckit/transaction/TxnLog.h
new file mode 100644
index 0000000..9bdf7ed
--- /dev/null
+++ b/eckit/src/eckit/transaction/TxnLog.h
@@ -0,0 +1,94 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Jan 97
+
+#ifndef eckit_TxnLog_h
+#define eckit_TxnLog_h
+
+#include "eckit/runtime/Main.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/transaction/TxnEvent.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// T should be a subclass of TxnEvent
+
+template<class T>
+struct TxnRecoverer {
+   virtual ~TxnRecoverer() {}
+	virtual void push(T*) = 0;
+};
+
+template<class T>
+struct TxnFinder {
+   virtual ~TxnFinder() {}
+	virtual bool found(T&) = 0;
+	virtual bool old() { return false; }
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class TxnArray;
+
+
+template<class T>
+class TxnLog {
+public:
+
+// -- Contructors
+
+	TxnLog(const std::string& = Main::instance().name());
+
+// -- Destructor
+
+	~TxnLog();
+
+// -- Methods
+
+	void begin(T&);
+	void end(T&,bool);
+	void recover(TxnRecoverer<T>&,bool,long);
+	void find(TxnFinder<T>&);
+
+private:
+
+// No copy allowed
+
+	TxnLog(const TxnLog<T>&);
+	TxnLog<T>& operator=(const TxnLog<T>&);
+
+// -- Methods
+
+	PathName name(T& event);
+
+// -- Members
+
+	PathName           path_;
+	PathName           next_;   // Should be declared after 'path_'
+    TxnArray*          nextID_; // Should be declared after 'next_'
+
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "TxnLog.cc"
+
+#endif
diff --git a/eckit/src/eckit/types/ClimateDate.cc b/eckit/src/eckit/types/ClimateDate.cc
new file mode 100644
index 0000000..e45a07a
--- /dev/null
+++ b/eckit/src/eckit/types/ClimateDate.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/ClimateDate.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Climate date is a 30 day date
+
+static Date makeDate(long year,long month,long day)
+{
+
+	ASSERT(month >= 1 && month <= 12);
+	ASSERT(day   >= 1 && day   <= 30);
+
+	long m = (month-1)*30 + day;
+	ASSERT ( m >= 1 && m <= 12*30);
+
+	return Date(year,m);
+}
+
+ClimateDate::ClimateDate(long year,long month,long day):
+	date_(makeDate(year,month,day))
+{
+}
+
+static Date makeDate(const std::string& s)
+{
+	Date date(s);
+	long year = date.year();
+	Date first(year,1,1);
+	long day = (date - first + 1);
+
+	ASSERT ( day >= 1 && day <= 12*30);
+
+	return Date(year,day);
+}
+
+ClimateDate::ClimateDate(const std::string& s):
+	date_(makeDate(s))
+{
+}
+
+ClimateDate::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+void ClimateDate::print(std::ostream& s) const
+{
+
+	long year      = date_.year();
+	long dayOfYear = date_ - Date(year,1,1);
+#if 0
+	long month     = dayOfYear / 30 + 1;
+	long day       = dayOfYear % 30 + 1;
+
+	char oldfill = s.fill();
+	s << year << '-' << std::setw(2) << std::setfill('0') << month
+		<< '-' << std::setw(2) << std::setfill('0') << day << std::setfill(oldfill);
+#else
+
+	char oldfill = s.fill();
+	 s << year << '-' << std::setw(3) << std::setfill('0') << (dayOfYear + 1)
+		<< std::setfill(oldfill);
+#endif
+}
+
+void ClimateDate::dump(DumpLoad& a) const
+{
+	date_.dump(a);
+}
+
+void ClimateDate::load(DumpLoad& a)
+{
+	date_.load(a);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/ClimateDate.h b/eckit/src/eckit/types/ClimateDate.h
new file mode 100644
index 0000000..8f471f1
--- /dev/null
+++ b/eckit/src/eckit/types/ClimateDate.h
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClimateDate.h
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_ClimateDate_h
+#define eckit_ClimateDate_h
+
+#include "eckit/types/Date.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class ClimateDate {
+public:
+
+// -- Contructors
+
+	ClimateDate()                               {}
+	ClimateDate(const Date& date) : date_(date) {}
+	ClimateDate(const std::string& s);
+	ClimateDate(long,long,long);
+
+#include "eckit/types/ClimateDate.b"
+
+// -- Destructor
+
+	~ClimateDate() {}
+
+// -- Convertors
+	
+	operator std::string() const;
+
+// -- Operators
+
+	bool operator==(const ClimateDate& other) const 
+		{ return date_ == other.date_ ;}
+
+	bool operator!=(const ClimateDate& other) const
+		{ return date_ != other.date_ ;}
+
+	bool operator<(const ClimateDate& other)  const
+		{ return date_ < other.date_ ;}
+
+	bool operator>(const ClimateDate& other)  const
+		{ return date_ > other.date_ ;}
+
+	bool operator<=(const ClimateDate& other)  const
+		{ return date_ <= other.date_ ;}
+
+	bool operator>=(const ClimateDate& other)  const
+		{ return date_ >= other.date_ ;}
+
+// -- Methods
+
+	const Date& date() { return date_ ; }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class methods
+
+// -- Friends
+
+	friend std::ostream& operator<< (std::ostream& s, const ClimateDate& date)
+		{ date.print(s); return s; }
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+	
+	Date date_;
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+// -- Class methods
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/types/Coord.cc b/eckit/src/eckit/types/Coord.cc
new file mode 100644
index 0000000..26f0e1f
--- /dev/null
+++ b/eckit/src/eckit/types/Coord.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Coord.h"
+#include "eckit/persist/DumpLoad.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Longitude::Longitude(double l):
+	Coord(l)
+{
+	while (value_ <    0.0) value_ += 360.0;
+	while (value_ >= 360.0) value_ -= 360.0;
+}
+
+Latitude::Latitude(double l):
+	Coord(l)
+{
+	ASSERT(l <= 90.0 && l >= -90.0);
+}
+
+void Coord::dump(DumpLoad& a) const
+{
+	a.dump(value_);
+}
+
+void Coord::load(DumpLoad& a)
+{
+	a.load(value_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Coord.h b/eckit/src/eckit/types/Coord.h
new file mode 100644
index 0000000..4df8156
--- /dev/null
+++ b/eckit/src/eckit/types/Coord.h
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// Claude  Gibert - ECMWF Dec 97
+
+#ifndef eckit_Coord_h
+#define eckit_Coord_h
+
+#include "eckit/serialisation/Stream.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad;
+
+class Coord {
+public:
+	
+	friend std::ostream& operator<<(std::ostream& s,const Coord& x)
+		{ x.print(s); return s; }
+
+	friend Stream& operator>>(Stream& s,Coord& x)
+		{ return s >> x.value_; }
+
+// -- Contructors
+
+	Coord(double l = 0) : value_(l) {}
+	Coord(const Coord& other) : value_(other.value_) {}
+
+
+// -- Operators
+
+	Coord& operator=(const Coord& other) 
+		{ value_ = other.value_; return *this;}
+
+	Coord operator+(const Coord& other) const
+		{ return Coord(value_ + other.value_);}
+
+	Coord& operator+=(const Coord& other) 
+		{ value_ += other.value_; return *this;}
+
+	bool operator==(const Coord& other) const
+		{ return value_ == other.value_; }
+
+	bool operator<(const Coord& other) const
+		{ return value_ < other.value_; }
+
+	bool operator<=(const Coord& other) const
+		{ return value_ <= other.value_; }
+
+	bool operator>(const Coord& other) const
+		{ return value_ > other.value_; }
+
+	bool operator>=(const Coord& other) const
+		{ return value_ >= other.value_; }
+
+// -- 
+#if 0
+	double operator-(const Coord& other) const
+		{ return value_ - other.value_;}
+#endif
+
+// -- Methods
+
+	void print(std::ostream& s) const { s << value_; }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class methods
+
+	double value() { return value_; }
+
+protected:
+
+
+// -- Members
+
+	double value_;
+
+
+	friend class Area;
+};
+
+typedef std::vector<Coord> CoordList;
+
+//---------------------------------------------------------
+// Longitude
+//---------------------------------------------------------
+
+class Longitude: public Coord {
+public:
+	
+// -- Contructors
+
+	Longitude(double l = 0);
+
+};
+
+//---------------------------------------------------------
+// Latitude
+//---------------------------------------------------------
+
+class Latitude: public Coord {
+public:
+	
+// -- Contructors
+
+	Latitude(double l = 0);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Date.cc b/eckit/src/eckit/types/Date.cc
new file mode 100644
index 0000000..422100c
--- /dev/null
+++ b/eckit/src/eckit/types/Date.cc
@@ -0,0 +1,339 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Date.h"
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static const char* months[] =
+{ "jan","feb","mar","apr","may","jun",
+  "jul","aug","sep","oct","nov","dec"
+};
+
+static void check(const Date& date,long value)
+{
+
+	if(value <= 999999 ) value += 19000000;
+
+	if(value != date.yyyymmdd())
+	{
+        std::ostringstream os;
+        os << "Invalid date " << value << " becomes " << date;
+        throw BadDate(os.str());
+	}
+}
+
+
+Date::Date(long date):
+	julian_(dateToJulian(date))
+{
+	if(date>0) check(*this,date);
+}
+
+Date::Date(long year,long month,long day):
+	julian_(dateToJulian(year*10000 + month*100 +day))
+{
+	check(*this,year*10000 + month*100 +day);
+}
+
+// Warning, unchecked...
+Date::Date(long year,long dayOfYear)
+{
+	julian_ = dateToJulian(year*10000 + 101); // 1 of jan
+	julian_ += (dayOfYear - 1);
+	ASSERT(this->year() == year);
+}
+
+long Date::parse(const std::string& s)
+{
+
+	Tokenizer parse("-");
+	std::vector<std::string> result;
+
+	parse(s,result);
+
+	bool err  = false;
+	long value = 0;
+	int i;
+
+	switch(result.size())
+	{
+		case 1:
+			switch(s.length())
+			{
+				case 3:
+					// For climatology
+					err = true;
+					for(i = 0; i < 12; i++)
+						if(s == months[i])
+						{
+							value = 1900 * 10000 + (i+1)*100 + 1;
+							err   = false;
+							break;
+						}
+					break;
+
+				case 6:
+				case 8:
+					value   = atol(s.c_str());
+					break;
+
+				default:
+					err = true;
+					break;
+			}
+			break;
+
+
+		case 2:
+
+			// Dates as mm-dd
+			if(result[0].length() == 2 && result[1].length() == 2)
+			{
+				long month = atol(result[0].c_str());
+				long day   = atol(result[1].c_str());
+
+				Date date(2004,month,day); // 2004 is a leap year
+
+				value  = date.yyyymmdd();
+			}
+			else
+			{
+
+				// Dates as yyyy-ddd
+
+				if(result[0].length() != 2 && result[0].length() != 4) err = true;
+
+				if(result[1].length() != 3) err = true;
+
+				{
+					long year = atol(result[0].c_str());
+					long day  = atol(result[1].c_str());
+
+					Date date(year,1,1);
+					date += day - 1;
+					value = date.yyyymmdd();
+				}
+			}
+
+			break;
+
+		// Dates as yyyy-mm-dd
+
+		case 3:
+
+			if(result[0].length() != 2 && result[0].length() != 4) err = true;
+			if(result[1].length() != 2) err = true;
+			if(result[2].length() != 2) err = true;
+
+			value = atol(result[0].c_str()) * 10000 +
+					atol(result[1].c_str()) * 100 +
+					atol(result[2].c_str());
+
+			break;
+
+
+		default:
+			err = true;
+			break;
+	}
+
+	if(err) throw BadDate(std::string("Invalid date ") + s);
+
+	// Come back here....
+	// temp patch for monthly means
+	if( (value%100) == 0) value++;
+
+	return value;
+}
+
+Date::Date(const std::string& s)
+{
+	long value = parse(s);
+	julian_    = dateToJulian(value);
+	check(*this,value);
+}
+
+Date::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+// Returns a date in the format yyyymmdd from a julian number
+
+long Date::julianToDate(long jdate)
+{
+	long x,y,d,m,e;
+	long day,month,year;
+
+	x = 4 * jdate - 6884477;
+	y = (x / 146097) * 100;
+	e = x % 146097;
+	d = e / 4;
+
+	x = 4 * d + 3;
+	y = (x / 1461) + y;
+	e = x % 1461;
+	d = e / 4 + 1;
+
+	x = 5 * d - 3;
+	m = x / 153 + 1;
+	e = x % 153;
+	d = e / 5 + 1;
+
+	if( m < 11 )
+		month = m + 2;
+	else
+		month = m - 10;
+
+
+	day = d;
+	year = y + m / 11;
+
+	return year * 10000 + month * 100 + day;
+}
+
+long Date::today(void)
+{
+	time_t now;
+	time(&now);
+
+#ifdef EC_HAVE_GMTIME_R
+	struct tm t;
+	gmtime_r(&now,&t);
+	long td = (1900 + t.tm_year) * 10000 + (t.tm_mon+1)* 100 + t.tm_mday;
+#else
+	struct tm *t;
+	t = gmtime(&now);
+	long td = (1900 + t->tm_year) * 10000 + (t->tm_mon+1)* 100 + t->tm_mday;
+#endif
+	return dateToJulian(td);
+}
+
+// Returns a julian number from a yyyymmdd date
+
+long Date::dateToJulian(long ddate)
+{
+	long  m1,y1,a,b,c,d,j1;
+
+	long month,day,year;
+
+	year = ddate / 10000;
+	ddate %= 10000;
+	month  = ddate / 100;
+	ddate %= 100;
+	day = ddate;
+
+	// Negative dates are relative to today
+	if(ddate <= 0)
+		return today() + ddate;
+
+	if(year < 100)
+	{
+//		throw SeriousBug("Please, use 4 digits dates... 2000 is near");
+//      Log::warning() << "Please, use 4 digits dates... 2000 is near" << std::endl;
+		year = year + 1900;
+	}
+
+	if(month > 2)
+	{
+		m1 = month - 3;
+		y1 = year;
+	}
+	else
+	{
+		m1 = month + 9;
+		y1 = year - 1;
+	}
+	a = 146097*(y1/100)/4;
+	d = y1 % 100;
+	b = 1461*d/4;
+	c = (153*m1+2)/5+day+1721119;
+	j1 = a+b+c;
+
+	return(j1);
+}
+
+
+void Date::print(std::ostream& s) const
+{
+	long ddate = julianToDate(julian_);
+	long month,day,year;
+
+	year = ddate / 10000;
+	ddate %= 10000;
+	month  = ddate / 100;
+	ddate %= 100;
+	day = ddate;
+
+	char oldfill = s.fill();
+	s << year << '-' << std::setw(2) << std::setfill('0') << month
+		<< '-' << std::setw(2) << std::setfill('0') << day << std::setfill(oldfill);
+}
+
+long Date::year() const
+{
+	long ddate = julianToDate(julian_);
+	return ddate / 10000;
+}
+
+long Date::month() const
+{
+	long ddate = julianToDate(julian_);
+	ddate %= 10000;
+	return ddate / 100;
+}
+
+long Date::day() const
+{
+	long ddate = julianToDate(julian_);
+	return ddate % 100;
+}
+
+long Date::yyyymmdd() const
+{
+	return julianToDate(julian_);
+}
+
+std::string Date::monthName() const
+{
+	long ddate = julianToDate(julian_);
+	ddate %= 10000;
+	long n = ddate / 100;
+	ASSERT(n >= 1 && n <= 12);
+	return months[n-1];
+}
+
+BadDate::BadDate(const std::string& s):
+	BadValue(s)
+{
+}
+
+void Date::dump(DumpLoad& a) const
+{
+	a.dump(julian_);
+}
+
+void Date::load(DumpLoad& a)
+{
+	a.load(julian_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/types/Date.h b/eckit/src/eckit/types/Date.h
new file mode 100644
index 0000000..8a24b53
--- /dev/null
+++ b/eckit/src/eckit/types/Date.h
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Date.h
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_Date_h
+#define eckit_Date_h
+
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad;
+class Bless;
+
+class Date {
+public:
+
+	enum { MONDAY = 0, TUESDAY=1, WEDNESDAY=2, THURSDAY=3, FRIDAY=4, SATURDAY=5, SUNDAY=6};
+
+// -- Contructors
+
+	Date() : julian_(0) { }
+	Date(long);
+	Date(long,long,long);
+	Date(const std::string&);
+	Date(long,long);
+
+#include "eckit/types/Date.b"
+
+// -- Copy
+
+	Date(const Date& other):
+		julian_(other.julian_) {}
+
+	Date& operator=(const Date& other)
+		{ julian_ = other.julian_; return *this; }
+
+// -- Destructor
+
+	~Date() {}
+
+// -- Convertors
+
+	operator std::string() const;
+
+// -- Operators
+
+	bool operator==(const Date& other) const
+		{ return julian_ == other.julian_ ;}
+
+	bool operator!=(const Date& other) const
+		{ return julian_ != other.julian_ ;}
+
+	bool operator<(const Date& other)  const
+		{ return julian_ <  other.julian_ ;}
+
+	bool operator>(const Date& other)  const
+		{ return julian_ >  other.julian_ ;}
+
+	bool operator<=(const Date& other)  const
+		{ return julian_ <=  other.julian_ ;}
+
+	bool operator>=(const Date& other)  const
+		{ return julian_ >=  other.julian_ ;}
+
+
+	Date& operator++() { julian_++; return *this; }
+	Date& operator--() { julian_--; return *this; }
+
+	Date& operator+=(long d) { julian_ += d; return *this; }
+	Date& operator-=(long d) { julian_ -= d; return *this; }
+
+// -- Methods
+
+	long year() const;
+	long month() const;
+	long day() const;
+	long yyyymmdd() const;
+
+	long julian() const { return julian_;                  }
+	Date round(int n)   { return Date((julian_/n)*n,true); }
+	std::string monthName() const;
+	long dayOfWeek() const { return julian_ % 7; }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class methods
+
+	static long         parse(const std::string&);
+
+// -- Friends
+
+	friend std::ostream& operator<< (std::ostream& s, const Date& date)
+		{ date.print(s); return s; }
+
+protected:
+
+	Date(long julian,bool) : julian_(julian) {}
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+
+	long julian_;
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+// -- Class methods
+
+	static long julianToDate(long);
+	static long dateToJulian(long);
+	static long today();
+
+// -- Friends
+
+	friend long operator-(const Date& d1, const Date& d2)
+		{ return (d1.julian_ - d2.julian_); }
+
+//	friend long operator-(const Date& d1)
+//		{ NOTIMP; return 0; }
+
+	friend Date operator+(const Date& d1, const long n)
+		{ return Date::julianToDate(d1.julian_ + n); }
+
+	friend Date operator+(const long n, const Date& d1)
+		{ return d1+n; }
+
+
+	friend class DateTime;
+};
+
+class BadDate: public BadValue {
+public:
+	BadDate(const std::string& t);
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/DateTime.cc b/eckit/src/eckit/types/DateTime.cc
new file mode 100644
index 0000000..cd4127a
--- /dev/null
+++ b/eckit/src/eckit/types/DateTime.cc
@@ -0,0 +1,195 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <locale>
+
+#include "eckit/eckit.h"
+
+#include "eckit/types/DateTime.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+DateTime::DateTime(const Date& d, const Time& t) :
+    date_(d),
+    time_(t)
+{
+}
+
+DateTime::DateTime(double julian) :
+    date_(long(julian), true),
+    time_((julian - long(julian))*24*60*60)
+{
+}
+
+DateTime::DateTime(const std::string& s)
+{
+	Tokenizer parse(" ");
+	std::vector<std::string> result;
+
+	parse(s,result);
+	
+	ASSERT(result.size() == 2);
+	date_ = Date(result[0]);
+	time_ = Time(result[1]);
+}
+
+static std::locale& getLocale()
+{
+    static std::locale loc = std::locale::classic();
+    try {
+        loc = std::locale("");
+    }
+    catch (...)
+    {
+        Log::error() << "Problem to setup the locale\n"
+                     << "Check your LANG variable - current value: " <<  getenv("LANG") << std::endl;
+        loc = std::locale::classic();
+    }
+    return loc;
+}
+
+std::string DateTime::format( const std::string& fmt )
+{
+      std::ostringstream out;
+
+      struct tm convert = { 0, 0, 0, 0, 0, 0, 0, 0 };
+      convert.tm_mday = date().day();
+      convert.tm_mon = date().month() - 1;
+      convert.tm_year = date().year() - 1900;
+
+      int mm =date(). month();
+      int yy = date().year();
+      if (mm < 3)
+      {
+          mm += 12;
+          --yy;
+      }
+      
+      convert.tm_wday  = (date().day() + (13 * mm - 27)/5 + yy + yy/4 - yy/100 + yy/400) % 7;
+      convert.tm_sec = time().seconds();
+      convert.tm_min = time().minutes();
+      convert.tm_hour = time().hours();
+
+      out.imbue( getLocale() );
+
+      const std::time_put<char>& tfac = std::use_facet<std::time_put<char> >(getLocale());
+
+      tfac.put(out, out, ' ', &convert, fmt.c_str(), fmt.c_str() + fmt.length());
+
+      return out.str();
+}
+  
+DateTime::DateTime(time_t thetime)
+{
+
+    // prefer reentrant version ( gmtime_r )
+
+#ifdef EC_HAVE_GMTIME_R
+	struct tm t;
+	gmtime_r(&thetime,&t);
+	long td   = (1900 + t.tm_year) * 10000 + (t.tm_mon+1)* 100 + t.tm_mday;
+	long hour = t.tm_hour;
+	long min  = t.tm_min;
+	long sec  = t.tm_sec;
+#else
+	struct tm *t;
+	t = gmtime(&thetime);
+	long td   = (1900 + t->tm_year) * 10000 + (t->tm_mon+1)* 100 + t->tm_mday;
+	long hour = t->tm_hour;
+	long min  = t->tm_min;
+	long sec  = t->tm_sec;
+#endif
+
+	date_ = Date(td);
+	time_ = Time(hour,min,sec);
+}
+
+void DateTime::print(std::ostream& s) const
+{
+	s << date_ << ' ' << time_;
+}
+
+DateTime::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+DateTime& DateTime::operator=(const DateTime& other)
+{
+	date_ = other.date_;
+	time_ = other.time_;
+	return *this;
+}
+
+Second DateTime::operator-(const DateTime& other) const
+{
+	Second date = (date_ - other.date_) * 24 * 3600;
+	Second time = time_ - other.time_;
+
+	return date + time;
+}
+
+DateTime DateTime::operator+(const Second& s) const
+{
+	Date d = date();
+	long t = time();
+	d += long(s) / (24 * 3600);
+	t += long(s) % (24 * 3600); 
+	while (t >= 3600 * 24)
+	{
+		d += 1;
+		t -= 3600 * 24;
+	}
+
+	return DateTime(d,Second(t));
+}
+
+DateTime DateTime::round(const Second& rnd) const
+{
+	long long seconds = double(date_.julian_) * 24.0 * 3600 + Second(time_);
+	seconds = (seconds / long(rnd)) * rnd;
+	
+	long	d = seconds / (3600 * 24);
+	Second	t = seconds % (3600 * 24);
+
+	return DateTime(Date(d,true),Time(t));
+}
+
+void DateTime::dump(DumpLoad& a) const
+{
+	date_.dump(a);
+	time_.dump(a);
+}
+
+void DateTime::load(DumpLoad& a)
+{
+	date_.load(a);
+	time_.load(a);
+}
+
+std::string DateTime::iso(bool UTC) const
+{
+    std::stringstream s;
+    s << date_ << "T" << time_;
+    if(UTC) s << "Z";
+    return s.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/DateTime.h b/eckit/src/eckit/types/DateTime.h
new file mode 100644
index 0000000..d38cd72
--- /dev/null
+++ b/eckit/src/eckit/types/DateTime.h
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_DateTime_h
+#define eckit_DateTime_h
+
+
+#include "eckit/types/Date.h"
+#include "eckit/types/Time.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DateTime {
+public:
+
+    // Contructors
+
+	DateTime(time_t = ::time(0));
+	DateTime(const Date&, const Time&);
+	DateTime(const std::string&);
+    DateTime(double);
+
+#include "eckit/types/DateTime.b"
+
+	~DateTime() {}
+
+	bool operator<(const DateTime& other) const
+		{ return (date_ == other.date_)
+			?(time_ < other.time_)
+			:(date_ < other.date_); }
+
+	bool operator==(const DateTime& other) const
+		{ return (date_ == other.date_) && (time_ == other.time_); }
+
+	bool operator!=(const DateTime& other) const
+		{ return (date_ != other.date_) || (time_ != other.time_); }
+
+	bool operator>(const DateTime& other) const
+		{ return (date_ == other.date_)
+			?(time_ > other.time_)
+			:(date_ > other.date_); }
+
+	bool operator>=(const DateTime& other) const
+		{ return !(*this < other); }
+
+	bool operator<=(const DateTime& other) const
+		{ return !(*this > other); }
+
+	DateTime& operator=(const DateTime&);
+
+	Second operator-(const DateTime&) const;
+	DateTime operator+(const Second&) const;
+
+	operator std::string() const;
+
+	const Date& date() const { return date_; }
+	const Time& time() const { return time_; }
+
+	DateTime round(const Second& seconds) const;
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+    
+    std::string format( const std::string& fmt );
+
+    std::string iso(bool UTC = true) const;
+    
+protected: // members
+
+	Date date_;
+	Time time_;
+
+private: // methods
+
+	void print(std::ostream&) const;
+
+	friend std::ostream& operator<<(std::ostream& s,const DateTime& p)
+		{ p.print(s); return s; }
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/DayOfYear.cc b/eckit/src/eckit/types/DayOfYear.cc
new file mode 100644
index 0000000..6ad7813
--- /dev/null
+++ b/eckit/src/eckit/types/DayOfYear.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/DayOfYear.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static long date2Value(const Date& date)
+{
+	Date jan01(date.year(),1,1);
+	Date feb28(date.year(),2,28);
+	Date nextJan01(date.year()+1,1,1);
+
+	int numberOfDays = nextJan01 - jan01;
+
+	long value = date - jan01;
+
+	if(numberOfDays == 365 &&  value > feb28-jan01)
+		value++;
+
+	return value;
+}
+
+DayOfYear::DayOfYear(const Date& date):
+	value_(date2Value(date))
+{
+}
+
+DayOfYear::DayOfYear(const std::string& s):
+	value_(date2Value(Date(s)))
+{
+}
+
+DayOfYear::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+void DayOfYear::print(std::ostream& s) const
+{
+	Date d(2004,value_+1);         // Take a leap year so it can accept 29th feb
+
+	char oldfill = s.fill();
+	s << std::setw(2) << std::setfill('0') << d.month() << '-' << std::setw(2) << std::setfill('0') << d.day() 
+	  << std::setfill(oldfill);
+}
+
+void DayOfYear::dump(DumpLoad& a) const
+{
+	NOTIMP;
+}
+
+void DayOfYear::load(DumpLoad& a)
+{
+	NOTIMP;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
diff --git a/eckit/src/eckit/types/DayOfYear.h b/eckit/src/eckit/types/DayOfYear.h
new file mode 100644
index 0000000..634d4e3
--- /dev/null
+++ b/eckit/src/eckit/types/DayOfYear.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DayOfYear.h
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_DayOfYear_h
+#define eckit_DayOfYear_h
+
+#include "eckit/types/Date.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DayOfYear {
+public:
+
+// -- Contructors
+
+	DayOfYear(const Date& = Date(0));
+	DayOfYear(long,long);
+	DayOfYear(const std::string&);
+    
+#include "eckit/types/DayOfYear.b"
+
+// -- Destructor
+
+	~DayOfYear() {}
+
+// -- Convertors
+	
+	operator std::string() const;
+
+// -- Operators
+
+	bool operator==(const DayOfYear& other) const
+		{ return value_ == other.value_ ;}
+
+	bool operator!=(const DayOfYear& other) const
+		{ return value_ != other.value_ ;}
+
+	bool operator<(const DayOfYear& other)  const
+		{ return value_ < other.value_; }
+
+	bool operator>(const DayOfYear& other)  const
+		{ return value_ > other.value_; }
+
+	bool operator<=(const DayOfYear& other)  const
+		{ return value_ <= other.value_; }
+
+	bool operator>=(const DayOfYear& other)  const
+		{ return value_ >= other.value_; }
+
+
+// -- Methods
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class methods
+
+
+// -- Friends
+
+	friend std::ostream& operator<< (std::ostream& s, const DayOfYear& month)
+		{ month.print(s); return s; }
+
+protected:
+
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+	
+	long value_;
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+// -- Class methods
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Double.cc b/eckit/src/eckit/types/Double.cc
new file mode 100644
index 0000000..c2600c4
--- /dev/null
+++ b/eckit/src/eckit/types/Double.cc
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <math.h>
+
+#include "eckit/types/Double.h"
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/utils/Translator.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Double::Double(double d):
+	value_(d)
+{
+}
+
+Double::Double(const std::string& s):
+	value_(Translator<std::string,double>()(s))
+{
+	
+}
+
+void Double::dump(DumpLoad& a) const
+{
+	a.dump(value_);
+}
+
+void Double::load(DumpLoad& a)
+{
+	a.load(value_);
+}
+
+double Double::round() const
+{
+	double precision = 1000.0;
+	// Warning gcc needs two lines
+	double x = floor(precision * value_ + 0.5) / precision;
+	return x;
+}
+
+void Double::print(std::ostream& s) const
+{
+	s << round();
+}
+
+Double::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Double.h b/eckit/src/eckit/types/Double.h
new file mode 100644
index 0000000..6a4bd83
--- /dev/null
+++ b/eckit/src/eckit/types/Double.h
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Double.h
+// Baudouin Raoult - ECMWF Dec 97
+
+#ifndef eckit_Double_h
+#define eckit_Double_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad;
+
+class Double {
+public:
+
+	Double(double = 0);
+	Double(const std::string&);
+
+#include "eckit/types/Double.b"
+
+	~Double() {}
+
+public: // operators
+
+	operator std::string() const;
+
+	bool operator==(const Double& other) const
+		{ return round() == other.round(); }
+
+	bool operator!=(const Double& other) const
+		{ return round() != other.round(); }
+
+	bool operator<(const Double& other) const
+		{ return round() < other.round(); }
+
+	bool operator>(const Double& other) const
+		{ return round() > other.round(); }
+
+public: // methods
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+	double round() const;
+
+protected: // methods
+
+	void print(std::ostream& s) const;
+
+private: // members
+
+	double value_;
+
+	friend std::ostream& operator<<(std::ostream& s,const Double& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/FixedString.h b/eckit/src/eckit/types/FixedString.h
new file mode 100644
index 0000000..f837f45
--- /dev/null
+++ b/eckit/src/eckit/types/FixedString.h
@@ -0,0 +1,176 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef eckit_types_FixedString_h
+#define eckit_types_FixedString_h
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// To be used as a key or value in BTree or other file-based classed
+
+/// FixedString<SIZE> manages a SIZE-byte char array as a relatively thin utility wrapper.
+///
+/// - Strings <= the given SIZE may be stored
+/// - Strings shorter than the given SIZE are null-character terminated
+/// - All relevant overflow and termination safety is handled internally inside the class when interacting with C
+///   library functions.
+///
+/// @note length() returns the length of the stored string, and size() returns the number of bytes taken by the array
+///       such that length() <= size().
+
+template< int SIZE >
+class FixedString {
+public:
+
+    /// Constructors
+    /// @note that constructing FixedStrings initialised from another FixedString of a different SIZE actually
+    ///       routes through the const std::string& constructor, via the provided implicit cast to std::string below.
+
+    FixedString();
+    FixedString(const std::string&);
+    FixedString(const FixedString&);
+    FixedString(const char*);
+
+	FixedString& operator=(const FixedString&);
+	FixedString& operator=(const std::string&);
+
+	bool operator<(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) < 0; }
+
+	bool operator>(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) > 0; }
+
+	bool operator==(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) == 0; }
+
+	bool operator!=(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) != 0; }
+
+	bool operator>=(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) >= 0; }
+
+	bool operator<=(const FixedString& other) const { return memcmp(data_, other.data_, SIZE) <= 0; }
+
+    /// The number of characters in the stored string,
+    /// excluding the termination character if the string is shorter than SIZE.
+	size_t length() const;
+
+    bool empty() const { return length() == 0; }
+
+    std::string asString() const;
+    
+    operator std::string() const;
+
+	char* data() { return data_; }
+	const char* data() const { return data_; }
+
+    /// The number of bytes in the managed array (always equal to SIZE).
+    size_t size() const { return SIZE; }
+
+	static size_t static_size() { return SIZE; }
+
+private:
+
+	char data_[SIZE];
+
+    void print(std::ostream& s) const ;
+
+    friend std::ostream& operator<<(std::ostream& s,const FixedString& p)
+    {
+        p.print(s);
+        return s;
+    }
+};
+
+//-----------------------------------------------------------------------------
+
+template<int SIZE>
+FixedString<SIZE>::FixedString()
+{
+	zero(data_);
+}
+
+template<int SIZE>
+FixedString<SIZE>:: FixedString(const std::string& s)
+{
+	ASSERT(s.length() <= SIZE && sizeof(s[0]) == 1);
+	zero(data_);
+	std::copy(s.begin(), s.end(), data_);
+}
+
+template<int SIZE>
+FixedString<SIZE>:: FixedString(const FixedString& other)
+{
+    ::memcpy(data_,other.data_,SIZE);
+}
+
+template<int SIZE>
+FixedString<SIZE>::FixedString(const char* s) {
+    ASSERT(sizeof(char) == 1 && s && strlen(s) <= SIZE);
+	zero(data_);
+    ::memcpy(data_, s, strlen(s));
+}
+
+template<int SIZE>
+FixedString<SIZE>& FixedString<SIZE>::operator=(const FixedString& s)
+{
+	if (this != &s)
+	{
+        ::memcpy(data_,s.data_,SIZE);
+	}
+	return *this;
+}
+
+template<int SIZE>
+FixedString<SIZE>& FixedString<SIZE>::operator=(const std::string& s)
+{
+	ASSERT(s.length() <= SIZE && sizeof(s[0]) == 1);
+
+    ::memcpy(data_, s.c_str(), s.length());
+    if(s.length() < SIZE) {
+        ::memset(data_ + s.length(), 0, SIZE - s.length());
+    }
+
+	return *this;
+}
+
+template<int SIZE>
+size_t FixedString<SIZE>::length() const
+{
+	return std::find(data_, data_ + SIZE, 0) - data_;
+}
+
+template<int SIZE>
+std::string FixedString<SIZE>::asString() const
+{
+	return std::string(data_, data_ + length());
+}
+
+template<int SIZE>
+void FixedString<SIZE>::print(std::ostream& s) const
+{
+	s.write(data_,length());
+}
+
+template<int SIZE>
+FixedString<SIZE>::operator std::string() const
+{
+	return std::string(data_, data_ + length());
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/FloatCompare.cc b/eckit/src/eckit/types/FloatCompare.cc
new file mode 100644
index 0000000..5e473b3
--- /dev/null
+++ b/eckit/src/eckit/types/FloatCompare.cc
@@ -0,0 +1,147 @@
+#include <math.h>
+#include <limits>
+#include <sys/types.h>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/types/FloatCompare.h"
+
+namespace eckit {
+namespace types {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace detail {
+
+// FIXME: The following functions are available in std:: as of C++11:
+// * fpclassify
+// * isinf
+// * isnan
+// * signbit
+// For the moment we have to use the (non namespaced) versions from math.h
+
+template <class T>
+inline T abs(T);
+
+template<>
+double abs(double x) { return fabs(x); }
+
+template<>
+float abs(float x) { return fabsf(x); }
+
+// Used for accessing the integer representation of floating-point numbers
+// (aliasing through unions works on most platforms).
+union Double {
+    typedef double float_t;
+    typedef int64_t int_t;
+
+    float_t f_;
+    int_t i_;
+
+    Double(double x) : f_(x) {}
+};
+
+// Used for accessing the integer representation of floating-point numbers
+// (aliasing through unions works on most platforms).
+union Float {
+    typedef float float_t;
+    typedef int32_t int_t;
+
+    float_t f_;
+    int_t i_;
+
+    Float(float x) : f_(x) {}
+};
+
+// The difference between the bit representations of two floating point
+// numbers of the same sign interpreted as a signed integer is equal to their
+// distance from each other i.e. how many representable floating point numbers
+// they are "apart". This is a very fast way to compute this difference.
+
+Double::int_t float_distance(double x, double y) {
+    const Double::int_t dist = Double(x).i_ - Double(y).i_;
+    return dist >= 0 ? dist : -dist;
+}
+
+Float::int_t float_distance(float x, float y) {
+    const Float::int_t dist = Float(x).i_ - Float(y).i_;
+    return dist >= 0 ? dist : -dist;
+}
+
+}  // namespace detail
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Compare 2 floats with an absolute epsilon check (values near zero), then based on ULPs
+///
+/// a, b       : two floating-point numbers (single/double precision) to compare.
+/// epsilon    : epsilon for floating-point absolute epsilon check (should be some
+///              small multiple of std::numeric_limits<T>::epsilon().
+/// maxUlpsDiff: unit in the last place or unit of least precision.
+///              maxUlpsDiff can also be interpreted in terms of how many representable floats we are
+///              willing to accept between a and b. This function will allow maxUlpsDiff-1 floats
+///              between a and b.
+///
+/// If two numbers are identical except for a one-bit difference in the last digit of their mantissa
+/// then this function will calculate ulpsDiff as one.
+/// If one number is the maximum number for a particular exponent, say 1.99999988,
+/// and the other number is the smallest number for the next exponent, say 2.0,
+/// then this function will again calculate ulpsDiff as one.
+/// In both cases the two numbers are the closest floats there are.
+///
+/// Assumptions:
+///   * Bit identical numbers are equal for any epsilon.
+///   * A NaN is different from any number (even another NaN).
+///   * Infinity is different from any number but infinity (with the same sign).
+///   * Subnormal numbers are treated as equal to 0
+///   * +/-std::numeric_limits<T>::min() has ULP distance 1 from 0
+///   * -std::numeric_limits<T>::min() has ULP distance 2 from std::numeric_limits<T>::min()
+///   * ULP distance from 0 is 1 + ULP distance from std::numeric_limits<T>::min() (for positive numbers)
+///
+template< typename T >
+bool is_approximately_equal_ulps(T a, T b, T epsilon, int maxUlpsDiff) {
+
+    // Bit identical is equal for any epsilon
+    if (a == b) return true;
+
+    // NaNs and infinity are always different
+    if (isnan(a) || isnan(b) || isinf(a) || isinf(b)) return false;
+
+    // Subnormal numbers are treated as 0
+    if (fpclassify(a) == FP_SUBNORMAL) a = 0;
+    if (fpclassify(b) == FP_SUBNORMAL) b = 0;
+
+    // Check if the numbers are really close -- needed
+    // when comparing numbers near zero.
+    if (detail::abs(a - b) <= epsilon) return true;
+
+    // If either is zero, compare the absolute value of the other to the minimum normal number
+    if (a == 0) {
+        return (1 + detail::float_distance(detail::abs(b), std::numeric_limits<T>::min())) <= maxUlpsDiff;
+    }
+    if (b == 0) {
+        return (1 + detail::float_distance(detail::abs(a), std::numeric_limits<T>::min())) <= maxUlpsDiff;
+    }
+
+    if (signbit(a) == signbit(b)) return detail::float_distance(a, b) <= maxUlpsDiff;
+
+    // If signs are different, add ULP distances from minimum normal number on both sides of 0
+    const int64_t dp = detail::float_distance(a > 0 ? a : b, std::numeric_limits<T>::min());
+    const int64_t dn = detail::float_distance(a < 0 ? a : b, -std::numeric_limits<T>::min());
+    // Protect against integer overlow in case float distance for both numbers is close to maxint
+    return dp < maxUlpsDiff && dn < maxUlpsDiff && (2 + dp + dn) <= maxUlpsDiff;
+}
+
+template<>
+bool is_approximately_equal(float a, float b, float epsilon, int maxUlpsDiff) {
+    return is_approximately_equal_ulps(a, b, epsilon, maxUlpsDiff);
+}
+
+template<>
+bool is_approximately_equal(double a, double b, double epsilon, int maxUlpsDiff) {
+    return is_approximately_equal_ulps(a, b, epsilon, maxUlpsDiff);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace types
+} // namespace eckit
diff --git a/eckit/src/eckit/types/FloatCompare.h b/eckit/src/eckit/types/FloatCompare.h
new file mode 100644
index 0000000..d31bc96
--- /dev/null
+++ b/eckit/src/eckit/types/FloatCompare.h
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Pedro Maciel
+/// @author Florian Rathgeber
+/// @date Jun 2015
+
+#ifndef eckit_types_FloatCompare_h
+#define eckit_types_FloatCompare_h
+
+#include <limits>
+
+namespace eckit {
+namespace types {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Compare 2 floats with an absolute epsilon check (values near zero), then based on ULPs
+///
+/// epsilon    : epsilon for floating-point absolute epsilon check (should be some
+///              small multiple of std::numeric_limits<T>::epsilon()).
+///              Used to check values near zero
+/// maxUlpsDiff: maximum spacing between the floating-point numbers A and B
+///              unit in the last place or unit of least precision. defaults to 10.
+///              maxUlpsDiff can also be interpreted in terms of how many representable floats we are
+///              willing to accept between A and B. This function will allow maxUlpsDiff-1 floats
+///              between A and B.
+///
+/// Assumptions:
+///   * Bit identical numbers are equal for any epsilon.
+///   * A NaN is different from any number (even another NaN).
+///   * Infinity is different from any number but infinity (with the same sign).
+///   * Subnormal numbers are treated as equal to 0
+///   * +/-std::numeric_limits<T>::min() has ULP distance 1 from 0
+///   * -std::numeric_limits<T>::min() has ULP distance 2 from std::numeric_limits<T>::min()
+///   * ULP distance from 0 is 1 + ULP distance from std::numeric_limits<T>::min() (for positive numbers)
+
+/// Compares values equality within a range defined by an epsilon and a
+/// This function only has specializations for double and float
+template< typename T >
+bool is_approximately_equal( T a, T b, T epsilon = std::numeric_limits<T>::epsilon(), int maxUlpsDiff = 10);
+
+/// Compare values equality
+template< typename T >
+bool is_equal(const T& a,  const T& b)  { return (a == b); }
+
+/// Compare values inequality: "is greater or equal to"
+template< typename T >
+bool is_greater_or_equal(const T& a,  const T& b) { return (a >= b); }
+
+/// Compare values inequality: "is greater or approximately equal to"
+template< typename T >
+bool is_approximately_greater_or_equal(const T& a,
+                                       const T& b,
+                                       T epsilon = std::numeric_limits<T>::epsilon(),
+                                       int maxUlpsDiff = 10)
+{
+    return (a >= b) || eckit::types::is_approximately_equal(a, b, epsilon, maxUlpsDiff);
+}
+
+/// Compare values inequality: "is less or approximately equal to"
+template< typename T >
+bool is_approximately_lesser_or_equal(const T& a,
+                                      const T& b,
+                                      T epsilon = std::numeric_limits<T>::epsilon(),
+                                      int maxUlpsDiff = 10)
+{
+    return (a <= b) || eckit::types::is_approximately_equal(a, b, epsilon, maxUlpsDiff);
+}
+
+/// Compare values inequality: "is strictly greater than"
+template< typename T >
+bool is_strictly_greater(const T& a,
+                         const T& b,
+                         T epsilon = std::numeric_limits<T>::epsilon(),
+                         int maxUlpsDiff = 10)
+{
+    return (a > b) && !eckit::types::is_approximately_equal(a, b, epsilon, maxUlpsDiff);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Helper class that memorizes the comparison setttings
+template < typename T >
+class CompareApproximatelyEqual {
+
+	T eps_;
+	int maxUlps_;
+
+public:
+
+    CompareApproximatelyEqual(T eps = std::numeric_limits<T>::epsilon(), int maxUlps = 10) :
+		eps_(eps),
+		maxUlps_(maxUlps)
+	{}
+
+    bool operator() (const T& a, const T& b) { return eckit::types::is_approximately_equal(a, b, eps_, maxUlps_); }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace types
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Backward compatible (deprecated) interface
+
+template < typename T >
+class FloatCompare {
+
+public: // methods
+
+    static bool isApproximatelyEqual(const T& a, const T& b) {
+        return  types::is_approximately_equal(a, b);
+    }
+
+    static bool isStrictlyGreater(const T& a, const T& b) {
+        return  types::is_strictly_greater(a, b);
+    }
+
+    static bool isApproximatelyGreaterOrEqual(const T& a, const T& b) {
+        return  types::is_approximately_greater_or_equal(a, b);
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Fraction.cc b/eckit/src/eckit/types/Fraction.cc
new file mode 100644
index 0000000..8dce2ef
--- /dev/null
+++ b/eckit/src/eckit/types/Fraction.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Fraction.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+
+#include <limits>
+#include <cmath>
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+const long long max = std::sqrt(std::numeric_limits<long long>::max());
+
+
+static long long gcd(long long a, long long b) {
+    while (b != 0)
+    {
+        long long r = a % b;
+        a = b;
+        b = r;
+    }
+    return a;
+}
+
+//-----------------------------------------------------------------------------
+
+Fraction::Fraction(long long top, long long bottom) {
+
+    ASSERT(bottom != 0);
+
+    long long sign = 1;
+    if (top < 0) {
+        top = -top;
+        sign = -sign;
+    }
+
+    if (bottom < 0) {
+        bottom = -bottom;
+        sign = -sign;
+    }
+
+    long long g = gcd(top, bottom);
+    top_ = sign * top / g;
+    bottom_ = bottom / g;
+}
+
+
+Fraction::Fraction(double x) {
+    long long sign = 1;
+    if (x < 0) {
+        sign = -sign;
+        x = -x;
+    }
+
+    long long m00 = 1, m11 = 1, m01 = 0, m10 = 0;
+    long long a = x;
+    long long t2 = m10 * a + m11;
+
+    while (t2 <= max) {
+
+        long long t1  = m00 * a + m01;
+        m01 = m00;
+        m00 = t1;
+
+        m11 = m10;
+        m10 = t2;
+
+        if (x == a) {
+            break;
+        }
+
+        x = 1.0 / (x - a);
+
+        if (x > std::numeric_limits<long long>::max()) {
+            break;
+        }
+
+        a = x;
+        t2 = m10 * a + m11;
+    }
+
+    top_ = sign * m00;
+    bottom_ = m10;
+
+}
+
+Fraction::Fraction(const std::string& s) {
+    static Tokenizer parse("/");
+
+    std::vector<std::string> v;
+    parse(s, v);
+    if (v.size() > 1) {
+        ASSERT(v.size() == 2);
+        static Translator<std::string, long long> s2l;
+        Fraction f(s2l(v[0]), s2l(v[1]));
+        top_ = f.top_;
+        bottom_ = f.bottom_;
+        return;
+    }
+
+    static Translator<std::string, double> s2d;
+    Fraction f(s2d(s));
+    top_ = f.top_;
+    bottom_ = f.bottom_;
+
+}
+
+Fraction::Fraction(const char* c) {
+    std::string s(c);
+    Fraction f(s);
+    top_ = f.top_;
+    bottom_ = f.bottom_;
+}
+
+
+void Fraction::print(std::ostream& out) const {
+    if (bottom_ == 1) {
+        out << top_;
+    }
+    else {
+        out << top_ << '/' << bottom_;
+    }
+}
+
+Fraction::operator long long() const {
+    if (bottom_ == 1) {
+        return top_;
+    }
+    std::ostringstream oss;
+    oss << "Cannot convert fraction " << *this << " to integer";
+    throw eckit::SeriousBug(oss.str());
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Fraction.h b/eckit/src/eckit/types/Fraction.h
new file mode 100644
index 0000000..3e6ff59
--- /dev/null
+++ b/eckit/src/eckit/types/Fraction.h
@@ -0,0 +1,274 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Fraction.h
+// Baudouin Raoult - ECMWF Mar 16
+
+#ifndef eckit_Fraction_h
+#define eckit_Fraction_h
+
+#include <string>
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class Fraction {
+private:
+
+	Fraction(long long top, long long bottom, bool): top_(top), bottom_(bottom) {}
+
+
+public: // methods
+
+// -- Contructors
+
+	Fraction(): top_(0), bottom_(1) {}
+
+	template<class T>
+	Fraction(T top): top_(top), bottom_(1) {}
+
+	Fraction(long long top, long long bottom);
+
+	Fraction(double);
+	Fraction(const std::string&);
+	Fraction(const char*);
+
+public: // operators
+
+	operator double() const {
+		return double(top_) / double(bottom_);
+	}
+
+	operator long long() const;
+
+	Fraction operator-()  const {
+		return Fraction(-top_, bottom_, true);
+	}
+
+
+	Fraction operator+(const Fraction& other) const {
+		return Fraction(top_ * other.bottom_ + bottom_ * other.top_,
+		                bottom_ * other.bottom_);
+	}
+
+	Fraction operator-(const Fraction& other) const {
+		return Fraction(top_ * other.bottom_ - bottom_ * other.top_,
+		                bottom_ * other.bottom_);
+	}
+
+	Fraction operator/(const Fraction& other) const {
+		return Fraction(top_ * other.bottom_, bottom_ * other.top_);
+	}
+
+	Fraction operator*(const Fraction& other) const {
+		return Fraction(top_ * other.top_, bottom_ * other.bottom_);
+	}
+
+	bool operator==(const Fraction& other) const {
+		return top_ == other.top_ && bottom_ == other.bottom_;
+	}
+
+	bool operator<(const Fraction& other) const {
+		return top_ * other.bottom_ < bottom_ * other.top_;
+	}
+
+	bool operator<=(const Fraction& other) const {
+		return top_ * other.bottom_ <= bottom_ * other.top_;
+	}
+
+	bool operator!=(const Fraction& other) const {
+		return top_ * other.bottom_ != bottom_ * other.top_;
+	}
+
+	bool operator>(const Fraction& other) const {
+		return top_ * other.bottom_ > bottom_ * other.top_;
+	}
+
+	bool operator>=(const Fraction& other) const {
+		return top_ * other.bottom_ >= bottom_ * other.top_;
+	}
+
+	Fraction& operator+=(const Fraction& other) {
+		*this = (*this) + other;
+		return *this;
+	}
+
+	Fraction& operator-=(const Fraction& other) {
+		*this = (*this) - other;
+		return *this;
+	}
+
+	Fraction& operator/=(const Fraction& other) {
+		*this = (*this) / other;
+		return *this;
+	}
+
+	Fraction& operator*=(const Fraction& other) {
+		*this = (*this) * other;
+		return *this;
+	}
+
+	template<class T>
+	Fraction operator+(T other) const {
+		return *this + Fraction(other);
+	}
+
+	template<class T>
+	Fraction operator-(T other) const {
+		return *this - Fraction(other);
+	}
+
+	template<class T>
+	Fraction operator/(T other) const {
+		return *this / Fraction(other);
+	}
+
+	template<class T>
+	Fraction operator*(T other) const {
+		return *this * Fraction(other);
+	}
+
+
+	template<class T>
+	bool operator==(T other) const {
+		return *this == Fraction(other);
+	}
+
+	template<class T>
+	bool operator<(T other) const {
+		return *this < Fraction(other);
+	}
+
+	template<class T>
+	bool operator<=(T other) const {
+		return *this <= Fraction(other);
+	}
+
+	template<class T>
+	bool operator!=(T other) const {
+		return *this != Fraction(other);
+	}
+
+	template<class T>
+	bool operator>(T other) const {
+		return *this > Fraction(other);
+	}
+
+	template<class T>
+	bool operator>=(T other) const {
+		return *this >= Fraction(other);
+	}
+
+	template<class T>
+	Fraction& operator+=(T other) {
+		return (*this) += Fraction(other);
+	}
+
+	template<class T>
+	Fraction& operator-=(T other) {
+		return (*this) -= Fraction(other);
+	}
+
+	template<class T>
+	Fraction& operator/=(T other) {
+		return (*this) /= Fraction(other);
+	}
+
+	template<class T>
+	Fraction& operator*=(T other) {
+		return (*this) *= Fraction(other);
+	}
+
+private: // members
+
+	long long top_;
+	long long bottom_;
+
+	void print(std::ostream& out) const;
+
+
+	friend std::ostream& operator<<(std::ostream& s, const Fraction& x) {
+		x.print(s);
+		return s;
+	}
+
+
+};
+
+template<class T>
+Fraction operator+(T n, const Fraction& f)
+{
+	return Fraction(n) + f;
+}
+
+template<class T>
+Fraction operator-(T n, const Fraction& f)
+{
+	return Fraction(n) - f;
+}
+
+template<class T>
+Fraction operator/(T n, const Fraction& f)
+{
+	return Fraction(n) / f;
+}
+
+template<class T>
+Fraction operator*(T n, const Fraction& f)
+{
+	return Fraction(n) * f;
+}
+
+template<class T>
+bool operator==(T n, const Fraction& f)
+{
+	return Fraction(n) == f;
+}
+
+template<class T>
+bool operator<(T n, const Fraction& f)
+{
+	return Fraction(n) < f;
+}
+
+template<class T>
+bool operator<=(T n, const Fraction& f)
+{
+	return Fraction(n) <= f;
+}
+
+template<class T>
+bool operator!=(T n, const Fraction& f)
+{
+	return Fraction(n) != f;
+}
+
+template<class T>
+bool operator>(T n, const Fraction& f)
+{
+	return Fraction(n) > f;
+}
+
+template<class T>
+bool operator>=(T n, const Fraction& f)
+{
+	return Fraction(n) >= f;
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Grid.cc b/eckit/src/eckit/types/Grid.cc
new file mode 100644
index 0000000..40d73fd
--- /dev/null
+++ b/eckit/src/eckit/types/Grid.cc
@@ -0,0 +1,233 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/types/Grid.h"
+#include "eckit/log/Log.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+/// Helper which contains all possible grids
+/// @note Not thread safe
+
+struct Grids {
+public:
+	static char        lookUp(const Grid& g);
+	static const Grid& lookUp(char c);
+
+private:
+    typedef std::map<char,Grid,std::less<char> > CharGridTable;
+    typedef std::map<Grid,char,std::less<Grid> > GridCharTable;
+
+	static CharGridTable charGridTable_;
+	static GridCharTable gridCharTable_;
+	static char          nextChar_;
+
+};
+
+Grids::CharGridTable Grids::charGridTable_;
+Grids::GridCharTable Grids::gridCharTable_;
+char                 Grids::nextChar_ = 'a';
+
+char Grids::lookUp(const Grid& g)
+{
+	GridCharTable::iterator i = gridCharTable_.find(g);
+	if(i != gridCharTable_.end())
+		return (*i).second;
+
+	gridCharTable_[g]         = nextChar_;
+	charGridTable_[nextChar_] = g;
+
+	ASSERT(nextChar_ != 'z');
+
+	return nextChar_++;
+}
+
+const Grid& Grids::lookUp(char c)
+{
+	CharGridTable::iterator i = charGridTable_.find(c);
+	ASSERT(i != charGridTable_.end());
+	return (*i).second;
+}
+
+//-----------------------------------------------------------------------------
+
+Grid::Grid(const std::vector<double>& n)
+{
+	switch(n.size())
+	{
+		case 0:
+			northSouth_ = eastWest_ = undef;
+			break;
+
+		case 1: // For the moment, only support reduced gaussian
+			northSouth_ = n[0];
+			eastWest_ = 0;
+			break;
+
+		case 2:
+			northSouth_ = n[0];
+			eastWest_   = n[1];
+			break;
+
+		default:
+			throw UserError("Invalid grid specified");
+			break;
+	}
+}
+
+Grid::Grid(const std::string& s)
+{
+	Tokenizer parse("/");
+	std::vector<std::string> result;
+
+	parse(s,result);
+
+	switch(result.size())
+	{
+		case 1: // For the moment, only support reduced gaussian
+			northSouth_ = atof(result[0].c_str());
+			eastWest_   = 0;
+			break;
+	
+		case 2:
+			northSouth_ = atof(result[0].c_str());
+			eastWest_   = atof(result[1].c_str());
+			break;
+
+		default:
+			throw UserError("Invalid grid specified");
+			break;
+	}
+
+	Log::userInfo() << "GRID: " << s << " ---- " << *this << std::endl;
+
+}
+
+long Grid::score(const Grid& p) const
+{
+	double x = northSouth() / p.northSouth();	
+	double y = eastWest()   / p.eastWest();	
+	int s = 0;
+
+	if(x == long(x)) s++;
+	if(y == long(y)) s++;
+
+	return s;
+}
+
+double Grid::distance(const Grid& p) const
+{
+
+	double a = northSouth_ - p.northSouth_;
+	double b = eastWest_   - p.eastWest_;
+
+	/* return ::sqrt(a*a + b*b); */
+	return (a*a + b*b); // No need for sqrt, to speed up
+}
+
+
+Grid::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+void Grid::print(std::ostream& s) const
+{
+	if(undefined())
+		s << "(undefined)";
+	else
+	{
+		if(northSouth_)
+			s << northSouth_;
+		if(northSouth_ && eastWest_)
+			s << "/";
+		if(eastWest_)
+			s << eastWest_;
+	}
+}
+
+char Grid::gridName(const Grid& g)
+{
+	return Grids::lookUp(g);
+}
+
+Grid Grid::grid(const char c)
+{
+	return Grids::lookUp(c);
+}
+
+bool Grid::operator<(const Grid& other) const
+{
+	double x = (northSouth_*northSouth_) + (eastWest_*eastWest_);
+	double y = (other.northSouth_*other.northSouth_) + (other.eastWest_*other.eastWest_);
+
+	// distance or choice
+	return (x !=y ) ? (x < y):(northSouth_ < other.northSouth_);
+}
+
+const Grid& Grid::bestMatch(const std::vector<Grid>& v) const
+{
+
+    Log::userInfo() << "Grid::bestMatch " << v.size() << std::endl;
+    Log::info() << "Grid::bestMatch " << v.size() << std::endl;
+    for(std::vector<Grid>::const_iterator j = v.begin(); j != v.end(); ++j)
+	{
+		Log::userInfo() << "Grid::bestMatch " << (*j) << std::endl;
+		Log::info() << "Grid::bestMatch " << (*j) << std::endl;
+	}
+
+	ASSERT( v.size() > 0 );
+
+	// Perfect match
+    if( std::find(v.begin(),v.end(),*this) != v.end() )
+		return *this;
+		
+	long   smax = score(v[0]);
+	int    choice = 0;
+
+	for(size_t j = 1; j < v.size(); ++j)
+	{
+		long   s = score(v[j]);
+
+		if( (s > smax) || (s == smax && v[choice] > v[j]) )
+		{
+			smax = s;
+			choice = j;
+		}
+	}
+
+	return v[choice];
+				
+}
+
+void Grid::dump(DumpLoad& a) const
+{
+	a.dump(northSouth_);
+	a.dump(eastWest_);
+}
+
+void Grid::load(DumpLoad& a)
+{
+	a.load(northSouth_);
+	a.load(eastWest_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/types/Grid.h b/eckit/src/eckit/types/Grid.h
new file mode 100644
index 0000000..e849bdd
--- /dev/null
+++ b/eckit/src/eckit/types/Grid.h
@@ -0,0 +1,145 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Grid.h
+// Baudouin Raoult - ECMWF Dec 97
+
+#ifndef eckit_Grid_h
+#define eckit_Grid_h
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad;
+
+class Grid {
+public:
+
+	enum { undef = -1 };
+
+// -- Exceptions
+
+	class InvalidGrid : public Exception {
+	public:
+		InvalidGrid(const std::string& s): 
+			Exception("Invalid Mars Grid '" + s + "'") {}
+	};
+
+// -- Contructors
+
+	Grid(const std::string&);
+
+	Grid(double ns = undef,double ew = undef):
+		northSouth_(ns), eastWest_(ew) {}
+
+	Grid(const std::vector<double>&);
+
+#include "eckit/types/Grid.b"
+
+// -- Destructor
+
+	~Grid() {}
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+	operator std::string() const;
+
+	bool operator==(const Grid& other) const
+		{ return northSouth_ == other.northSouth_ && eastWest_ == other.eastWest_; }
+
+	bool operator!=(const Grid& other) const 
+		{ return northSouth_ != other.northSouth_ || eastWest_ != other.eastWest_; }
+
+	bool operator<(const Grid& other) const;
+
+	bool operator>(const Grid& other) const
+		{ return other < *this; }
+
+// -- Methods
+
+	double northSouth() const { return northSouth_; }
+	double eastWest() const   { return eastWest_; }   
+
+	// Score return 2 if the grid is a multiple of the parameter
+	// 1 if only one value is a multiple
+	// or 0 if the grid are not compatible.
+
+	long score(const Grid&) const;
+	double distance(const Grid&) const;
+	const Grid& bestMatch(const std::vector<Grid>&) const;
+
+	bool undefined() const
+		{ return northSouth_ == undef && eastWest_ == undef; }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+
+	static char gridName(const Grid&);
+	static Grid grid(const char);
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	void print(std::ostream& s) const;
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+// -- Members
+
+	double northSouth_;
+	double eastWest_;
+
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const Grid& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Metadata.cc b/eckit/src/eckit/types/Metadata.cc
new file mode 100644
index 0000000..eb59bea
--- /dev/null
+++ b/eckit/src/eckit/types/Metadata.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+#include "eckit/types/Metadata.h"
+
+using namespace eckit;
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Metadata::~Metadata() {}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace eckit
+
diff --git a/eckit/src/eckit/types/Metadata.h b/eckit/src/eckit/types/Metadata.h
new file mode 100644
index 0000000..74359b9
--- /dev/null
+++ b/eckit/src/eckit/types/Metadata.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+
+#ifndef eckit_Metadata_H
+#define eckit_Metadata_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Metadata : private eckit::NonCopyable {
+
+public: // methods
+
+    virtual ~Metadata();
+
+    virtual std::vector<std::string> keywords() const = 0;
+
+    virtual bool has(const std::string& name) const = 0;
+
+    virtual void get(const std::string& name, std::string& value) const = 0;
+    virtual void get(const std::string& name, long& value) const = 0;
+    virtual void get(const std::string& name, double& value) const = 0;
+
+protected: // methods
+
+    friend std::ostream& operator<<(std::ostream& s, const Metadata& p) {
+        p.print(s);
+        return s;
+    }
+
+
+    virtual void print(std::ostream&) const = 0;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace eckit
+
+#endif
+
diff --git a/eckit/src/eckit/types/Month.cc b/eckit/src/eckit/types/Month.cc
new file mode 100644
index 0000000..9f9ab22
--- /dev/null
+++ b/eckit/src/eckit/types/Month.cc
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Month.h"
+#include "eckit/parser/Tokenizer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Month::Month(long year,long month):
+	date_(year,month,1)
+{
+}
+
+Month::Month(const Date& date):
+	date_(date.year(),date.month(),1)
+{
+}
+
+Month::Month(const std::string& s):
+	date_(1997,10,10)
+{
+	Tokenizer parse("-");
+	std::vector<std::string> result;
+
+	parse(s,result);
+
+	bool err = false;
+	long value;
+
+	switch(result.size())
+	{
+		case 1:
+			switch(s.length())
+			{
+				case 6:
+				case 8:
+					value = atol(s.c_str());
+					if((value%100) == 0) value++; // For dates as 970900
+					date_ = Date(value);
+					break;
+
+				default:
+					err = false;
+					break;
+			}
+			break;
+
+		case 2:
+			if(result[0].length() != 2 && result[0].length() != 4) err = true;
+			if(result[1].length() != 2) err = true;
+
+			value = atol(result[0].c_str()) * 10000 + 
+				    atol(result[1].c_str()) * 100   + 
+					1;
+
+			date_ = Date(value);
+
+			break;
+
+		case 3:
+
+			if(result[0].length() != 2 && result[0].length() != 4) err = true;
+			if(result[1].length() != 2) err = true;
+			if(result[2].length() != 2) err = true;
+
+			value = atol(result[0].c_str()) * 10000 + 
+				    atol(result[1].c_str()) * 100   + 
+					atol(result[2].c_str());
+
+			if((value%100) == 0) value++; // For dates as 970900
+			date_ = Date(value);
+
+			break;
+
+		default:
+			err = false;
+			break;
+	}
+
+	if(err) throw SeriousBug(std::string("Invalid month ") + s);
+}
+
+Month::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+void Month::print(std::ostream& s) const
+{
+	char oldfill = s.fill();
+	s << year() << '-' << std::setw(2) << std::setfill('0') << month() 
+	  << std::setfill(oldfill);
+}
+
+void Month::dump(DumpLoad& a) const
+{
+	date_.dump(a);
+}
+
+void Month::load(DumpLoad& a)
+{
+	date_.load(a);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Month.h b/eckit/src/eckit/types/Month.h
new file mode 100644
index 0000000..6a6db5e
--- /dev/null
+++ b/eckit/src/eckit/types/Month.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Month.h
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_Month_h
+#define eckit_Month_h
+
+#include "eckit/types/Date.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Month {
+public:
+
+// -- Contructors
+
+	Month(const Date& = Date(0));
+	Month(long,long);
+	Month(const std::string&);
+
+#include "eckit/types/Month.b"
+
+// -- Destructor
+
+	~Month() {}
+
+// -- Convertors
+	
+	operator std::string() const;
+
+// -- Operators
+
+	bool operator==(const Month& other) const
+		{ return date_ == other.date_ ;}
+
+	bool operator!=(const Month& other) const
+		{ return date_ != other.date_ ;}
+
+	bool operator<(const Month& other)  const
+		{ return date_ <  other.date_ ;}
+
+	bool operator>(const Month& other)  const
+		{ return date_ >  other.date_ ;}
+
+	bool operator<=(const Month& other)  const
+		{ return date_ <=  other.date_ ;}
+
+	bool operator>=(const Month& other)  const
+		{ return date_ >=  other.date_ ;}
+
+
+// -- Methods
+
+	long year() const  { return date_.year(); }
+	long month() const { return date_.month(); }
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class methods
+
+
+// -- Friends
+
+	friend std::ostream& operator<< (std::ostream& s, const Month& month)
+		{ month.print(s); return s; }
+
+protected:
+
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+	
+	Date date_;
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+// -- Class methods
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Time.cc b/eckit/src/eckit/types/Time.cc
new file mode 100644
index 0000000..8d51e8a
--- /dev/null
+++ b/eckit/src/eckit/types/Time.cc
@@ -0,0 +1,216 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "eckit/persist/DumpLoad.h"
+#include "eckit/types/Time.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+inline void printTime(std::ostream& s, long n)
+{
+	if(n<10) s << '0';
+	s << n;
+}
+
+Time::Time(long seconds):
+	seconds_(seconds)
+{
+	if(seconds >= 86400 || seconds < 0)
+	{
+		std::string msg = "Time in seconds cannot exceed 86400 ";
+		Translator<long,std::string> t;
+		msg += t(seconds);
+		throw BadTime(msg);
+	}
+}
+
+Time::Time(const std::string& s)
+{
+	Tokenizer parse(":");
+	std::vector<std::string> result;
+
+	parse(s,result);
+
+	long hh = 0, mm = 0 , ss = 0;
+	bool err = false;
+	long t = atol(s.c_str());
+
+	switch(result.size())
+	{
+		case 1:
+			// hh or hhmm or hhmmss
+			switch(s.length())
+			{
+				case 2: hh = t; break;
+				case 4: hh = t/100; mm = t % 100; break;
+				case 6: hh = t/10000; mm = (t%10000)/100; ss = (t%10000)%100; break;
+				default: err = true; break;
+			}
+			break;
+
+		case 2:
+			// hh:mm
+			err =  result[0].length() != 2 
+				|| result[1].length() != 2;
+
+			hh = atol(result[0].c_str());
+			mm = atol(result[1].c_str());
+
+			break;
+			
+		case 3:
+			// hh:mm:ss
+
+			err =  result[0].length() != 2 
+			    || result[1].length() != 2 
+				|| result[2].length() != 2;
+
+			hh = atol(result[0].c_str());
+			mm = atol(result[1].c_str());
+			ss = atol(result[2].c_str());
+
+			break;
+
+		default: 
+			err = true;
+			break;
+	}
+
+	if(err) throw BadTime(std::string("Invalid time ") + s);
+
+	if(hh >= 24 || mm >= 60 || ss >= 60
+	   || hh < 0 || mm < 0 || ss < 0)
+	{
+        std::string msg = "Wrong input for time: ";
+        Translator<long,std::string> t;
+        msg += t(hh); msg += " hours ";
+        msg += t(mm); msg += " minutes ";
+        msg += t(ss); msg += " seconds";
+        throw BadTime(msg);
+	}
+
+	seconds_ = hh*3600+mm*60+ss;
+}
+
+Time::operator std::string() const
+{
+    std::ostringstream os;
+    os << *this;
+    return os.str();
+}
+
+Time::Time(const Time& other):
+	seconds_(other.seconds_)
+{
+}
+
+Time& Time::operator=(const Time& other)
+{
+	seconds_ = other.seconds_;
+	return *this;
+}
+
+Time::Time(long hh, long mm, long ss):
+	seconds_(hh*3600+mm*60+ss)
+{
+	if(hh >= 24 || mm >= 60 || ss >= 60
+	   || hh < 0 || mm < 0 || ss < 0)
+	{
+        std::string msg = "Wrong input for time: ";
+        Translator<long,std::string> t;
+        msg += t(hh); msg += " hours ";
+        msg += t(mm); msg += " minutes ";
+        msg += t(ss); msg += " seconds";
+        throw BadTime(msg);
+	}
+}
+
+Time::~Time()
+{
+}
+
+long Time::hours() const
+{
+	long l = seconds_;
+	return l / 3600;
+}
+
+long Time::minutes() const
+{
+	long l = seconds_;
+	return (l % 3600) / 60;
+}
+
+long Time::seconds() const
+{
+	long l = seconds_;
+	return l % 60;
+}
+
+long Time::hhmmss() const
+{
+	return hours() * 10000 + minutes() * 100 + seconds();
+}
+
+void Time::print(std::ostream& s) const
+{
+	printTime(s,hours());
+	s << ':';
+	printTime(s,minutes());
+	s << ':';
+	printTime(s,seconds());
+}
+
+Time Time::now()
+{
+    time_t now;
+    time(&now);
+	struct tm *pt;
+
+#ifdef EC_HAVE_GMTIME_R
+    struct tm t;
+    gmtime_r(&now,&t);
+	pt = &t;
+#else
+    struct tm *t;
+    t = gmtime(&now);
+	pt = t;
+#endif
+
+	return Time(pt->tm_hour,pt->tm_min,pt->tm_sec);
+}
+
+BadTime::BadTime(const std::string& s):
+	BadValue(s)
+{
+}
+
+void Time::dump(DumpLoad& a) const
+{
+	a.dump(seconds_);
+}
+
+void Time::load(DumpLoad& a)
+{
+	a.load(seconds_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Time.h b/eckit/src/eckit/types/Time.h
new file mode 100644
index 0000000..0da79ec
--- /dev/null
+++ b/eckit/src/eckit/types/Time.h
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Time.h
+// Manuel Fuentes - ECMWF Oct 96
+
+#ifndef eckit_Time_h
+#define eckit_Time_h
+
+#include "eckit/exception/Exceptions.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DumpLoad;
+class Bless;
+
+// Forwarded declarations
+
+typedef double Second;
+
+class Time {
+
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	Time(long, long, long);
+	Time(long seconds = 0);
+	Time(const std::string&);
+
+#include "eckit/types/Time.b"
+
+// -- Copy
+
+	Time(const Time&);
+	Time& operator=(const Time&);
+
+// -- Destructor
+
+	~Time();
+
+// -- Convertors
+
+	operator std::string() const;
+	operator Second() const { return seconds_; }
+
+// -- Operators
+
+	bool operator==(const Time& other) const
+		{ return seconds_ == other.seconds_; }
+
+	bool operator!=(const Time& other) const
+		{ return (seconds_ != other.seconds_); }
+
+	bool operator>(const Time& other) const
+		{ return (seconds_ > other.seconds_); }
+
+	bool operator<(const Time& other) const
+		{ return (seconds_ < other.seconds_); }
+
+	bool operator>=(const Time& other) const
+		{ return (seconds_ >= other.seconds_); }
+
+	bool operator<=(const Time& other) const
+		{ return (seconds_ <= other.seconds_); }
+
+	Second operator-(const Time& other) const
+		{ return seconds_ - other.seconds_; }
+
+//  Does not make sens
+//	Time operator+(const Time& other) const
+//		{ return seconds_ + other.seconds_; }
+
+	Time& operator+=(const Second& sec)
+		{ seconds_ += sec; return *this; }	
+
+	Time& operator-=(const Second& sec)
+		{ seconds_ -= sec; return *this; }	
+
+// -- Methods
+	long hours() const;
+	long minutes() const;
+	long seconds() const;
+	long hhmmss() const;
+
+	void dump(DumpLoad&) const;
+	void load(DumpLoad&);
+
+// -- Class Methods
+
+	static Time now();
+
+protected:
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+private:
+
+// -- Members
+
+	Second seconds_;
+
+	friend std::ostream& operator<<(std::ostream& s,const Time& t)
+		{ t.print(s); return s; }
+
+};
+
+
+class BadTime: public BadValue {
+public:
+	BadTime(const std::string& t);
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/TimeInterval.cc b/eckit/src/eckit/types/TimeInterval.cc
new file mode 100644
index 0000000..95a6791
--- /dev/null
+++ b/eckit/src/eckit/types/TimeInterval.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/types/TimeInterval.h"
+#include "eckit/types/DateTime.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+TimeInterval::TimeInterval(const DateTime& t1, const DateTime& t2):
+	begin_(t1),
+	end_(t2)
+{
+}
+
+void TimeInterval::print(std::ostream& s) const
+{
+	s << "[" << begin_ << "," << end_ << "]";
+}
+
+TimeInterval TimeInterval::intersect(const TimeInterval& other) const
+{
+    return TimeInterval(std::max(begin_,other.begin_),std::min(end_,other.end_));
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/TimeInterval.h b/eckit/src/eckit/types/TimeInterval.h
new file mode 100644
index 0000000..dac2349
--- /dev/null
+++ b/eckit/src/eckit/types/TimeInterval.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File TimeInterval.h
+// Manuel Fuentes - ECMWF Oct 96
+
+#ifndef eckit_TimeInterval_h
+#define eckit_TimeInterval_h
+
+#include "eckit/types/DateTime.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class TimeInterval {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	TimeInterval(const DateTime& b=DateTime(), const DateTime& e=DateTime());
+	TimeInterval(const DateTime& b,const Second& interval);
+
+// -- Convertors
+
+	const DateTime&	begin() const { return begin_; }
+	const DateTime&	end() const { return end_; }
+
+// -- Operators
+	bool operator<(const TimeInterval& other) const
+		{ return begin_ < other.begin_; }
+
+// -- Methods
+	TimeInterval intersect(const TimeInterval& other) const;
+	bool empty() const { return begin_>end_; }
+
+private:
+
+// -- Members
+
+	DateTime begin_;
+	DateTime end_;
+
+// -- Methods
+	void print(std::ostream&) const; 	
+
+// -- Friends
+	friend std::ostream& operator<<(std::ostream& s,const TimeInterval& p)
+		{ p.print(s); return s; }
+
+	friend bool operator==(const TimeInterval& p1, const TimeInterval& p2)
+		{ return p1.begin_ == p2.begin_ && p1.end_ == p2.end_; }
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/Types.cc b/eckit/src/eckit/types/Types.cc
new file mode 100644
index 0000000..07839b1
--- /dev/null
+++ b/eckit/src/eckit/types/Types.cc
@@ -0,0 +1,144 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+void operator<<(Stream& s,const std::vector<T>& t)
+{
+	s << Ordinal(t.size());
+    for(typename std::vector<T>::const_iterator i = t.begin(); i != t.end() ; ++i)
+		s << (*i);
+}
+
+template<class T>
+void operator>>(Stream& s, std::vector<T>& t)
+{
+
+	Ordinal size;
+	s >> size;
+
+	t.clear();
+	t.reserve(size);
+
+	for(Ordinal i = 0; i < size; i++)
+	{
+		T n;
+		s >> n;
+		t.push_back(n);
+	}
+}
+
+template<class K, class V>
+void operator<<(Stream& s,const std::map<K,V>& t)
+{
+    s << Ordinal(t.size());
+    for(typename std::map<K,V>::const_iterator i = t.begin(); i != t.end() ; ++i)
+        s << i->first << i->second;
+}
+
+template<class K, class V>
+void operator>>(Stream& s, std::map<K,V>& t)
+{
+
+	Ordinal size;
+	s >> size;
+
+	t.clear();
+
+	for(Ordinal i = 0; i < size; i++)
+	{
+		K k;
+		s >> k;
+        V v(s);
+		t[k] = v;
+	}
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+output_list<T>::output_list(std::ostream& s):
+    first_(true), s_(s)
+{
+	s_ << '[';
+}
+
+template<class T>
+output_list<T>::~output_list()
+{
+	flush();
+	s_ << ']';
+}
+
+template<class T>
+void output_list<T>::push_back(const T& value)
+{
+	if(v_.size() < 2 )
+		v_.push_back(value);
+	else {
+		long long diff1 = v_[1]  - v_[0];
+		long long diff2 = value  - v_.back();
+
+		if(diff1 != diff2 || diff2 < 0 )
+			flush();
+
+		v_.push_back(value);
+	}
+}
+
+template<class T>
+void output_list<T>::flush()
+{
+	long long diff;
+
+	if(!first_) s_ << ',';
+
+	switch(v_.size())
+	{
+		case 0: break;
+		case 1: s_ << v_[0] ; break;
+		case 2: s_ << v_[0] << ',' << v_[1] ; break;
+		default:
+			diff = v_[1]  - v_[0];
+#ifdef __hpux
+			switch(int(diff))
+#else
+			switch(diff)
+#endif
+			{
+				case 0:
+					s_ << v_.size() << '*' << v_[0];
+					break;
+
+				case 1:
+					s_ << v_[0] << '-' << v_.back();
+					break;
+
+				default:
+					s_ << v_[0] << '-' << v_.back() << '-' << diff;
+					break;
+			}
+			break;
+
+	}
+	v_.clear();
+	first_ = false;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/Types.h b/eckit/src/eckit/types/Types.h
new file mode 100644
index 0000000..a1aa45b
--- /dev/null
+++ b/eckit/src/eckit/types/Types.h
@@ -0,0 +1,195 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Types.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_Types_h
+#define eckit_Types_h
+
+#include <utility>
+
+#include "eckit/eckit.h"
+
+#include "eckit/runtime/TaskID.h" // to be removed
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef unsigned long Ordinal;  ///< for counting
+
+typedef std::vector<Ordinal>                OrdinalList;
+
+typedef std::vector<std::string>            StringList;
+typedef std::set<std::string>               StringSet;
+typedef std::map<std::string,std::string>   StringDict;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<typename S, typename T>
+inline std::ostream& operator<<(std::ostream& s, const std::pair<S, T>& p)
+{
+    s << "<" << p.first << "," << p.second << ">";
+    return s;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+class output_list {
+
+    std::vector<T> v_;
+	bool      first_;
+    std::ostream&  s_;
+
+	void flush();
+
+public:
+	void push_back(const T&);
+	output_list(std::ostream&);
+	~output_list();
+};
+
+template<class T>
+class output_list_iterator : public output_iterator {
+
+	output_list<T> *list_;
+
+public:
+	output_list_iterator(output_list<T>* l) : list_(l) {}
+	~output_list_iterator() {}
+
+    output_list_iterator<T>& operator=(const T& v) { list_->push_back(v); return *this; }
+    output_list_iterator<T>& operator*() { return *this; }
+    output_list_iterator<T>& operator++() { return *this; }
+    output_list_iterator<T>& operator++(int) { return *this; }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class VectorPrintSimple{};
+class VectorPrintContracted{};
+
+template <typename T>
+struct VectorPrintSelector { typedef VectorPrintContracted selector; };
+
+template <> struct VectorPrintSelector<std::string> { typedef VectorPrintSimple selector; };
+template <> struct VectorPrintSelector<double> { typedef VectorPrintSimple selector; };
+template <typename K, typename V> struct VectorPrintSelector<std::pair<K,V> > { typedef VectorPrintSimple selector; };
+
+
+template<class T>
+inline std::ostream& __print_list(std::ostream& s, const T& t, VectorPrintContracted)
+{
+	output_list<typename T::value_type> l(s);
+	output_list_iterator<typename T::value_type> os(&l);
+    std::copy(t.begin(), t.end(), os);
+    return s;
+}
+
+template <typename T>
+inline std::ostream& __print_list(std::ostream& s, const std::vector<T>& t, VectorPrintSimple) {
+
+    s << '[';
+    for(Ordinal i = 0; i < t.size(); i++) {
+        if (i != 0)
+            s << ',';
+        s << t[i];
+    }
+    s << ']';
+    return s;
+}
+
+template<typename K, typename V>
+inline std::ostream& __print_container(std::ostream& s, const std::map<K,V>& m)
+{
+    const char* sep = "";
+    s << "{";
+    for (typename std::map<K,V>::const_iterator it = m.begin(); it != m.end(); ++it)
+    {
+        s << sep << it->first << "=" << it->second;
+        sep = ",";
+    }
+    s << "}";
+    return s;
+}
+
+template<typename T>
+inline std::ostream& __print_container(std::ostream& s, const std::set<T>& m)
+{
+    const char *sep = "";
+    s << "{";
+    for (typename std::set<T>::const_iterator it = m.begin(); it != m.end(); ++it )
+    {
+        s << sep << *it;
+        sep = ",";
+    }
+    s << "}";
+    return s;
+}
+
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace std {
+
+    // n.b. This overload needs to go either in the namespace std:: (which contains
+    //      ostream, vector), the global namespace, or the namespace containing T.
+    //      Otherwise it will not be found when doing lookups.
+    //
+    //  --> Probably best to put it in std::. It is acceptable to add "template
+    //      specializations for any standard library template" if the "declaration
+    //      depends on user-defined types".
+
+    template<class T>
+    inline std::ostream& operator<<(std::ostream& s,const std::vector<T>& v)
+    {
+        return eckit::__print_list(s, v, typename eckit::VectorPrintSelector<T>::selector());
+    }
+
+    template<typename K, typename V>
+    inline std::ostream& operator<<(std::ostream& s, const std::map<K,V>& m)
+    {
+        return eckit::__print_container(s,m);
+    }
+
+    template< typename T >
+    inline std::ostream& operator<<(std::ostream& s, const std::set<T>& m)
+    {
+        return eckit::__print_container(s,m);
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Stream; // forward
+
+/// Operators to send vectors in streams
+template<class T> void operator<<(Stream&,const std::vector<T>&);
+template<class T> void operator>>(Stream&,std::vector<T>&);
+
+/// Operators to send maps in streams
+/// Note: the value type V must have a constructor from Stream&
+template<class K, class V> void operator<<(Stream&,const std::map<K,V>&);
+template<class K, class V> void operator>>(Stream&,std::map<K,V>&);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/types/Types.cc"
+
+#endif
diff --git a/eckit/src/eckit/types/UUID.cc b/eckit/src/eckit/types/UUID.cc
new file mode 100644
index 0000000..5591a13
--- /dev/null
+++ b/eckit/src/eckit/types/UUID.cc
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cctype>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/types/UUID.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static char to_char( size_t i )
+{
+	if (i <= 9)
+		return static_cast<char>('0' + i);
+	else
+		return static_cast<char>('a' + (i-10));
+}
+
+//-----------------------------------------------------------------------------
+
+UUID::UUID()
+{
+	zero(data_);
+}
+
+UUID::UUID(const std::string& s)
+{
+	ASSERT( s.size() == hexSize() );
+	fromString(s);
+}
+
+std::string UUID::asString() const
+{
+	std::string result;
+	result.reserve( hexSize() );
+
+	std::size_t i=0;
+	for (UUID::const_iterator it = begin(); it != end(); ++it, ++i)
+	{
+		const size_t hi = ((*it) >> 4) & 0x0F;
+		result += to_char(hi);
+
+		const size_t lo = (*it) & 0x0F;
+		result += to_char(lo);
+	}
+	return result;
+}
+
+void UUID::fromString( const std::string& s )
+{
+	std::stringstream str(s);
+	str >> *this;
+}
+
+void UUID::print(std::ostream& s) const
+{
+	s << asString();
+}
+
+UUID::operator std::string() const
+{
+	return asString();
+}
+
+std::istream& operator>>(std::istream& is,UUID& u)
+{
+	typedef std::istream::char_type char_type;
+
+	const std::istream::sentry ok(is);
+	if (ok)
+	{
+		unsigned char data[16];
+
+		char_type xdigits[] = "0123456789ABCDEF";
+		char_type*const xdigits_end = xdigits+16;
+
+		char_type c;
+		for (std::size_t i=0; i<u.size() && is; ++i)
+		{
+			is >> c;
+			c = toupper(c);
+
+			char_type* f = std::find(xdigits, xdigits_end, c);
+			if (f == xdigits_end) {
+				is.setstate(std::ios_base::failbit);
+				break;
+			}
+
+			unsigned char byte = static_cast<unsigned char>(std::distance(&xdigits[0], f));
+
+			is >> c;
+			c = toupper(c);
+			f = std::find(xdigits, xdigits_end, c);
+			if (f == xdigits_end) {
+				is.setstate(std::ios_base::failbit);
+				break;
+			}
+
+			byte <<= 4;
+			byte |= static_cast<unsigned char>(std::distance(&xdigits[0], f));
+
+			data[i] = byte;
+		}
+
+		if (is) {
+			std::copy(data, data+16, u.begin());
+		}
+	}
+	return is;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/UUID.h b/eckit/src/eckit/types/UUID.h
new file mode 100644
index 0000000..49652b2
--- /dev/null
+++ b/eckit/src/eckit/types/UUID.h
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @date Dec 2014
+
+#ifndef eckit_UUID_h
+#define eckit_UUID_h
+
+#include <stdint.h>
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class UUID {
+
+public: // types
+
+	typedef uint8_t value_type;
+
+	typedef uint8_t* iterator;
+	typedef uint8_t  const* const_iterator;
+
+public:
+
+	UUID();
+	UUID( const std::string& );
+
+	~UUID() {}
+
+public: // methods
+
+	iterator begin() { return data_; } const_iterator begin() const { return data_; }
+	iterator end() { return data_ + size(); }
+	const_iterator end() const { return data_ + size(); }
+
+	static size_t size() { return 16; }
+	static size_t hexSize() { return 32; } // 16 hex digits
+
+	bool isNil() const
+	{
+		for(size_t i=0; i < size(); ++i)
+		{
+			if ( data_[i] != 0U )
+				return false;
+		}
+		return true;
+	}
+
+	operator std::string() const;
+
+	std::string asString() const;
+	void fromString( const std::string& );
+
+protected: // methods
+
+	void print(std::ostream& s) const;
+
+private: // members
+
+	uint8_t data_[16];
+
+	friend std::ostream& operator<<(std::ostream& os,const UUID& u) { u.print(os); return os; }
+	friend std::istream& operator>>(std::istream& is,UUID& u);
+
+};
+
+//-----------------------------------------------------------------------------
+
+inline bool operator==(UUID const& lhs, UUID const& rhs)
+{
+	return std::equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+inline bool operator!=(UUID const& lhs, UUID const& rhs)
+{
+	return !(lhs == rhs);
+}
+
+inline bool operator<(UUID const& lhs, UUID const& rhs)
+{
+	return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+
+inline bool operator>(UUID const& lhs, UUID const& rhs)
+{
+	return rhs < lhs;
+}
+
+inline bool operator<=(UUID const& lhs, UUID const& rhs)
+{
+	return !(rhs < lhs);
+}
+
+inline bool operator>=(UUID const& lhs, UUID const& rhs)
+{
+	return !(lhs < rhs);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/types/VerifyingDate.cc b/eckit/src/eckit/types/VerifyingDate.cc
new file mode 100644
index 0000000..2e2787a
--- /dev/null
+++ b/eckit/src/eckit/types/VerifyingDate.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/VerifyingDate.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+VerifyingDate::VerifyingDate(const Date& d, const Time& t):
+	DateTime(d,t)
+{
+	ASSERT( Second(time_) == 0 );
+}
+
+VerifyingDate::VerifyingDate(const std::string& s):
+	DateTime(s)
+{
+	ASSERT( Second(time_) == 0 );
+}
+
+
+VerifyingDate::VerifyingDate(time_t thetime):
+	DateTime(thetime)
+{
+	ASSERT( Second(time_) == 0 );
+}
+
+VerifyingDate::VerifyingDate(const DateTime& thetime):
+	DateTime(thetime)
+{
+	ASSERT( Second(time_) == 0 );
+}
+
+VerifyingDate::operator std::string() const
+{
+	return std::string(date_);
+}
+
+void VerifyingDate::print(std::ostream& s) const
+{
+	s << date_;	
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/types/VerifyingDate.h b/eckit/src/eckit/types/VerifyingDate.h
new file mode 100644
index 0000000..4824aa2
--- /dev/null
+++ b/eckit/src/eckit/types/VerifyingDate.h
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// Baudouin Raoult - ECMWF Sep 96
+
+#ifndef eckit_VerifyingDate_h
+#define eckit_VerifyingDate_h
+
+#include "eckit/types/DateTime.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class VerifyingDate : public DateTime {
+public:
+
+// -- Contructors
+
+	VerifyingDate(time_t = ::time(0));
+	VerifyingDate(const Date&, const Time&);
+	VerifyingDate(const std::string&);
+	VerifyingDate(const DateTime&);
+
+#include "eckit/types/VerifyingDate.b"
+    
+// -- Destructor
+
+	~VerifyingDate() {}
+
+// -- Operators
+
+	operator std::string() const;
+
+// -- Methods
+
+	// -- Class methods
+	
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// -- Members
+	
+// -- Methods
+
+	void print(std::ostream&) const;
+
+// -- Class methods
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const VerifyingDate& p)
+		{ p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/Hash.cc b/eckit/src/eckit/utils/Hash.cc
new file mode 100644
index 0000000..6fe1c7c
--- /dev/null
+++ b/eckit/src/eckit/utils/Hash.cc
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstring>
+
+#include "eckit/utils/Hash.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static eckit::Mutex *local_mutex = 0;
+static std::map<std::string, HashFactory *> *m = 0;
+
+static void init() {
+    local_mutex = new eckit::Mutex();
+    m = new std::map<std::string, HashFactory *>();
+}
+
+
+HashFactory::HashFactory(const std::string& name):
+    name_(name) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    ASSERT(m->find(name) == m->end());
+
+    (*m)[name] = this;
+}
+
+
+HashFactory::~HashFactory() {
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+    m->erase(name_);
+}
+
+bool HashFactory::has(const std::string& name)
+{
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    return m->find(name) != m->end();
+}
+
+void HashFactory::list(std::ostream& out) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    const char* sep = "";
+    for (std::map<std::string, HashFactory *>::const_iterator j = m->begin() ; j != m->end() ; ++j) {
+        out << sep << (*j).first;
+        sep = ", ";
+    }
+}
+
+
+Hash *HashFactory::build(const std::string &name) {
+
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    std::map<std::string, HashFactory *>::const_iterator j = m->find(name);
+
+    eckit::Log::debug() << "Looking for HashFactory [" << name << "]" << std::endl;
+
+    if (j == m->end()) {
+        eckit::Log::error() << "No HashFactory for [" << name << "]" << std::endl;
+        eckit::Log::error() << "HashFactories are:" << std::endl;
+        for (j = m->begin() ; j != m->end() ; ++j)
+            eckit::Log::error() << "   " << (*j).first << std::endl;
+        throw eckit::SeriousBug(std::string("No HashFactory called ") + name);
+    }
+
+    return (*j).second->make();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Hash::Hash() {
+}
+
+Hash::~Hash() {
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+NoHash::NoHash() {
+}
+
+NoHash::~NoHash() {
+}
+
+void NoHash::reset() const {
+}
+
+Hash::digest_t NoHash::compute(const void*, long) {
+    return std::string();
+}
+
+void NoHash::update(const void*, long) {
+}
+
+Hash::digest_t NoHash::digest() const {
+    return digest_; // should be empty
+}
+
+namespace  {
+    HashBuilder<NoHash> builder1("None");
+    HashBuilder<NoHash> builder2("NoHash");
+}
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/Hash.h b/eckit/src/eckit/utils/Hash.h
new file mode 100644
index 0000000..1e801a2
--- /dev/null
+++ b/eckit/src/eckit/utils/Hash.h
@@ -0,0 +1,147 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_Hash_H
+#define eckit_utils_Hash_H
+
+#include "eckit/eckit_config.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+
+#include "eckit/memory/NonCopyable.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Hash : private eckit::NonCopyable {
+
+public:  // types
+
+  typedef std::string digest_t;
+
+public:  // methods
+
+  Hash();
+
+  virtual ~Hash();
+
+  virtual void reset() const = 0;
+
+  virtual digest_t digest() const = 0;
+
+  // for one shot, stateless computation of the hash of the buffer
+  virtual digest_t compute(const void*, long) = 0;
+
+  void add(char x){ update(&x, sizeof(x)); }
+  void add(unsigned char x){ update(&x, sizeof(x)); }
+
+  void add(bool x){ update(&x, sizeof(x)); }
+
+  void add(int x){ update(&x, sizeof(x)); }
+  void add(unsigned int x){ update(&x, sizeof(x)); }
+
+  void add(short x){ update(&x, sizeof(x)); }
+  void add(unsigned short x){ update(&x, sizeof(x)); }
+
+  void add(long x){ update(&x, sizeof(x)); }
+  void add(unsigned long x){ update(&x, sizeof(x)); }
+
+  void add(long long x){ update(&x, sizeof(x)); }
+  void add(unsigned long long x){ update(&x, sizeof(x)); }
+
+  void add(float x){ update(&x, sizeof(x)); }
+  void add(double x){ update(&x, sizeof(x)); }
+
+  void add(const void* x, long size) { update(x, size); }
+
+  void add(const std::string& x) { update(x.c_str(), x.size()); }
+
+  template<class T>
+  Hash& operator<<(const T& x) { add(x); return *this; }
+
+  operator std::string() { return digest(); }
+
+protected: // methods
+
+  // for incremental hashing
+  virtual void update(const void*, long) = 0;
+
+private:  // types
+
+  // Make sure this is not called with a pointer
+  template<class T> void add(const T* x);
+  void add(const void*);
+
+  /// Double hashing
+  void add(const Hash& hash) { add(hash.digest()); }
+
+protected: // members
+
+  mutable digest_t digest_;  ///< cached digest
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class NoHash : public Hash {
+
+public:  // types
+
+  NoHash();
+
+  virtual ~NoHash();
+
+  virtual void reset() const;
+
+  virtual digest_t compute(const void*, long);
+
+  virtual void update(const void*, long);
+
+  virtual digest_t digest() const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class HashFactory {
+
+    std::string name_;
+    virtual Hash* make() = 0;
+
+  protected:
+
+    HashFactory(const std::string &);
+    virtual ~HashFactory();
+
+  public:
+
+    static bool has(const std::string& name);
+    static void list(std::ostream &);
+    static Hash* build(const std::string&);
+
+};
+
+template< class T>
+class HashBuilder : public HashFactory {
+    virtual Hash* make() {
+        return new T();
+    }
+  public:
+    HashBuilder(const std::string &name) : HashFactory(name) {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/HyperCube.cc b/eckit/src/eckit/utils/HyperCube.cc
new file mode 100644
index 0000000..cb10598
--- /dev/null
+++ b/eckit/src/eckit/utils/HyperCube.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/utils/HyperCube.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static void addLoop(
+	Ordinal      d,   
+	Ordinal      which,
+	Ordinal      where,
+	Ordinal      count,
+	Ordinal      depth,
+	HyperCube&   target,
+	const HyperCube::Dimensions&  dims,
+	HyperCube::Coordinates& coord,
+	HyperCube::Remapping&   remap)
+{
+	if(d == depth)
+		remap.push_back(target.index(coord));
+	else {
+
+		int k = 0;
+		for( size_t i = 0; i < dims[d]; i++,k++)
+		{
+			if(which == d && i == where) k += count;
+			coord[d]   = k;
+			addLoop(d+1,which,where,count,depth,target,dims,coord,remap);
+		}
+	}
+}
+
+HyperCube HyperCube::addToDimension(Ordinal which,
+	Ordinal where,Ordinal howMuch,Remapping& remap) const
+{
+
+	remap.clear();
+	remap.reserve(count());
+
+	Dimensions  newdims = dimensions_;
+	Coordinates coord(dimensions_.size());
+
+
+	newdims[which] += howMuch;
+	
+	HyperCube target(newdims);
+
+	addLoop(0,which,where,howMuch,dimensions_.size(),target,dimensions_,coord,remap);
+
+	return target;
+}
+
+void HyperCube::coordinates(Ordinal index,Coordinates& result) const
+{
+	ASSERT(result.size() == dimensions_.size());
+
+	for(int i = dimensions_.size()-1; i >= 0; i--)
+	{
+		result[i] = (index % dimensions_[i]);
+		index    /= dimensions_[i];
+	}
+
+	ASSERT(index == 0);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/HyperCube.h b/eckit/src/eckit/utils/HyperCube.h
new file mode 100644
index 0000000..6b4a7a4
--- /dev/null
+++ b/eckit/src/eckit/utils/HyperCube.h
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HyperCube.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef eckit_HyperCube_h
+#define eckit_HyperCube_h
+
+#include "eckit/types/Types.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Helper class to handle multi-dimension objects
+// The first dimension should be the one most likely to change
+
+class HyperCube {
+public:
+
+
+	typedef std::vector<Ordinal> Dimensions;
+	typedef std::vector<Ordinal> Coordinates;
+	typedef std::vector<Ordinal> Remapping;
+
+// -- Contructors
+
+	HyperCube(const Dimensions& d) : dimensions_(d) {}
+
+// -- Methods
+
+	// Translate coordinates into an index to a 1 dimension array.
+
+	Ordinal index(const Coordinates&) const;
+
+	// Return the number of elemets
+
+	Ordinal count() const;
+
+	// Translate index to coordinates
+
+	void coordinates(Ordinal index, Coordinates&) const;
+
+	// Accessors
+
+	const Dimensions& dimensions() const    { return dimensions_;        }
+	Dimensions& dimensions()                { return dimensions_;        }
+	Ordinal     dimensions(Ordinal n) const { return dimensions_[n];     }
+	Ordinal     size() const                { return dimensions_.size(); }
+
+	// Return the 'remapping' std::vector needing to add 'count' labels
+	// for the dimension 'which' at position 'where'
+
+	HyperCube addToDimension(Ordinal which,Ordinal where,Ordinal count,Remapping&) const;
+
+	// Combine two 'remapping' vectors
+
+	static void combine(Remapping&,const Remapping&);
+
+private:
+
+// -- Members
+
+	Dimensions dimensions_;
+
+};
+
+inline  // For speed
+Ordinal HyperCube::count() const
+{
+    return accumulate(dimensions_.begin(),dimensions_.end(),1,std::multiplies<Ordinal>());
+}
+
+
+inline // For speed
+Ordinal HyperCube::index(const Coordinates& coord) const
+{
+	Ordinal n = 1;
+	Ordinal a = 0;
+
+	ASSERT(coord.size() == dimensions_.size());
+
+	// The fact that this is in reverse is important for addToDimension
+
+	for(int i = coord.size()-1; i >= 0; i--)
+	{
+		ASSERT(/*coord[i] >= 0 &&*/ coord[i] < dimensions_[i]);
+		a += coord[i] * n;
+		n *= dimensions_[i];
+	}
+
+	return a;
+}
+
+
+inline // For speed
+void HyperCube::combine(Remapping& map1,const Remapping& map2)
+{
+	if(map1.size() == 0)
+		map1 = map2;
+	else for(Remapping::iterator i = map1.begin() ; i != map1.end() ; ++i)
+	{
+		ASSERT(*i < map2.size());
+		*i = map2[*i];
+	}
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/MD4.cc b/eckit/src/eckit/utils/MD4.cc
new file mode 100644
index 0000000..1b62acb
--- /dev/null
+++ b/eckit/src/eckit/utils/MD4.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstring>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/utils/MD4.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const char* hex = "0123456789abcdef";
+static std::string toString(unsigned char digest[MD4_DIGEST_LENGTH]) {
+
+    char x[2*MD4_DIGEST_LENGTH];
+
+    size_t j = 0;
+    for(size_t i = 0; i<MD4_DIGEST_LENGTH; ++i) {
+        x[j++] = hex[(digest[i] & 0xf0) >> 4];
+        x[j++] = hex[(digest[i] & 0xf)];
+    }
+
+    return std::string(x, 2*MD4_DIGEST_LENGTH);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MD4::~MD4() {}
+
+void MD4::reset() const
+{
+    MD4_Init(&ctx_);
+}
+
+Hash::digest_t MD4::compute(const void* buffer, long size)
+{
+    MD4_CTX s;
+    MD4_Init(&s);
+    MD4_Update(&s, static_cast<const unsigned char*>(buffer), size);
+    unsigned char digest[MD4_DIGEST_LENGTH];
+    MD4_Final(digest, &s);
+    return toString(digest);
+}
+
+MD4::MD4() {
+    MD4_Init(&ctx_);
+}
+
+MD4::MD4(const char* s) {
+    MD4_Init(&ctx_);
+    add( s, strlen(s) );
+}
+
+MD4::MD4(const std::string& s) {
+    MD4_Init(&ctx_);
+    add( s.c_str(), s.size() );
+}
+
+MD4::MD4(const void* data, size_t len) {
+    MD4_Init(&ctx_);
+    add( data, len );
+}
+
+void MD4::update(const void* buffer, long length) {
+    if (length > 0) {
+        MD4_Update(&ctx_, static_cast<const unsigned char*>(buffer), length);
+        if (!digest_.empty())
+            digest_ = digest_t(); // reset the digest
+    }
+}
+
+MD4::digest_t MD4::digest() const {
+
+    if (digest_.empty()) { // recompute the digests
+        unsigned char digest[MD4_DIGEST_LENGTH];
+        MD4_Final(digest, &ctx_);
+        digest_ = toString(digest);
+    }
+
+    return digest_;
+}
+
+namespace  {
+    HashBuilder<MD4> builder("MD4");
+}
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/MD4.h b/eckit/src/eckit/utils/MD4.h
new file mode 100644
index 0000000..895a799
--- /dev/null
+++ b/eckit/src/eckit/utils/MD4.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_MD4_H
+#define eckit_utils_MD4_H
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_SSL
+#include <openssl/md4.h>
+#else
+#error "eckit was not configured with OpenSSL, SHA1 is disabled. Use conditional ECKIT_HAVE_SSL from eckit/eckit_config.h"
+#endif
+
+#ifndef MD4_DIGEST_LENGTH
+#define MD4_DIGEST_LENGTH 16
+#endif
+
+#include "eckit/utils/Hash.h"
+
+namespace eckit {
+
+class MD4 : public Hash {
+
+public:  // types
+
+  MD4();
+
+  explicit MD4(const char*);
+  explicit MD4(const std::string&);
+
+  MD4(const void* data, size_t len);
+
+  virtual ~MD4();
+
+  virtual void reset() const;
+
+  virtual digest_t compute(const void*, long);
+
+  virtual void update(const void*, long);
+
+  virtual digest_t digest() const;
+
+  template<class T>
+  MD4& operator<<(const T& x) { add(x); return *this; }
+
+private: // members
+
+  mutable MD4_CTX ctx_;
+
+};
+
+}  // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/MD5.cc b/eckit/src/eckit/utils/MD5.cc
new file mode 100644
index 0000000..d1b7fa4
--- /dev/null
+++ b/eckit/src/eckit/utils/MD5.cc
@@ -0,0 +1,365 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <limits>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/utils/MD5.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Cray C++ compiler should not try to optimize this code
+#if _CRAYC
+    #pragma _CRI noopt
+#endif
+
+/* Constants for MD5Transform routine. */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits. */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+   Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+    (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+}
+#define GG(a, b, c, d, x, s, ac) { \
+    (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+}
+#define HH(a, b, c, d, x, s, ac) { \
+    (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+}
+#define II(a, b, c, d, x, s, ac) { \
+    (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context. */
+void MD5::Init (MD5_CTX *context)
+{
+  context->count[0] = context->count[1] = 0;
+  /* Load magic initialization constants.*/
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+   operation, processing another message block, and updating the
+   context.
+ */
+void MD5::Update (MD5_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+    unsigned int i, index, partLen;
+
+    /* Compute number of bytes mod 64 */
+    index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
+        context->count[1]++;
+    context->count[1] += ((UINT4)inputLen >> 29);
+
+    partLen = 64 - index;
+
+    /* Transform as many times as possible. */
+    if (inputLen >= partLen) {
+        ::memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+        MD5::Transform (context->state, context->buffer);
+
+        for (i = partLen; i + 63 < inputLen; i += 64)
+            MD5::Transform (context->state, &input[i]);
+
+        index = 0;
+    }
+    else
+        i = 0;
+
+    /* Buffer remaining input */
+    ::memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the the message digest and zeroizing the context.
+ */
+void MD5::Final (unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *context)
+{
+  unsigned char bits[8];
+  unsigned int index, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64. */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  MD5::Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD5::Update (context, bits, 8);
+
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information.*/
+  ::memset((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+void MD5::Transform (UINT4 state[4], const unsigned char block[64])
+{
+  UINT4 a = state[0];
+  UINT4 b = state[1];
+  UINT4 c = state[2];
+  UINT4 d = state[3];
+  UINT4 x[16];
+
+  ::memset((POINTER)x, 0, sizeof (x));
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information.*/
+  ::memset((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+   a multiple of 4.
+ */
+void MD5::Encode (unsigned char *output, UINT4 *input, unsigned int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+        output[j]   = (unsigned char)(input[i] & 0xff);
+        output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+        output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+        output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+    }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+   a multiple of 4.
+ */
+void MD5::Decode(UINT4 *output, const unsigned char *input, unsigned int len)
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+      output[i] = ((UINT4)input[j]) |
+                  (((UINT4)input[j+1]) << 8) |
+                  (((UINT4)input[j+2]) << 16) |
+                  (((UINT4)input[j+3]) << 24);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const char* hex = "0123456789abcdef";
+static std::string toString(unsigned char digest[MD5_DIGEST_LENGTH]) {
+
+    char x[2*MD5_DIGEST_LENGTH];
+
+    size_t j = 0;
+    for(size_t i = 0; i<MD5_DIGEST_LENGTH; ++i) {
+        x[j++] = hex[(digest[i] & 0xf0) >> 4];
+        x[j++] = hex[(digest[i] & 0xf)];
+    }
+
+    return std::string(x, 2*MD5_DIGEST_LENGTH);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MD5::~MD5() {}
+
+void MD5::reset() const
+{
+    Init(&s_);
+}
+
+Hash::digest_t MD5::compute(const void* buffer, long size)
+{
+    MD5_CTX s;
+    Init(&s);
+    Update(&s, static_cast<const unsigned char*>(buffer), size);
+    unsigned char digest[MD5_DIGEST_LENGTH];
+    Final(digest, &s);
+    return toString(digest);
+}
+
+MD5::MD5() {
+    Init(&s_);
+}
+
+MD5::MD5(const char* s) {
+    Init(&s_);
+    add( s, strlen(s) );
+}
+
+MD5::MD5(const std::string& s) {
+    Init(&s_);
+    add( s.c_str(), s.size() );
+}
+
+MD5::MD5(const void* data, size_t len) {
+    Init(&s_);
+    add( data, len );
+}
+
+void MD5::update(const void* buffer, long length) {
+
+    if(length > std::numeric_limits<unsigned int>::max()) {
+        throw BadParameter("Buffer length too large for MD5 algorithm", Here());
+    }
+
+    if (length > 0) {
+        void* b = const_cast<void*>(buffer);
+        MD5::Update(&s_, static_cast<unsigned char*>(b), length);
+        if(!digest_.empty())
+            digest_ = digest_t(); // reset the digest
+    }
+}
+
+MD5::digest_t MD5::digest() const {
+
+    // recompute the digest
+    if (digest_.empty()) {
+        unsigned char digest[MD5_DIGEST_LENGTH];
+        MD5::Final(digest, &s_);
+        digest_ = toString(digest);
+    }
+
+    return digest_;
+}
+
+namespace  {
+    HashBuilder<MD5> builder("MD5");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/MD5.h b/eckit/src/eckit/utils/MD5.h
new file mode 100644
index 0000000..14504eb
--- /dev/null
+++ b/eckit/src/eckit/utils/MD5.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_MD5_H
+#define eckit_utils_MD5_H
+
+#ifndef MD5_DIGEST_LENGTH
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+#include "eckit/utils/Hash.h"
+
+namespace eckit {
+
+class MD5 : public Hash {
+
+public:  // types
+
+  MD5();
+
+  explicit MD5(const char*);
+  explicit MD5(const std::string&);
+
+  MD5(const void* data, size_t len);
+
+  virtual ~MD5();
+
+  virtual void reset() const;
+
+  virtual digest_t compute(const void*, long);
+
+  virtual void update(const void*, long);
+
+  virtual digest_t digest() const;
+
+  template<class T>
+  MD5& operator<<(const T& x) { add(x); return *this; }
+
+private: // members
+
+  mutable digest_t digest_;  ///< cached digest
+
+  /* POINTER defines a generic pointer type */
+  typedef unsigned char *POINTER;
+
+  /* UINT4 defines a four byte word */
+  typedef uint32_t UINT4;
+
+  /* MD5 context. */
+  typedef struct {
+    UINT4 state[4];                                   /* state (ABCD) */
+    UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+    unsigned char buffer[64];                         /* input buffer */
+  } MD5_CTX;
+
+  mutable MD5_CTX s_;
+
+  static void Init    (MD5_CTX *);
+  static void Update  (MD5_CTX *, const unsigned char *, unsigned int);
+  static void Final   (unsigned char [16], MD5_CTX *);
+  static void Transform (UINT4 [4], const unsigned char [64]);
+  static void Encode (unsigned char *, UINT4 *, unsigned int);
+  static void Decode (UINT4 *, const unsigned char *, unsigned int);
+
+};
+
+}  // end namespace eckit
+
+#endif
\ No newline at end of file
diff --git a/eckit/src/eckit/utils/RLE.cc b/eckit/src/eckit/utils/RLE.cc
new file mode 100644
index 0000000..fa0ead1
--- /dev/null
+++ b/eckit/src/eckit/utils/RLE.cc
@@ -0,0 +1,341 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/serialisation/Stream.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// Version 2: warning all numbers must be signed but positive...
+
+template<class T>
+class dummy_iterator: public std::iterator<std::output_iterator_tag,T>
+{
+public:
+	dummy_iterator() { }
+	dummy_iterator<T> &operator=(const T & value) { return *this; }
+	dummy_iterator<T> &operator*()                { return *this; }
+	dummy_iterator<T> &operator++()               { return *this; }
+	dummy_iterator<T> &operator++(int)            { return *this; }
+};
+
+template<class T>
+dummy_iterator<T> make_dummy(T *)
+{
+	return dummy_iterator<T>();
+}
+
+
+template<class T,class U>
+long long RLEencode2(T first,T last,U output,long long maxLoop)
+{
+	long long x = 0;
+	long long m = 0;
+	long long j = 0;
+	long long size = last - first;
+
+	if(size <= 0)
+		return 0;
+
+	long long size2 = (size + 2) / 2;
+
+    long long enough = std::min(size2,maxLoop);
+
+	for (long long n = 1; n < size2; n++)
+	{
+		long long sizen = size - n;
+		for (long long i = 0; i < sizen; i += n)
+		{
+			T from  = first + i;
+			T other = from + n;
+			long long a = 0;
+
+			while(from != last && *from == *other)
+				{ ++from; ++other; a++; }
+
+			/* long long a = (mismatch (from, last, from + n).first - from); */
+
+			if(a > m)
+			{
+				m = a;
+				x = n;
+				j = i;
+				if (m > enough)
+					goto stop;
+			}
+
+		}
+	}
+stop:
+
+	if(m == 0)
+	{
+		copy(first, last, output);
+		return last - first;
+	}
+	else
+	{
+		long long k = 0;
+		T from = first + j;
+		T other = from;
+		while((other + x <= last) && equal(from, from + x, other))
+		{
+			k++;
+			other += x;
+		}
+
+		long long n = RLEencode2(first, from, output, maxLoop);
+
+		if(k > 1)
+		{
+			*output++ = -k;
+			n++;
+			int m = RLEencode2(from, from + x, make_dummy(
+			(typename std::iterator_traits<T>::value_type*)(0)
+			),maxLoop);
+
+			if (m > 1)
+			{
+				*output++ = -m;
+				n++;
+			}
+		}
+
+		n += RLEencode2(from, from + x, output, maxLoop);
+		n += RLEencode2(from + k * x, last, output, maxLoop);
+
+		return n;
+	}
+}
+
+template < class T, class U >
+void RLEdecode2 (T first, T last, U output)
+{
+	while(first != last)
+	{
+		if( (long long)*first < 0)
+		{
+			long long repeat = -*first++;
+			if( (long long)*first < 0)
+			{
+				long long length = -*first++;
+				while(repeat--)
+					RLEdecode2(first, first + length, output);
+				first += length;
+			}
+			else
+			{
+				while(repeat--)
+					*output++ = *first;
+				first++;
+			}
+		}
+		else
+			*output++ = *first++;
+	}
+}
+
+template < class T >
+void RLEprint(std::ostream& out,T first, T last)
+{
+	while(first != last)
+	{
+		if( (long long)*first < 0)
+		{
+			long long repeat = -*first++;
+			if( (long long)*first < 0)
+			{
+				long long length = -*first++;
+				out << repeat << "*(";
+				RLEprint(out,first, first + length);
+				first += length;
+				out << ')';
+				if (first != last) out << ',';
+			}
+			else
+			{
+				out << repeat << '*' << *first;
+				first++;
+				if(first != last)
+				out << ',';
+			}
+		}
+		else
+		{
+			out << *first++;
+			if (first != last)
+				out << ',';
+		}
+	}
+}
+
+//=======================================================================
+
+template<class InputIterator,class OutputIterator>
+bool DIFFencode(InputIterator first,InputIterator last,OutputIterator result)
+{
+	if(first == last) return true;
+
+	InputIterator prev = first;
+
+	*result = *first; ++first; ++result;
+	while(first != last)
+	{
+		if (*first < *prev)  return false;
+
+		*result  = (long long)(*first - *prev); // Come back here
+		prev     = first;
+		++first;
+		++result;
+	}
+
+	return true;
+}
+
+template<class InputIterator,class OutputIterator,class T>
+void DIFFdecode(InputIterator first,InputIterator last,OutputIterator result,T*)
+{
+	if(first == last) return;
+
+	T value = *first;
+	*result = *first; ++first; ++result;
+
+	while(first != last)
+	{
+		*result  = value = *first + value;
+		++first;
+		++result;
+	}
+}
+
+template<class InputIterator,class OutputIterator>
+void DIFFdecode(InputIterator first,InputIterator last,OutputIterator result)
+{
+	DIFFdecode(first,last,result,
+#if defined(__GNUC__) && __GNUC__ >= 3
+			(typename InputIterator::value_type*)(0)
+#else
+#if defined(VISUAL_AGE) || defined(__hpux)
+			(typename InputIterator::value_type*)(0)
+#else
+			value_type(first)
+#endif
+#endif
+   );
+}
+
+//==========================================================================
+
+template<class InputIterator,class T>
+Stream& RLEwrite(Stream& s,InputIterator first,InputIterator last,
+	long long maxLoop,T*)
+{
+	std::vector<T> tmp; tmp.reserve(last-first);
+	RLEencode2(first,last, std::back_inserter(tmp),maxLoop);
+	s << tmp;
+    std::cout << "RLEwrite : " << last-first << " -> " << tmp.size() << std::endl;
+	return s;
+}
+
+template<class InputIterator>
+Stream& RLEwrite(Stream& s,InputIterator first,InputIterator last,
+	long long maxLoop)
+{
+	return RLEwrite(s,first,last,maxLoop,
+#if defined(__GNUC__) && __GNUC__ >= 3
+			(typename InputIterator::value_type*)(0)
+#else
+#if defined(VISUAL_AGE) || defined(__hpux)
+			(typename InputIterator::value_type*)(0)
+#else
+			value_type(first)
+#endif
+#endif
+	);
+}
+
+template<class OutputIterator,class T>
+Stream& RLEread(Stream& s,OutputIterator result,T*)
+{
+	std::vector<T> tmp;
+	s >> tmp;
+	RLEdecode2(tmp.begin(),tmp.end(),result);
+	return s;
+}
+
+template<class InputIterator,class T>
+Stream& RLEDIFFwrite(Stream& s,InputIterator first,InputIterator last,
+	long long maxLoop,T*)
+{
+	std::vector<T> tmp; tmp.reserve(last-first);
+	bool diff = DIFFencode(first,last, std::back_inserter(tmp));
+
+	s << diff;
+
+	if(!diff)
+	{
+		// Warning, does not work with ostream iterator,
+		// as we reuse first, last
+		Log::warning() << "DIFF encoding failed." << std::endl;
+		tmp.clear();
+		std::copy(first,last, std::back_inserter(tmp));
+		s << tmp;
+	}
+	else {
+		return RLEwrite(s,tmp.begin(),tmp.end(),maxLoop);
+	}
+
+	return s;
+}
+
+template<class InputIterator>
+Stream& RLEDIFFwrite(Stream& s,InputIterator first,InputIterator last,
+	long long maxLoop)
+{
+	return RLEDIFFwrite(s,first,last,maxLoop,
+#if defined(__GNUC__) && __GNUC__ >= 3
+			(typename InputIterator::value_type*)(0)
+#else
+#if defined(VISUAL_AGE) || defined(__hpux)
+			(typename InputIterator::value_type*)(0)
+#else
+			value_type(first)
+#endif
+#endif
+   );
+}
+
+template<class OutputIterator,class T>
+Stream& RLEDIFFread(Stream& s,OutputIterator result,T* dummy)
+{
+	bool diff;
+	s >> diff;
+
+	if(diff) {
+		std::vector<T> tmp; 
+		RLEread(s, std::back_inserter(tmp),dummy);
+		DIFFdecode(tmp.begin(),tmp.end(),result);
+	}
+	else {
+		std::vector<T> tmp; 
+		s >> tmp;
+		std::copy(tmp.begin(),tmp.end(),result);
+	}
+	return s;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/utils/RLE.h b/eckit/src/eckit/utils/RLE.h
new file mode 100644
index 0000000..319315e
--- /dev/null
+++ b/eckit/src/eckit/utils/RLE.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File RLE.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef eckit_RLE_h
+#define eckit_RLE_h
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+
+template <class InputIterator, class OutputIterator>
+long long RLEencode2(InputIterator first, InputIterator last,
+	OutputIterator result,long long maxloop);
+
+template <class InputIterator, class OutputIterator>
+void RLEdecode2(InputIterator first, InputIterator last,
+	OutputIterator result);
+
+template <class InputIterator>
+void RLEprint(std::ostream&,InputIterator first, InputIterator last);
+
+
+template<class InputIterator,class OutputIterator>
+bool DIFFencode(InputIterator first,InputIterator last,OutputIterator result);
+
+template<class InputIterator,class OutputIterator>
+void DIFFdecode(InputIterator first,InputIterator last,OutputIterator result);
+
+
+
+
+//==========================================================================
+
+template<class InputIterator>
+Stream& RLEwrite(Stream&,InputIterator,InputIterator,long long);
+
+template<class OutputIterator,class T>
+Stream& RLEread(Stream&,OutputIterator,T*);
+
+template<class InputIterator>
+Stream& RLEDIFFwrite(Stream&,InputIterator,InputIterator,long long);
+
+template<class OutputIterator,class T>
+Stream& RLEDIFFread(Stream&,OutputIterator,T*);
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#include "eckit/utils/RLE.cc"
+
+#endif
diff --git a/eckit/src/eckit/utils/Regex.cc b/eckit/src/eckit/utils/Regex.cc
new file mode 100644
index 0000000..5aee4fa
--- /dev/null
+++ b/eckit/src/eckit/utils/Regex.cc
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/io/Buffer.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/utils/Regex.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Regex::Regex(const std::string& s,bool shell):
+	str_(s)
+{
+	//Log::debug() << "Regex " << str_ << std::endl;
+	if(shell) {
+		long len = s.length()*3 + 1;
+		Buffer buffer(len);
+		char *re = buffer;
+
+        std::string::size_type i = 0;
+		int j = 0;
+
+        if(shell) { re[j++] = '^'; }
+
+		while(i < s.length())
+		{
+			switch(s[i])
+			{
+			case '?':
+				re[j++] = '.';
+				break;
+
+			case '*':
+				re[j++] = '.';
+				re[j++] = '*';
+				break;
+
+			case '.':
+				re[j++] = '\\';
+				re[j++] = '.';
+				break;
+			
+			case '[':
+				re[j++] = '['; i++;
+				while(i < s.length() && s[i] != ']')
+					re[j++] = s[i++];
+				re[j++] = ']';
+				break;
+
+			default:
+				re[j++] = s[i];
+				break;
+			}
+			i++;
+			ASSERT(j < len);
+		}
+        if(shell) { re[j++] = '$'; }
+		re[j] = 0;
+		str_ = re;
+	}
+	//Log::debug() << "Regex " << str_ << std::endl;
+	compile(str_.c_str());
+}
+
+Regex::~Regex()
+{
+	regfree(&re_);
+}
+
+void Regex::print(std::ostream& s) const
+{
+    s << "/" << str_ << "/";
+}
+
+bool Regex::match(const std::string& s) const
+{ 
+	regmatch_t pm;
+	//Log::debug() << "Match " << s << " with " << str_ << " -> " << (regexec(&re_,s.c_str(),1,&pm,0) == 0) << std::endl;
+	return regexec(&re_,s.c_str(),1,&pm,0) == 0;
+}
+
+void Regex::compile(const char* p)
+{
+	int n = regcomp(&re_,p,0);
+	if(n)
+	{
+		char buf[1024];
+		regerror(n,&re_,buf,sizeof(buf));
+		throw SeriousBug(buf);
+	}
+}
+
+Regex::Regex(const Regex& other):
+	str_(other.str_)
+{
+	compile(str_.c_str());
+}
+
+Regex& Regex::operator=(const Regex& other)
+{
+	regfree(&re_);
+	str_ = other.str_;
+	compile(str_.c_str());
+	return *this;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/utils/Regex.h b/eckit/src/eckit/utils/Regex.h
new file mode 100644
index 0000000..9c9149c
--- /dev/null
+++ b/eckit/src/eckit/utils/Regex.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Regex.h
+// Baudouin Raoult - ECMWF Jan 98
+
+#ifndef eckit_Regex_h
+#define eckit_Regex_h
+
+#include <regex.h>
+
+#include "eckit/eckit.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Regex {
+public:
+
+// -- Contructors
+
+	Regex(const std::string& = ".*",bool shell = false);
+	Regex(const Regex&);
+
+	~Regex();
+
+// -- Methods
+
+	Regex& operator=(const Regex&);
+
+	bool match(const std::string& s) const;
+
+	operator const std::string&() const  { return str_; }
+    
+	bool operator==(const Regex& other) const { return str_ == other.str_; }
+
+protected: // methods
+
+	void print(std::ostream&) const; 
+
+private: // members
+
+	std::string str_;
+	regex_t re_;
+
+private: // methods
+
+	void compile(const char*);
+
+	friend std::ostream& operator<<(std::ostream& s,const Regex& p) { p.print(s); return s; }
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/RendezvousHash.cc b/eckit/src/eckit/utils/RendezvousHash.cc
new file mode 100644
index 0000000..a919837
--- /dev/null
+++ b/eckit/src/eckit/utils/RendezvousHash.cc
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/RendezvousHash.h"
+
+#include "eckit/config/LibEcKit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/AutoLock.h"
+#include "eckit/types/Types.h"
+#include "eckit/utils/MD5.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::string RendezvousHash::md5(const std::string& str) {
+    eckit::MD5 md5(str.c_str(), str.size());
+    return md5.digest();
+}
+
+RendezvousHash::RendezvousHash(const RendezvousHash::hash_func_ptr hash) :
+    hash_(hash)
+{
+}
+
+RendezvousHash::RendezvousHash(const std::set<RendezvousHash::Node>& nodes, const RendezvousHash::hash_func_ptr hash) :
+    hash_(hash),
+    nodes_(nodes)
+{
+}
+
+RendezvousHash::~RendezvousHash()
+{
+}
+
+bool RendezvousHash::addNode(const RendezvousHash::Node& node)
+{
+    AutoLock<Mutex> lock(mutex_);
+
+    std::pair<iterator, bool> r = nodes_.insert(node);
+
+    return r.second;
+}
+
+bool RendezvousHash::removeNode(const RendezvousHash::Node& node)
+{
+    AutoLock<Mutex> lock(mutex_);
+
+    return bool(nodes_.erase(node));
+}
+
+RendezvousHash::Node RendezvousHash::selectNode(const RendezvousHash::Key& key)
+{
+    AutoLock<Mutex> lock(mutex_);
+
+    std::string skey = flatten(key);
+
+    iterator highest = nodes_.end();
+    std::string hashest;
+
+    std::vector<std::string> vs;
+
+    for(iterator itr = nodes_.begin(); itr != nodes_.end(); ++itr) {
+        std::string toHash = skey + "+" + *itr;
+        std::string h = hash_(toHash);
+
+        Log::debug<LibEcKit>() << "node=" << *itr << ", str=" << toHash << ", hash = " << h << std::endl;
+
+        if(h > hashest) {
+            hashest = h;
+            highest = itr;
+        }
+
+        vs.push_back(h);
+    }
+
+    std::sort(vs.begin(), vs.end());
+
+    Log::debug<LibEcKit>() << vs << std::endl;
+
+    if(highest == nodes_.end()) {
+        std::ostringstream oss;
+        oss << "Couldn't find highest rendezvous hash, node list is likely empty. Nodes: " << nodes_;
+        throw eckit::BadParameter(oss.str(), Here());
+    }
+
+    Log::debug<LibEcKit>() << "highest=" << *highest << ", hashest=" << hashest << std::endl;
+
+    return *highest;
+}
+
+std::string RendezvousHash::flatten(const RendezvousHash::Key& key) const
+{
+    std::ostringstream flat;
+
+    for(Key::const_iterator itr = key.begin(); itr != key.end(); ++itr) {
+        flat << "/" << itr->first << ":" << itr->second;
+    }
+
+    return flat.str();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/RendezvousHash.h b/eckit/src/eckit/utils/RendezvousHash.h
new file mode 100644
index 0000000..3fac57d
--- /dev/null
+++ b/eckit/src/eckit/utils/RendezvousHash.h
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_RendezvousHash_H
+#define eckit_utils_RendezvousHash_H
+
+#include <string>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/thread/Mutex.h"
+
+namespace eckit {
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// This class implements the Rendezvous or Highest Random Weight (HRW) hashing
+/// It is thread-safe, in terms that threads can add and remove nodes whilts others can
+/// compute the hash and obtain the rendezvous node
+///
+/// @todo Make node a template parameter? Must be a serializable object
+
+class RendezvousHash : private eckit::NonCopyable {
+
+public: // types
+
+    typedef std::string Node;
+
+    typedef std::map<std::string, std::string> Key;
+
+    typedef std::string (*hash_func_ptr)(const std::string&);
+
+private: // types
+
+    typedef std::set<Node>::iterator iterator;
+
+public: // methods
+
+    static std::string md5(const std::string& str);
+
+  RendezvousHash(const hash_func_ptr hash = &md5);
+
+  RendezvousHash(const std::set<Node>& nodes, const hash_func_ptr hash = &md5);
+
+  ~RendezvousHash();
+
+  /// Selects the rendezvous node based on a Key
+  /// @returns Rendezvous Node
+  Node selectNode(const Key& key);
+
+  /// Adds node to node list. No effect if node already present
+  /// @returns true is node insertion was successful
+  bool addNode(const Node& node);
+
+  /// Removes node from node list. No effect if node not present
+  /// @returns true is node removal was successful
+  bool removeNode(const Node& node);
+
+private: // methods
+
+  std::string flatten(const Key&) const;
+
+private:  // types
+
+  eckit::Mutex mutex_;   //< protects addition and removal of nodes
+
+  hash_func_ptr hash_;
+
+  std::set<Node> nodes_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+}  // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/SHA1.cc b/eckit/src/eckit/utils/SHA1.cc
new file mode 100644
index 0000000..ac0bd45
--- /dev/null
+++ b/eckit/src/eckit/utils/SHA1.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstring>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/utils/SHA1.h"
+
+
+namespace eckit {
+
+SHA1::SHA1() {
+    SHA1_Init(&ctx_);
+}
+
+SHA1::SHA1(const char* s)  {
+    SHA1_Init(&ctx_);
+    add( s, strlen(s) );
+}
+
+SHA1::SHA1(const std::string& s) {
+    SHA1_Init(&ctx_);
+    add( s.c_str(), s.size() );
+}
+
+SHA1::SHA1(const void* data, size_t len) {
+    SHA1_Init(&ctx_);
+    add( data, len );
+}
+
+SHA1::~SHA1() {}
+
+void SHA1::reset() const
+{
+    SHA1_Init(&ctx_);
+}
+
+Hash::digest_t SHA1::compute(const void* buffer, long size)
+{
+    SHA1 hash(buffer, size);
+    return hash.digest();
+}
+
+void SHA1::update(const void* buffer, long length) {
+    if (length > 0) {
+        SHA1_Update(&ctx_, static_cast<const unsigned char*>(buffer), length);
+        if (!digest_.empty())
+            digest_ = digest_t(); // reset the digest
+    }
+}
+
+static const char* hex = "0123456789abcdef";
+
+SHA1::digest_t SHA1::digest() const {
+
+    if (digest_.empty()) { // recompute the digest
+
+        unsigned char digest[SHA_DIGEST_LENGTH];
+        SHA1_Final(digest, &ctx_);
+
+        char x[2*SHA_DIGEST_LENGTH];
+
+        size_t j = 0;
+        for(size_t i = 0; i<SHA_DIGEST_LENGTH; ++i) {
+            x[j++] = hex[(digest[i] & 0xf0) >> 4];
+            x[j++] = hex[(digest[i] & 0xf)];
+        }
+
+        digest_ = std::string(x, 2*SHA_DIGEST_LENGTH);
+    }
+
+    return digest_;
+}
+
+namespace  {
+    HashBuilder<SHA1> builder("SHA1");
+}
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/SHA1.h b/eckit/src/eckit/utils/SHA1.h
new file mode 100644
index 0000000..1629fe8
--- /dev/null
+++ b/eckit/src/eckit/utils/SHA1.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_SHA1_H
+#define eckit_utils_SHA1_H
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_SSL
+#include <openssl/sha.h>
+#else
+#error "eckit was not configured with OpenSSL, SHA1 is disabled. Use conditional ECKIT_HAVE_SSL from eckit/eckit_config.h"
+#endif
+
+#ifndef SHA_DIGEST_LENGTH
+#define SHA_DIGEST_LENGTH 20
+#endif
+
+#include "eckit/utils/Hash.h"
+
+namespace eckit {
+
+class SHA1 : public Hash {
+
+public:  // types
+
+  SHA1();
+
+  explicit SHA1(const char*);
+  explicit SHA1(const std::string&);
+
+  SHA1(const void* data, size_t len);
+
+  virtual ~SHA1();
+
+  virtual void reset() const;
+
+  virtual digest_t compute(const void*, long);
+
+  virtual void update(const void*, long);
+
+  virtual digest_t digest() const;
+
+  template<class T>
+  SHA1& operator<<(const T& x) { add(x); return *this; }
+
+private: // members
+
+  mutable SHA_CTX ctx_;
+
+};
+
+}  // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/Translator.cc b/eckit/src/eckit/utils/Translator.cc
new file mode 100644
index 0000000..43a464e
--- /dev/null
+++ b/eckit/src/eckit/utils/Translator.cc
@@ -0,0 +1,272 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstdlib>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+static unsigned long long multiplier(const char* p) {
+    while(isspace(*p)) p++;
+
+    if(*p && *(p+1))
+    {
+        if(towlower(*(p+1)) == 'b')
+        {
+            switch(towlower(*p)) {
+                case 'k': return (1LL << 10);  break;
+                case 'm': return (1LL << 20);  break;
+                case 'g': return (1LL << 30);  break;
+                case 't': return (1LL << 40);  break;
+                case 'p': return (1LL << 50);  break;
+                case 'e': return (1LL << 60);  break;
+                //case 'z': return (1LL << 70);  break;
+                //case 'y': return (1LL << 80);  break;
+            }
+        }
+    }
+
+    return 1;
+}
+
+std::string Translator<bool,std::string>::operator()(bool value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+bool Translator<std::string,bool>::operator()(const std::string& s)
+{
+
+    if(s == "no" || s == "off" || s == "false") return false;
+    if(s == "yes"|| s == "on"  || s == "true")  return true;
+
+    // Catter for ints
+    return atoi(s.c_str());
+}
+
+std::string Translator<int,std::string>::operator()(int value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+std::string Translator<unsigned int,std::string>::operator()(unsigned int value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+int Translator<std::string,int>::operator()(const std::string& s)
+{
+
+    if(s == "no" || s == "off" || s == "false") return false;
+    if(s == "yes"|| s == "on"  || s == "true")  return true;
+
+    // Catter for ints
+    char *more;
+    int result =  strtol(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+unsigned int Translator<std::string,unsigned int>::operator()(const std::string& s)
+{
+
+    if(s == "no" || s == "off" || s == "false") return false;
+    if(s == "yes"|| s == "on"  || s == "true")  return true;
+
+    // Catter for ints
+    char *more;
+    unsigned int result =  strtoul(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+std::string Translator<long,std::string>::operator()(long value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+long Translator<std::string,long>::operator()(const std::string& s)
+{
+    char *more;
+    long result =  strtol(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+std::string Translator<double,std::string>::operator()(double value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+double Translator<std::string,double>::operator()(const std::string& s)
+{
+    char* pend;
+    errno = 0;
+
+    double d = ::strtod( s.c_str(), &pend );
+
+//    ECKIT_DEBUG_VAR( d );
+//    ECKIT_DEBUG_VAR( s );
+//    ECKIT_DEBUG_VAR( s.size() );
+//    ECKIT_DEBUG_VAR( pend - s.c_str() );
+//    ECKIT_DEBUG_VAR( errno );
+
+    if( s.empty() || s[0] == ' ' || static_cast<size_t>(pend - s.c_str()) != s.size() || (errno != 0) )
+    {
+        throw BadParameter( "Bad conversion from std::string '" + s + "' to double" , Here() );
+    }
+
+    return d;
+
+    //    return atof(s.c_str());
+}
+
+unsigned long Translator<std::string,unsigned long>::operator()(const std::string& s)
+{
+    char *more;
+    unsigned long result =  strtoul(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+std::string Translator<unsigned long,std::string>::operator()(unsigned long value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+unsigned long long Translator<std::string,unsigned long long>::operator()(const std::string& s)
+{
+    char *more;
+    unsigned long long result =  strtoull(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+std::string Translator<unsigned long long,std::string>::operator()(unsigned long long value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+long long Translator<std::string,long long>::operator()(const std::string& s)
+{
+    char *more;
+    long long result =  strtoll(s.c_str(),&more,10);
+    return result * multiplier(more);
+}
+
+std::string Translator<long long,std::string>::operator()(long long value)
+{
+    std::ostringstream s;
+    s << value;
+    return s.str();
+}
+
+std::vector<std::string> Translator<std::string, std::vector<std::string> >::operator()(const std::string& s)
+{
+    std::vector<std::string> result;
+    Tokenizer parse(", \t");
+
+    parse(s,result);
+    return result;
+}
+
+std::vector<long> Translator<std::string, std::vector<long> >::operator()(const std::string& s)
+{
+    std::vector<std::string> r;
+    Tokenizer parse(", \t");
+
+    parse(s,r);
+
+    std::vector<long> result;
+    for(size_t i = 0; i < r.size(); i++)
+        result.push_back(Translator<std::string,long>()(r[i]));
+    return result;
+}
+
+std::string Translator< std::vector<long>,std::string >::operator()(const std::vector<long>& v)
+{
+    std::string result;
+    for(size_t i=0; i < v.size(); ++i)
+    {
+        if(i) result += " ";
+        result += Translator<long,std::string>()(v[i]);
+    }
+    return result;
+}
+
+std::string Translator<std::vector<std::string>, std::string>::operator()(const std::vector<std::string>& v)
+{
+    std::string result;
+    for(size_t i=0; i < v.size(); ++i)
+    {
+        if(i) result += ",";
+
+        result += v[i];
+    }
+    return result;
+}
+
+std::set<std::string> Translator<std::string, std::set<std::string> >::operator()(const std::string& s)
+{
+    std::vector<std::string> v;
+    Tokenizer parse(", \t");
+
+    parse(s,v);
+
+    std::set<std::string> result(v.begin(),v.end());
+    return result;
+}
+
+std::string Translator<std::set<std::string>, std::string>::operator()(const std::set<std::string>& v)
+{
+    std::string result;
+    for(std::set<std::string>::const_iterator i= v.begin(); i != v.end(); ++i)
+    {
+        if(result.length()) result += ",";
+        result += *i;
+    }
+    return result;
+}
+
+
+char Translator<std::string,char>::operator()(const std::string& s)
+{
+    ASSERT(s.length() == 1);
+    return s[0];
+}
+
+std::string Translator<char,std::string>::operator()(char c)
+{
+    std::string s;
+    s = c;
+    return s;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/utils/Translator.h b/eckit/src/eckit/utils/Translator.h
new file mode 100644
index 0000000..3d0a92f
--- /dev/null
+++ b/eckit/src/eckit/utils/Translator.h
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   Translator.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Jun 1996
+
+#ifndef eckit_Translator_h
+#define eckit_Translator_h
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+// Translate a type from From to To
+
+template<class From,class To> struct Translator {
+
+// Default template calls cast
+    To operator()(const From& from)  { return To(from); }
+};
+
+// Those are predefined
+
+template<>
+struct Translator<bool,std::string>
+    { std::string operator()(bool);          };
+
+template<>
+struct Translator<std::string,bool>
+    { bool   operator()(const std::string&); };
+
+template<>
+struct Translator<int,std::string>
+    { std::string operator()(int);          };
+
+template<>
+struct Translator<unsigned int,std::string>
+    { std::string operator()(unsigned int);  };
+
+template<>
+struct Translator<std::string,int>
+    { int   operator()(const std::string&); };
+
+template<>
+struct Translator<std::string,unsigned int>
+    { unsigned int operator()(const std::string&); };
+
+template<>
+struct Translator<double,std::string>
+    { std::string operator()(double);          };
+
+template<>
+struct Translator<std::string,double>
+    { double   operator()(const std::string&); };
+
+template<>
+struct Translator<long,std::string>
+    { std::string operator()(long);          };
+
+template<>
+struct Translator<std::string,long>
+    { long   operator()(const std::string&); };
+
+template<>
+struct Translator<unsigned long,std::string>
+    { std::string operator()(unsigned long);          };
+
+template<>
+struct Translator<std::string,unsigned long>
+    { unsigned long   operator()(const std::string&); };
+
+template<>
+struct Translator<std::string,unsigned long long>
+    { unsigned long long   operator()(const std::string&); };
+
+template<>
+struct Translator<std::string,long long>
+    { long long   operator()(const std::string&); };
+
+template<>
+struct Translator<unsigned long long,std::string>
+    { std::string  operator()(unsigned long long); };
+
+template<>
+struct Translator<long long,std::string>
+    { std::string operator()(long long); };
+
+template<>
+struct Translator<std::string,char>
+    { char operator()(const std::string&); };
+
+template<>
+struct Translator<char,std::string>
+    { std::string operator()(char); };
+
+template<>
+struct Translator<std::string, std::vector<std::string> >
+    { std::vector<std::string> operator()(const std::string&); };
+
+template<>
+struct Translator<std::string, std::vector<long> >
+    { std::vector<long> operator()(const std::string&); };
+
+template<>
+struct Translator<std::vector<long>, std::string>
+    { std::string operator()(const std::vector<long>&); };
+
+template<>
+struct Translator<std::vector<std::string>, std::string>
+    { std::string  operator()(const std::vector<std::string>&); };
+
+template<>
+struct Translator<std::string, std::set<std::string> >
+    { std::set<std::string> operator()(const std::string&); };
+
+template<>
+struct Translator<std::set<std::string>, std::string>
+    { std::string  operator()(const std::set<std::string>&); };
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/utils/xxHash.cc b/eckit/src/eckit/utils/xxHash.cc
new file mode 100644
index 0000000..64cbee6
--- /dev/null
+++ b/eckit/src/eckit/utils/xxHash.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include <iostream>
+#include <cstring>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/utils/xxHash.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const char* hex = "0123456789abcdef";
+static std::string toString(XXH64_hash_t hash) {
+
+    char buffer[2 * 8];
+
+    for(int i = 2*8; i--; ) {
+        buffer[i] = hex[hash & 15];
+        hash >>= 4;
+    }
+
+    return std::string(buffer, buffer+2*8);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+xxHash::xxHash() {
+    ctx_ = XXH64_createState();
+    XXH64_reset(ctx_, 0);
+}
+
+xxHash::xxHash(const char* s) {
+    ctx_ = XXH64_createState();
+    XXH64_reset(ctx_, 0);
+    add( s, strlen(s) );
+}
+
+xxHash::xxHash(const std::string& s) {
+    ctx_ = XXH64_createState();
+    XXH64_reset(ctx_, 0);
+    add( s.c_str(), s.size() );
+}
+
+xxHash::xxHash(const void* data, size_t len) {
+    ctx_ = XXH64_createState();
+    XXH64_reset(ctx_, 0);
+    add( data, len );
+}
+
+xxHash::~xxHash() {}
+
+void xxHash::reset() const
+{
+    XXH64_reset(ctx_, 0);
+}
+
+Hash::digest_t xxHash::compute(const void* buffer, long size)
+{
+    XXH64_hash_t hash = XXH64(buffer, size, 0);
+    return toString(hash);
+}
+
+void xxHash::update(const void* buffer, long length) {
+    if (length > 0) {
+        XXH64_update(ctx_, static_cast<const unsigned char*>(buffer), length);
+        if (!digest_.empty())
+            digest_ = digest_t(); // reset the digest
+    }
+}
+
+xxHash::digest_t xxHash::digest() const {
+
+    if (digest_.empty()) { // recompute the digest
+
+        XXH64_hash_t hash =  XXH64_digest(ctx_);
+
+        digest_ = toString(hash);
+    }
+
+    return digest_;
+}
+
+namespace  {
+    HashBuilder<xxHash> builder("xxHash");
+}
+
+} // namespace eckit
diff --git a/eckit/src/eckit/utils/xxHash.h b/eckit/src/eckit/utils/xxHash.h
new file mode 100644
index 0000000..4633c45
--- /dev/null
+++ b/eckit/src/eckit/utils/xxHash.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_utils_xxHash_H
+#define eckit_utils_xxHash_H
+
+#include "eckit/eckit_config.h"
+
+#ifdef ECKIT_HAVE_XXHASH
+#include "xxhash/xxhash.h"
+#else
+#error "eckit was not configured with xxHash, xxHash is disabled. Use conditional ECKIT_HAVE_XXHASH from eckit/eckit_config.h"
+#endif
+
+#include "eckit/utils/Hash.h"
+
+namespace eckit {
+
+class xxHash : public Hash {
+
+public:  // types
+
+  xxHash();
+
+  explicit xxHash(const char*);
+  explicit xxHash(const std::string&);
+
+  xxHash(const void* data, size_t len);
+
+  virtual ~xxHash();
+
+  virtual void reset() const;
+
+  virtual digest_t compute(const void*, long);
+
+  virtual void update(const void*, long);
+
+  virtual digest_t digest() const;
+
+  template<class T>
+  xxHash& operator<<(const T& x) { add(x); return *this; }
+
+private: // members
+
+  XXH64_state_t* ctx_;
+
+};
+
+}  // end namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/BoolContent.cc b/eckit/src/eckit/value/BoolContent.cc
new file mode 100644
index 0000000..4dc80fc
--- /dev/null
+++ b/eckit/src/eckit/value/BoolContent.cc
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/value/BoolContent.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec BoolContent::classSpec_ = {&Content::classSpec(),"BoolContent",};
+Reanimator<BoolContent> BoolContent::reanimator_;
+
+BoolContent::BoolContent(bool l):
+    value_(l)
+{
+}
+
+BoolContent::BoolContent(Stream& s):
+	Content(s),
+	value_(false)
+{
+    s >> value_;
+}
+
+Content* BoolContent::clone() const {
+    return new BoolContent(value_);
+}
+
+void BoolContent::encode(Stream& s) const
+{
+	Content::encode(s);
+    s << value_;
+}
+
+BoolContent::~BoolContent()
+{
+}
+
+void BoolContent::print(std::ostream& s) const
+{
+    s << ( value_ ? "true" : "false" );
+}
+
+void BoolContent::json(JSON& s) const
+{
+    s << value_;
+}
+
+int BoolContent::compare(const Content& other) const
+{
+    return -other.compareBool(*this);
+}
+
+int BoolContent::compareBool(const BoolContent& other) const
+{
+    if(value_ - other.value_)
+        return 0;
+    if(!value_)
+		return -1;
+
+	return 1;
+}
+
+void BoolContent::value(bool& l) const
+{
+    l = value_;
+}
+
+void BoolContent::value(std::string& s) const
+{
+    s = value_ ? "true" : "false";
+}
+
+void BoolContent::value(long long& l) const
+{
+    l = value_;
+}
+
+void BoolContent::value(double& d) const
+{
+    d = value_;
+}
+
+Content* BoolContent::add(const Content& other) const
+{
+    return other.addBool(*this);
+}
+
+
+Content* BoolContent::sub(const Content& other) const
+{
+    return other.subBool(*this);
+}
+
+Content* BoolContent::mul(const Content& other) const
+{
+    return other.mulBool(*this);
+}
+
+Content* BoolContent::div(const Content& other) const
+{
+    return other.divBool(*this);
+}
+
+Content* BoolContent::mod(const Content& other) const
+{
+    return other.modBool(*this);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/BoolContent.h b/eckit/src/eckit/value/BoolContent.h
new file mode 100644
index 0000000..796fab6
--- /dev/null
+++ b/eckit/src/eckit/value/BoolContent.h
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BoolContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_BoolContent_h
+#define eckit_BoolContent_h
+
+#include "eckit/value/Content.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class BoolContent : public Content {
+
+protected:
+
+    // -- Constructor
+
+    BoolContent(bool);
+    BoolContent(Stream&);
+
+    // -- Destructor
+
+    virtual ~BoolContent();
+
+    // -- Overridden methods
+
+    // -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const;
+    virtual void value(long long& n)   const;
+    virtual void value(double& n)      const;
+    virtual void value(std::string& n) const;
+    virtual void value(Date& n)        const { Content::value(n); }
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const { Content::value(n); }
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const;
+    virtual int  compareNumber(const NumberContent&)        const {return 1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return 1; }
+    virtual int  compareString(const StringContent&)        const {return 1; }
+    virtual int  compareNil(const NilContent&)              const {return 1; }
+    virtual int  compareList(const ListContent&)            const {return 1; }
+    virtual int  compareMap(const MapContent&)              const {return 1; }
+    virtual int  compareDate(const DateContent&)            const {return 1; }
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+    virtual Content* add(const Content&)  const;
+    virtual Content* sub(const Content&) const;
+    virtual Content* mul(const Content&) const;
+    virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    //        virtual Content* addBool(const BoolContent&) const;
+    //        virtual Content* subBool(const BoolContent&) const;
+    //        virtual Content* mulBool(const BoolContent&) const;
+    //        virtual Content* divBool(const BoolContent&) const;
+
+    //        virtual Content* addNumber(const NumberContent&) const;
+    //        virtual Content* subNumber(const NumberContent&) const;
+    //        virtual Content* mulNumber(const NumberContent&) const;
+    //        virtual Content* divNumber(const NumberContent&) const;
+
+    virtual void    print(std::ostream&) const;
+    virtual void    json(JSON&)     const;
+    virtual std::string  typeName()      const { return "Bool"; }
+    virtual bool    isBool()      const { return true; }
+    virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+    // -- Class methods
+
+    static  const ClassSpec&  classSpec()            { return classSpec_;}
+
+private:
+
+    BoolContent(const BoolContent&);
+    BoolContent& operator=(const BoolContent&);
+
+    // -- Members
+
+    bool value_;
+
+    // -- Class Members
+
+    static  ClassSpec                  classSpec_;
+    static  Reanimator<BoolContent>  reanimator_;
+
+    // -- Friends
+
+    friend class Reanimator<BoolContent>;
+    friend class Value;
+    friend class DateContent;
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/CompositeParams.cc b/eckit/src/eckit/value/CompositeParams.cc
new file mode 100644
index 0000000..d202b6c
--- /dev/null
+++ b/eckit/src/eckit/value/CompositeParams.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/value/CompositeParams.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+
+//----------------------------------------------------------------------------
+
+CompositeParams::CompositeParams() : plist_()
+{
+}
+
+CompositeParams::CompositeParams(const Params::List& plist) : plist_(plist)
+{
+}
+
+CompositeParams::CompositeParams( Stream& s )
+{
+    Params::List::size_type len;
+    s >> len;
+    for (Params::List::size_type i = 0; i < len; ++i)
+        push_back(Params::decode(s));
+}
+
+CompositeParams& CompositeParams::push_front(const Params& p)
+{
+    plist_.push_front(p);
+    return *this;
+}
+
+CompositeParams& CompositeParams::push_back(const Params& p)
+{
+    plist_.push_back(p);
+    return *this;
+}
+
+Params::value_t getValue( const CompositeParams& p, const Params::key_t& key )
+{
+    for( Params::List::const_iterator citr = p.plist_.begin(); citr != p.plist_.end(); ++citr )
+    {
+        Value v = getValue(*citr, key);
+        if( !v.isNil() )
+            return v;
+    }
+    return Value();
+}
+
+//----------------------------------------------------------------------------
+
+void print( const CompositeParams& p, std::ostream& s )
+{
+    for( Params::List::const_iterator citr = p.plist_.begin(); citr != p.plist_.end(); ++citr )
+        print(*citr, s);
+}
+
+void encode( const CompositeParams& p, Stream& s )
+{
+    s << p.plist_.size();
+    for( Params::List::const_iterator citr = p.plist_.begin(); citr != p.plist_.end(); ++citr )
+        encode(*citr, s);
+}
+
+//----------------------------------------------------------------------------
+
+Params::Factory<CompositeParams> compositeParamsFactory;
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/value/CompositeParams.h b/eckit/src/eckit/value/CompositeParams.h
new file mode 100644
index 0000000..c431009
--- /dev/null
+++ b/eckit/src/eckit/value/CompositeParams.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Florian Rathgeber
+/// @date March 2015
+
+#ifndef eckit_value_CompositeParams_H
+#define eckit_value_CompositeParams_H
+
+#include "eckit/value/Params.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+class CompositeParams {
+
+public: // methods
+
+    CompositeParams();
+    CompositeParams( const Params::List& );
+    CompositeParams( Stream& );
+
+    CompositeParams& push_front( const Params& p );
+    CompositeParams& push_back( const Params& p );
+
+    CompositeParams& pop_front() { plist_.pop_front(); return *this; }
+    CompositeParams& pop_back()  { plist_.pop_back(); return *this; }
+
+    static const char* className() { return "eckit::CompositeParams"; }
+
+private: // methods
+
+    friend Params::value_t getValue( const CompositeParams& p, const Params::key_t& key );
+    friend void print( const CompositeParams& p, std::ostream& s );
+    friend void encode( const CompositeParams& p, Stream& s );
+
+private: // members
+
+    Params::List plist_;
+};
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/Content.cc b/eckit/src/eckit/value/Content.cc
new file mode 100644
index 0000000..9b9613d
--- /dev/null
+++ b/eckit/src/eckit/value/Content.cc
@@ -0,0 +1,577 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/Content.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec Content::classSpec_ = {&Streamable::classSpec(),"Content",};
+Reanimator<Content> Content::reanimator_;
+
+void Content::encode(Stream& s) const
+{
+	Streamable::encode(s);
+}
+
+Content::Content(Stream& s):
+	Streamable(s)
+{
+}
+
+Content::~Content()
+{
+}
+
+void Content::badConvertion(const std::string& to) const
+{
+    std::ostringstream s;
+    s << "Cannot convert " << *this << " (" << typeName() << ") to " << to;
+    throw BadConversion(s.str(), Here());
+}
+
+void Content::badComparison(const std::string& to) const
+{
+    std::ostringstream s;
+    s << "Cannot compare " << *this << " (" << typeName() << ") with " << to;
+    throw BadComparison(s.str(), Here());
+}
+
+void Content::badOperator(const std::string& op, const std::string& to) const
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") " << op << " " << to;
+    throw BadOperator(s.str(), Here());
+}
+
+Value& Content::element(const Value&)
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") method 'element' not implemented";
+    throw BadOperator(s.str(), Here());
+}
+
+Value Content::keys() const
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") method 'keys' not implemented";
+    throw BadOperator(s.str(), Here());
+}
+
+size_t Content::size() const
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") method 'size' not implemented";
+    throw BadOperator(s.str(), Here());
+}
+
+
+bool Content::contains(const Value&) const
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") method 'contains' not implemented";
+    throw BadOperator(s.str(), Here());
+}
+
+Value Content::negate() const
+{
+    std::ostringstream s;
+    s << *this << " (" << typeName() << ") method 'negate' not implemented";
+    throw BadOperator(s.str(), Here());
+}
+
+void Content::value(long long&) const
+{
+	badConvertion("long long");
+}
+
+void Content::value(bool&) const
+{
+    badConvertion("bool");
+}
+
+void Content::value(double&) const
+{
+    badConvertion("double");
+}
+
+void Content::value(std::string&) const
+{
+	badConvertion("std::string");
+}
+
+void Content::value(Date&) const
+{
+	badConvertion("Date");
+}
+
+void Content::value(Time&) const
+{
+	badConvertion("Time");
+}
+
+void Content::value(DateTime&) const
+{
+	badConvertion("DateTime");
+}
+
+void Content::value(ValueMap & v) const
+{
+    badConvertion("Map");
+}
+
+
+void Content::value(ValueList & v) const
+{
+	// Cast away constness, so the Contnt can be attached by the value
+	v.push_back(Value((Content*)this));
+}
+
+bool Content::operator==(const Content& other) const
+{
+	Log::debug() << *this << " == " << other << std::endl;
+	return (this->compare(other) == 0);
+}
+
+bool Content::operator<(const Content& other) const
+{
+	return (this->compare(other) < 0);
+}
+
+
+int Content::compareNumber(const NumberContent&) const
+{
+    badComparison("Number");
+    return 0;
+}
+
+int Content::compareBool(const BoolContent&) const
+{
+    badComparison("Bool");
+    return 0;
+}
+
+int Content::compareDouble(const DoubleContent&) const
+{
+    badComparison("Double");
+    return 0;
+}
+
+int Content::compareString(const StringContent&) const
+{
+    badComparison("String");
+    return 0;
+}
+
+int Content::compareNil(const NilContent&) const
+{
+    badComparison("Nil");
+    return 0;
+}
+
+int Content::compareList(const ListContent&) const
+{
+    badComparison("List");
+    return 0;
+}
+
+int Content::compareMap(const MapContent&) const
+{
+    badComparison("Map");
+    return 0;
+}
+
+int Content::compareDate(const DateContent&) const
+{
+    badComparison("Date");
+    return 0;
+}
+
+int Content::compareTime(const TimeContent&) const
+{
+    badComparison("Time");
+    return 0;
+}
+
+int Content::compareDateTime(const DateTimeContent&) const
+{
+    badComparison("DateTime");
+    return 0;
+}
+
+Content* Content::operator+(const Content& other) const
+{
+	return add(other);
+}
+
+Content* Content::add(const Content& other) const
+{
+	badOperator("+",other.typeName());
+	return 0;
+}
+
+Content* Content::addNumber(const NumberContent&) const
+{
+	badOperator("+","Number");
+	return 0;
+}
+
+Content* Content::addBool(const BoolContent&) const
+{
+    badOperator("+","Bool");
+    return 0;
+}
+
+Content* Content::addDouble(const DoubleContent&) const
+{
+    badOperator("+","Number");
+    return 0;
+}
+
+Content* Content::addString(const StringContent&) const
+{
+	badOperator("+","String");
+	return 0;
+}
+
+Content* Content::addNil(const NilContent&) const
+{
+	badOperator("+","Nil");
+	return 0;
+}
+
+Content* Content::addList(const ListContent&) const
+{
+	badOperator("+","List");
+	return 0;
+}
+
+Content* Content::addMap(const MapContent&) const
+{
+    badOperator("+","List");
+    return 0;
+}
+
+Content* Content::addDate(const DateContent&) const
+{
+	badOperator("+","Date");
+	return 0;
+}
+
+Content* Content::addTime(const TimeContent&) const
+{
+	badOperator("+","Time");
+	return 0;
+}
+
+Content* Content::addDateTime(const DateTimeContent&) const
+{
+	badOperator("+","DateTime");
+	return 0;
+}
+
+Content* Content::operator-(const Content& other) const
+{
+	return sub(other);
+}
+
+Content* Content::sub(const Content& other) const
+{
+	badOperator("-",other.typeName());
+	return 0;
+}
+
+Content* Content::subNumber(const NumberContent&) const
+{
+	badOperator("-","Number");
+	return 0;
+}
+
+Content* Content::subDouble(const DoubleContent&) const
+{
+    badOperator("-","Double");
+    return 0;
+}
+
+Content* Content::subBool(const BoolContent&) const
+{
+    badOperator("-","Bool");
+    return 0;
+}
+
+Content* Content::subString(const StringContent&) const
+{
+	badOperator("-","String");
+	return 0;
+}
+
+Content* Content::subNil(const NilContent&) const
+{
+	badOperator("-","Nil");
+	return 0;
+}
+
+Content* Content::subList(const ListContent&) const
+{
+	badOperator("-","List");
+	return 0;
+}
+
+Content* Content::subMap(const MapContent&) const
+{
+    badOperator("-","Map");
+    return 0;
+}
+
+Content* Content::subDate(const DateContent&) const
+{
+	badOperator("-","Date");
+	return 0;
+}
+
+Content* Content::subTime(const TimeContent&) const
+{
+	badOperator("-","Time");
+	return 0;
+}
+
+Content* Content::subDateTime(const DateTimeContent&) const
+{
+	badOperator("-","DateTime");
+	return 0;
+}
+
+Content* Content::operator*(const Content& other) const
+{
+	return mul(other);
+}
+
+Content* Content::mul(const Content& other) const
+{
+	badOperator("*",other.typeName());
+	return 0;
+}
+
+Content* Content::mulNumber(const NumberContent&) const
+{
+	badOperator("*","Number");
+	return 0;
+}
+
+Content* Content::mulBool(const BoolContent&) const
+{
+    badOperator("*","Bool");
+    return 0;
+}
+
+Content* Content::mulDouble(const DoubleContent&) const
+{
+    badOperator("*","Double");
+    return 0;
+}
+
+Content* Content::mulString(const StringContent&) const
+{
+	badOperator("*","String");
+	return 0;
+}
+
+Content* Content::mulNil(const NilContent&) const
+{
+	badOperator("*","Nil");
+	return 0;
+}
+
+Content* Content::mulList(const ListContent&) const
+{
+	badOperator("*","List");
+	return 0;
+}
+
+Content* Content::mulMap(const MapContent&) const
+{
+    badOperator("*","Map");
+    return 0;
+}
+
+Content* Content::mulDate(const DateContent&) const
+{
+	badOperator("*","Date");
+	return 0;
+}
+
+Content* Content::mulTime(const TimeContent&) const
+{
+	badOperator("*","Time");
+	return 0;
+}
+
+Content* Content::mulDateTime(const DateTimeContent&) const
+{
+	badOperator("*","DateTime");
+	return 0;
+}
+
+Content* Content::operator/(const Content& other) const
+{
+	return div(other);
+}
+
+Content* Content::div(const Content& other) const
+{
+	badOperator("/",other.typeName());
+	return 0;
+}
+
+Content* Content::divDouble(const DoubleContent&) const
+{
+    badOperator("/","Double");
+	return 0;
+}
+
+Content* Content::divNumber(const NumberContent&) const
+{
+    badOperator("/","Number");
+    return 0;
+}
+
+Content* Content::divBool(const BoolContent&) const
+{
+    badOperator("/","Bool");
+    return 0;
+}
+
+
+Content* Content::divString(const StringContent&) const
+{
+	badOperator("/","String");
+	return 0;
+}
+
+Content* Content::divNil(const NilContent&) const
+{
+	badOperator("/","Nil");
+	return 0;
+}
+
+Content* Content::divList(const ListContent&) const
+{
+	badOperator("/","List");
+	return 0;
+}
+
+Content* Content::divMap(const MapContent&) const
+{
+    badOperator("/","Map");
+    return 0;
+}
+
+Content* Content::divDate(const DateContent&) const
+{
+	badOperator("/","Date");
+	return 0;
+}
+
+Content* Content::divTime(const TimeContent&) const
+{
+	badOperator("/","Time");
+	return 0;
+}
+
+Content* Content::divDateTime(const DateTimeContent&) const
+{
+	badOperator("/","DateTime");
+	return 0;
+}
+
+
+Content* Content::mod(const Content& other) const
+{
+    badOperator("%",other.typeName());
+    return 0;
+}
+
+Content* Content::modDouble(const DoubleContent&) const
+{
+    badOperator("%","Double");
+    return 0;
+}
+
+Content* Content::modNumber(const NumberContent&) const
+{
+    badOperator("%","Number");
+    return 0;
+}
+
+Content* Content::modBool(const BoolContent&) const
+{
+    badOperator("%","Bool");
+    return 0;
+}
+
+
+Content* Content::modString(const StringContent&) const
+{
+    badOperator("%","String");
+    return 0;
+}
+
+Content* Content::modNil(const NilContent&) const
+{
+    badOperator("%","Nil");
+    return 0;
+}
+
+Content* Content::modList(const ListContent&) const
+{
+    badOperator("%","List");
+    return 0;
+}
+
+Content* Content::modMap(const MapContent&) const
+{
+    badOperator("%","Map");
+    return 0;
+}
+
+Content* Content::modDate(const DateContent&) const
+{
+    badOperator("%","Date");
+    return 0;
+}
+
+Content* Content::modTime(const TimeContent&) const
+{
+    badOperator("%","Time");
+    return 0;
+}
+
+Content* Content::modDateTime(const DateTimeContent&) const
+{
+    badOperator("/%","DateTime");
+    return 0;
+}
+
+#ifndef IBM
+template<>
+Streamable* Reanimator<Content>::ressucitate(Stream& s) const
+{
+	return 0;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/value/Content.h b/eckit/src/eckit/value/Content.h
new file mode 100644
index 0000000..213c187
--- /dev/null
+++ b/eckit/src/eckit/value/Content.h
@@ -0,0 +1,269 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Content.h
+// Manuel Fuentes - ECMWF Jun 96
+
+#ifndef eckit_Content_h
+#define eckit_Content_h
+
+#include "eckit/memory/Counted.h"
+#include "eckit/serialisation/Streamable.h"
+
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Within eckit, these exceptions are only used internally to Content.cc. However, they are declared in the
+// header so that they are visible to the unit tests.
+
+class BadConversion:  public Exception {
+public:
+    BadConversion(const std::string& w, const CodeLocation& loc) :
+        Exception(std::string("Bad Conversion: ") + w, loc) {}
+};
+
+class BadComparison:  public Exception {
+public:
+    BadComparison(const std::string& w, const CodeLocation& loc) :
+        Exception(std::string("Bad Comparison: ") + w, loc) {}
+};
+
+class BadOperator:  public Exception {
+public:
+    BadOperator(const std::string& w, const CodeLocation& loc) :
+        Exception(std::string("Bad operator: ") + w, loc) {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// List here all the Content's
+
+class DoubleContent;
+class BoolContent;
+class NumberContent;
+class StringContent;
+class NilContent;
+class ListContent;
+class MapContent;
+class DateContent;
+class TimeContent;
+class DateTimeContent;
+class Date;
+class Time;
+class DateTime;
+class Value;
+class JSON;
+
+typedef std::vector<Value>     ValueList;
+typedef std::map<Value,Value>  ValueMap;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Assuptions for comparisons:
+// Nil < Number < String < List
+
+class Content : public Counted, public Streamable {
+
+public:
+
+	// Double-dispatching on subclasses
+	// Needs a compare??? for every new subclass
+
+    virtual int  compareBool(const BoolContent&) const;
+    virtual int  compareNumber(const NumberContent&) const;
+    virtual int  compareDouble(const DoubleContent&) const;
+	virtual int  compareString(const StringContent&) const;
+	virtual int  compareNil(const NilContent&)       const;
+	virtual int  compareList(const ListContent&)     const;
+    virtual int  compareMap(const MapContent&)     const;
+	virtual int  compareDate(const DateContent&)         const;
+	virtual int  compareTime(const TimeContent&)         const;
+	virtual int  compareDateTime(const DateTimeContent&) const;
+
+	// Double-dispatching on subclasses for addition
+	// Needs an add??? for every new subclass
+
+	virtual Content* addNumber(const NumberContent&)  const;
+    virtual Content* addBool(const BoolContent&)  const;
+    virtual Content* addDouble(const DoubleContent&)  const;
+	virtual Content* addString(const StringContent&)  const;
+	virtual Content* addNil(const NilContent&)        const;
+	virtual Content* addList(const ListContent&)      const;
+    virtual Content* addMap(const MapContent&)      const;
+	virtual Content* addDate(const DateContent&)         const;
+	virtual Content* addTime(const TimeContent&)         const;
+	virtual Content* addDateTime(const DateTimeContent&) const;
+
+	virtual Content* subNumber(const NumberContent&)  const;
+    virtual Content* subDouble(const DoubleContent&)  const;
+    virtual Content* subBool(const BoolContent&)  const;
+	virtual Content* subString(const StringContent&)  const;
+	virtual Content* subNil(const NilContent&)        const;
+	virtual Content* subList(const ListContent&)      const;
+    virtual Content* subMap(const MapContent&)      const;
+	virtual Content* subDate(const DateContent&)         const;
+	virtual Content* subTime(const TimeContent&)         const;
+	virtual Content* subDateTime(const DateTimeContent&) const;
+
+	virtual Content* mulNumber(const NumberContent&)  const;
+    virtual Content* mulDouble(const DoubleContent&)  const;
+    virtual Content* mulBool(const BoolContent&)  const;
+	virtual Content* mulString(const StringContent&)  const;
+	virtual Content* mulNil(const NilContent&)        const;
+	virtual Content* mulList(const ListContent&)      const;
+    virtual Content* mulMap(const MapContent&)      const;
+	virtual Content* mulDate(const DateContent&)         const;
+	virtual Content* mulTime(const TimeContent&)         const;
+	virtual Content* mulDateTime(const DateTimeContent&) const;
+
+	virtual Content* divNumber(const NumberContent&)  const;
+    virtual Content* divDouble(const DoubleContent&)  const;
+    virtual Content* divBool(const BoolContent&)  const;
+	virtual Content* divString(const StringContent&)  const;
+	virtual Content* divNil(const NilContent&)        const;
+	virtual Content* divList(const ListContent&)      const;
+    virtual Content* divMap(const MapContent&)      const;
+	virtual Content* divDate(const DateContent&)         const;
+	virtual Content* divTime(const TimeContent&)         const;
+	virtual Content* divDateTime(const DateTimeContent&) const;
+
+    virtual Content* modNumber(const NumberContent&)  const;
+    virtual Content* modDouble(const DoubleContent&)  const;
+    virtual Content* modBool(const BoolContent&)  const;
+    virtual Content* modString(const StringContent&)  const;
+    virtual Content* modNil(const NilContent&)        const;
+    virtual Content* modList(const ListContent&)      const;
+    virtual Content* modMap(const MapContent&)      const;
+    virtual Content* modDate(const DateContent&)         const;
+    virtual Content* modTime(const TimeContent&)         const;
+    virtual Content* modDateTime(const DateTimeContent&) const;
+
+
+protected:
+
+// -- Constructor
+
+	Content()          {  }
+	Content(Stream&);
+
+// -- Destructor
+
+	virtual ~Content();
+
+// -- Operators
+
+	bool operator==(const Content&) const;
+	bool operator<(const Content&)  const;
+
+
+//	void *operator new(size_t s)  { return MemoryPool::fastAllocate(s);}
+//	void operator delete(void* p) { MemoryPool::fastDeallocate(p);     }
+
+// -- Methods
+
+    virtual void value(bool&) const;
+	virtual void value(long long&) const;
+    virtual void value(double&) const;
+	virtual void value(std::string&)    const;
+	virtual void value(Date&)      const;
+	virtual void value(Time&)      const;
+	virtual void value(DateTime&)  const;
+	virtual void value(ValueList&) const;
+    virtual void value(ValueMap&)  const;
+
+	Content* operator+(const Content&) const;
+	Content* operator-(const Content&) const;
+	Content* operator*(const Content&) const;
+	Content* operator/(const Content&) const;
+
+
+// -- Methods
+
+	virtual void   print(std::ostream&) const  = 0;
+	virtual std::string typeName()      const = 0;
+    virtual void   json(JSON&)     const  = 0;
+    virtual Content* clone() const = 0;
+
+    virtual bool   isNil()      const  { return false; }
+    virtual bool   isNumber()   const  { return false; }
+    virtual bool   isBool()     const  { return false; }
+    virtual bool   isDouble()   const  { return false; }
+    virtual bool   isString()   const  { return false; }
+    virtual bool   isList()     const  { return false; }
+    virtual bool   isMap()      const  { return false; }
+    virtual bool   isDate()     const  { return false; }
+    virtual bool   isTime()     const  { return false; }
+    virtual bool   isDateTime() const  { return false; }
+
+
+    virtual bool contains(const Value&) const;
+    virtual Value& element(const Value&);
+    virtual Value keys() const;
+    virtual Value negate() const;
+    virtual size_t size() const;
+
+
+// -- Overridden methods
+
+	// From Streamble
+
+	virtual void  encode(Stream&) const;
+	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+private:
+
+// -- No copy allowed
+
+	Content(const Content&);
+	Content& operator=(const Content&);
+
+// -- Class members
+
+    static  ClassSpec            classSpec_;
+    static  Reanimator<Content>  reanimator_;
+
+// -- Methods
+
+	virtual int      compare(const Content&)    const = 0;
+	virtual Content* add(const Content&)        const = 0;
+	virtual Content* sub(const Content&)        const = 0;
+	virtual Content* mul(const Content&)        const = 0;
+	virtual Content* div(const Content&)        const = 0;
+    virtual Content* mod(const Content&)        const = 0;
+
+	void    badConvertion(const std::string&) const;
+	void    badComparison(const std::string&) const;
+	void    badOperator(const std::string&, const std::string&) const;
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s, const Content& content)
+		{ content.print(s); return s; }
+    friend JSON& operator<<(JSON& s, const Content& content)
+        { content.json(s); return s; }
+
+	friend class Value;
+};
+
+template<>
+Streamable* Reanimator<Content>::ressucitate(Stream& s) const
+#ifdef IBM
+{ return 0;}
+#else
+;
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif // eckit_Content_h
diff --git a/eckit/src/eckit/value/DateContent.cc b/eckit/src/eckit/value/DateContent.cc
new file mode 100644
index 0000000..418985d
--- /dev/null
+++ b/eckit/src/eckit/value/DateContent.cc
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/DateContent.h"
+#include "eckit/value/NumberContent.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec DateContent::classSpec_ = {&Content::classSpec(),"DateContent",};
+Reanimator<DateContent> DateContent::reanimator_;
+
+DateContent::DateContent(const Date& d):
+    value_(d)
+{
+}
+
+DateContent::DateContent(Stream& s):
+	Content(s)
+{
+	std::string dd;
+	s >> dd;
+    value_ = Date(dd);
+}
+
+void DateContent::encode(Stream& s) const
+{
+	Content::encode(s);
+    std::string dd = value_;
+	s << dd;
+}
+
+DateContent::~DateContent()
+{
+}
+
+Content* DateContent::clone() const {
+    return new DateContent(value_);
+}
+
+void DateContent::print(std::ostream& s) const
+{
+    s << value_;
+}
+
+void DateContent::json(JSON& s) const
+{
+    s << std::string(value_);
+}
+
+int DateContent::compare(const Content& other) const
+{
+	return -other.compareDate(*this);
+}
+
+int DateContent::compareDate(const DateContent& other) const
+{
+    if(value_ < other.value_)
+		return -1;
+    else if(value_ == other.value_)
+		return 1;
+
+	return 0;
+}
+
+void DateContent::value(Date& d) const
+{
+    d = value_;
+}
+
+Content* DateContent::add(const Content& other) const
+{
+	return other.addDate(*this);
+}
+
+Content* DateContent::sub(const Content& other) const
+{
+	return other.subDate(*this);
+}
+
+Content* DateContent::subDate(const DateContent& other) const
+{
+    return new NumberContent(value_ - other.value_);
+}
+
+Content* DateContent::mul(const Content& other) const
+{
+    return other.mulDate(*this);
+}
+
+Content* DateContent::div(const Content& other) const
+{
+    return other.divDate(*this);
+}
+
+Content* DateContent::mod(const Content& other) const
+{
+    return other.modDate(*this);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/value/DateContent.h b/eckit/src/eckit/value/DateContent.h
new file mode 100644
index 0000000..cd75d2b
--- /dev/null
+++ b/eckit/src/eckit/value/DateContent.h
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DateContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_DateContent_h
+#define eckit_DateContent_h
+
+#include "eckit/value/Content.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DateContent : public Content {
+
+protected:
+
+// -- Constructor
+
+	DateContent(const Date&);
+	DateContent(Stream&);
+
+// -- Destructor
+
+	virtual ~DateContent();
+
+// -- Overridden methods
+
+	// -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const { Content::value(n); }
+    virtual void value(long long& n)   const { Content::value(n); }
+    virtual void value(double& n)      const { Content::value(n); }
+    virtual void value(std::string& n) const { Content::value(n); }
+    virtual void value(Date& n)        const;
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const { Content::value(n); }
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const {return -1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return -1; }
+    virtual int  compareString(const StringContent&)        const {return -1; }
+    virtual int  compareNil(const NilContent&)              const {return -1; }
+    virtual int  compareList(const ListContent&)            const {return -1; }
+    virtual int  compareMap(const MapContent&)              const {return -1; }
+    virtual int  compareDate(const DateContent&)            const;
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+	virtual Content* add(const Content&) const;
+	virtual Content* sub(const Content&) const;
+	virtual Content* mul(const Content&) const;
+	virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+	virtual Content* subDate(const DateContent&) const;
+
+	virtual void    print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+	virtual std::string  typeName() const       { return "Date"; }
+	virtual bool    isDate() const         { return true; }
+    virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()            { return classSpec_;}
+
+private:
+
+	DateContent(const DateContent&);
+	DateContent& operator=(const DateContent&);
+
+// -- Members
+
+    Date value_;
+
+// -- Class Members
+
+	static  ClassSpec                classSpec_;
+    static  Reanimator<DateContent>  reanimator_;
+
+// -- Friends
+
+	friend class Reanimator<DateContent>;
+	friend class Value;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/DispatchParams.h b/eckit/src/eckit/value/DispatchParams.h
new file mode 100644
index 0000000..d6adf4b
--- /dev/null
+++ b/eckit/src/eckit/value/DispatchParams.h
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Florian Rathgeber
+/// @date March 2015
+
+#ifndef eckit_value_DispatchParams_H
+#define eckit_value_DispatchParams_H
+
+#include "eckit/value/Params.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+template < class Derived >
+class DispatchParams {
+
+public: // methods
+
+    DispatchParams() {}
+    DispatchParams( Stream& s ) { NOTIMP; }
+
+    static const char* className() { return "eckit::DispatchParams"; }
+
+    template < typename T >
+    friend Params::value_t getValue( const DispatchParams<T>& p, const Params::key_t& key );
+    template < typename T >
+    friend void print( const DispatchParams<T>&, std::ostream& );
+    template < typename T >
+    friend void encode( const DispatchParams<T>&, Stream& );
+
+protected: // members
+
+    typedef Params::value_t ( Derived::* parametrizer_t ) ( const Params::key_t& ) const ;
+    typedef std::map< std::string, parametrizer_t > store_t;
+
+    store_t dispatch_;
+};
+
+template < class Derived >
+Params::value_t getValue( const DispatchParams<Derived>& p, const Params::key_t& key )
+{
+    typename DispatchParams<Derived>::store_t::const_iterator i = p.dispatch_.find(key);
+    if( i != p.dispatch_.end() )
+    {
+        typename DispatchParams<Derived>::parametrizer_t fptr = i->second;
+        const Derived* pobj = static_cast<const Derived*>(&p);
+        return (pobj->*fptr)( key );
+    }
+    else
+        return Params::value_t();
+}
+
+template < class Derived >
+void print( const DispatchParams<Derived>&, std::ostream& )
+{
+    NOTIMP;
+}
+
+template < class Derived >
+void encode( const DispatchParams<Derived>&, Stream& )
+{
+    NOTIMP;
+}
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/DoubleContent.cc b/eckit/src/eckit/value/DoubleContent.cc
new file mode 100644
index 0000000..a5e9abc
--- /dev/null
+++ b/eckit/src/eckit/value/DoubleContent.cc
@@ -0,0 +1,152 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/DoubleContent.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/value/NumberContent.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec DoubleContent::classSpec_ = {&Content::classSpec(),"DoubleContent",};
+Reanimator<DoubleContent> DoubleContent::reanimator_;
+
+DoubleContent::DoubleContent(double l):
+    value_(l)
+{
+}
+
+DoubleContent::DoubleContent(Stream& s):
+    Content(s),
+    value_(0)
+{
+    s >> value_;
+}
+
+Content* DoubleContent::clone() const {
+    return new DoubleContent(value_);
+}
+
+void DoubleContent::encode(Stream& s) const
+{
+    Content::encode(s);
+    s << value_;
+}
+
+DoubleContent::~DoubleContent()
+{
+}
+
+void DoubleContent::print(std::ostream& s) const
+{
+    s << value_;
+}
+
+void DoubleContent::json(JSON& s) const
+{
+    s << value_;
+}
+
+
+int DoubleContent::compare(const Content& other) const
+{
+    return -other.compareDouble(*this);
+}
+
+int DoubleContent::compareDouble(const DoubleContent& other) const
+{
+    double dif = (value_ - other.value_);
+    if(dif == 0)
+        return dif;
+    if(dif<0)
+        return -1;
+    return 1;
+}
+
+int DoubleContent::compareNumber(const NumberContent& other) const
+{
+    double dif = (value_ - other.value_);
+    if(dif == 0)
+        return dif;
+    if(dif<0)
+        return -1;
+    return 1;
+}
+
+void DoubleContent::value(double& l) const
+{
+    l = value_;
+}
+
+void DoubleContent::value(std::string& s) const
+{
+    s = Translator<double,std::string>()(value_);
+}
+
+Content* DoubleContent::add(const Content& other) const
+{
+    return other.addDouble(*this);
+}
+
+Content* DoubleContent::addDouble(const DoubleContent& other) const
+{
+    return new DoubleContent(other.value_ + value_);
+}
+
+Content* DoubleContent::sub(const Content& other) const
+{
+    return other.subDouble(*this);
+}
+
+Content* DoubleContent::subDouble(const DoubleContent& other) const
+{
+    return new DoubleContent(other.value_ - value_);
+}
+
+Content* DoubleContent::mul(const Content& other) const
+{
+    return other.mulDouble(*this);
+}
+
+Content* DoubleContent::mulDouble(const DoubleContent& other) const
+{
+    return new DoubleContent(other.value_ * value_);
+}
+
+Content* DoubleContent::div(const Content& other) const
+{
+    return other.divDouble(*this);
+}
+
+Content* DoubleContent::mod(const Content& other) const
+{
+    return other.modDouble(*this);
+}
+
+
+Content* DoubleContent::divDouble(const DoubleContent& other) const
+{
+    return new DoubleContent(other.value_ / value_);
+}
+
+Value DoubleContent::negate() const
+{
+    return Value(-value_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/DoubleContent.h b/eckit/src/eckit/value/DoubleContent.h
new file mode 100644
index 0000000..d00501a
--- /dev/null
+++ b/eckit/src/eckit/value/DoubleContent.h
@@ -0,0 +1,127 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DoubleContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_DoubleContent_h
+#define eckit_DoubleContent_h
+
+#include "eckit/value/Content.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class DoubleContent : public Content {
+
+protected:
+
+// -- Constructor
+
+        DoubleContent(double);
+        DoubleContent(Stream&);
+
+// -- Destructor
+
+        virtual ~DoubleContent();
+
+// -- Overridden methods
+
+	// -- From Content
+        virtual int compare(const Content& other) const;
+
+        virtual void value(bool& n)        const { Content::value(n); }
+        virtual void value(long long& n)   const { Content::value(n); }
+        virtual void value(double& n)      const;
+        virtual void value(std::string& n) const;
+        virtual void value(Date& n)        const { Content::value(n); }
+        virtual void value(Time& n)        const { Content::value(n); }
+        virtual void value(DateTime& n)    const { Content::value(n); }
+        virtual void value(ValueList& n)   const { Content::value(n); }
+        virtual void value(ValueMap& n)    const { Content::value(n); }
+
+        virtual int  compareBool(const BoolContent&)            const {return -1; }
+        virtual int  compareNumber(const NumberContent&)        const;
+        virtual int  compareDouble(const DoubleContent&)        const;
+        virtual int  compareString(const StringContent&)        const {return 1; }
+        virtual int  compareNil(const NilContent&)              const {return 1; }
+        virtual int  compareList(const ListContent&)            const {return 1; }
+        virtual int  compareMap(const MapContent&)              const {return 1; }
+        virtual int  compareDate(const DateContent&)            const {return 1; }
+        virtual int  compareTime(const TimeContent&)            const {return 1; }
+        virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+
+        virtual Content* add(const Content&) const;
+        virtual Content* sub(const Content&) const;
+        virtual Content* mul(const Content&) const;
+        virtual Content* div(const Content&) const;
+        virtual Content* mod(const Content&) const;
+
+        virtual Content* addDouble(const DoubleContent&) const;
+        virtual Content* subDouble(const DoubleContent&) const;
+        virtual Content* mulDouble(const DoubleContent&) const;
+        virtual Content* divDouble(const DoubleContent&) const;
+
+
+        virtual Value negate() const;
+
+//        virtual Content* addNumber(const NumberContent&) const;
+//        virtual Content* subNumber(const NumberContent&) const;
+//        virtual Content* mulNumber(const NumberContent&) const;
+//        virtual Content* divNumber(const NumberContent&) const;
+
+        virtual void    print(std::ostream&) const;
+        virtual void   json(JSON&)      const;
+        virtual std::string  typeName()      const { return "Double"; }
+        virtual bool    isDouble()      const { return true; }
+        virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()            { return classSpec_;}
+
+private:
+
+        DoubleContent(const DoubleContent&);
+        DoubleContent& operator=(const DoubleContent&);
+
+// -- Members
+
+        double value_;
+
+// -- Class Members
+
+	static  ClassSpec                  classSpec_;
+    static  Reanimator<DoubleContent>  reanimator_;
+
+// -- Friends
+
+        friend class Reanimator<DoubleContent>;
+	friend class Value;
+	friend class DateContent;
+    friend class NumberContent;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/Expression.h b/eckit/src/eckit/value/Expression.h
new file mode 100644
index 0000000..1118928
--- /dev/null
+++ b/eckit/src/eckit/value/Expression.h
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Expression.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef Expression_H
+#define Expression_H
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/value/Value.h"
+
+//====================================================================
+
+inline const char *opname(const negate<eckit::Value>&)         { return "-";}
+inline const char *opname(const multiplies<eckit::Value>&)          { return "*";}
+inline const char *opname(const divides<eckit::Value>&)        { return "/";}
+inline const char *opname(const modulus<eckit::Value>&)        { return "%";}
+inline const char *opname(const plus<eckit::Value>&)           { return "+";}
+inline const char *opname(const minus<eckit::Value>&)          { return "-";}
+inline const char *opname(const greater<eckit::Value>&)        { return ">";}
+inline const char *opname(const equal_to<eckit::Value>&)       { return "==";}
+inline const char *opname(const less<eckit::Value>&)           { return "<";}
+inline const char *opname(const greater_equal<eckit::Value>&)  { return ">=";}
+inline const char *opname(const less_equal<eckit::Value>&)     { return "<=";}
+inline const char *opname(const not_equal_to<eckit::Value>&)   { return "!=";}
+inline const char *opname(const logical_not<eckit::Value>&)    { return "!";}
+inline const char *opname(const logical_and<eckit::Value>&)    { return "&&";}
+inline const char *opname(const logical_or<eckit::Value>&)     { return "||";}
+
+//====================================================================
+
+class EvalError : public eckit::Exception {
+public:
+	EvalError(const std::string& s) : Exception(std::string("EvalError: ") + s) {}
+};
+
+template <class T>
+class Expression : private eckit::NonCopyable {
+	virtual void print(std::ostream&) const = 0;
+public:
+	virtual eckit::Value eval(T&) const = 0;
+	virtual ~Expression()  { }
+	friend std::ostream& operator<<(std::ostream& s,const Expression<T>& c)
+		{ c.print(s); return s; }
+};
+
+template<class T, class U>
+class CondUnary : public Expression<U> {
+
+	auto_ptr<Expression<U> > cond_;
+
+	virtual void print(std::ostream& s) const 
+		{ s << opname(T()) << '(' << *cond_ << ')'; }
+
+public:
+	CondUnary(Expression<U>* cond): cond_(cond) {}
+	virtual ~CondUnary(){}
+	virtual eckit::Value eval(U& task) const { return T()(cond_->eval(task));}
+};
+
+template<class T, class U>
+class CondBinary : public Expression<U> {
+
+	auto_ptr<Expression<U> > left_;
+	auto_ptr<Expression<U> > right_;
+
+	virtual void print(std::ostream& s) const
+		{ s << '(' << *left_ << ' ' << opname(T()) << ' ' << *right_ << ')'; }
+public:
+
+	CondBinary(Expression<U>* left,Expression<U>* right):
+        left_(left), right_(right) {}
+
+	virtual ~CondBinary(){}
+	eckit::Value eval(U& task) const;
+};
+
+template <class T, class U>
+inline eckit::Value CondBinary<T,U>::eval(U& task) const
+{
+	return T()(left_->eval(task),right_->eval(task));
+}
+
+template <class T>
+class StringExpression : public Expression<T> {
+	std::string str_;
+	virtual void print(std::ostream& s) const { s << str_; } 
+public:
+	StringExpression(const std::string& s) : str_(s) {}
+	virtual ~StringExpression(){}
+	virtual eckit::Value eval(T&) const { return eckit::Value(str_); }
+};
+
+template <class T>
+class NumberExpression : public Expression<T> {
+	long long value_;
+	virtual void print(std::ostream& s) const { s << value_; } 
+protected:
+	long long value() const              { return value_; }
+public:
+	NumberExpression(long long n) : value_(n) {}
+	virtual ~NumberExpression() {}
+	virtual eckit::Value eval(T&) const { return eckit::Value(value_); }
+};
+
+template <class T>
+class ListExpression : public Expression<T> {
+	std::vector<Expression<T>*> v_;
+	virtual void print(std::ostream& s) const;
+public:
+	ListExpression();
+	ListExpression(const std::vector<Expression<T>*>& v) : v_(v) {}
+	virtual ~ListExpression();
+	virtual eckit::Value eval(T&) const;
+};
+
+template <class T>
+ListExpression<T>::~ListExpression()
+{
+	for(size_t i = 0; i < v_.size(); i++)
+		delete v_[i];
+}
+
+template <class T>
+void ListExpression<T>::print(std::ostream& s) const
+{
+	s << '[';
+	for( size_t i = 0; i < v_.size(); i++)
+	{
+		if(i) s << ',';
+		if(v_[i])
+			s << *v_[i];
+		else
+			s << "(null)";
+	}
+	s << ']';
+}
+
+template <class T>
+eckit::Value ListExpression<T>::eval(T& t) const
+{
+	std::vector<eckit::Value> v;
+	for(size_t i = 0; i < v_.size(); i++)
+		if(v_[i]) v.push_back(v_[i]->eval(t));
+			
+	return v;
+}
+
+#endif
diff --git a/eckit/src/eckit/value/ListContent.cc b/eckit/src/eckit/value/ListContent.cc
new file mode 100644
index 0000000..e9c80b4
--- /dev/null
+++ b/eckit/src/eckit/value/ListContent.cc
@@ -0,0 +1,215 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/ListContent.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec ListContent::classSpec_ = {&Content::classSpec(),"ListContent",};
+Reanimator<ListContent>  ListContent::reanimator_;
+
+
+ListContent::ListContent()
+{
+}
+
+ListContent::ListContent(const ValueList & v)
+{
+    std::copy(v.begin(),v.end(), std::back_inserter(value_));
+}
+
+ListContent::ListContent(const Value& v)
+{
+    value_.push_back(v);
+}
+
+ListContent::ListContent(Stream& s):
+	Content(s)
+{
+	long count;
+	s >> count;
+	for(int i = 0; i< count; i++)
+        value_.push_back(Value(s));
+
+}
+
+Content* ListContent::clone() const {
+    ValueList v;
+    v.reserve(value_.size());
+    for(size_t i = 0; i < value_.size(); ++i) {
+        v.push_back(value_[i].clone());
+    }
+    return new ListContent(v);
+}
+
+void ListContent::encode(Stream& s) const
+{
+	Content::encode(s);
+    long count = value_.size();
+	s << count;
+	for(int i = 0; i < count; ++i)
+        s << value_[i];
+
+}
+
+ListContent::~ListContent()
+{
+}
+
+size_t ListContent::size() const {
+    return value_.size();
+}
+
+void ListContent::value(ValueList& v) const
+{
+    v = value_;
+}
+
+int ListContent::compare(const Content& other)const
+{
+	return -other.compareList(*this);
+}
+
+int ListContent::compareList(const ListContent& other) const
+{
+    if(value_ == other.value_)
+        return 0;
+    if(value_ < other.value_)
+        return -1;
+    return 1;
+
+}
+
+void ListContent::json(JSON& s) const
+{
+    s.startList();
+
+    for(size_t i = 0; i < value_.size(); i++)
+	{
+        s << value_[i];
+	}
+
+    s.endList();
+}
+
+
+void ListContent::print(std::ostream& s) const
+{
+    s << '(';
+
+    for(size_t i = 0; i < value_.size(); i++)
+    {
+        if(i>0) s << ',';
+        s << value_[i];
+    }
+
+    s << ')';
+}
+
+
+Content* ListContent::add(const Content& other) const
+{
+	return other.addList(*this);
+}
+
+Content* ListContent::addList(const ListContent& other) const
+{
+    ValueList tmp;
+    std::copy(other.value_.begin(), other.value_.end(), std::back_inserter(tmp));
+    std::copy(value_.begin(), value_.end(), std::back_inserter(tmp));
+	return new ListContent(tmp);
+}
+
+Content* ListContent::sub(const Content& other) const
+{
+    return other.subList(*this);
+}
+
+Content* ListContent::mul(const Content& other) const
+{
+    return other.mulList(*this);
+}
+
+Content* ListContent::div(const Content& other) const
+{
+    return other.divList(*this);
+}
+
+Content* ListContent::mod(const Content& other) const
+{
+    return other.modList(*this);
+}
+
+
+void ListContent::value(long long& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+	else Content::value(n);
+}
+
+void ListContent::value(bool& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+    else Content::value(n);
+}
+
+void ListContent::value(double& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+    else Content::value(n);
+}
+
+void ListContent::value(std::string& n) const
+{
+    if(value_.size() == 1) n = std::string(value_[0]);
+	else Content::value(n);
+}
+
+void ListContent::value(Date& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+	else Content::value(n);
+}
+
+void ListContent::value(Time& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+	else Content::value(n);
+}
+
+void ListContent::value(DateTime& n) const
+{
+    if(value_.size() == 1) n = value_[0];
+	else Content::value(n);
+}
+
+Value& ListContent::element(const Value& v)
+{
+    long long n = v;
+    ASSERT( n >= 0 && (size_t) n < value_.size() );
+    return value_.at(n);
+}
+
+bool ListContent::contains(const Value& v) const {
+    long long n = v;
+    return( n >= 0 && (size_t) n < value_.size() );
+}
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/ListContent.h b/eckit/src/eckit/value/ListContent.h
new file mode 100644
index 0000000..3e976c9
--- /dev/null
+++ b/eckit/src/eckit/value/ListContent.h
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ListContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_ListContent_h
+#define eckit_ListContent_h
+
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class ListContent : public Content {
+
+protected:
+
+// -- Constructor
+
+	ListContent();
+	ListContent(const ValueList&);
+	ListContent(const Value&);
+
+	ListContent(Stream&);
+
+// -- Destructor
+
+	virtual ~ListContent();
+
+// -- Overridden methods
+
+    // -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const;
+    virtual void value(long long& n)   const;
+    virtual void value(double& n)      const;
+    virtual void value(std::string& n) const;
+    virtual void value(Date& n)        const;
+    virtual void value(Time& n)        const;
+    virtual void value(DateTime& n)    const;
+    virtual void value(ValueList& n)   const;
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const {return -1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return -1; }
+    virtual int  compareString(const StringContent&)        const {return -1; }
+    virtual int  compareNil(const NilContent&)              const {return -1; }
+    virtual int  compareList(const ListContent&)            const;
+    virtual int  compareMap(const MapContent&)              const {return 1; }
+    virtual int  compareDate(const DateContent&)            const {return 1; }
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+    virtual Content* add(const Content&) const;
+	virtual Content* sub(const Content&) const;
+	virtual Content* mul(const Content&) const;
+	virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    virtual Content* addList(const ListContent&) const;
+
+	virtual void   print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+	virtual std::string typeName() const       { return "List"; }
+
+	virtual bool   isList() const         { return true; }
+    virtual Value& element(const Value&);
+    virtual bool contains(const Value& key) const;
+    virtual Content* clone() const;
+    virtual size_t size() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()         { return classSpec_;}
+
+private:
+
+	ListContent(const ListContent&);
+	ListContent& operator=(const ListContent&);
+
+// -- Members
+
+    ValueList value_;
+
+// -- Class Members
+
+    static  ClassSpec                  classSpec_;
+    static  Reanimator<ListContent>  reanimator_;
+
+// -- Friends
+
+    friend class Reanimator<ListContent>;
+	friend class Value;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/MapContent.cc b/eckit/src/eckit/value/MapContent.cc
new file mode 100644
index 0000000..b7f60cc
--- /dev/null
+++ b/eckit/src/eckit/value/MapContent.cc
@@ -0,0 +1,166 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/MapContent.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec MapContent::classSpec_ = {&Content::classSpec(),"MapContent",};
+Reanimator<MapContent>  MapContent::reanimator_;
+
+
+MapContent::MapContent()
+{
+}
+
+MapContent::MapContent(const ValueMap& v)
+{
+    value_ = v;
+}
+
+
+MapContent::MapContent(Stream& s):
+    Content(s)
+{
+    bool more;
+    s >> more;
+    while(more)
+    {
+        Value k(s);
+        Value v(s);
+        value_[k] = v;
+        s >> more;
+    }
+
+}
+
+void MapContent::encode(Stream& s) const
+{
+    Content::encode(s);
+    for(ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j)
+    {
+        s << true;
+        s << (*j).first;
+        s << (*j).second;
+    }
+    s << false;
+}
+
+MapContent::~MapContent()
+{
+}
+
+Value& MapContent::element(const Value& key)
+{
+    return value_[key];
+}
+
+bool MapContent::contains(const Value& key) const
+{
+    return value_.find(key) != value_.end();
+}
+
+void MapContent::value(ValueMap& v) const
+{
+    v = value_;
+}
+
+int MapContent::compare(const Content& other)const
+{
+    return -other.compareMap(*this);
+}
+
+int MapContent::compareMap(const MapContent& other) const
+{
+    if(value_ == other.value_)
+        return 0;
+    if(value_ < other.value_)
+        return -1;
+    return 1;
+
+}
+
+Value MapContent::keys() const {
+    ValueList list;
+    for(ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
+        list.push_back((*j).first);
+    }
+    return Value::makeList(list);
+}
+
+void MapContent::print(std::ostream& s) const
+{
+    s << '{';
+    for(ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j)
+    {
+        if(j != value_.begin()) s << " , ";
+        s << (*j).first;
+        s << " => ";
+        s << (*j).second;
+    }
+    s << '}';
+}
+
+void MapContent::json(JSON& s) const
+{
+    s.startObject();
+    for(ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j)
+    {
+        s << (*j).first;
+        s << (*j).second;
+    }
+    s.endObject();
+}
+
+
+Content* MapContent::clone() const {
+    ValueMap v;
+    for(ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
+        v[(*j).first.clone()] = (*j).second.clone();
+    }
+    return new MapContent(v);
+}
+
+
+Content* MapContent::add(const Content& other) const
+{
+    return other.addMap(*this);
+}
+
+Content* MapContent::sub(const Content& other) const
+{
+    return other.subMap(*this);
+}
+
+Content* MapContent::mul(const Content& other) const
+{
+    return other.mulMap(*this);
+}
+
+Content* MapContent::div(const Content& other) const
+{
+    return other.divMap(*this);
+}
+
+Content* MapContent::mod(const Content& other) const
+{
+    return other.modMap(*this);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/MapContent.h b/eckit/src/eckit/value/MapContent.h
new file mode 100644
index 0000000..133af7c
--- /dev/null
+++ b/eckit/src/eckit/value/MapContent.h
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MapContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_MapContent_h
+#define eckit_MapContent_h
+
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MapContent : public Content {
+
+protected:
+
+// -- Constructor
+
+    MapContent();
+    MapContent(const ValueMap&);
+    //MapContent(const Value&);
+
+    MapContent(Stream&);
+
+// -- Destructor
+
+    virtual ~MapContent();
+
+// -- Overridden methods
+
+    // -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const { Content::value(n); }
+    virtual void value(long long& n)   const { Content::value(n); }
+    virtual void value(double& n)      const { Content::value(n); }
+    virtual void value(std::string& n) const { Content::value(n); }
+    virtual void value(Date& n)        const { Content::value(n); }
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const { Content::value(n); }
+    virtual void value(ValueMap& n)    const;
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const {return -1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return -1; }
+    virtual int  compareString(const StringContent&)        const {return -1; }
+    virtual int  compareNil(const NilContent&)              const {return -1; }
+    virtual int  compareList(const ListContent&)            const {return -1; }
+    virtual int  compareMap(const MapContent&)              const;
+    virtual int  compareDate(const DateContent&)            const {return -1; }
+    virtual int  compareTime(const TimeContent&)            const {return -1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return -1; }
+
+	virtual Content* add(const Content&)         const;
+	virtual Content* sub(const Content&) const;
+	virtual Content* mul(const Content&) const;
+	virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    virtual Value   keys() const;
+    virtual Value&   element(const Value&);
+    virtual bool contains(const Value& key) const;
+
+//    virtual Content* addMap(const MapContent&) const;
+
+	virtual void   print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+    virtual std::string typeName() const       { return "Map"; }
+
+    virtual bool   isMap() const         { return true; }
+    virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()         { return classSpec_;}
+
+private:
+
+    MapContent(const MapContent&);
+    MapContent& operator=(const MapContent&);
+
+// -- Members
+
+    ValueMap value_;
+
+// -- Class Members
+
+    static  ClassSpec                  classSpec_;
+    static  Reanimator<MapContent>  reanimator_;
+
+// -- Friends
+
+    friend class Reanimator<MapContent>;
+	friend class Value;
+
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/NilContent.cc b/eckit/src/eckit/value/NilContent.cc
new file mode 100644
index 0000000..30018ea
--- /dev/null
+++ b/eckit/src/eckit/value/NilContent.cc
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/NilContent.h"
+#include "eckit/value/Value.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+ClassSpec NilContent::classSpec_ = {&Content::classSpec(), "NilContent",};
+Reanimator<NilContent> NilContent::reanimator_;
+
+
+NilContent::NilContent()
+{
+}
+
+
+NilContent::NilContent(Stream& s):
+    Content(s)
+{
+}
+
+Content* NilContent::clone() const {
+    return new NilContent();
+}
+
+void NilContent::encode(Stream& s) const
+{
+    Content::encode(s);
+}
+
+NilContent::~NilContent()
+{
+}
+
+void NilContent::value(ValueList & v) const
+{
+    v = ValueList();
+}
+
+void NilContent::print(std::ostream& out) const
+{
+    out << "(nil)";
+}
+
+void NilContent::json(JSON & s) const
+{
+    s.null();
+}
+
+int NilContent::compare(const Content& other) const
+{
+    return -other.compareNil(*this);
+}
+
+int NilContent::compareNil(const NilContent&) const
+{
+    return 0;  // They're equals
+}
+
+Content* NilContent::add(const Content& other) const
+{
+    return other.addNil(*this);
+}
+
+Content* NilContent::addNil(const NilContent&) const
+{
+    return (Content*)this;
+}
+
+Content* NilContent::sub(const Content& other) const
+{
+    return other.subNil(*this);
+}
+
+Content* NilContent::subNil(const NilContent&) const
+{
+    return (Content*)this;
+}
+
+Content* NilContent::mul(const Content& other) const
+{
+    return other.mulNil(*this);
+}
+
+Content* NilContent::mulNil(const NilContent&) const
+{
+    return (Content*)this;
+}
+
+Content* NilContent::div(const Content& other) const
+{
+    return other.divNil(*this);
+}
+
+Content* NilContent::divNil(const NilContent&) const
+{
+    return (Content*)this;
+}
+
+Content* NilContent::mod(const Content& other) const
+{
+    return other.divNil(*this);
+}
+
+bool NilContent::contains(const Value&) const {
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/NilContent.h b/eckit/src/eckit/value/NilContent.h
new file mode 100644
index 0000000..406cc16
--- /dev/null
+++ b/eckit/src/eckit/value/NilContent.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NilContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_NilContent_h
+#define eckit_NilContent_h
+
+#include "eckit/value/Content.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class NilContent : public Content {
+protected:
+
+// -- Constructors
+
+    NilContent();
+    NilContent(Stream&);
+
+// -- Destructors
+
+    virtual ~NilContent();
+
+// -- Overridden Methods
+
+    // From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const { Content::value(n); }
+    virtual void value(long long& n)   const { Content::value(n); }
+    virtual void value(double& n)      const { Content::value(n); }
+    virtual void value(std::string& n) const { Content::value(n); }
+    virtual void value(Date& n)        const { Content::value(n); }
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const;
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const {return -1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return -1; }
+    virtual int  compareString(const StringContent&)        const {return -1; }
+    virtual int  compareNil(const NilContent&)              const;
+    virtual int  compareList(const ListContent&)            const {return 1; }
+    virtual int  compareMap(const MapContent&)              const {return 1; }
+    virtual int  compareDate(const DateContent&)            const {return 1; }
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+    virtual Content* add(const Content&) const;
+    virtual Content* sub(const Content&) const;
+    virtual Content* mul(const Content&) const;
+    virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    virtual Content* addNil(const NilContent&)  const;
+    virtual Content* subNil(const NilContent&)  const;
+    virtual Content* mulNil(const NilContent&)  const;
+    virtual Content* divNil(const NilContent&)  const;
+
+    virtual bool     isNil()     const  { return true; }
+    virtual std::string   typeName()  const  { return "Nil"; }
+    virtual void     print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+    virtual Content* clone() const;
+
+    virtual bool contains(const Value&) const;
+
+    // From Streamable
+
+    virtual void     encode(Stream&)              const;
+    virtual const    ReanimatorBase& reanimator() const { return reanimator_; }
+
+
+private:
+
+// -- No copy allowed
+
+    NilContent(const NilContent&);
+    NilContent& operator=(const NilContent&);
+
+// -- Class Members
+
+    static  ClassSpec               classSpec_;
+    static  Reanimator<NilContent>  reanimator_;
+
+// -- Friends
+
+    friend class Reanimator<NilContent>;
+    friend class Value;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/NumberContent.cc b/eckit/src/eckit/value/NumberContent.cc
new file mode 100644
index 0000000..b8d403c
--- /dev/null
+++ b/eckit/src/eckit/value/NumberContent.cc
@@ -0,0 +1,173 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/value/DoubleContent.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/value/NumberContent.h"
+#include "eckit/utils/Translator.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class BadBoolConversion:  public Exception {
+	public:
+		BadBoolConversion(const std::string& w):
+			Exception(std::string("Bad Bool Conversion: ") + w)   {  }
+};
+
+//=============================================================================
+
+ClassSpec NumberContent::classSpec_ = {&Content::classSpec(),"NumberContent",};
+Reanimator<NumberContent> NumberContent::reanimator_;
+
+NumberContent::NumberContent(long long l):
+    value_(l)
+{
+}
+
+NumberContent::NumberContent(Stream& s):
+	Content(s),
+	value_(0)
+{
+    s >> value_;
+}
+
+Content* NumberContent::clone() const {
+    return new NumberContent(value_);
+}
+
+void NumberContent::encode(Stream& s) const
+{
+	Content::encode(s);
+    s << value_;
+}
+
+NumberContent::~NumberContent()
+{
+}
+
+void NumberContent::print(std::ostream& s) const
+{
+    s << value_;
+}
+
+void NumberContent::json(JSON& s) const
+{
+    s << value_;
+}
+
+int NumberContent::compare(const Content& other) const
+{
+	return -other.compareNumber(*this);
+}
+
+int NumberContent::compareNumber(const NumberContent& other) const
+{
+    long long dif = (value_ - other.value_);
+	if(dif == 0)
+		return dif;
+	if(dif<0)
+		return -1;
+
+	return 1;
+}
+
+int NumberContent::compareDouble(const DoubleContent& other) const
+{
+    double dif = (value_ - other.value_);
+    if(dif == 0)
+        return dif;
+    if(dif<0)
+        return -1;
+    return 1;
+}
+
+void NumberContent::value(bool& b) const
+{
+    if( value_ == 0 )
+        b = false;
+    else
+        b = true;
+}
+
+void NumberContent::value(long long& l) const
+{
+    l = value_;
+}
+
+void NumberContent::value(double& l) const
+{
+    l = value_;
+}
+
+void NumberContent::value(std::string& s) const
+{
+    s = Translator<long long,std::string>()(value_);
+}
+
+Content* NumberContent::add(const Content& other) const
+{
+	return other.addNumber(*this);
+}
+
+Content* NumberContent::addNumber(const NumberContent& other) const
+{
+    return new NumberContent(other.value_ + value_);
+}
+
+Content* NumberContent::sub(const Content& other) const
+{
+	return other.subNumber(*this);
+}
+
+Content* NumberContent::subNumber(const NumberContent& other) const
+{
+    return new NumberContent(other.value_ - value_);
+}
+
+Content* NumberContent::mul(const Content& other) const
+{
+	return other.mulNumber(*this);
+}
+
+Content* NumberContent::mod(const Content& other) const
+{
+    return other.modNumber(*this);
+}
+
+Content* NumberContent::mulNumber(const NumberContent& other) const
+{
+    return new NumberContent(other.value_ * value_);
+}
+
+Content* NumberContent::div(const Content& other) const
+{
+	return other.divNumber(*this);
+}
+
+Content* NumberContent::divNumber(const NumberContent& other) const
+{
+    return new NumberContent(other.value_ / value_);
+}
+
+Value NumberContent::negate() const
+{
+    return Value(-value_);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/NumberContent.h b/eckit/src/eckit/value/NumberContent.h
new file mode 100644
index 0000000..37c48b4
--- /dev/null
+++ b/eckit/src/eckit/value/NumberContent.h
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NumberContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_NumberContent_h
+#define eckit_NumberContent_h
+
+#include "eckit/value/Content.h"
+#include "eckit/value/Value.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class NumberContent : public Content {
+
+protected:
+
+// -- Constructor
+
+	NumberContent(long long);
+	NumberContent(Stream&);
+
+// -- Destructor
+
+	virtual ~NumberContent();
+
+// -- Overridden methods
+
+	// -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const;
+    virtual void value(long long& n)   const;
+    virtual void value(double& n)      const;
+    virtual void value(std::string& n) const;
+    virtual void value(Date& n)        const { Content::value(n); }
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const { Content::value(n); }
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const;
+    virtual int  compareDouble(const DoubleContent&)        const;
+    virtual int  compareString(const StringContent&)        const {return 1; }
+    virtual int  compareNil(const NilContent&)              const {return 1; }
+    virtual int  compareList(const ListContent&)            const {return 1; }
+    virtual int  compareMap(const MapContent&)              const {return 1; }
+    virtual int  compareDate(const DateContent&)            const {return 1; }
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+    virtual Content* add(const Content&) const;
+	virtual Content* sub(const Content&) const;
+	virtual Content* mul(const Content&) const;
+	virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    virtual Content* addNumber(const NumberContent&) const;
+    virtual Content* subNumber(const NumberContent&) const;
+    virtual Content* mulNumber(const NumberContent&) const;
+    virtual Content* divNumber(const NumberContent&) const;
+
+    virtual Value negate() const;
+
+//    virtual Content* addDouble(const DoubleContent&) const;
+//    virtual Content* subDouble(const DoubleContent&) const;
+//    virtual Content* mulDouble(const DoubleContent&) const;
+//    virtual Content* divDouble(const DoubleContent&) const;
+
+    virtual void    print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+	virtual std::string  typeName()      const { return "Number"; }
+	virtual bool    isNumber()      const { return true; }
+    virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+    static  const ClassSpec&  classSpec()            { return classSpec_;}
+
+private:
+
+	NumberContent(const NumberContent&);
+	NumberContent& operator=(const NumberContent&);
+
+// -- Members
+
+    long long value_;
+
+// -- Class Members
+
+	static  ClassSpec                  classSpec_;
+    static  Reanimator<NumberContent>  reanimator_;
+
+// -- Friends
+
+	friend class Reanimator<NumberContent>;
+	friend class Value;
+    friend class DateContent;
+    friend class DoubleContent;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/Params.cc b/eckit/src/eckit/value/Params.cc
new file mode 100644
index 0000000..6f8f681
--- /dev/null
+++ b/eckit/src/eckit/value/Params.cc
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/value/Params.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+bool Params::has(const std::string& key) const
+{
+  return !getValue(*this, key).isNil();
+}
+/*
+bool Params::get(const std::string& name, std::string& value) const
+{
+  if(!has(name)) return false;
+  value = std::string(operator[](name));
+  return true;
+}
+bool Params::get(const std::string& name, bool& value) const
+{
+  if(!has(name)) return false;
+  value = operator[](name);
+  return true;
+}
+bool Params::get(const std::string& name, long& value) const
+{
+  if(!has(name)) return false;
+  value = operator[](name);
+  return true;
+}
+bool Params::get(const std::string& name, size_t& value) const
+{
+  if(!has(name)) return false;
+  value = operator[](name);
+  return true;
+}
+bool Params::get(const std::string& name, double& value) const
+{
+  if(!has(name)) return false;
+  value = operator[](name);
+  return true;
+}
+bool Params::get(const std::string& name, std::vector<int>& value) const
+{
+  if(!has(name)) return false;
+  std::vector<eckit::Value> v = operator[](name);
+  value.assign(v.begin(),v.end());
+  return true;
+}
+bool Params::get(const std::string& name, std::vector<long>& value) const
+{
+  if(!has(name)) return false;
+  std::vector<eckit::Value> v = operator[](name);
+  value.assign(v.begin(),v.end());
+  return true;
+}
+bool Params::get(const std::string& name, std::vector<double>& value) const
+{
+  if(!has(name)) return false;
+  std::vector<eckit::Value> v = operator[](name);
+  value.assign(v.begin(),v.end());
+  return true;
+}
+*/
+Params::value_t Params::operator[]( const Params::key_t& key ) const
+{
+    value_t v = getValue(*this, key);
+    if( v.isNil() )
+        throw BadParameter("Params does not contain key: " + key, Here());
+    return v;
+}
+
+Params::factory_map &Params::factories()
+{
+    // Prevent static initialisation order fiasco by constructing the static
+    // map on first use
+    static factory_map factories_;
+    return factories_;
+}
+
+Stream& operator<<(Stream & s, const Params & p)
+{
+    encode(p, s);
+    return s;
+}
+
+std::ostream& operator<<(std::ostream & s, const Params & p)
+{
+    print(p, s);
+    return s;
+}
+
+Params::value_t getValue( const Params& p, const Params::key_t& key )
+{
+    return p.self_->get_(key);
+}
+
+void print( const Params& p, std::ostream& s )
+{
+    p.self_->print_(s);
+}
+
+void encode( const Params& p, Stream& s )
+{
+    p.self_->encode_(s);
+}
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/value/Params.h b/eckit/src/eckit/value/Params.h
new file mode 100644
index 0000000..fdb11a4
--- /dev/null
+++ b/eckit/src/eckit/value/Params.h
@@ -0,0 +1,199 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Florian Rathgeber
+/// @date July 2014
+
+#ifndef eckit_value_Params_H
+#define eckit_value_Params_H
+
+#include <list>
+
+#include "eckit/serialisation/Stream.h"
+#include "eckit/value/Value.h"
+#include "eckit/config/Parametrisation.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+/// Params provides a value-based behaviour for parametrisations of objects.
+
+/// A class `MyParams` to be wrapped into Params needs to implement:
+///
+/// 1. A constructor from `Stream&`
+///
+/// 2. A static method `className` returning a `const char*`
+///
+/// 3. A `get` function taking a `Params::key_t` and returning a `Params::value_t`:
+///
+///        Params::value_t get( const MyParams&, const Params::key_t& )
+///
+///    This function performs the lookup of a parameter by key.
+///
+/// 4. A `print` function taking a `std::ostream&`:
+///
+///        void print( const MyParams&, std::ostream& )
+///
+///   This function prints a representation of the parameters to the ostream.
+///
+/// 5. An `encode` function taking a `Stream&`:
+///
+///        void encode( const MyParams&, Stream& )
+///
+///    This function encodes the parameters to the Stream.
+///
+/// If the free functions `get`, `print` and `encode` need access to private
+/// members of `MyParams`, they need to be declared as friend functions.
+///
+/// In addition a `Params::Factory<MyParams>` needs to be initialised.
+
+class Params {
+
+    struct Concept;
+
+public: // types
+
+    typedef std::list<Params> List;
+    typedef std::string  key_t;
+    typedef Value value_t;
+
+    struct BaseFactory {
+        virtual ~BaseFactory() {}
+        virtual Concept* build( Stream& s ) = 0;
+    };
+
+    typedef BaseFactory* factory_t;
+
+    template <typename T>
+    struct Factory : BaseFactory {
+        Factory()
+        {
+            Params::registerFactory(T::className(), this);
+        }
+
+        Concept* build( Stream& s );
+    };
+
+public: // methods
+
+    template <typename T>
+    explicit Params( const T& x ) : self_(new Model<T>(x)) {}
+
+    Params( const Params& x ) : self_(x.self_->copy_()) {}
+
+    static void registerFactory( const std::string& name, factory_t f )
+    {
+        factories()[name] = f;
+    }
+
+    static factory_t& getFactory(const std::string& name )
+    {
+        return factories()[name];
+    }
+
+    static Params build(const std::string& name, Stream& s)
+    {
+         return Params( getFactory(name)->build(s) ); // returns Concept*
+    }
+
+    static Params decode( Stream& s )
+    {
+        std::string name;
+        s >> name;
+        return build(name,s);
+    }
+
+    ~Params() { delete self_; }
+
+    Params& operator=( Params x )
+    {
+        std::swap(x.self_, this->self_);
+        return *this;
+    }
+
+    bool has(const std::string& name ) const;
+
+    value_t operator[] ( const key_t& key ) const;
+
+    friend void print( const Params& p, std::ostream& s );
+
+    friend void encode( const Params& p, Stream& s );
+
+    friend value_t getValue( const Params& p, const key_t& key );
+
+private: // internal classes
+
+    typedef std::map<std::string, factory_t> factory_map;
+    static factory_map& factories();
+
+    Params(Concept* concept) : self_(concept) {}
+
+    struct Concept {
+        virtual ~Concept() {}
+        virtual Concept* copy_() const = 0;
+        virtual value_t get_( const key_t& key ) const = 0;
+        virtual void print_( std::ostream& s ) const = 0;
+        virtual void encode_( Stream& s ) const = 0;
+    };
+
+    template <typename T>
+    struct Model : Concept
+    {
+        Model( T x ) : data_(x) {}
+        Model( Stream& s ) : data_(s) {}
+
+        virtual Concept* copy_() const {
+            return new Model(data_);
+        }
+
+        virtual value_t get_( const key_t& key ) const {
+            return getValue( data_, key );
+        }
+
+        virtual void print_( std::ostream& s ) const {
+            print( data_, s );
+        }
+
+        virtual void encode_( Stream& s ) const {
+            s << T::className();
+            encode( data_, s );
+        }
+
+        T data_;
+    };
+
+private: // methods
+
+    friend std::ostream& operator<<(std::ostream& s, const Params& p);
+
+    friend Stream& operator<<(Stream& s, const Params& p);
+
+private: // members
+
+    const Concept* self_;
+};
+
+//----------------------------------------------------------------------------
+
+template <typename T>
+Params::Concept* Params::Factory<T>::build(Stream & s)
+{
+    return new Model<T>(s);
+}
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/Properties.cc b/eckit/src/eckit/value/Properties.cc
new file mode 100644
index 0000000..51c60e1
--- /dev/null
+++ b/eckit/src/eckit/value/Properties.cc
@@ -0,0 +1,142 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/JSON.h"
+#include "eckit/types/Types.h"
+#include "eckit/value/Params.h"
+#include "eckit/value/Properties.h"
+#include "eckit/utils/MD5.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Properties::Properties()
+{
+}
+
+Properties::Properties(const property_t& value)
+{
+  ASSERT( value.isMap() );
+  ValueMap value_map = value;
+  for( ValueMap::const_iterator vit = value_map.begin(); vit != value_map.end(); ++vit )
+  {
+    props_[vit->first]=vit->second;
+  }
+}
+
+Properties::Properties(Stream &s)
+{
+    s >> props_;
+}
+
+bool Properties::has(const key_t & k) const
+{
+    return ( props_.find(k) != props_.end() );
+}
+
+Properties::property_t Properties::get(const key_t & k) const
+{
+    PropertyMap::const_iterator vit = props_.find(k);
+    if( vit != props_.end() )
+        return (*vit).second;
+    else
+        return property_t(); // return Nil Value...
+}
+
+Properties& Properties::set(const key_t & k, const property_t& v)
+{
+    props_[k] = v;
+    return *this;
+}
+
+Properties& Properties::set( const key_t& k, const Properties& p )
+{
+  ValueMap pmap;
+  for( PropertyMap::const_iterator vit = p.props_.begin(); vit != p.props_.end(); ++vit )
+  {
+    pmap[vit->first]=vit->second;
+  }
+  props_[k] = Value::makeMap(pmap);
+  return *this;
+}
+
+Properties& Properties::set( const Properties& p )
+{
+  for( PropertyMap::const_iterator vit = p.props_.begin(); vit != p.props_.end(); ++vit )
+  {
+    props_[vit->first]=vit->second;
+  }
+  return *this;
+}
+
+bool Properties::remove(const key_t & k)
+{
+    return props_.erase(k);
+}
+
+void Properties::hash(MD5& md5) const
+{
+    for( PropertyMap::const_iterator vit = props_.begin(); vit != props_.end(); ++vit ) {
+        md5.add((*vit).first);
+        /// @note below, we assume all Values translate to std::string, this needs more verification
+        md5.add((*vit).second.as<std::string>());
+    }
+}
+
+void Properties::json( JSON& s ) const
+{
+    s << props_;
+}
+
+void Properties::print( std::ostream& s ) const
+{
+    for( PropertyMap::const_iterator vit = props_.begin(); vit != props_.end(); ++vit )
+        s << "(" << (*vit).first << "," << (*vit).second << ")";
+}
+
+void Properties::encode( Stream& s ) const
+{
+    s << props_;
+}
+
+Properties::operator property_t() const
+{
+  ValueMap vmap = Value::makeMap();
+  for( PropertyMap::const_iterator vit = props_.begin(); vit != props_.end(); ++vit )
+  {
+    vmap[vit->first]=vit->second;
+  }
+  return vmap;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Properties::property_t getValue( const Properties& p, const Properties::key_t& key )
+{
+    return p.get(key);
+}
+
+void print( const Properties& p, std::ostream& s )
+{
+    s << p;
+}
+
+void encode( const Properties& p, Stream& s )
+{
+    s << p;
+}
+
+Params::Factory<Properties> propertiesFactory;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/Properties.h b/eckit/src/eckit/value/Properties.h
new file mode 100644
index 0000000..6ec3b08
--- /dev/null
+++ b/eckit/src/eckit/value/Properties.h
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Properties.h
+/// @author Tiago Quintino
+/// @date Jun 2014
+
+#ifndef eckit_Properties_h
+#define eckit_Properties_h
+
+#include <map>
+#include <string>
+
+#include "eckit/value/Value.h"
+#include "eckit/value/Params.h"
+
+namespace eckit {
+
+class MD5;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Properties {
+
+public: // types
+
+    typedef Value property_t;
+    typedef std::string key_t;
+
+public: // methods
+
+    Properties();
+    Properties(const property_t&);
+
+    Properties(Stream&);
+
+    virtual ~Properties() {}
+
+    /// @returns true is a property exists
+    bool has( const key_t& ) const;
+
+    /// @returns a property
+    property_t get( const key_t& k ) const;
+
+    /// Sets a property by inserting a new or overwrites an existing property
+    Properties& set( const key_t& k, const property_t& v );
+
+    /// Sets a property by inserting a new or overwrites an existing property
+    Properties& set( const key_t& k, const Properties& p );
+
+    /// merge other properties
+    Properties& set( const Properties& p );
+
+    /// Removes a property
+    bool remove( const key_t& k );
+
+    /// @returns a property
+    property_t operator[]( const key_t& k ) const { return get(k); }
+
+    /// @returns a bool, true if empty false otherwise
+    bool empty() const { return props_.empty(); }
+
+    static const char* className() { return "eckit::Properties"; }
+
+    operator property_t() const;
+
+    void hash(eckit::MD5&) const;
+
+protected:
+
+    void print(std::ostream& s) const;
+
+private: // types
+
+    typedef std::map< key_t, property_t > PropertyMap;
+
+private: // members
+
+    PropertyMap  props_; //< storage of values
+
+protected: // methods
+
+    void json(JSON& s) const;
+    void encode(Stream& s) const;
+
+    friend JSON& operator<<(JSON& s, const Properties& v) { v.json(s);  return s; }
+    friend std::ostream& operator<<(std::ostream& s, const Properties& v) { v.print(s);  return s; }
+    friend Stream&  operator<<(Stream&  s, const Properties& v) { v.encode(s); return s; }
+
+    friend property_t getValue( const Properties& p, const key_t& key );
+    friend void print( const Properties& p, std::ostream& s );
+    friend void encode( const Properties& p, Stream& s );
+
+    friend class Value;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/ScopeParams.cc b/eckit/src/eckit/value/ScopeParams.cc
new file mode 100644
index 0000000..4b21cbc
--- /dev/null
+++ b/eckit/src/eckit/value/ScopeParams.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/StringTools.h"
+
+#include "eckit/value/ScopeParams.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+ScopeParams::ScopeParams(const Params::key_t& scope_key, const Params & p ) :
+    scope_( scope_key + "." ),
+    p_(p)
+{
+}
+
+ScopeParams::ScopeParams( Stream & s ) : p_( Params::decode(s) )
+{
+    s >> scope_;
+}
+
+Params::value_t getValue( const ScopeParams& p, const Params::key_t& key )
+{
+    if( StringTools::startsWith(key, p.scope_) )
+    {
+        return getValue( p.p_, key.substr(p.scope_.length()) );
+    }
+    else
+    {
+        return Params::value_t();
+    }
+}
+
+void print( const ScopeParams& p, std::ostream &s )
+{
+    print(p.p_, s);
+}
+
+void encode( const ScopeParams& p, Stream &s )
+{
+    s << p.p_;
+    s << p.scope_;
+}
+
+Params::Factory<ScopeParams> scopeParamsFactory;
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/value/ScopeParams.h b/eckit/src/eckit/value/ScopeParams.h
new file mode 100644
index 0000000..13a5101
--- /dev/null
+++ b/eckit/src/eckit/value/ScopeParams.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+/// @author Florian Rathgeber
+/// @date March 2015
+
+#ifndef eckit_value_ScopeParams_H
+#define eckit_value_ScopeParams_H
+
+#include "eckit/value/Params.h"
+
+//----------------------------------------------------------------------------
+
+namespace eckit {
+
+//----------------------------------------------------------------------------
+
+/// Wraps the parameters within a given scope
+
+class ScopeParams {
+
+public: // methods
+
+    ScopeParams( const Params::key_t& scope_key, const Params& p );
+    ScopeParams( Stream& s );
+
+    static const char* className() { return "eckit::ScopeParams"; }
+
+private: // methods
+
+    friend Params::value_t getValue( const ScopeParams& p, const Params::key_t& key );
+    friend void print( const ScopeParams& p, std::ostream& s );
+    friend void encode( const ScopeParams& p, Stream& s );
+
+private: // members
+
+    Params::key_t scope_;
+    Params p_;
+};
+
+//----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/StringContent.cc b/eckit/src/eckit/value/StringContent.cc
new file mode 100644
index 0000000..4f7d520
--- /dev/null
+++ b/eckit/src/eckit/value/StringContent.cc
@@ -0,0 +1,140 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+#include "eckit/value/StringContent.h"
+#include "eckit/parser/JSON.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+ClassSpec StringContent::classSpec_ = {&Content::classSpec(),"StringContent",};
+Reanimator<StringContent> StringContent::reanimator_;
+
+
+StringContent::StringContent(const std::string& s):
+	value_(s)
+{
+}
+
+StringContent::StringContent(const char* s):
+	value_(s)
+{
+}
+
+StringContent::StringContent(Stream& s):
+	Content(s)
+{
+	s >> value_;
+}
+
+Content* StringContent::clone() const {
+    return new StringContent(value_);
+}
+
+
+void StringContent::encode(Stream& s) const
+{
+	Content::encode(s);
+	s << value_;
+}
+
+StringContent::~StringContent()
+{
+}
+
+void StringContent::print(std::ostream& s) const
+{
+	s << value_;
+}
+
+void StringContent::json(JSON& s) const
+{
+    s << value_;
+}
+
+int StringContent::compare(const Content& other) const
+{
+	return -other.compareString(*this);
+}
+
+int StringContent::compareString(const StringContent& other) const
+{
+	return ::strcmp(value_.c_str(),other.value_.c_str());
+}
+
+void StringContent::value(std::string& s) const
+{
+	s = value_;
+}
+
+void StringContent::value(bool& b) const
+{
+    if( value_ == "true" || value_ == "on" || value_ == "yes" || value_ == "1" )
+    {
+        b = true;
+    }
+    else
+    {
+            if( value_ == "false" || value_ == "off" || value_ == "no" || value_ == "0" )
+                b = false;
+            else
+                Content::value(b);
+    }
+}
+
+void StringContent::value(long long& l) const
+{
+	l = Translator<std::string,long long>()(value_);
+}
+
+void StringContent::value(double& d) const
+{
+	d = Translator<std::string,double>()(value_);
+}
+
+Content* StringContent::add(const Content& other) const
+{
+	return other.addString(*this);
+}
+
+Content* StringContent::addString(const StringContent& other) const
+{
+	return new StringContent(other.value_ + value_);
+}
+
+Content* StringContent::sub(const Content& other) const
+{
+    return other.subString(*this);
+}
+
+Content* StringContent::mul(const Content& other) const
+{
+    return other.mulString(*this);
+}
+
+Content* StringContent::div(const Content& other) const
+{
+    return other.divString(*this);
+}
+
+Content* StringContent::mod(const Content& other) const
+{
+    return other.modString(*this);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/StringContent.h b/eckit/src/eckit/value/StringContent.h
new file mode 100644
index 0000000..a493157
--- /dev/null
+++ b/eckit/src/eckit/value/StringContent.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StringContent.h
+// Manuel Fuentes - ECMWF Jun 97
+
+#ifndef eckit_StringContent_h
+#define eckit_StringContent_h
+
+#include "eckit/value/Content.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class StringContent : public Content {
+
+protected:
+
+    // -- Constructor
+
+    StringContent(const std::string&);
+    StringContent(const char*);
+    StringContent(Stream&);
+
+    // -- Destructor
+
+    virtual ~StringContent();
+
+    // -- Overridden methods
+
+    // -- From Content
+
+    virtual int compare(const Content& other) const;
+
+    virtual void value(bool& n)        const;
+	virtual void value(long long& n)   const;
+    virtual void value(double& n)      const;
+	virtual void value(std::string& n) const;
+    virtual void value(Date& n)        const { Content::value(n); }
+    virtual void value(Time& n)        const { Content::value(n); }
+    virtual void value(DateTime& n)    const { Content::value(n); }
+    virtual void value(ValueList& n)   const { Content::value(n); }
+    virtual void value(ValueMap& n)    const { Content::value(n); }
+
+    virtual int  compareBool(const BoolContent&)            const {return -1; }
+    virtual int  compareNumber(const NumberContent&)        const {return -1; }
+    virtual int  compareDouble(const DoubleContent&)        const {return -1; }
+    virtual int  compareString(const StringContent&)        const;
+    virtual int  compareNil(const NilContent&)              const {return 1; }
+    virtual int  compareList(const ListContent&)            const {return 1; }
+    virtual int  compareMap(const MapContent&)              const {return 1; }
+    virtual int  compareDate(const DateContent&)            const {return 1; }
+    virtual int  compareTime(const TimeContent&)            const {return 1; }
+    virtual int  compareDateTime(const DateTimeContent&)    const {return 1; }
+
+    virtual Content* add(const Content&)             const;
+    virtual Content* sub(const Content&) const;
+    virtual Content* mul(const Content&) const;
+    virtual Content* div(const Content&) const;
+    virtual Content* mod(const Content&) const;
+
+    virtual Content* addString(const StringContent&) const;
+
+    virtual void    print(std::ostream&) const;
+    virtual void   json(JSON&)     const;
+    virtual std::string  typeName() const      { return "String"; }
+    virtual bool    isString() const      { return true; }
+    virtual Content* clone() const;
+
+    // -- From Streamable
+
+    virtual void encode(Stream&) const;
+    virtual const ReanimatorBase& reanimator() const { return reanimator_; }
+
+    // -- Class methods
+
+    static  const ClassSpec&  classSpec()         { return classSpec_;}
+
+private:
+
+    // -- No copy allowed
+
+    StringContent(const StringContent&);
+    StringContent& operator=(const StringContent&);
+
+    // -- Members
+
+    std::string value_;
+
+    static  ClassSpec                  classSpec_;
+    static  Reanimator<StringContent>  reanimator_;
+
+    // -- Friends
+
+    friend class Reanimator<StringContent>;
+    friend class Value;
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/value/Value.cc b/eckit/src/eckit/value/Value.cc
new file mode 100644
index 0000000..ebf0dcc
--- /dev/null
+++ b/eckit/src/eckit/value/Value.cc
@@ -0,0 +1,375 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/value/DateContent.h"
+#include "eckit/value/ListContent.h"
+#include "eckit/value/NilContent.h"
+#include "eckit/value/NumberContent.h"
+#include "eckit/value/StringContent.h"
+#include "eckit/value/BoolContent.h"
+#include "eckit/value/DoubleContent.h"
+#include "eckit/value/MapContent.h"
+#include "eckit/value/Value.h"
+#include "eckit/io/Length.h"
+#include "eckit/filesystem/PathName.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+Value::Value():
+	content_(new NilContent())
+{
+	content_->attach();
+}
+
+Value::Value(int l):
+    content_(new NumberContent(l))
+{
+    content_->attach();
+}
+
+Value::Value(long long l):
+	content_(new NumberContent(l))
+{
+	content_->attach();
+}
+
+Value::Value(unsigned long long l):
+    content_(new NumberContent(l))
+{
+    content_->attach();
+}
+
+Value::Value(unsigned long l):
+    content_(new NumberContent(l))
+{
+    content_->attach();
+}
+
+
+Value::Value(unsigned int l):
+    content_(new NumberContent(l))
+{
+    content_->attach();
+}
+
+Value::Value(long l):
+    content_(new NumberContent(l))
+{
+    content_->attach();
+}
+
+
+Value::Value(bool l):
+    content_(new BoolContent(l))
+{
+    content_->attach();
+}
+
+Value::Value(double l):
+    content_(new DoubleContent(l))
+{
+    content_->attach();
+}
+
+Value::Value(const std::string& s):
+	content_(new StringContent(s))
+{
+	content_->attach();
+}
+
+Value::Value(const char* s):
+	content_(new StringContent(s))
+{
+	content_->attach();
+}
+
+Value::Value(const Length& l):
+	content_(new NumberContent(l))
+{
+	content_->attach();
+}
+
+Value::Value(const PathName& p):
+	content_(new StringContent(p.asString()))
+{
+	content_->attach();
+}
+
+Value::Value(const Date& d):
+	content_(new DateContent(d))
+{
+	content_->attach();
+}
+
+Value::Value(Stream& s):
+    content_(Reanimator<Content>::reanimate(s))
+{
+    ASSERT(content_);
+    content_->attach();
+}
+
+Value::~Value()
+{
+	content_->detach();
+}
+
+Value::Value(const Value& other):
+	content_(other.content_)
+{
+	content_->attach();
+}
+
+Value Value::clone() const {
+    return Value(content_->clone());
+}
+
+bool Value::shared() const {
+    return content_->count() > 1;
+}
+
+Value& Value::operator=(const Value& other)
+{
+	other.content_->attach();
+	content_->detach();
+	content_ = other.content_;
+
+	return *this;
+}
+
+Value Value::operator+(const Value& v) const
+{
+	return Value(content_->add(*(v.content_)));
+}
+
+Value& Value::operator+=(const Value& v)
+{
+	*this = *this + v;
+	return *this;
+}
+
+Value Value::operator-(const Value& v) const
+{
+	return Value(content_->sub(*(v.content_)));
+}
+
+Value& Value::operator-=(const Value& v)
+{
+	*this = *this - v;
+	return *this;
+}
+
+Value Value::operator*(const Value& v) const
+{
+	return Value(content_->mul(*(v.content_)));
+}
+
+Value& Value::operator*=(const Value& v)
+{
+	*this = *this * v;
+	return *this;
+}
+
+Value Value::operator/(const Value& v) const
+{
+	return Value(content_->div(*(v.content_)));
+}
+
+Value& Value::operator/=(const Value& v)
+{
+	*this = *this / v;
+	return *this;
+}
+
+Value Value::operator%(const Value& v) const
+{
+    return Value(content_->mod(*(v.content_)));
+}
+
+Value& Value::operator%=(const Value& v)
+{
+    *this = *this % v;
+    return *this;
+}
+
+Value Value::makeList()
+{
+	return Value(new ListContent());
+}
+
+Value Value::makeMap()
+{
+    return Value(new MapContent());
+}
+
+Value Value::makeMap(const ValueMap & m)
+{
+    return Value(new MapContent(m));
+}
+
+Value Value::makeList(const Value& v)
+{
+	return Value(new ListContent(v));
+}
+
+Value Value::makeList(const ValueList& v)
+{
+    return Value(new ListContent(v));
+}
+
+Value::Value(const ValueList& v):
+	content_(new ListContent(v))
+{
+	content_->attach();
+}
+
+Value::Value(const ValueMap& m):
+	content_(new MapContent(m))
+{
+	content_->attach();
+}
+
+Value::Value(Content* c):
+	content_(c)
+{
+	content_->attach();
+}
+
+Value Value::head() const
+{
+	ValueList v;
+	content_->value(v);
+
+//	std::cout << __FUNCTION__ << " " << *this << " " << v.size() << std::endl;
+
+	return v.size() > 0 ? v[0] : Value();
+}
+
+Value Value::tail() const
+{
+	ValueList v;
+	content_->value(v);
+
+//	std::cout << __FUNCTION__ << " " << *this << " " << v.size() << std::endl;
+
+	if (v.size() > 1)
+	{
+		v.erase(v.begin());
+		return v;
+	}
+	else
+		return Value();
+}
+
+Value::operator ValueList() const
+{
+	ValueList v;
+	content_->value(v);
+	return v;
+}
+
+Value::operator ValueMap() const
+{
+	ValueMap v;
+	content_->value(v);
+	return v;
+}
+
+//=========================================================
+const Value& Value::operator[](const Value& key) const
+{
+    return content_->element(key);
+}
+
+const Value& Value::operator[](const char* key) const
+{
+    return content_->element(Value(std::string(key)));
+}
+
+const Value& Value::operator[](const std::string& key) const
+{
+    return content_->element(Value(key));
+}
+
+//=========================================================
+
+Value& Value::operator[](const Value& key)
+{
+    return content_->element(key);
+}
+
+Value& Value::operator[](const char* key)
+{
+    return content_->element(Value(std::string(key)));
+}
+
+Value& Value::operator[](const std::string& key)
+{
+    return content_->element(Value(key));
+}
+
+Value& Value::operator[](int key)
+{
+    return content_->element(Value(key));
+}
+//=========================================================
+
+Value& Value::element(const std::string& key) {
+    return content_->element(Value(key));
+}
+
+const Value& Value::operator[](int key) const
+{
+    return content_->element(Value(key));
+}
+
+bool Value::contains(const Value& key) const
+{
+    return content_->contains(key);
+}
+
+bool Value::contains(const char* key) const
+{
+    return content_->contains(Value(std::string(key)));
+}
+
+bool Value::contains(const std::string& key) const
+{
+    return content_->contains(Value(key));
+}
+
+bool Value::contains(int key) const
+{
+    return content_->contains(Value(key));
+}
+
+Value Value::operator-() const
+{
+    return content_->negate();
+}
+
+
+Value Value::keys() const
+{
+    return content_->keys();
+}
+
+size_t Value::size() const
+{
+    return content_->size();
+}
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/value/Value.h b/eckit/src/eckit/value/Value.h
new file mode 100644
index 0000000..3b290cc
--- /dev/null
+++ b/eckit/src/eckit/value/Value.h
@@ -0,0 +1,353 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Value.h
+/// @author Manuel Fuentes
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Jun 97
+
+#ifndef eckit_Value_h
+#define eckit_Value_h
+
+#include "eckit/value/Content.h"
+#include "eckit/types/Date.h"
+#include "eckit/types/DateTime.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+
+namespace eckit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Known issues
+/// ============
+///
+/// NOTE: Objectives included not breaking eckit (including the testsuite). Therefore the failing unit tests are
+///       commented with ///.
+///
+/// 1. Value(const Time&) and Value(const DateTime&) are unimplemented, even though exposed in the header.
+///
+/// 2. Length(val) or (Length)val fail due to an ambiguity in the Length constructor, which doesn't know whether to
+///    cast via Length or long long. The explicit ::as<Length>() cast doesn't exist. Same with the Date() constructor.
+///
+///    The definition of operator= differs from the constructors, so that it is possible to do:
+///
+///    Length len = val;
+///
+/// 3. Value(bool) happily converts to a double, even explicitly with ::as<double>(). See BoolContent.cc/h
+///
+/// 4. Length() and Offset() are supposed to define a well-defined algebra (which avoids errors such as adding two
+///    offsets). Within a Value they are stored as raw long long types, and no checking is performed when casting. Any
+///    numeric value (including negative values) may be used silently as Length/Offsets.
+///
+/// 5. unsigned long long values are stored in signed long longs, resulting in integer overflows at (potentially) half
+///    the anticipated value.
+///
+/// 6. Cannot initialise (copy) one ValueList with another using the implicit casts. ValueList is a std::vector, and
+///    there are two ambiguous constructors with appropriate overloads provided by Value
+///
+///    std::vector<Value>( std::vector<Value>& rhs );
+///    std::vector<Value>( size_type n );
+///
+///    Either:
+///
+///      - Assign using operator=
+///      - Explicitly cast using ::as<ValueList>()
+///
+/// 7. Operators ==, >, <, >=, <= act on the memory address of the allocated internal Content object, and have nothing
+///    to do with the content of the value. The compare() method should be used instead.
+///
+///    Value(true) != Value(true)
+///
+/// 8. Value(bool)::compare() has inverted sense to the other compare methods
+///
+///    - It will return 0 if the Values are different
+///    - It will return -1 if the values are true
+///    - It will return 1 if the values are false
+///
+/// 9. On conversion falure Value(std::string> --> long long, zero is returned rather than an exception being thrown.
+///
+/// 10. On conversion failure Value(std::string) --> double, a BadParameter exception is thrown, rather than a
+///     BadConversion exception as for all other conversion failures (except the integer case noted above).
+///
+/// 11. Comparison operators for Value(Date), Value(Time), Value(DateTime) have the wrong sign in MapContent.h:66-68
+///
+/// 12. Equality testing for ValueMaps gives unintuitive results, as the maps contain copies of Values, and these will
+///     have differing memory addresses (see (7)).
+///
+/// 13. operator[] discards constness of ValueMaps:
+///
+///         ValueMap vm;
+///         const Value cv(vm);
+///         cv[10];
+///
+///         Log::info() << cv; // This gives {10 => (nil)}
+///
+/// 14. Indexing ValueMaps with Value(bool) doesn't work. I suspect this is due to point (8).
+///
+/// 15. Indexing Value(ValueList) with std::string, or Value(std::string) spuriously returns element zero, rather than
+///     hitting an assertion or throwing another exception. This is a result of Value(std::string) silently casting any
+///     string to zero if used as an integer.
+///
+///     ValueList vl;
+///     vl.push_back(123);
+///     Value val(vl);
+///
+///     Log::info() << val["hello"]; // This prints "123".
+///
+/// 16. Similarly, Value(ValueList)[Value(bool)] returns element zero or one depending on the content type.
+///
+/// 17. Value(ValueList)::contains() should probably return false, rather than an exception, if indexed with bools,
+///     floats, ...
+///
+/// 18. Although they are otherwise freely interconvertible, the arithmetic operators don't work between Number and
+///     Double types.
+///
+/// 19. Although the mod() function is implemented on NumberContent, the corresponding modNumber() routine is not, and
+///     so BadOperator is returned incorrectly.
+///
+///     More generally, the modulus functionality does not seem to be implemented for ANY of the Value() types.
+///
+/// 20. Subtraction operators for Value(Date) have a sign error. A later date minus a newer date should be positive.
+///
+/// 21. compare() function for Value(Date()) is buggy. Currently:
+///
+///     date1 == date2 --> 1
+///     date1 <  date2 --> -1
+///     date1 >  date2 --> 0
+///
+/// 22. The Value::head() and Value::tail() members make an entire copy of the contained ValueList before selecting
+///     and returning the head/tail element (which is copied). This will involve head allocations (and then
+///     deallocations during cleanup) for every contained element.
+///
+///       -- This seems crazy.
+///       -- Accessing the first element of a list shouldn't be O[N] in cpu and memory...
+///
+/// 23. The head() and tail() methods are clearly intended to be used on Value(ValueList) types, but if called on any
+///     other Value(X), it will create a single element list, and then return the first (and only) element.
+///
+/// 24. The tail() method returns the entire list, excluding the first element (or an empty Value(), not an empty list,
+///     if there are no remaining elements).
+///
+///     -- This is inconsistent with the naming, and the behaviour of head()
+///     -- It is closer to the car/cdr or first/rest functionality in lisp
+///     -- It should certainly at least return an empty list, not a nil value
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Length;
+class PathName;
+class JSON;
+
+class Value {
+public:
+
+// -- Contructors
+
+    Value();
+
+    Value(bool);
+    Value(int);
+    Value(long);
+    Value(long long);
+    Value(unsigned int);
+    Value(unsigned long);
+    Value(unsigned long long);
+    Value(double);
+
+    Value(const std::string&);
+    Value(const char*);
+
+    Value(const Length&);
+    Value(const Date&);
+    Value(const Time&);
+    Value(const DateTime&);
+    Value(const PathName&);
+
+    Value(Stream&);
+    Value(const ValueList&);
+    Value(const ValueMap&);
+
+// -- Copy
+
+	Value(const Value&);
+	Value& operator=(const Value&);
+
+// -- Destructor
+
+	~Value();
+
+// -- Operators
+
+    /// Explicitly cast value to the given type. For list of supported types, see the definitions of the
+    /// member function value() in eckit/value/Content.h
+    template< typename T > T as() const { T r; content_->value(r); return r; }
+
+    operator short() const              { long long l;  content_->value(l); return l; }
+    operator unsigned short() const     { long long l;  content_->value(l); return l; }
+
+    operator int() const                { long long l;  content_->value(l); return l; }
+    operator unsigned int() const       { long long l;  content_->value(l); return l; }
+
+    operator long() const               { long long l;  content_->value(l); return l; }
+    operator unsigned long() const      { long long l;  content_->value(l); return l; }
+
+    operator long long() const          { long long l;  content_->value(l); return l; }
+    operator unsigned long long() const { long long l;  content_->value(l); return l; }
+
+    operator double() const             { double d;     content_->value(d); return d; }
+    operator bool() const               { bool d;       content_->value(d); return d; }
+
+    operator std::string() const  { std::string s; content_->value(s); return s; }
+    operator PathName() const     { std::string s; content_->value(s); return s; }
+
+    operator Date() const      { Date d; content_->value(d); return d; }
+    operator Time() const      { Time t; content_->value(t); return t; }
+    operator DateTime() const  { DateTime d; content_->value(d); return d; }
+
+    operator Length() const    { long long l;  content_->value(l); return l; }
+    operator Offset() const    { long long l;  content_->value(l); return l; }
+
+    operator ValueList() const;
+    operator ValueMap() const;
+
+    bool operator<(const Value& v) const  { return *content_ < *(v.content_); }
+    bool operator==(const Value& v) const { return *content_ == *(v.content_); }
+
+    bool operator>(const Value& v) const  { return v < *this;     }
+    bool operator!=(const Value& v) const { return !(*this == v); }
+
+    bool operator>=(const Value& v) const  { return !(*this < v);     }
+    bool operator<=(const Value& v) const  { return !(v < *this);     }
+
+
+    Value operator+(const Value&) const;
+    Value& operator+=(const Value&);
+
+    Value operator-() const;
+
+    Value operator-(const Value&) const;
+    Value& operator-=(const Value&);
+
+    Value operator*(const Value&) const;
+    Value& operator*=(const Value&);
+
+    Value operator/(const Value&) const;
+    Value& operator/=(const Value&);
+
+    Value operator%(const Value&) const;
+    Value& operator%=(const Value&);
+
+    const Value& operator[](const char*) const;
+    const Value& operator[](const std::string&) const;
+    const Value& operator[](const Value&) const;
+    const Value& operator[](int) const;
+
+    Value& operator[](const char*);
+    Value& operator[](const std::string&);
+    Value& operator[](const Value&);
+    Value& operator[](int);
+
+    Value keys() const;
+    size_t size() const;
+
+public:
+    bool contains(const char*) const;
+    bool contains(const std::string&) const;
+    bool contains(const Value&) const;
+    bool contains(int) const;
+
+    Value& element(const std::string&);
+
+    // -- Methods
+
+    int      compare(const Value& v) const { return content_->compare(*(v.content_)); }
+
+    bool     isNil()      const { return content_->isNil(); }
+    bool     isNumber()   const { return content_->isNumber(); }
+    bool     isBool()     const { return content_->isBool(); }
+    bool     isDouble()   const { return content_->isDouble(); }
+    bool     isString()   const { return content_->isString(); }
+    bool     isList()     const { return content_->isList(); }
+    bool     isMap()      const { return content_->isMap(); }
+    bool     isDate()     const { return content_->isDate(); }
+    bool     isTime()     const { return content_->isTime(); }
+    bool     isDateTime() const { return content_->isDateTime(); }
+
+    Value	 tail() const;
+    Value	 head() const;
+
+    Value    clone() const;
+    bool     shared() const; // Ensure that value is not shared
+
+// -- Class Methods
+
+    static Value makeList();
+    static Value makeList(const Value&);
+    static Value makeList(const ValueList&);
+
+    static Value makeMap();
+    static Value makeMap(const ValueMap&);
+
+protected:
+
+	Value(Content*);
+
+private: // members
+
+	Content* content_;
+
+private: // methods
+
+    void json(JSON& s) const        { s << *content_; }
+    void print(std::ostream& s) const    { s << *content_; }
+    void encode(Stream& s) const    { s << *content_; }
+
+    friend JSON& operator<<(JSON& s, const Value& v) { v.json(s);  return s; }
+	friend std::ostream& operator<<(std::ostream& s, const Value& v) { v.print(s);  return s; }
+	friend Stream&  operator<<(Stream&  s, const Value& v) { v.encode(s); return s; }
+
+	friend class Content;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template < typename T >
+Value makeVectorValue( const std::vector<T>& v )
+{
+    ValueList r;
+    r.reserve(v.size());
+    for( size_t i = 0; i < v.size(); ++i )
+        r.push_back( Value(v[i]) );
+    return Value::makeList(r);
+}
+
+template < typename T >
+Value makeVectorValue( const std::list<T>& l )
+{
+    ValueList r;
+    r.reserve(l.size());
+    for(typename std::list<T>::const_iterator j = l.begin(); j != l.end(); ++j) {
+        r.push_back( Value( *j ) );
+    }
+    return Value::makeList(r);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template <> struct VectorPrintSelector<Value> { typedef VectorPrintSimple selector; };
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/AgentResource.cc b/eckit/src/eckit/web/AgentResource.cc
new file mode 100644
index 0000000..5929f5a
--- /dev/null
+++ b/eckit/src/eckit/web/AgentResource.cc
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/ResizableBuffer.h"
+#include "eckit/web/AgentResource.h"
+#include "eckit/web/JavaAgent.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class MemStream : public eckit::Stream {
+
+	Buffer in_;
+	ResizableBuffer out_;
+	long   pos_;
+	long   length_;
+
+	virtual long write(const void* buf,long len);
+	virtual long read(void* buf,long len);
+	virtual std::string name() const { return "MemStream"; }
+
+public:
+	MemStream(const char* p,long len);
+
+	long length()      const { return length_; }
+	const char* data() const { return out_; }
+};
+
+
+MemStream::MemStream(const char* p,long len):
+	in_(p,len),
+	out_(10240),
+        pos_(0),
+        length_(0)
+{
+}
+
+long MemStream::write(const void* buf,long len)
+{
+    if(out_.size() - length_ < static_cast<size_t>(len))
+		out_.resize(out_.size()*2);
+
+	::memcpy((char*)out_ + length_, buf , len);
+
+	length_ += len;
+	return len;
+}
+
+long MemStream::read(void* buf,long len)
+{
+    long size = std::min(long(len),long(in_.size() - pos_));
+
+	if(size <= 0)
+		return -1;
+
+	::memcpy(buf,(char*)in_ + pos_,size);
+	pos_ += size;
+
+	return size;
+}
+
+AgentResource::AgentResource():
+	HtmlResource("/agent")
+{
+}
+
+AgentResource::~AgentResource()
+{
+}
+
+void AgentResource::html(std::ostream&,Url& url)
+{
+	static std::ifstream in("/dev/null");	
+    static std::ofstream out("/dev/null");
+
+	MemStream s(url.headerIn().content(), url.headerIn().contentLength());
+
+	std::string token = url.headerIn().getHeader("mars-token");
+
+    std::cout << "Token is " << token << std::endl;
+	
+	JavaAgent::serve(s,in,out);
+	url.headerOut().content(s.data(),s.length());
+
+	token = "token";
+	url.headerOut().setHeader("mars-token",token);
+}
+
+static AgentResource agentResourceInstance;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/AgentResource.h b/eckit/src/eckit/web/AgentResource.h
new file mode 100644
index 0000000..70b5551
--- /dev/null
+++ b/eckit/src/eckit/web/AgentResource.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File AgentResource.h
+// Baudouin Raoult - ECMWF Sep 97
+
+#ifndef AgentResource_H
+#define AgentResource_H
+
+#include "eckit/web/HtmlResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class AgentResource : public HtmlResource {
+public:
+
+
+	AgentResource();
+
+	virtual ~AgentResource();
+
+protected: // members
+
+	std::string name_;
+
+
+protected: // overridden methods
+
+	virtual void html(std::ostream&,Url&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/CMakeLists.txt b/eckit/src/eckit/web/CMakeLists.txt
new file mode 100644
index 0000000..ae7253f
--- /dev/null
+++ b/eckit/src/eckit/web/CMakeLists.txt
@@ -0,0 +1,51 @@
+list( APPEND eckit_web_srcs
+AgentResource.cc
+AgentResource.h
+CgiResource.cc
+CgiResource.h
+Configure.cc
+FileResource.cc
+FileResource.h
+FtpRequest.h
+Html.cc
+Html.h
+HtmlObject.cc
+HtmlObject.h
+HtmlResource.cc
+HtmlResource.h
+HttpBuf.cc
+HttpBuf.h
+HttpHeader.cc
+HttpHeader.h
+HttpServer.cc
+HttpServer.h
+HttpService.cc
+HttpService.h
+HttpUser.h
+JSONResource.cc
+JSONResource.h
+JavaAgent.cc
+JavaAgent.h
+JavaResource.cc
+JavaResource.h
+JavaServer.cc
+JavaServer.h
+JavaService.cc
+JavaService.h
+JavaUser.cc
+JavaUser.h
+Url.cc
+Url.h )
+
+
+ecbuild_add_library( TARGET eckit_web
+					 INSTALL_HEADERS LISTED
+					 SOURCES
+						${eckit_web_srcs}
+					 PERSISTENT
+						HtmlObject.h
+					 HEADER_DESTINATION
+						${INSTALL_INCLUDE_DIR}/eckit/web
+					 LIBS
+						eckit )
+
diff --git a/eckit/src/eckit/web/CgiResource.cc b/eckit/src/eckit/web/CgiResource.cc
new file mode 100644
index 0000000..6c28363
--- /dev/null
+++ b/eckit/src/eckit/web/CgiResource.cc
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/StdPipe.h"
+#include "eckit/web/CgiResource.h"
+#include "eckit/web/HttpBuf.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+CgiResource::CgiResource():
+	HtmlResource("/cgi")
+{
+}
+
+CgiResource::~CgiResource()
+{
+}
+
+void CgiResource::html(std::ostream& s,Url& url)
+{
+	eckit::PathName path("~/http/" + url.name());
+    std::ostringstream cmd;
+
+	std::string mode = url["parameter"];
+
+	if(mode == "")
+	{
+		cmd << "env ";
+		url.cgiParam(cmd,' ');
+        cmd << " " << path;
+	}
+	else
+	{
+		cmd << path << ' ';
+        url.cgiParam(cmd,' ');
+	}
+
+    StdPipe pipe(cmd.str(),"r");
+	char line[1024];
+
+	s << HttpBuf::dontEncode;
+
+	while(fgets(line,sizeof(line),pipe))
+		s << line;
+
+	s << HttpBuf::doEncode;
+}
+
+static CgiResource cgiResourceInstance;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/CgiResource.h b/eckit/src/eckit/web/CgiResource.h
new file mode 100644
index 0000000..187f5c4
--- /dev/null
+++ b/eckit/src/eckit/web/CgiResource.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File CgiResource.h
+// Baudouin Raoult - ECMWF Sep 97
+
+#ifndef CgiResource_H
+#define CgiResource_H
+
+#include "eckit/web/HtmlResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class CgiResource : public HtmlResource {
+public:
+
+	CgiResource();
+
+	virtual ~CgiResource();
+
+protected: // members
+
+	std::string name_;
+
+protected: // overridden methods
+
+	virtual void html(std::ostream&,Url&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/Configure.cc b/eckit/src/eckit/web/Configure.cc
new file mode 100644
index 0000000..4c1b521
--- /dev/null
+++ b/eckit/src/eckit/web/Configure.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @note This code is here to avoid linking it in if you don't need it
+
+#include "eckit/config/Configurable.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+
+#include "eckit/web/Html.h"
+#include "eckit/web/HtmlResource.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+void Configurable::htmlAllResources(std::ostream& s,Url& url)
+{
+	callAll(&Configurable::htmlResources,s,url);
+}
+
+void Configurable::htmlResources(std::ostream& s,Url& url)
+{
+	for(Set::const_iterator i=resources_.begin();
+		i != resources_.end();++i)
+		(*i)->html(s,url);
+}
+
+void ResourceBase::html(std::ostream& s,Url& url)
+{
+
+	std::string n = name();
+	std::string u = url["name"];
+
+	init();
+
+	if( n == u)
+	{
+		std::string v = url[n];
+		Log::info() << "New value for " << n << ": " << v << std::endl;
+        ResourceMgr::instance().set(n,v);
+		Configurable::reconfigureAll();
+	}
+
+	dump(s);
+
+	s << Html::BeginForm();
+	s << Html::TextField(n,getValue(),n + ": ");
+	s << Html::SubmitButton();
+	s << Html::ResetButton();
+	s << Html::HiddenField("name",n);
+	s << Html::EndForm();
+	s << Html::Line();
+}
+
+
+//=================================================================
+
+class ConfigResource : public HtmlResource {
+	virtual bool restricted() { return true; }
+	virtual void html(std::ostream&,Url&);
+public:
+	ConfigResource() : HtmlResource("/config") {}
+};
+
+void ConfigResource::html(std::ostream& s,Url& url)
+{
+	Configurable::htmlAllResources(s,url);
+}
+
+static ConfigResource ostoreResource;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/FileResource.cc b/eckit/src/eckit/web/FileResource.cc
new file mode 100644
index 0000000..74d1edc
--- /dev/null
+++ b/eckit/src/eckit/web/FileResource.cc
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/StdFile.h"
+#include "eckit/web/FileResource.h"
+#include "eckit/web/HttpBuf.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+FileResource::FileResource():
+	HtmlResource("/html")
+{
+}
+
+FileResource::~FileResource()
+{
+}
+
+void FileResource::html(std::ostream& s,Url& url)
+{
+	eckit::PathName path("~/http/" + url.name());
+
+	StdFile file(path,"r");
+	char line[1024];
+
+	s << HttpBuf::dontEncode;
+
+	while(fgets(line,sizeof(line),file))
+		s << line;
+
+	s << HttpBuf::doEncode;
+}
+
+static FileResource fileResourceInstance;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/FileResource.h b/eckit/src/eckit/web/FileResource.h
new file mode 100644
index 0000000..ed15e0d
--- /dev/null
+++ b/eckit/src/eckit/web/FileResource.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FileResource.h
+// Baudouin Raoult - ECMWF Sep 97
+
+#ifndef FileResource_H
+#define FileResource_H
+
+#include "eckit/web/HtmlResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class FileResource : public HtmlResource {
+public:
+
+	FileResource();
+
+	virtual ~FileResource();
+
+protected:
+
+	std::string name_;
+
+	virtual void html(std::ostream&,Url&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/FtpRequest.h b/eckit/src/eckit/web/FtpRequest.h
new file mode 100755
index 0000000..48af607
--- /dev/null
+++ b/eckit/src/eckit/web/FtpRequest.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FtpRequest.h
+// Baudouin Raoult - ECMWF Feb 02
+
+#ifndef FtpRequest_H
+#define FtpRequest_H
+
+#include "eckit/memory/NonCopyable.h"
+
+//-----------------------------------------------------------------------------
+
+class Retrieve;
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Length;
+class PathName;
+
+class FtpRequest : private eckit::NonCopyable {
+public:
+
+// -- Contructors
+
+	FtpRequest() {}
+
+// -- Destructor
+
+	virtual ~FtpRequest() {}
+
+// -- Methods
+
+	virtual std::string next() = 0;
+
+	virtual bool here() = 0;
+	virtual void cdDown() = 0;
+	virtual void cdUp() = 0;
+
+	virtual void directory(const std::string&) = 0;
+	virtual void file(const std::string&,const eckit::Length&,Retrieve*) = 0;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/Html.cc b/eckit/src/eckit/web/Html.cc
new file mode 100644
index 0000000..284f1aa
--- /dev/null
+++ b/eckit/src/eckit/web/Html.cc
@@ -0,0 +1,399 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/config/Resource.h"
+#include "eckit/web/Html.h"
+#include "eckit/web/HtmlObject.h"
+#include "eckit/web/HtmlResource.h"
+#include "eckit/web/HttpBuf.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+std::ostream& operator<<(std::ostream& s,const Html::Tag& tag)
+{
+	s << HttpBuf::dontEncode;	
+	tag.print(s);
+	s << HttpBuf::doEncode;	
+	return s;
+}
+
+std::string Html::addHex(const std::string& s)
+{
+	std::string t;
+	int index  = 0;
+	int length = s.length();
+
+	while(index < length)
+	{
+		char c = s[index];
+		bool ok = false;
+
+		switch(c)
+		{
+			case '+':
+			case '&':
+			case '/':
+			case '=':
+			case '?':
+				ok = true;
+				break;
+
+			default:
+				ok = isalnum(c);
+				break;
+		}
+
+		if(ok)
+			t += c;
+		else {
+			t += '%';
+
+			unsigned int h = ((unsigned char)c) / 16;
+			unsigned int l = ((unsigned char)c) % 16;
+
+			if(h>=10) c = h - 10 + 'A'; else c = h + '0';
+			t += c;
+
+			if(l>=10) c = l - 10 + 'A'; else c = l + '0';
+			t += c;
+		}
+		index++;
+	}
+
+	return t;
+}
+
+std::string Html::removeHex(const std::string& s)
+{
+	std::string t;
+	int index  = 0;
+	int length = s.length();
+
+	while(index < length)
+	{
+		if(s[index] == '+')
+		{
+			t += ' ';	
+		} else if(s[index] == '%')
+		{
+			char h = s[index+1];
+			char l = s[index+2];
+			index += 2;
+
+			unsigned int a = (h>='A')?(h-'A'+10):(h-'0');
+			unsigned int b = (l>='A')?(l-'A'+10):(l-'0');
+
+			t += char(a * 16 + b);
+		}
+		else t += s[index];
+
+		index++;
+	}
+	return t;
+}
+
+//=======================================================
+
+Html::Include::Include(const std::string& name,HtmlObject* sub):
+	sub_(sub),
+	name_(name)
+{
+}
+
+Html::Include::Include(const std::string& name,HtmlObject& sub):
+	sub_(&sub),
+	name_(name)
+{
+}
+
+//=======================================================
+
+void Html::Include::print(std::ostream& s) const
+{
+	eckit::PathName path = eckit::Resource<PathName>("htmlPath","~/html");
+
+	path = path + '/' + name_;
+
+	std::ifstream in(path.localPath());
+
+	if(!in) 
+	{
+		s << path << ": " << Log::syserr << std::endl;
+		return ;
+	}
+
+
+	char   c;
+	std::string p;
+	bool word = false;
+
+
+	s << HttpBuf::dontEncode;
+
+	while(in.get(c))
+	{
+		if(c == '%')
+		{
+			if(word)
+			{
+				if(sub_)
+					sub_->substitute(s,p);
+				else
+					s << '%' << p << '%';
+				p = "";
+				word = false;
+			}
+			else word = true;
+			in.get(c);
+		}
+
+		if(word)
+			p += c;
+		else
+			s << c;
+	}
+
+	s << HttpBuf::doEncode;
+
+}
+
+//=======================================================
+
+Html::Image::Image(const std::string& name):
+	name_(name)
+{
+}
+
+void Html::Image::print(std::ostream& s) const
+{
+	s << "<IMG SRC=\"" << resource() + '/' + name_ << "\">";
+}
+
+std::string Html::Image::resource()
+{
+	return "/image";
+}
+
+
+//=======================================================
+
+Html::Link::Link(Url& url):
+	url_(addHex(url.str()))
+{
+}
+
+void Html::Link::print(std::ostream& s) const
+{
+	if(url_.length())
+		s << "<A HREF=\"" <<  url_ << "\">" ;
+	else
+		s << "</A>" ;
+}
+
+//=======================================================
+
+void Html::Substitute::substitute(std::ostream& s,const std::string& p)
+{
+    std::map<std::string,std::string,std::less<std::string> >::iterator i = map_.find(p);
+	if(i ==  map_.end())
+		s << '%' << p << '%';
+	else
+	 	s << HttpBuf::doEncode << (*i).second << HttpBuf::dontEncode;
+}
+
+Html::Substitute::Substitute()
+{
+}
+
+Html::Substitute::~Substitute()
+{
+}
+
+
+std::string& Html::Substitute::operator[](const std::string& p) 
+{
+	return map_[p];
+}
+
+
+void Html::Class::print(std::ostream& s) const
+{
+	std::string p;
+	long len = str_.length();
+	std::string base = "http://wwwec.ecmwf.int/dhs/classfinder?file=";
+
+	for(int i = 0; i < len; i++)
+	{
+		char c = str_[i];
+		if(isalnum(c) || c == '_')
+			p += c;
+		else if(p.length())
+		{
+			s << Link(base+p) << p << Link();
+			s << c;
+			p = "";
+		}
+		else s << c;
+	}
+	if(p.length()) s << Link(base+p) << p << Link();
+}
+
+void Html::BeginForm::print(std::ostream& s) const  
+{ 
+	s << "<FORM METHOD=\"POST\""; 
+
+	if(str_.length())
+		s << " ACTION=\"" << str_ << "\"";
+
+	s << ">";
+}
+
+void Html::EndForm::print(std::ostream& s) const
+{ 
+	s << "</FORM>"; 
+}
+
+void Html::TextField::print(std::ostream& s) const
+{
+	s << title_ << HttpBuf::dontEncode;	
+	s << "<INPUT NAME=\"" << name_ << "\" VALUE=\"" << value_ << "\">";
+	s << HttpBuf::doEncode;	
+}
+
+void Html::HiddenField::print(std::ostream& s) const
+{
+	s << "<INPUT TYPE=\"hidden\" NAME=\"" << name_ << "\" VALUE=\"" << value_ << "\">";
+}
+
+void Html::CheckBox::print(std::ostream& s) const
+{
+	s << "<INPUT TYPE=\"checkbox\" ";
+	if(on_) s << "checked ";
+	s << "NAME=\"" << name_ << "\" VALUE=\"" << value_ << "\">";
+}
+
+void Html::Button::print(std::ostream& s) const
+{
+	s << "<INPUT TYPE=\"" << type_ << "\" VALUE=\"" << title_ << "\">";
+}
+
+void Html::BeginTextArea::print(std::ostream& s) const
+{
+	s << HttpBuf::dontEncode;
+	s << "<TEXTAREA NAME=\"" << name_ 
+	   << "\" ROWS=" << row_ << " COLS=" << col_ << ">";
+}
+
+void Html::EndTextArea::print(std::ostream& s) const
+{
+    s << "</TEXTAREA>" << std::endl << HttpBuf::doEncode;
+}
+
+//-----------------------------------------------------------------------------
+
+class ImageProvider : public HtmlResource {
+public:
+    ImageProvider(): HtmlResource(Html::Image::resource()) { }
+    void html(std::ostream& , Url&);
+};
+
+static ImageProvider imageProvider;
+
+void ImageProvider::html(std::ostream& out, Url& url)
+{
+	eckit::PathName path = eckit::Resource<PathName>("imagePath","~/html/image");
+
+	for(int i = 1; i < url.size() ; i++)
+		path = path + "/" + url[i] ;
+
+	std::ifstream in(path.localPath());
+	if(!in)	
+	{
+		(url.headerOut()).status(404);  // Not Found
+		out << path << ": " << Log::syserr << std::endl;
+	}
+	else
+	{
+		(url.headerOut()).type("image/gif");
+
+		out << HttpBuf::dontEncode;
+		char c;
+		while(in.get(c))
+			out << c;
+		out << HttpBuf::doEncode;
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+class HtmlProvider : public HtmlResource {
+public:
+	HtmlProvider(): HtmlResource("/html") { }
+	virtual ~HtmlProvider() {}
+	void html(std::ostream& , Url&);
+};
+
+static HtmlProvider htmlProvider;
+
+void HtmlProvider::html(std::ostream& s, Url& url)
+{
+	std::string path; 
+
+	for(int i = 1; i < url.size() ; i++)
+		path += "/" + url[i] ;
+
+	Html::Substitute empty;
+	Html::Include include(path,empty); 
+	s << include;
+}
+
+
+//-----------------------------------------------------------------------------
+
+void Html::BeginTable::print(std::ostream& s) const
+{
+	s << "<TABLE";
+	if(border_)  s << " BORDER";
+	if(padding_) s << " CELLPADDING=" << padding_;
+	if(spacing_) s << " CELLSPACING=" << spacing_;
+	if(width_)   s << " WIDTH=" << '"' << width_ << '%' << '"' ;
+	s << ">";
+}
+
+void Html::TableTag::print(std::ostream& s) const
+{
+	s << '<' << tag_;
+
+	if(align_) {
+		
+		if( (align_&Center) ) s << " ALIGN=center";
+		if( (align_&Left  ) ) s << " ALIGN=left";
+		if( (align_&Right ) ) s << " ALIGN=right";
+		if( (align_&Top   ) ) s << " VALIGN=top";
+		if( (align_&Bottom) ) s << " VALIGN=bottom";
+	}
+
+	if(colspan_) s << " COLSPAN=" << colspan_;
+	if(rowspan_) s << " ROWSPAN=" << rowspan_;
+
+	s << '>';
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/Html.h b/eckit/src/eckit/web/Html.h
new file mode 100644
index 0000000..9b64dec
--- /dev/null
+++ b/eckit/src/eckit/web/Html.h
@@ -0,0 +1,310 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Html_H
+#define Html_H
+
+#include "eckit/eckit.h"
+
+#include "eckit/web/HtmlObject.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Url;
+
+class Html {
+public:
+
+	enum { None = 0, Left = 1, Right = 2, Center = 4, 
+		Top = 8, Bottom = 16 };
+
+    class Tag {
+        virtual void print(std::ostream& s) const = 0;
+        friend std::ostream& operator<<(std::ostream& s, const Tag&);
+    };
+
+    class RawTag : public Tag {
+        std::string tag_;
+        virtual void print(std::ostream& s) const { s << tag_; }
+    protected:
+        RawTag(const std::string& s) : tag_(s) {}
+    };
+
+    class Line : public RawTag {
+    public:
+        Line() : RawTag("<HR>") {}
+    };
+
+    // To be used with 'include'
+
+    class Substitute : public HtmlObject {
+        std::map<std::string,std::string,std::less<std::string> > map_;
+        virtual void substitute(std::ostream&,const std::string&);
+    public:
+        Substitute();
+        ~Substitute();
+        std::string& operator[](const std::string&);
+    };
+
+    class Include {
+    public:
+
+        Include(const std::string&,HtmlObject* = 0);
+        Include(const std::string&,HtmlObject&);
+
+    private:
+
+        Include(const Include&);
+        Include& operator=(const Include&);
+
+        HtmlObject*          sub_;
+        std::string               name_;
+
+        void print(std::ostream& s) const;
+
+        friend std::ostream& operator<<(std::ostream& s, const Include& n)
+        { n.print(s); return s; }
+    };
+
+    class Image : public Tag {
+    public:
+        Image(const std::string&);
+
+        static std::string resource();
+
+    private:
+        Image(const Image&);
+        Image& operator=(const Image&);
+
+        std::string  name_;
+        virtual void print(std::ostream& s) const;
+    };
+
+    class Link : public Tag {
+    public:
+        Link(Url&);
+        Link() {}
+        Link(const std::string& url) : url_(addHex(url)) {}
+    private:
+        virtual void print(std::ostream& s) const;
+        std::string url_;
+    };
+
+    class Class {
+    public:
+        Class(const std::string& str) : str_(str) {}
+    private:
+        std::string str_;
+        void print(std::ostream& s) const;
+        friend std::ostream& operator<<(std::ostream& s, const Class& n)
+        { n.print(s); return s; }
+    };
+
+    //-----------------------------------------------------------------------------
+
+    class BeginFormatted : public RawTag {
+    public:
+        BeginFormatted() : RawTag("<PRE>") {}
+    };
+
+    class EndFormatted : public RawTag {
+    public:
+        EndFormatted() : RawTag("</PRE>") {}
+    };
+    class BeginFixed : public RawTag {
+    public:
+        BeginFixed() : RawTag("<TT>") {}
+    };
+
+    class EndFixed : public RawTag {
+    public:
+        EndFixed() : RawTag("</TT>") {}
+    };
+
+    class BeginBold : public RawTag {
+    public:
+        BeginBold() : RawTag("<B>") {}
+    };
+
+    class EndBold : public RawTag {
+    public:
+        EndBold() : RawTag("</B>") {}
+    };
+
+    //-----------------------------------------------------------------------------
+
+    class BeginForm : public Tag {
+    public:
+        BeginForm(const std::string& str = "") : str_(str) {}
+    private:
+        std::string str_;
+        virtual void print(std::ostream& s) const;
+    };
+
+    class EndForm : public Tag {
+        virtual void print(std::ostream& s) const;
+    };
+
+    class BeginTextArea {
+    public:
+        BeginTextArea(const std::string& name,int row,int col):
+            name_(name), row_(row),col_(col) {}
+    private:
+        std::string name_;
+        int row_;
+        int col_;
+        void print(std::ostream& s) const;
+        friend std::ostream& operator<<(std::ostream& s, const BeginTextArea& n)
+        { n.print(s); return s; }
+    };
+
+    class EndTextArea {
+        void print(std::ostream& s) const;
+        friend std::ostream& operator<<(std::ostream& s, const EndTextArea& n)
+        { n.print(s); return s; }
+    };
+
+    class TextField {
+        std::string name_;
+        std::string value_;
+        std::string title_;
+        void print(std::ostream& s) const;
+    public:
+        TextField(const std::string& name,const std::string& value = "",const std::string& title = ""):
+            name_(name),value_(value),title_(title) {}
+        friend std::ostream& operator<<(std::ostream& s, const TextField& n)
+        { n.print(s); return s; }
+    };
+
+    class HiddenField : public Tag {
+        std::string name_;
+        std::string value_;
+        virtual void print(std::ostream& s) const;
+    public:
+        HiddenField(const std::string& name,const std::string& value):
+            name_(name),value_(value) {}
+    };
+
+
+    class Button : public Tag {
+        std::string type_;
+        std::string title_;
+        virtual void print(std::ostream& s) const;
+    public:
+        Button(const std::string& type,const std::string& title):
+            type_(type),title_(title) {}
+    };
+
+    class CheckBox : public Tag {
+        std::string name_;
+        std::string value_;
+        bool   on_;
+        virtual void print(std::ostream& s) const;
+    public:
+        CheckBox(const std::string& name,const std::string& value,bool on):
+            name_(name), value_(value), on_(on) {}
+    };
+
+    class ResetButton : public Button {
+    public:
+        ResetButton(const std::string& title = "Reset"):
+            Button("reset",title) {}
+    };
+
+    class SubmitButton : public Button {
+    public:
+        SubmitButton(const std::string& title = "Submit"):
+            Button("submit",title) {}
+    };
+
+    class Hidden : public Tag {
+    };
+
+    static std::string addHex(const std::string&);
+    static std::string removeHex(const std::string&);
+
+    //-----------------------------------------------------------------------------
+
+    // Table stuff
+
+    class BeginTable : public Tag {
+        bool border_;
+        int  padding_;
+        int  spacing_;
+        int width_;
+        virtual void print(std::ostream& s) const;
+    public:
+        BeginTable(bool border = true,int width = 0,
+                   int padding = 0, int spacing = 0):
+            border_(border), padding_(padding), spacing_(spacing), width_(width) {}
+    };
+
+    class EndTable : public RawTag {
+    public:
+        EndTable() : RawTag("</TABLE>") {}
+    };
+
+
+    class TableTag : public Tag {
+        std::string tag_;
+        int align_;
+        int colspan_;
+        int rowspan_;
+        virtual void print(std::ostream& s) const;
+    protected:
+        TableTag(const std::string& tag,int align,int colspan, int rowspan):
+            tag_(tag),align_(align),colspan_(colspan),rowspan_(rowspan) {}
+    };
+
+    class BeginHeader : public TableTag {
+    public:
+        BeginHeader(int align = None,int colspan = 0, int rowspan = 0):
+            TableTag("TH",align,colspan,rowspan) {}
+    };
+
+    class EndHeader : public RawTag {
+    public:
+        EndHeader() : RawTag("</TH>") {}
+    };
+
+    class BeginRow : public TableTag {
+    public:
+        BeginRow(int align = None,int colspan = 0, int rowspan = 0):
+            TableTag("TR",align,colspan,rowspan) {}
+    };
+
+    class EndRow : public RawTag {
+    public:
+        EndRow() : RawTag("</TR>") {}
+    };
+
+    class BeginData : public TableTag {
+    public:
+        BeginData(int align = None,int colspan = 0, int rowspan = 0):
+            TableTag("TD",align,colspan,rowspan) {}
+    };
+
+    class EndData : public RawTag {
+    public:
+        EndData() : RawTag("</TD>") {}
+    };
+
+}; // class Html
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/HtmlObject.cc b/eckit/src/eckit/web/HtmlObject.cc
new file mode 100644
index 0000000..10d59df
--- /dev/null
+++ b/eckit/src/eckit/web/HtmlObject.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/web/HtmlObject.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+HtmlObject::HtmlObject()
+{
+}
+
+HtmlObject::~HtmlObject()
+{
+}
+
+void HtmlObject::html(std::ostream& s,Url& url)
+{
+	print(s);
+}
+
+void HtmlObject::substitute(std::ostream& s,const std::string& p)
+{
+	s << '%' << p << '%';
+}
+
+void HtmlObject::print(std::ostream& s) const
+{
+	s << "No print method defined for this object";
+}
+
+void HtmlObject::java(JavaAgent&)
+{
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/HtmlObject.h b/eckit/src/eckit/web/HtmlObject.h
new file mode 100644
index 0000000..ffda24b
--- /dev/null
+++ b/eckit/src/eckit/web/HtmlObject.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HtmlObject.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef HtmlObject_H
+#define HtmlObject_H
+
+#include "eckit/eckit.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Url;
+class JavaAgent;
+class Bless;
+
+class HtmlObject {
+public:
+
+	HtmlObject();
+
+#include "eckit/web/HtmlObject.b"
+
+	virtual ~HtmlObject();
+
+	virtual void html(std::ostream&,Url&);
+	virtual void java(JavaAgent&);
+	virtual void substitute(std::ostream&,const std::string&);
+
+protected:
+	
+	 virtual void print(std::ostream&) const; 
+
+private:
+
+	friend std::ostream& operator<<(std::ostream& s,const HtmlObject& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/HtmlResource.cc b/eckit/src/eckit/web/HtmlResource.cc
new file mode 100644
index 0000000..ea8b09f
--- /dev/null
+++ b/eckit/src/eckit/web/HtmlResource.cc
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/config/Resource.h"
+#include "eckit/web/Html.h"
+#include "eckit/web/HtmlResource.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+// remember to add a mutex
+
+HtmlResourceMap HtmlResource::resources_;
+
+HtmlResource::HtmlResource(const std::string& s)
+{
+	resources_.init();
+	(*resources_)[s] = this;
+}
+
+HtmlResource::~HtmlResource()
+{
+	// Should do something here...
+}
+
+void HtmlResource::dispatch(eckit::Stream& s,std::istream& in,std::ostream& out,Url& url)
+{
+	std::string str;
+
+	for(int i = 0; i < url.size() ; i++)
+	{
+		str += "/" + url[i];
+
+
+		HtmlResourceMap::iterator j = resources_->find(str);
+		if(j != resources_->end())
+		{
+			HtmlResource *r = (*j).second;
+
+			if(r->restricted() && !url.authenticated())
+			{
+				url.authenticate();
+				return;
+			}
+
+			r->html(out,url);
+			return;
+		}		
+	}
+
+	url.status(Url::notFound);
+	out << "Url not found: " << url << std::endl;
+
+
+	std::string home = eckit::Resource<std::string>("homePage","http://hades.ecmwf.int/mars/");
+
+	out << "Please, try the " << Html::Link(home) << "MARS home page" << Html::Link() << 
+		'.' << std::endl;
+
+}
+
+void HtmlResource::index(std::ostream& s,Url& url)
+{
+	for(HtmlResourceMap::iterator j = resources_->begin(); j != resources_->end(); ++j)
+	{
+		s   << Html::Link((*j).first) << (*j).first << Html::Link() << std::endl;		
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
diff --git a/eckit/src/eckit/web/HtmlResource.h b/eckit/src/eckit/web/HtmlResource.h
new file mode 100644
index 0000000..be55883
--- /dev/null
+++ b/eckit/src/eckit/web/HtmlResource.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HtmlResource.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef HtmlResource_H
+#define HtmlResource_H
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/web/HtmlObject.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Stream;
+class HtmlResource;
+
+class HtmlResourceMap {
+
+private:
+	typedef std::map<std::string,HtmlResource*,std::less<std::string> > Map;
+	Map * ptr_;
+
+public:
+	void init() {
+		if (!ptr_) ptr_ = new Map;
+	}
+
+	~HtmlResourceMap() {
+		if (ptr_) {
+			delete ptr_;
+			ptr_ = 0;
+		}
+	}
+
+	Map * operator->() const {
+		return ptr_;
+	}
+
+	Map & operator*() const {
+		return *ptr_;
+	}
+
+	typedef Map::iterator iterator;
+};
+
+class HtmlResource : public HtmlObject,
+                     public eckit::NonCopyable {
+public:
+
+// -- Contructors
+
+	HtmlResource(const std::string&);
+
+// -- Destructor
+
+	virtual ~HtmlResource();
+
+
+// -- Methods
+
+	virtual bool restricted() { return false; }
+
+
+// -- Class methods
+
+    static void dispatch(eckit::Stream&,std::istream&,std::ostream&,Url&);
+	static void index(std::ostream&,Url&);
+
+	static HtmlResourceMap resources_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/HttpBuf.cc b/eckit/src/eckit/web/HttpBuf.cc
new file mode 100644
index 0000000..68b36f6
--- /dev/null
+++ b/eckit/src/eckit/web/HttpBuf.cc
@@ -0,0 +1,168 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/web/HttpBuf.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+static int xindex = std::ios::xalloc();
+
+//-----------------------------------------------------------------------------
+
+typedef std::vector<char> VC;
+
+class back_encoder_iterator : public std::iterator<std::output_iterator_tag,char>
+{
+	VC& container;
+	void push(const char* p) { while(*p) container.push_back(*p++); }
+public:
+	back_encoder_iterator(VC& v) : container(v) {}
+	back_encoder_iterator& operator=(char);
+	back_encoder_iterator& operator*() { return *this; }
+	back_encoder_iterator& operator++() { return *this; }
+	back_encoder_iterator& operator++(int) { return *this; }
+
+};
+
+
+back_encoder_iterator& 
+back_encoder_iterator::operator=(char c)
+{
+	switch(c)
+	{
+		case '<' : push("<") ; break;
+		case '>' : push(">") ; break;
+		case '&' : push("&"); break;
+		case '\n': push("<BR>\n"); break;
+		default  : container.push_back(c); break;
+	}
+
+	return *this;
+}
+
+inline back_encoder_iterator back_encoder(VC& x) {
+	return back_encoder_iterator(x);
+}	
+
+//-----------------------------------------------------------------------------
+
+HttpBuf::HttpBuf(HttpStream& owner):
+	owner_(owner)
+{ 
+	setp(out_, out_ + sizeof(out_)); 
+}
+
+HttpBuf::~HttpBuf()
+{
+	sync();
+}
+
+int HttpBuf::sync()
+{
+
+	if(owner_.iword(xindex))
+        std::copy(pbase(),pptr(),back_encoder(buffer_));
+	else 
+        std::copy(pbase(),pptr(), std::back_inserter(buffer_));
+
+	setp(pbase(), epptr());
+	return 0;
+}
+
+int HttpBuf::overflow(int c) 
+{
+	sync();
+	if(c == EOF)
+		return 0;
+
+	sputc(c);
+	return 0;
+}
+
+void HttpBuf::write(std::ostream& out, Url& url)
+{
+	HttpHeader& header = url.headerOut();
+//	header.length(buffer_.size() + 6 + 7);
+	header.length(buffer_.size());
+
+	Log::debug() << "Header: " << std::endl;
+
+	// Send header 
+
+	out << header;
+	Log::debug() << header;
+
+	// Send data
+
+//	out << "<HTML>";
+	std::ostream_iterator<char> oi(out);
+    std::copy(buffer_.begin(),buffer_.end(),oi);
+//	out << "</HTML>";
+
+#if 0
+	Log::debug() << "Data: " << std::endl;
+
+	for(std::vector<char>::iterator i = buffer_.begin(); i != buffer_.end(); ++i)
+		if(isprint(*i) || isspace(*i)) 
+			Log::debug() << *i;
+		else
+			break;
+
+	Log::debug() << std::endl;
+#endif
+}
+
+std::ostream& HttpBuf::dontEncode(std::ostream& s)
+{
+	ASSERT(s.iword(xindex) == 1);
+    //s.rdbuf()->sync(); // << std::flush;
+    s << std::flush;
+	s.iword(xindex) = 0;
+	return s;
+}
+
+std::ostream& HttpBuf::doEncode(std::ostream& s)
+{
+	ASSERT(s.iword(xindex) == 0);
+    //s.rdbuf()->sync(); // << std::flush;
+    s << std::flush;
+	s.iword(xindex) = 1;
+	return s;
+}
+
+HttpStream::HttpStream():
+	std::ostream(new HttpBuf(*this))
+{
+	buf_ = (HttpBuf*)rdbuf();
+	iword(xindex) = 1; // encode
+}
+
+HttpStream::~HttpStream()
+{
+	delete buf_;
+}
+
+void HttpStream::write(std::ostream& s,Url& url)
+{
+	flush();
+	buf_->write(s,url);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/HttpBuf.h b/eckit/src/eckit/web/HttpBuf.h
new file mode 100644
index 0000000..62ae7fb
--- /dev/null
+++ b/eckit/src/eckit/web/HttpBuf.h
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HttpBuf.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef HttpBuf_H
+#define HttpBuf_H
+
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class HttpStream;
+
+class HttpBuf : public std::streambuf {
+
+
+	char    out_[4096];
+	virtual int overflow(int c);
+	virtual int sync();
+
+	HttpStream& owner_;
+
+public:
+
+	HttpBuf(HttpStream&);
+	~HttpBuf();
+
+	void write(std::ostream&, Url&);
+
+	static std::ostream& dontEncode(std::ostream&);
+	static std::ostream& doEncode(std::ostream&);
+
+private:
+	
+	std::vector<char>  buffer_;
+};
+
+//-----------------------------------------------------------------------------
+
+class HttpStream : public std::ostream {
+	HttpBuf* buf_;
+public:
+	HttpStream();
+	~HttpStream();
+	void write(std::ostream&,Url&);
+};
+
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/HttpHeader.cc b/eckit/src/eckit/web/HttpHeader.cc
new file mode 100644
index 0000000..31bc806
--- /dev/null
+++ b/eckit/src/eckit/web/HttpHeader.cc
@@ -0,0 +1,232 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/log/Log.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/web/HttpHeader.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+// Check the <A HREF=http://www.ics.uci.edu/pub/ietf/http/rfc1945.html">HTTP/1.0</A> syntax
+// Check the <A HREF=http://src.doc.ic.ac.uk/computing/internet/rfc/rfc2068.txt">HTTP/1.1</A> syntax
+
+const std::string WWW_Authenticate = "WWW-Authenticate";
+const std::string Authorization    = "Authorization";
+const std::string Content_Type     = "Content-Type";
+const std::string Content_Length   = "Content-Length";
+const std::string Location         = "Location";
+const std::string DefaultType      = "application/x-www-form-urlencoded";
+
+bool HttpHeader::compare::operator()(const std::string& a,const std::string& b) const
+{
+	return strcasecmp(a.c_str(),b.c_str()) < 0;
+}
+
+
+HttpHeader::HttpHeader():
+	version_("HTTP/1.0"),
+	statusCode_(200),
+	contentLength_(0),
+	content_(0,false)
+{
+	header_[Content_Type] = " text/html";
+}
+
+HttpHeader& HttpHeader::operator=(std::map<std::string,std::string,std::less<std::string> >& parsed)
+{
+    for(std::map<std::string,std::string,std::less<std::string> >::const_iterator i = parsed.begin();
+		i != parsed.end(); ++i)
+			header_[(*i).first] = (*i).second;
+
+	Map::const_iterator j = header_.find(Content_Length);
+
+	if(j != header_.end())
+		contentLength_ = atol(((*j).second).c_str());
+	else
+		contentLength_ = 0;
+
+	return *this;
+}
+
+HttpHeader::~HttpHeader()
+{
+}
+
+void HttpHeader::print(std::ostream& s) const
+{
+
+	// Status line + CRLF
+	s << version_ << ' ' << statusCode_ << " Document follows" << '\r' << '\n';
+
+	// General-Header : Date, Pragma
+
+	s << "Cache-Control: no-cache" << std::endl;
+
+	// Response-Header: Location, Server, WWW-Authenticate
+	s << "MIME-Version: 1.0" << std::endl;
+
+	Map::const_iterator i = header_.find(Location);
+	if( i != header_.end() )
+		s << (*i).first <<  ": " << (*i).second << std::endl;
+
+	i = header_.find(WWW_Authenticate);
+	if( i != header_.end() )
+	{
+		s << "This is the debug output... " << std::endl;
+		s << (*i).first <<  ": " << (*i).second << std::endl;
+	}
+
+	// Entity-Header  : Allow, Content-Encoding,
+	// Content-Length, Content-Type, Expires, Last-Modified
+
+	s << Content_Length << ": " << contentLength_ + content_.size() << std::endl;
+
+	i = header_.find(Content_Type);
+
+	if( i != header_.end() )
+		s << (*i).first <<  ": " << (*i).second;
+
+	// CRLF
+	s << '\r' << '\n' << std::endl;
+
+
+	long len = content_.size();
+	const char *p  = content_;
+	while(len-->0)
+		s.put(*p++);
+}
+
+void HttpHeader::forward(const std::string& s)
+{
+	header_[Location] = s;
+}
+
+void HttpHeader::length(const long l)
+{
+	contentLength_ = l;
+}
+
+long HttpHeader::contentLength() const
+{
+	return contentLength_;
+}
+
+void HttpHeader::type(const std::string& s)
+{
+	header_[Content_Type] = s;
+}
+
+const std::string& HttpHeader::type() const
+{
+    Map::const_iterator i = header_.find(Content_Type);
+
+    if( i != header_.end() )
+        return (*i).second;
+
+    return DefaultType;
+}
+
+void HttpHeader::status(const long code)
+{
+	statusCode_ = code;
+}
+
+void HttpHeader::authenticate(const std::string& login)
+{
+	header_[WWW_Authenticate] = ("Basic realm=\"" + login + "\"");
+	status(401);
+}
+
+void HttpHeader::dontCache()
+{
+}
+
+bool HttpHeader::authenticated() const
+{
+	Map::const_iterator i = header_.find(Authorization);
+
+	if(i != header_.end())
+	{
+		const char *s = (*i).second.c_str();
+
+		while(*s != ' ' && *s != '\t') s++;
+		while(*s == ' ' || *s == '\t') s++;
+
+		unsigned char b64[256];
+		for(int j = 0; j < 256; j++) b64[j] = 64;
+
+		for(unsigned char c = 'A'; c <= 'Z' ; c++) b64[c] = c - 'A';
+		for(unsigned char c = 'a'; c <= 'z' ; c++) b64[c] = c - 'a' + 26;
+		for(unsigned char c = '0'; c <= '9' ; c++) b64[c] = c - '0' + 52;
+
+		b64['+']=62;
+		b64['/']=63;
+
+		const unsigned char *p = (const unsigned char*)s;
+
+		std::string q;
+
+		int n = 2;
+
+		while(b64[*p] < 64 && b64[*(p+1)] < 64)
+		{
+			q += char((b64[p[0]] << n) | (b64[p[1]] >> (6-n)));
+			n += 2;
+			if(n == 8) {
+				p++;
+				n = 2;
+			}
+			p++;
+		}
+
+		std::cout << q << std::endl;
+
+
+		Tokenizer parse(":");
+		std::vector<std::string> v;
+		parse(q,v);
+		if(v.size()==2 && v[0] == "mars" && v[1]=="clave")
+		{
+            Log::info() << "client authenticated " << q << " -> " << (*i).second << std::endl;
+			return true;
+		}
+
+        Log::info() << "client denied " << q << " -> " << (*i).second << std::endl;
+	}
+
+	return false;
+}
+
+void HttpHeader::content(const char* p,long len)
+{
+	content_.resize(len);
+	::memcpy((char*)content_,p,len);
+}
+
+void HttpHeader::setHeader(const std::string& k,const std::string& v)
+{
+	header_[k] = v;
+}
+
+const std::string& HttpHeader::getHeader(const std::string& k) const
+{
+	return ((HttpHeader*)this)->header_[k];
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/HttpHeader.h b/eckit/src/eckit/web/HttpHeader.h
new file mode 100644
index 0000000..463b78a
--- /dev/null
+++ b/eckit/src/eckit/web/HttpHeader.h
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HttpHeader.h
+// Manuel Fuentes - ECMWF Oct 96
+
+#ifndef HttpHeader_H
+#define HttpHeader_H
+
+#include "eckit/eckit.h"
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/io/ResizableBuffer.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class HttpHeader : private eckit::NonCopyable {
+
+public: // methods
+
+	HttpHeader();
+
+    ~HttpHeader();
+
+    HttpHeader& operator=(std::map<std::string,std::string,std::less<std::string> >&);
+
+	void length(const long);
+	long contentLength() const;
+	void type(const std::string& );
+	void status(const long);
+	void authenticate(const std::string& );
+	bool authenticated() const;
+	void forward(const std::string& );
+	void dontCache();
+
+    const std::string& type() const;
+
+	const std::string& getHeader(const std::string&) const;
+	void setHeader(const std::string&,const std::string&);
+
+	void  content(const char*,long);
+    const char* content() const { return content_; }
+
+protected: // methods
+	
+    void print(std::ostream&) const;
+
+private: // members
+
+	std::string version_;
+	long   statusCode_;
+	long   contentLength_;
+
+	struct compare {
+		bool operator()(const std::string&,const std::string&) const;
+	};
+
+	typedef std::map<std::string,std::string,HttpHeader::compare> Map;
+
+	Map header_;
+    eckit::ResizableBuffer content_;
+
+private: // methods
+
+	friend std::ostream& operator<<(std::ostream& s,const HttpHeader& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/HttpServer.cc b/eckit/src/eckit/web/HttpServer.cc
new file mode 100644
index 0000000..ee359ac
--- /dev/null
+++ b/eckit/src/eckit/web/HttpServer.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/web/HttpServer.h"
+#include "eckit/web/HttpService.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+HttpServer::HttpServer(int port):
+    HtmlResource("/"),
+    http_(new HttpService(port))
+{
+	http_.start();
+}
+
+HttpServer::~HttpServer()
+{
+	http_.stop();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/HttpServer.h b/eckit/src/eckit/web/HttpServer.h
new file mode 100644
index 0000000..dc6d168
--- /dev/null
+++ b/eckit/src/eckit/web/HttpServer.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HttpServer.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef HttpServer_H
+#define HttpServer_H
+
+#include "eckit/thread/ThreadControler.h"
+#include "eckit/web/HtmlResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class Url;
+
+class HttpServer : public HtmlResource {
+public:
+
+// -- Contructors
+
+	HttpServer(int port);
+
+// -- Destructor
+
+	~HttpServer();
+
+private:
+
+// No copy allowed
+
+	HttpServer(const HttpServer&);
+	HttpServer& operator=(const HttpServer&);
+
+// -- Members
+
+	eckit::ThreadControler http_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/HttpService.cc b/eckit/src/eckit/web/HttpService.cc
new file mode 100644
index 0000000..5bc13ec
--- /dev/null
+++ b/eckit/src/eckit/web/HttpService.cc
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/web/HtmlResource.h"
+#include "eckit/web/HttpBuf.h"
+#include "eckit/web/HttpService.h"
+#include "eckit/web/HttpUser.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+HttpService::HttpService(int port):
+	NetService(port)
+{
+}
+
+HttpService::~HttpService()
+{
+}
+
+
+NetUser* HttpService::newUser(TCPSocket& protocol)
+{
+	return new HttpUser(protocol);
+}
+
+//-----------------------------------------------------------------------------
+
+HttpUser::HttpUser(TCPSocket& protocol):
+	NetUser(protocol)
+{
+}
+
+HttpUser::~HttpUser()
+{
+}
+
+void HttpUser::serve(eckit::Stream& s, std::istream& in, std::ostream& out)
+{
+
+	HttpStream http;
+
+	Url url(in);
+
+	try {
+		HtmlResource::dispatch(s,in,http,url);
+	}
+	catch(std::exception& e)
+	{
+		Log::error() << "** " << e.what() << " Caught in " 
+			<< Here() << std::endl;
+		Log::error() << "** Exception is ignored" << std::endl;
+		http << "Exception caught: " << e.what() << std::endl;
+	}
+	http.write(out,url);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/HttpService.h b/eckit/src/eckit/web/HttpService.h
new file mode 100644
index 0000000..9584085
--- /dev/null
+++ b/eckit/src/eckit/web/HttpService.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HttpService.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef HttpService_H
+#define HttpService_H
+
+#include "eckit/net/NetService.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class HttpService : public eckit::NetService {
+public:
+
+// -- Contructors
+
+	HttpService(int);
+
+// -- Destructor
+
+	~HttpService();
+
+private:
+
+// -- Overridden methods
+
+	// From NetService
+
+	virtual eckit::NetUser* newUser(eckit::TCPSocket&);
+	virtual std::string name() { return "http"; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/HttpUser.h b/eckit/src/eckit/web/HttpUser.h
new file mode 100755
index 0000000..571b364
--- /dev/null
+++ b/eckit/src/eckit/web/HttpUser.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HttpUser.h
+// Manuel Fuentes - ECMWF Jul 96
+
+#ifndef HttpUser_H
+#define HttpUser_H
+
+#include "eckit/net/NetUser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class HttpUser : public eckit::NetUser {
+public:
+	HttpUser(eckit::TCPSocket&);
+	virtual ~HttpUser();
+private:
+    virtual void serve(eckit::Stream&, std::istream&, std::ostream&);
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/JSONResource.cc b/eckit/src/eckit/web/JSONResource.cc
new file mode 100644
index 0000000..65d9358
--- /dev/null
+++ b/eckit/src/eckit/web/JSONResource.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/JSON.h"
+#include "eckit/web/JSONResource.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+JSONResource::JSONResource(const std::string& name):
+    HtmlResource(name)
+{
+    // Should do something here...
+}
+JSONResource::~JSONResource()
+{
+	// Should do something here...
+}
+
+void JSONResource::html(std::ostream& out,Url& url)
+{
+    url.headerOut().type("application/json");
+    JSON j(out);
+    json(j,url.json());
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JSONResource.h b/eckit/src/eckit/web/JSONResource.h
new file mode 100644
index 0000000..4d387c1
--- /dev/null
+++ b/eckit/src/eckit/web/JSONResource.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File HtmlResource.h
+// Baudouin Raoult - ECMWF Jun 12
+
+#ifndef JSONResource_H
+#define JSONResource_H
+
+#include "eckit/web/HtmlResource.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class JSON;
+class Value;
+
+class JSONResource : public HtmlResource {
+public:
+
+// -- Contructors
+
+    JSONResource(const std::string&);
+
+// -- Destructor
+
+    virtual ~JSONResource();
+
+private:
+
+    virtual void html(std::ostream&,Url&);
+    virtual void json(eckit::JSON&,const eckit::Value&) = 0;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/JavaAgent.cc b/eckit/src/eckit/web/JavaAgent.cc
new file mode 100644
index 0000000..1cc64e8
--- /dev/null
+++ b/eckit/src/eckit/web/JavaAgent.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/web/JavaAgent.h"
+
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/os/Password.h"
+
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+eckit::ClassSpec JavaAgent::classSpec_ = {&Streamable::classSpec(),"JavaAgent",};
+Reanimator<JavaAgent> JavaAgent::reanimator_;
+
+void JavaAgent::encode(eckit::Stream& s) const
+{
+        Streamable::encode(s);
+}
+
+JavaAgent::JavaAgent(eckit::Stream& s):
+        Streamable(s),
+		stream_(s)
+{
+}
+
+JavaAgent::~JavaAgent()
+{
+}
+
+void JavaAgent::startObject(const std::string& clss)
+{
+	stream_.startObject();
+	stream_ << clss;
+}
+
+void JavaAgent::endObject()
+{
+	stream_.endObject();
+}
+
+void JavaAgent::serve(eckit::Stream& s,std::istream& in,std::ostream& out)
+{
+    eckit::ScopedPtr<JavaAgent> a(Reanimator<JavaAgent>::reanimate(s));
+	ASSERT(a.get());
+
+	Log::info() << *a << std::endl;
+
+	int agentClearance = a->clearance();
+
+	if(agentClearance == JavaAgent::none)
+		s << int(0); // No need for password
+	else
+	{
+		s << int(1); // Ask for password;
+
+		std::string user;
+		std::string password;
+
+		s >> user;
+		s << Password::salt(user);
+		s >> password;
+
+		if(!Password::check(user,password))
+		{
+			s << int(-1);
+			return;
+		}
+		else
+			s << int(0);
+
+		a->user_ = user;
+	}
+
+	a->execute(s,in,out);
+}
+
+template<>
+Streamable* eckit::Reanimator<JavaAgent>::ressucitate(eckit::Stream& s) const 
+{ 
+	return 0; 
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JavaAgent.h b/eckit/src/eckit/web/JavaAgent.h
new file mode 100644
index 0000000..d572ed6
--- /dev/null
+++ b/eckit/src/eckit/web/JavaAgent.h
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File JavaAgent.h
+// Baudouin Raoult - ECMWF Nov 97
+
+#ifndef JavaAgent_H
+#define JavaAgent_H
+
+#include "eckit/serialisation/Streamable.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class JavaAgent : public eckit::Streamable {
+public:
+
+	enum { none, user, oper, admin, root };
+
+// -- Contructors
+
+	JavaAgent(eckit::Stream&);
+
+// -- Destructor
+
+	virtual ~JavaAgent(); 
+
+// -- Methods
+	
+	void startObject(const std::string&);
+	void endObject();
+	eckit::Stream& stream()  { return stream_; }
+
+	virtual void execute(eckit::Stream&,std::istream&,std::ostream&) = 0;
+	virtual int  clearance() = 0;
+
+// -- Overridden methods
+
+    virtual void encode(eckit::Stream&) const;
+	virtual const eckit::ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+    
+    static  const eckit::ClassSpec&  classSpec()        { return classSpec_;}
+	static void serve(eckit::Stream&,std::istream&,std::ostream&);
+
+protected: // members
+
+	eckit::Stream& stream_;
+	std::string  user_;
+
+protected: // methods
+	
+	virtual void print(std::ostream&) const = 0; 	
+
+private: // members
+	
+    static  eckit::ClassSpec               classSpec_;
+	static eckit::Reanimator<JavaAgent>  reanimator_;
+
+private: // methods
+
+	friend std::ostream& operator<<(std::ostream& s,const JavaAgent& p)
+		{ p.print(s); return s; }
+
+	friend class JavaUser;
+};
+
+template<> Streamable* Reanimator<JavaAgent>::ressucitate(Stream& s) const;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/JavaResource.cc b/eckit/src/eckit/web/JavaResource.cc
new file mode 100644
index 0000000..c761e2a
--- /dev/null
+++ b/eckit/src/eckit/web/JavaResource.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/web/HttpBuf.h"
+#include "eckit/web/JavaResource.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+JavaResource::JavaResource():
+	HtmlResource("/java")
+{
+}
+
+JavaResource::~JavaResource()
+{
+}
+
+void JavaResource::html(std::ostream& s,Url& url)
+{
+	url.dontCache();
+
+	eckit::PathName path("~/http/" + url.name());
+
+	std::ifstream in(path.localPath());
+	if(!in)	
+	{
+		(url.headerOut()).status(404);  // Not Found
+		s << path << ": " << Log::syserr << std::endl;
+	}
+	else
+	{
+	//	(url.headerOut()).type("image/gif");
+
+		s << HttpBuf::dontEncode;
+		char c;
+		while(in.get(c))
+			s << c;
+		s << HttpBuf::doEncode;
+	}
+}
+
+static JavaResource javaResourceInstance;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JavaResource.h b/eckit/src/eckit/web/JavaResource.h
new file mode 100644
index 0000000..3ee8a81
--- /dev/null
+++ b/eckit/src/eckit/web/JavaResource.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File JavaResource.h
+// Baudouin Raoult - ECMWF Sep 97
+
+#ifndef JavaResource_H
+#define JavaResource_H
+
+#include "eckit/web/HtmlResource.h"
+
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class JavaResource : public HtmlResource {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	JavaResource();
+
+// -- Destructor
+
+	virtual ~JavaResource();
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+
+protected:
+
+// -- Members
+	// None
+
+	std::string name_;
+
+// -- Methods
+	
+    // void print(std::ostream&) const;
+
+// -- Overridden methods
+
+	virtual void html(std::ostream&,Url&);
+
+// -- Class members
+	// None
+
+// -- Class methods
+
+
+private:
+
+// No copy allowed
+
+	JavaResource(const JavaResource&);
+	JavaResource& operator=(const JavaResource&);
+
+// -- Members
+
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const JavaResource& p)
+	//	{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/JavaServer.cc b/eckit/src/eckit/web/JavaServer.cc
new file mode 100644
index 0000000..830a19b
--- /dev/null
+++ b/eckit/src/eckit/web/JavaServer.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/web/JavaServer.h"
+#include "eckit/web/JavaService.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+JavaServer::JavaServer(int port):
+	java_(new JavaService(port))
+{
+	java_.start();
+}
+
+JavaServer::~JavaServer()
+{
+	java_.stop();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JavaServer.h b/eckit/src/eckit/web/JavaServer.h
new file mode 100644
index 0000000..ebdf667
--- /dev/null
+++ b/eckit/src/eckit/web/JavaServer.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File JavaServer.h
+// Baudouin Raoult - ECMWF Jun 96
+
+#ifndef JavaServer_H
+#define JavaServer_H
+
+#include "eckit/thread/ThreadControler.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class JavaServer {
+public:
+
+// -- Contructors
+
+	JavaServer(int port);
+
+// -- Destructor
+
+	~JavaServer();
+
+private:
+
+// No copy allowed
+
+	JavaServer(const JavaServer&);
+	JavaServer& operator=(const JavaServer&);
+
+// -- Members
+
+	eckit::ThreadControler java_;
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/JavaService.cc b/eckit/src/eckit/web/JavaService.cc
new file mode 100644
index 0000000..082a373
--- /dev/null
+++ b/eckit/src/eckit/web/JavaService.cc
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/web/JavaService.h"
+#include "eckit/web/JavaUser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+JavaService::JavaService(int port):
+	NetService(port)
+{
+}
+
+JavaService::~JavaService()
+{
+}
+
+NetUser* JavaService::newUser(TCPSocket& socket)
+{ 
+	return new JavaUser(socket); 
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JavaService.h b/eckit/src/eckit/web/JavaService.h
new file mode 100644
index 0000000..6ada85c
--- /dev/null
+++ b/eckit/src/eckit/web/JavaService.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File JavaService.h
+// Baudouin Raoult - ECMWF Apr 97
+
+#ifndef JavaService_H
+#define JavaService_H
+
+#include "eckit/net/NetService.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class JavaService : public eckit::NetService {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	JavaService(int port);
+
+// -- Destructor
+
+    ~JavaService();
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+    // void print(std::ostream&) const;
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	JavaService(const JavaService&);
+	JavaService& operator=(const JavaService&);
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+
+	virtual eckit::NetUser* newUser(eckit::TCPSocket& socket);
+	virtual std::string name() { return "java"; }
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const JavaService& p)
+	//	{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/eckit/web/JavaUser.cc b/eckit/src/eckit/web/JavaUser.cc
new file mode 100644
index 0000000..6dec782
--- /dev/null
+++ b/eckit/src/eckit/web/JavaUser.cc
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/web/JavaAgent.h"
+#include "eckit/web/JavaUser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+JavaUser::JavaUser(TCPSocket& socket):
+	NetUser(socket)
+{
+}
+
+JavaUser::~JavaUser()
+{
+}
+
+void JavaUser::serve(eckit::Stream& s, std::istream& in, std::ostream& out)
+{
+	JavaAgent::serve(s,in,out);
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
diff --git a/eckit/src/eckit/web/JavaUser.h b/eckit/src/eckit/web/JavaUser.h
new file mode 100644
index 0000000..70230ef
--- /dev/null
+++ b/eckit/src/eckit/web/JavaUser.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File JavaUser.h
+// Baudouin Raoult - ECMWF Apr 97
+
+#ifndef JavaUser_H
+#define JavaUser_H
+
+#include "eckit/net/NetUser.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+class JavaUser : public eckit::NetUser {
+public:
+
+	JavaUser(eckit::TCPSocket&);
+
+    ~JavaUser();
+
+private:
+
+    virtual void serve(eckit::Stream&, std::istream&, std::ostream&);
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/eckit/web/Url.cc b/eckit/src/eckit/web/Url.cc
new file mode 100644
index 0000000..6964371
--- /dev/null
+++ b/eckit/src/eckit/web/Url.cc
@@ -0,0 +1,326 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Log.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/parser/JSONParser.h"
+#include "eckit/web/Html.h"
+#include "eckit/web/Url.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+
+inline void header(char c)
+{
+#if 0
+	if(isprint(c))
+		std::cout << "header: " << c << std::endl;
+	else
+		std::cout << "header: " << hex << int(c) << dec << std::endl;
+#endif
+}
+
+void Url::parse(const std::string& url, bool param)
+{
+
+	int size    = url.length();
+	int index   = 0;
+	std::string s,p;
+
+	while(index < size)
+    {
+
+		switch(url[index])
+		{
+			case '?':
+				param = true;
+				if(s != "") url_.push_back(Html::removeHex(s));
+				s     = "";
+				break;
+
+			case '=':
+				p     = s;
+				s     = "";
+				break;
+
+			case '&':
+				map_[Html::removeHex(p)] = Html::removeHex(s);
+				s       = "";
+				p       = "";
+				break;
+
+			case '+':
+				// Do we need a multimap?
+				s += url[index];
+				break;
+
+			case '/':
+				if( ! param )
+				{
+					if(s != "") url_.push_back(Html::removeHex(s));
+					s = "";
+				}
+				else s += url[index];
+				break;
+
+			default:
+				s += url[index];
+				break;
+		}
+
+		++index;
+    }
+
+	if( ! param )
+	{
+		if(s != "") url_.push_back(Html::removeHex(s));
+	}
+	else {
+		if(p != "")
+			map_[Html::removeHex(p)] = Html::removeHex(s);
+	}
+
+}
+
+Url::Url(std::istream& in)
+{
+	std::string url;
+
+	in >> method_;
+	in >> url;
+	parse(url,false);
+
+	char c = 0;
+	while(in.get(c) && c != '\n')  
+		header(c);
+
+	parse(in);
+	Log::debug() << "Incomming url-> " << *this << std::endl;
+}
+
+void Url::parse(std::istream& in)
+{
+
+	char c = 0;
+
+    std::map<std::string,std::string,std::less<std::string> > m;
+
+	for(;;)
+	{
+		std::string s;
+		while(in.get(c) && c != ':' && c != '\r')
+		{
+			header(c);
+			s += c;
+		}
+
+		if(c != ':')
+		{
+			header(c);
+			in.get(c);    // Skip '\n'
+			header(c);
+			break;
+		}
+
+		header(c);
+		while(in.get(c) && c == ' ' && c != '\r')
+			header(c);
+		
+		if(c != '\r')
+		{
+			header(c);
+
+			std::string r;
+			r += c;
+			while(in.get(c) && c != '\r')
+			{
+				header(c);
+				r += c;
+			}
+
+			m[s] = r;
+			in.get(c);    // Skip '\n'
+			header(c);
+		}
+	}
+
+	in_ = m;
+
+	long len   = in_.contentLength();
+
+	if(len)
+	{
+        static const std::string FormType      = "application/x-www-form-urlencoded";
+        static const std::string JSONType      = "application/json";
+
+        //bool ascii = true;
+		Buffer content(len);
+        const std::string& type = in_.type();
+
+
+		char *p = content;
+
+		for(int i = 0; i < len; i++)
+		{
+			in.get(c); 
+			header(c);
+			*p++ = c;
+		}
+
+        if(type == FormType)
+		{
+			std::string s(static_cast<char*>(content),p - static_cast<char*>(content));
+			parse(s,true);
+		}
+
+        in_.content(content,len);
+
+        if(type == JSONType)
+        {
+            const char* p = in_.content();
+            std::istringstream in(p ? p : "null");
+
+            JSONParser q(in);
+            json_ = q.parse();
+        }
+
+	}
+
+	Log::debug() << *this << std::endl;
+}
+
+Url::Url(const std::string& url):
+	method_("GET")
+{
+	parse(url,false);
+	Log::debug() << "Incomming url-> " << *this << std::endl;
+}
+
+Url::~Url()
+{
+}
+
+std::string Url::name() const
+{
+	std::string s = "";
+	for(std::vector<std::string>::const_iterator j = url_.begin(); j != url_.end(); ++j)
+	{
+		s += "/" ;
+		s += *j ;
+	}
+	return s;
+}
+
+void Url::print(std::ostream& s) const
+{
+	for(std::vector<std::string>::const_iterator j = url_.begin();
+		j != url_.end(); ++j)
+		s << "/" << *j ;
+
+	char c = '?';
+	Map::const_iterator i;
+	for(i = map_.begin(); i != map_.end(); ++i)
+	{
+		s << c << (*i).first  << '=' << (*i).second;
+		c = '&';
+	}
+
+}
+
+void Url::cgiParam(std::ostream& s,char sep) const
+{
+	char c = ' ';
+	Map::const_iterator i;
+	for(i = map_.begin(); i != map_.end(); ++i)
+	{
+		s << c << (*i).first  << '=' << (*i).second;
+		c = sep;
+	}
+
+}
+
+UrlAccess Url::operator[](const std::string& s) 
+{
+	return UrlAccess(*this,s);
+}
+
+void Url::set(const std::string& p,const std::string& s)
+{
+	map_[p] = s;
+}
+
+const std::string& Url::get(const std::string& s)
+{
+	return map_[s];
+}
+
+void Url::erase(const std::string& s)
+{
+	map_.erase(s);
+}
+
+const std::string& Url::operator[](int n) const
+{
+	return url_[n];
+}
+
+
+std::string Url::str() const
+{
+    std::ostringstream s;
+    s << *this;
+    return s.str();
+}
+
+int Url::size() const
+{
+	return url_.size();
+}
+
+HttpHeader& Url::headerIn()
+{
+	return in_;
+}
+
+HttpHeader& Url::headerOut()
+{
+	return out_;
+}
+
+UrlAccess::operator long()
+{
+	return Translator<std::string,long>()(url_.get(s_));
+}
+
+UrlAccess::operator std::string()
+{
+	return url_.get(s_);
+}
+
+UrlAccess& UrlAccess::operator=(long n)
+{
+	url_.set(s_,Translator<long,std::string>()(n));
+	return *this;
+}
+
+UrlAccess& UrlAccess::operator=(const std::string& s)
+{
+	url_.set(s_,s);
+	return *this;
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
diff --git a/eckit/src/eckit/web/Url.h b/eckit/src/eckit/web/Url.h
new file mode 100644
index 0000000..8544752
--- /dev/null
+++ b/eckit/src/eckit/web/Url.h
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Url.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef Url_H
+#define Url_H
+
+#include "eckit/value/Value.h"
+#include "eckit/web/HttpHeader.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit {
+
+//-----------------------------------------------------------------------------
+
+class Url;
+
+//-----------------------------------------------------------------------------
+
+class UrlAccess {
+	Url&   url_;
+	std::string s_;
+	
+
+public:
+	UrlAccess(Url& url,const std::string& s) : url_(url), s_(s) {}
+
+	operator std::string();
+	operator long();
+
+	UrlAccess& operator=(const std::string&);
+	UrlAccess& operator=(long);
+};
+
+//-----------------------------------------------------------------------------
+
+class Url : private eckit::NonCopyable {
+public:
+
+	enum { 
+		notFound = 404
+	};
+
+// -- Contructors
+
+	Url(std::istream&);
+	Url(const std::string&);
+
+// -- Destructor
+
+    ~Url();
+
+// -- Operators
+	
+	UrlAccess operator[](const std::string&);
+
+// -- Methods
+
+	void erase(const std::string&);
+
+
+	void set(const std::string&,const std::string&);
+	const std::string& get(const std::string&);
+
+	const std::string& method() { return method_;}
+
+
+	HttpHeader&   headerIn();
+	HttpHeader&   headerOut();
+
+	const HttpHeader&   headerIn() const;
+	const HttpHeader&   headerOut() const;
+
+	std::string str() const;
+	std::string name() const;
+
+
+	int size() const;
+	const std::string& operator[](int) const;
+
+	bool authenticated()
+		{ return headerIn().authenticated(); }
+
+	void authenticate(const std::string& realm = "MARS") 
+		{ headerOut().authenticate(realm); }
+
+	void status(int s)
+		{ headerOut().status(s); }
+
+	void forward(const std::string& s)
+		{ headerOut().forward(s); }
+
+	void dontCache()
+		{ headerOut().dontCache(); }
+
+	void cgiParam(std::ostream&,char sep = ' ') const;
+
+    const eckit::Value& json() const
+        { return json_; }
+
+protected:
+
+// -- Methods
+	
+    void print(std::ostream&) const;
+
+private:
+
+// -- Members
+
+    typedef std::map<std::string,std::string,std::less<std::string> > Map;
+
+	Map            map_;
+    std::vector<std::string> url_;
+	HttpHeader     in_;
+	HttpHeader     out_;
+
+	std::string		   method_;
+
+    eckit::Value          json_;
+
+// -- Methods
+
+	void parse(const std::string&,bool);
+	void parse(std::istream&);
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const Url& p)
+		{ p.print(s); return s; }
+
+};
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit
+
+
+#endif
diff --git a/eckit/src/experimental/CMakeLists.txt b/eckit/src/experimental/CMakeLists.txt
new file mode 100644
index 0000000..8af53a0
--- /dev/null
+++ b/eckit/src/experimental/CMakeLists.txt
@@ -0,0 +1,13 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
+include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
+
+add_subdirectory( eckit )
+add_subdirectory( tests )
diff --git a/eckit/src/experimental/eckit/CMakeLists.txt b/eckit/src/experimental/eckit/CMakeLists.txt
new file mode 100644
index 0000000..705b54e
--- /dev/null
+++ b/eckit/src/experimental/eckit/CMakeLists.txt
@@ -0,0 +1,9 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+add_subdirectory( testing )
diff --git a/eckit/src/experimental/eckit/testing/CMakeLists.txt b/eckit/src/experimental/eckit/testing/CMakeLists.txt
new file mode 100644
index 0000000..85d3ae8
--- /dev/null
+++ b/eckit/src/experimental/eckit/testing/CMakeLists.txt
@@ -0,0 +1,11 @@
+list( APPEND eckit_testing_srcs
+UnitTest.h
+UnitTest.cc
+)
+
+ecbuild_add_library(TARGET eckit_testing
+					INSTALL_HEADERS ALL
+					SOURCES ${eckit_testing_srcs}
+					HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/eckit/testing
+					LIBS eckit )
+
diff --git a/eckit/src/experimental/eckit/testing/UnitTest.cc b/eckit/src/experimental/eckit/testing/UnitTest.cc
new file mode 100644
index 0000000..f5a3af7
--- /dev/null
+++ b/eckit/src/experimental/eckit/testing/UnitTest.cc
@@ -0,0 +1,84 @@
+#include "eckit/testing/UnitTest.h"
+
+
+
+void UnitTestRunner::setUp()
+{
+}
+
+void UnitTestRunner::test()
+{
+}
+
+void UnitTestRunner::tearDown()
+{
+}
+
+void UnitTestRunner::run()
+{
+    setUp();
+    test();
+    tearDown();
+}
+
+UnitTestRunner::UnitTestRunner(int argc, char** argv):
+    eckit::Tool(argc, argv)
+{
+}
+
+void SimpleTestRunner::test()
+{
+    t_();
+}
+
+void SimpleTestRunner::setUp()
+{
+    u_();
+}
+
+void SimpleTestRunner::tearDown()
+{
+    d_();
+}
+
+SimpleTestRunner::SimpleTestRunner(int argc, char** argv,
+                                   test_proc_t t,
+                                   test_proc_t u,
+                                   test_proc_t d):
+    UnitTestRunner(argc, argv),
+    t_(t),
+    u_(u),
+    d_(d)
+{
+
+}
+
+
+static OneTest* tests = 0;
+
+
+OneTest::OneTest(const char* n, test_proc_t t):
+    n_(n),
+    t_(t),
+    next_(tests)
+{
+    tests =  this;
+}
+
+void OneTest::test() {
+    if(next_) next_->test();
+    eckit::Log::info() << "Test " << n_ << std::endl;
+    t_();
+}
+
+void AllTestsRunner::test()
+{
+    tests->test();
+}
+
+AllTestsRunner::AllTestsRunner(int argc, char** argv):
+    UnitTestRunner(argc, argv)
+{
+
+}
+
diff --git a/eckit/src/experimental/eckit/testing/UnitTest.h b/eckit/src/experimental/eckit/testing/UnitTest.h
new file mode 100644
index 0000000..8a10264
--- /dev/null
+++ b/eckit/src/experimental/eckit/testing/UnitTest.h
@@ -0,0 +1,92 @@
+#ifndef eckit_UnitTest_H
+#define eckit_UnitTest_H
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/log/Log.h"
+
+class UnitTestRunner : public eckit::Tool {
+
+    virtual void setUp();
+    virtual void test();
+    virtual void tearDown();
+
+    virtual void run();
+
+public:
+    UnitTestRunner(int argc, char** argv);
+
+};
+
+typedef void (*test_proc_t)();
+
+//=========================================
+
+class OneTest {
+    const char* n_;
+    test_proc_t t_;
+    OneTest *next_;
+public:
+    OneTest(const char* n, test_proc_t t);
+    void test();
+};
+
+class AllTestsRunner : public UnitTestRunner {
+    virtual void test();
+public:
+    AllTestsRunner(int argc, char** argv);
+};
+
+//=========================================
+
+class SimpleTestRunner : public UnitTestRunner {
+    test_proc_t t_;
+    test_proc_t u_;
+    test_proc_t d_;
+
+    virtual void test();
+    virtual void setUp();
+    virtual void tearDown();
+
+public:
+    SimpleTestRunner(int argc, char** argv, test_proc_t t, test_proc_t u, test_proc_t d);
+
+};
+
+//=========================================
+
+
+#define TEST(name) \
+    static void name(); \
+    static OneTest call##name(#name, &name); \
+    static void name()
+
+#define TEST_FIXTURE(type, name) \
+    struct Test##type##name : public type {  void test(); }; \
+    static void test##type##name() { Test##type##name().test(); } \
+    static OneTest call##type##name("test"#type#name, &test##type##name); \
+    void Test##type##name::test()
+
+
+
+//=========================================
+
+#define RUN_SIMPLE_TEST \
+    int main(int c,char** v) { SimpleTestRunner x(c, v, &test, &setUp, &tearDown); x.start(); return 0; }
+
+#define RUN_ALL_TESTS \
+    int main(int c,char** v) { AllTestsRunner x(c, v); x.start(); return 0; }
+
+//=========================================
+
+
+#define CHECK(expected) \
+    ASSERT(expected)
+
+#define CHECK_EQUAL(expected, actual) \
+    ASSERT((expected) == (actual))
+
+
+#define CHECK_ARRAY_EQUAL(expected, actual, count) \
+    do { size_t n = count; for(size_t i=0;i<n;++i) CHECK_EQUAL(expected[i],actual[i]); } while(0)
+
+#endif
diff --git a/eckit/src/experimental/tests/CMakeLists.txt b/eckit/src/experimental/tests/CMakeLists.txt
new file mode 100644
index 0000000..3ab2738
--- /dev/null
+++ b/eckit/src/experimental/tests/CMakeLists.txt
@@ -0,0 +1,9 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+add_subdirectory( singleton )
diff --git a/eckit/src/experimental/tests/singleton/CMakeLists.txt b/eckit/src/experimental/tests/singleton/CMakeLists.txt
new file mode 100644
index 0000000..87b12e4
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/CMakeLists.txt
@@ -0,0 +1,20 @@
+ecbuild_append_to_rpath( "./" )
+
+ecbuild_add_library( TARGET      eckit_test_factory
+                     NOINSTALL
+                     SOURCES     TestFactory.cc TestFactory.h
+                     LIBS        eckit )
+
+ecbuild_add_library( TARGET      eckit_test_builder1
+                     NOINSTALL
+                     SOURCES     TestBuilder1.cc
+                     LIBS        eckit_test_factory )
+
+ecbuild_add_library( TARGET      eckit_test_builder2
+                     NOINSTALL
+                     SOURCES     TestBuilder2.cc
+                     LIBS        eckit_test_factory )
+
+ecbuild_add_test( TARGET      eckit_test_singleton
+                  SOURCES     test_singleton.cc
+                  LIBS        eckit_test_builder1 eckit_test_builder2 )
diff --git a/eckit/src/experimental/tests/singleton/TestBuilder1.cc b/eckit/src/experimental/tests/singleton/TestBuilder1.cc
new file mode 100644
index 0000000..9a9df15
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/TestBuilder1.cc
@@ -0,0 +1,25 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+
+#include "TestFactory.h"
+
+class TestBuilder1 : public TestBuilder {
+public:
+    void build()
+    {
+        std::cout << "building a b1" << std::endl;
+    }
+};
+
+//-----------------------------------------------------------------------------
+
+AutoRegistBuilder< TestBuilder1> builder1( "b1" );
diff --git a/eckit/src/experimental/tests/singleton/TestBuilder2.cc b/eckit/src/experimental/tests/singleton/TestBuilder2.cc
new file mode 100644
index 0000000..a2a5cd5
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/TestBuilder2.cc
@@ -0,0 +1,25 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+
+#include "TestFactory.h"
+
+class TestBuilder2 : public TestBuilder {
+public:
+    void build()
+    {
+        std::cout << "building a b2" << std::endl;
+    }
+};
+
+//-----------------------------------------------------------------------------
+
+AutoRegistBuilder< TestBuilder2> builder2( "b2" );
diff --git a/eckit/src/experimental/tests/singleton/TestFactory.cc b/eckit/src/experimental/tests/singleton/TestFactory.cc
new file mode 100644
index 0000000..3a91519
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/TestFactory.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+#include <cassert>
+#include <map>
+#include <algorithm>
+#include <exception>
+
+#include "TestFactory.h"
+
+struct TestFactory::PImpl {
+
+    typedef std::map<std::string,TestBuilder*> Store;
+    Store register_;
+
+    ~PImpl() {
+        for(Store::iterator it = register_.begin(); it != register_.end(); ++it) {
+            delete it->second;
+        }
+    }
+
+    void regist(const std::string& name, TestBuilder* builder)
+    {
+        std::cout << "registering builder [" << name << "]" << std::endl;
+        Store::iterator itr = register_.find(name);
+        if(itr != register_.end())
+            throw std::bad_alloc();
+        register_[name] = builder;
+    }
+
+    TestBuilder& get(const std::string& name)
+    {
+        std::cout << "get builder [" << name << "]" << std::endl;
+        Store::iterator itr = register_.find(name);
+        if(itr == register_.end())
+            throw std::bad_alloc();
+        ASSERT( itr->second );
+        return *(itr->second);
+    }
+};
+
+
+//-----------------------------------------------------------------------------
+
+TestFactory::TestFactory()
+    :
+    pimpl_( new TestFactory::PImpl() )
+{
+}
+
+TestFactory& TestFactory::instance()
+{
+    // autolock mutex
+
+    static TestFactory factory;
+    return factory;
+}
+
+void TestFactory::regist(const std::string& name, TestBuilder* builder)
+{
+    pimpl_->regist(name,builder);
+}
+
+TestBuilder& TestFactory::get(const std::string& name)
+{
+    return pimpl_->get(name);
+}
diff --git a/eckit/src/experimental/tests/singleton/TestFactory.h b/eckit/src/experimental/tests/singleton/TestFactory.h
new file mode 100644
index 0000000..3a34626
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/TestFactory.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TestFactory_h
+#define TestFactory_h
+
+#include <string>
+
+#include "eckit/memory/ScopedPtr.h"
+
+class TestBuilder {
+public:
+    virtual ~TestBuilder(){}
+    virtual void build() = 0;
+};
+
+class TestFactory {
+public: // methods
+
+    TestFactory();
+
+    static TestFactory& instance();
+
+    void regist( const std::string&, TestBuilder* );
+    TestBuilder& get( const std::string& );
+
+private:
+
+    struct PImpl;
+    eckit::ScopedPtr<PImpl> pimpl_;
+
+};
+
+template< typename T>
+class AutoRegistBuilder {
+public:
+    AutoRegistBuilder(const std::string& name)
+    {
+        std::cout << "auto register [" << name << "]" << std::endl;
+        TestFactory::instance().regist( name, new T() );
+    }
+};
+
+#endif /* TestFactory_h */
diff --git a/eckit/src/experimental/tests/singleton/test_singleton.cc b/eckit/src/experimental/tests/singleton/test_singleton.cc
new file mode 100644
index 0000000..3d17dda
--- /dev/null
+++ b/eckit/src/experimental/tests/singleton/test_singleton.cc
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+
+#include "TestFactory.h"
+
+int main( int argc, char** argv )
+{
+    TestBuilder& b1 = TestFactory::instance().get("b1");
+    TestBuilder& b2 = TestFactory::instance().get("b2");
+
+    b1.build();
+    b2.build();
+
+    return 0;
+}
diff --git a/eckit/src/tests/CMakeLists.txt b/eckit/src/tests/CMakeLists.txt
new file mode 100644
index 0000000..6035a91
--- /dev/null
+++ b/eckit/src/tests/CMakeLists.txt
@@ -0,0 +1,21 @@
+add_subdirectory( config   )
+add_subdirectory( container )
+add_subdirectory( filesystem )
+add_subdirectory( geometry )
+add_subdirectory( io )
+add_subdirectory( large_file )
+add_subdirectory( linalg )
+add_subdirectory( log )
+add_subdirectory( maths )
+add_subdirectory( memory )
+add_subdirectory( memory_map )
+add_subdirectory( mpi )
+add_subdirectory( option )
+add_subdirectory( parser )
+add_subdirectory( runtime )
+add_subdirectory( serialisation )
+add_subdirectory( thread )
+add_subdirectory( types )
+add_subdirectory( utils )
+add_subdirectory( value )
+add_subdirectory( system )
diff --git a/eckit/src/tests/config/CMakeLists.txt b/eckit/src/tests/config/CMakeLists.txt
new file mode 100644
index 0000000..9793b0a
--- /dev/null
+++ b/eckit/src/tests/config/CMakeLists.txt
@@ -0,0 +1,11 @@
+ecbuild_add_test( TARGET   eckit_test_config
+                  BOOST
+                  SOURCES  test_config.cc
+                  ENABLED  OFF
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_resource
+                  BOOST
+                  SOURCES  test_resource.cc
+                  ARGS     -integer 100 -listlong 88,99,11,22
+                  LIBS     eckit )
diff --git a/eckit/src/tests/config/test_config.cc b/eckit/src/tests/config/test_config.cc
new file mode 100644
index 0000000..f9c0e00
--- /dev/null
+++ b/eckit/src/tests/config/test_config.cc
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_config
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+#include "eckit/types/Types.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+// FIXME: re-enable this test
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {}
+
+//-----------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_config )
+
+BOOST_AUTO_TEST_CASE( test_parse )
+{
+//     ostringstream code;
+
+//     code <<
+//             " a = 1; "                            // assign digit
+//             " b = lolo; "                         // assign string
+//             " "
+// //            " [ if class in [ od , rd ] && stream = oper ]"   // condition with multiple statement
+//             " [ if class = od || rd && stream = oper ]"   // condition with multiple statement
+//             " {  "
+//             "   fdbRoot = \'/tmp/fdb\';  "        // paths
+//             " "
+//             "   c1 = \"4 x 4\"; "                 // double quote
+//             "   [ if date = today ] "             // nested branch
+//             "   {"
+//             "       d = 4; "
+//             "   }"                                // close after semi-colon
+//             "   c2 = '5 x 5'; "                   // single quote
+//             "   cc = lolo popo   gege  ;"         // string with spaces and not quotes 'lolo popo'
+//             "   e = '6'; "
+//             "   e = '66'; "                       // override
+//             "   f : 'fofo'; "                     // assign with :
+//             " }"
+//             " "
+//             " [ if xxx = yyy ] { f = ignored; }"   // branch not visited
+//             " "
+//             " [ if xxx = yyy ] {} || { s = ss } "  // else branch visited
+//             " "
+//             " [ if class = od ] {} "               // empty branch
+//             " "
+//             " [ if class = od || rd ] { t = 22 }"  // double or in branch
+//             " "
+//             " [ if ] { h = here ; }"               // always true
+//             " "
+//             " g = go; "                         // isolated statement
+//             " "
+//             " { } "                             // empty block
+//             " "
+//             " { k = koko; } "                   // stand alone block
+//             " "
+//             " [ function foo ] { m = momo }"    // function definition
+//             " "
+//             " [ call foo ]"                     // function call
+//             " "
+//             " { j = jojo  } "                   // finish assignement with block
+//             " z1 = 11 \n"                       // finish assignement with \n
+//             " z2 = 22 #"                        // finish assignement with #
+//             ;
+
+//     istringstream in(code.str());
+
+//     config::Compiler c(in);
+
+//     config::Script s(c);
+
+// //    s.print( std::cout );
+
+//     StringDict din;
+
+//     din["class"]    = "od";
+//     din["stream"]   = "oper";
+//     din["date"]     = "today";
+
+//     StringDict dout;
+
+//     s.execute(din,dout);
+
+//     for( StringDict::const_iterator i = dout.begin(); i != dout.end(); ++i )
+//         std::cout << i->first << " : " << i->second << std::endl;
+
+//     BOOST_CHECK( dout["a"] == "1" );
+//     BOOST_CHECK( dout["b"] == "lolo" );
+//     BOOST_CHECK( dout["fdbRoot"] == "/tmp/fdb" );
+//     BOOST_CHECK( dout["c1"] == "4 x 4" );
+//     BOOST_CHECK( dout["d"] == "4" );
+//     BOOST_CHECK( dout["c2"] == "5 x 5" );
+//     BOOST_CHECK( dout["cc"] == "lolo popo   gege" );
+//     BOOST_CHECK( dout["e"] == "66" );
+//     BOOST_CHECK( dout["s"] == "ss" );
+//     BOOST_CHECK( dout["t"] == "22" );
+//     BOOST_CHECK( dout["h"] == "here" );
+//     BOOST_CHECK( dout["g"] == "go" );
+//     BOOST_CHECK( dout["k"] == "koko" );
+//     BOOST_CHECK( dout["j"] == "jojo" );
+//     BOOST_CHECK( dout["m"] == "momo" );
+//     BOOST_CHECK( dout["z1"] == "11" );
+//     BOOST_CHECK( dout["z2"] == "22" );
+
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/config/test_resource.cc b/eckit/src/tests/config/test_resource.cc
new file mode 100644
index 0000000..0e4f10a
--- /dev/null
+++ b/eckit/src/tests/config/test_resource.cc
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE test_eckit_resource
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/types/Types.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+//-----------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE( Setup );
+
+BOOST_AUTO_TEST_SUITE( test_eckit_resource )
+
+BOOST_AUTO_TEST_CASE( test_default )
+{
+    std::string s = Resource<std::string>("s","some");
+
+    BOOST_CHECK( s == "some" );
+
+    double d = Resource<double>("d", 777.7);
+
+	BOOST_CHECK_CLOSE( d , 777.7, 0.0001 ); // accept 0.0001% tolerance
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_vector_long )
+{
+    std::vector<long> def(3,77);
+    std::vector<long> v = Resource< std::vector<long> >("listlong;-listlong",def);
+
+    BOOST_CHECK_EQUAL( v[0] , 88 );
+    BOOST_CHECK_EQUAL( v[1] , 99 );
+    BOOST_CHECK_EQUAL( v[2] , 11 );
+    BOOST_CHECK_EQUAL( v[3] , 22 );
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_command_line )
+{
+	int myint = Resource<int>("integer;-integer",0);
+	BOOST_CHECK_EQUAL( myint , 100 );
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_environment_var )
+{
+    char v [] = "TEST_ENV_INT=333";
+    putenv(v);
+
+	int intenv = Resource<int>("intEnv;$TEST_ENV_INT",777);
+	BOOST_CHECK_EQUAL( intenv , 333 );
+
+    char foo [] = "FOO=1Mb";
+    putenv(foo);
+
+	long l1 = Resource<long>("$FOO",0);
+	BOOST_CHECK_EQUAL( l1 , 1024*1024 );
+
+	long l2 = Resource<long>("$FOO;-foo",0);
+	BOOST_CHECK_EQUAL( l2 , 1024*1024);
+
+	long l3 = Resource<long>("-foo;$FOO",0);
+	BOOST_CHECK_EQUAL( l3, 1024*1024);
+
+	long l4 = Resource<long>("$FOO;foo;-foo",0);
+	BOOST_CHECK_EQUAL( l4 , 1024*1024);
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_config_file )
+{
+    // FIXME: re-enable this test
+
+ //    ostringstream code;
+
+ //    code << " buffer = 60 MB " << std::endl
+ //         << " b = foo " << std::endl
+ //         << " [ if class = od ] { b = bar }" << std::endl;
+
+ //    istringstream in(code.str());
+
+ //    ResourceMgr::instance().appendConfig(in);
+
+ //    StringDict args;
+
+ //    args["class"] = "od";
+
+	// std::string bar = Resource<string>("b","none",args);
+
+	// BOOST_CHECK_EQUAL( bar , std::string("bar") );
+
+ //    unsigned long long buffer = Resource<unsigned long long>("buffer",0);
+
+	// BOOST_CHECK_EQUAL( buffer , Bytes::MiB(60) );
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/eckit/src/tests/container/CMakeLists.txt b/eckit/src/tests/container/CMakeLists.txt
new file mode 100644
index 0000000..ac2a776
--- /dev/null
+++ b/eckit/src/tests/container/CMakeLists.txt
@@ -0,0 +1,24 @@
+ecbuild_add_test( TARGET   eckit_test_sharedmemarray
+                  BOOST
+                  SOURCES  test_sharedmemarray.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_btree
+                  BOOST
+                  SOURCES  test_btree.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_densemap
+                  BOOST
+                  SOURCES  test_densemap.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_cache_lru
+                  BOOST
+                  SOURCES  test_cache_lru.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_benchmark_densemap
+                  BOOST
+                  SOURCES  benchmark_densemap.cc
+                  LIBS     eckit )
diff --git a/eckit/src/tests/container/benchmark_densemap.cc b/eckit/src/tests/container/benchmark_densemap.cc
new file mode 100755
index 0000000..180ad22
--- /dev/null
+++ b/eckit/src/tests/container/benchmark_densemap.cc
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+#include <cstdlib>
+#include <map>
+
+// #include "ecbuild/boost_test_framework.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/container/DenseMap.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/log/Timer.h"
+#include "eckit/types/FixedString.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+#define MSIZE    500
+#define NSAMPLES 50000
+
+template < typename MAP >
+void benchmark_densemap_int_string( const std::string& tname )
+{
+    std::cout << "-------------------------------------------------------------" << std::endl;
+    std::cout << tname << std::endl;
+
+    Translator<int,std::string> itos;
+
+    MAP m;
+
+    {
+        Timer timer( "insert" );
+        m.reserve( MSIZE );
+        for( int i = 0; i < MSIZE; ++i )
+        {
+            m.insert( i, "foo" + itos(i) );
+        }
+    }
+
+    {
+        Timer timer( "sort" );
+        m.sort();
+    }
+
+    {
+        Timer timer( "find" );
+        for( int i = 0; i < NSAMPLES; ++i )
+        {
+            int idx = rand() % MSIZE;
+
+            typename MAP::const_iterator it = m.find( idx );
+            if( it != m.cend() )
+                ASSERT( m.get( it ) == "foo" + itos(idx) );
+            else
+                std::cout << "failed: " << idx << " " << std::string("foo") << itos(idx) << std::endl;
+        }
+    }
+}
+
+
+template < typename MAP >
+void benchmark_stdmap_int_string( const std::string& tname )
+{
+    std::cout << "-------------------------------------------------------------" << std::endl;
+    std::cout << tname << std::endl;
+
+    Translator<int,std::string> itos;
+
+    MAP m;
+
+    {
+        Timer timer( "insert" );
+        for( int i = 0; i < MSIZE; ++i )
+        {
+            m.insert( std::make_pair(i, "foo" + itos(i)) );
+        }
+    }
+
+    {
+        Timer timer( "find" );
+        for( int i = 0; i < NSAMPLES; ++i )
+        {
+            int idx = rand() % MSIZE;
+            typename MAP::const_iterator it = m.find( idx );
+			if( it != m.end() )
+                ASSERT( it->second == "foo" + itos(idx) );
+            else
+                std::cout << "failed: " << idx << " " << std::string("foo") << itos(idx) << std::endl;
+        }
+    }
+}
+
+int main()
+{
+    benchmark_densemap_int_string< DenseMap<int,std::string>  >("DenseMap<int,string>");
+    benchmark_stdmap_int_string< std::map<int,std::string> >("std::map<int,string>");
+
+    ///
+
+    benchmark_densemap_int_string< DenseMap<int, FixedString<256> > >("DenseMap<int,FixedString>");
+    benchmark_stdmap_int_string< std::map<int,FixedString<256> > >("std::map<int,FixedString>");
+}
diff --git a/eckit/src/tests/container/test_btree.cc b/eckit/src/tests/container/test_btree.cc
new file mode 100755
index 0000000..bc0916b
--- /dev/null
+++ b/eckit/src/tests/container/test_btree.cc
@@ -0,0 +1,365 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE eckit_test_btree
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/Types.h"
+#include "eckit/container/BTree.h"
+#include "eckit/os/Semaphore.h"
+#include "eckit/types/FixedString.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+struct UDF
+{
+	static const size_t SZ = 32;
+
+	FixedString< SZ > s_;
+
+	UDF( char c ) : s_() { std::string s; s += c; s_ = s; }
+	UDF( const std::string& s = std::string() ) : s_(s) {}
+
+	operator std::string() const { return s_.asString(); }
+
+	friend std::ostream& operator<<(std::ostream& s,const UDF& p)
+	{
+		s << p.s_; return s;
+	}
+
+};
+
+}
+
+//-----------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_container_btree )
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_int_int_locking )
+{
+    std::string test = "EzLPYjRkayhnTCv47SgoFV5MOqbGt6emNlD231JWIXUiBKfAupwc0rQ8xHsZd9";
+
+    unlink("foo");
+
+    BTree<int,int, 128, BTreeLock> btree("foo");
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+    {
+//		std::cout << "[" << test[i] << "," << -int(i) << "]" << std::endl;
+        int k = int(test[i]);
+        btree.set(k,-int(i));
+    }
+
+    BOOST_CHECK_EQUAL( btree.count() , test.size() );
+
+    std::vector< std::pair<int,int> > res;
+    btree.range(32,126, res);
+
+    //	for(int i = 0; i < res.size();  ++i)
+    //		std::cout << "[" << res[i].first << "," << res[i].second << "]" << std::endl;
+
+    BOOST_CHECK_EQUAL( btree.count(), res.size() );
+
+//	std::cout << std::endl;
+//	btree.dump() ;
+//	std::cout << std::endl;
+
+    for(std::string::size_type i = 0; i < test.size(); i++)
+    {
+        int k;
+        BOOST_CHECK( btree.get(test[i],k) );
+        BOOST_CHECK_EQUAL( k , (-int(i)) );
+    }
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_int_int_nolocking )
+{
+    std::string test = "EzLPYjRkayhnTCv47SgoFV5MOqbGt6emNlD231JWIXUiBKfAupwc0rQ8xHsZd9";
+
+    unlink("foo");
+
+    BTree<int,int, 128, BTreeNoLock> btree("foo");
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+    {
+//		std::cout << "[" << test[i] << "," << -int(i) << "]" << std::endl;
+        int k = int(test[i]);
+        btree.set(k,-int(i));
+    }
+
+    BOOST_CHECK_EQUAL( btree.count() , test.size() );
+
+    std::vector< std::pair<int,int> > res;
+    btree.range(32,126, res);
+
+    //	for(int i = 0; i < res.size();  ++i)
+    //		std::cout << "[" << res[i].first << "," << res[i].second << "]" << std::endl;
+
+    BOOST_CHECK_EQUAL( btree.count(), res.size() );
+
+//	std::cout << std::endl;
+//	btree.dump() ;
+//	std::cout << std::endl;
+
+    for(std::string::size_type i = 0; i < test.size(); i++)
+    {
+        int k;
+        BOOST_CHECK( btree.get(test[i],k) );
+        BOOST_CHECK_EQUAL( k , (-int(i)) );
+    }
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_random_char_udf )
+{
+	std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+    for(int j = 0; j < 1 ; ++j)
+	{
+		std::random_shuffle( test.begin(),test.end() );
+
+		unlink("foo");
+
+        BTree< char, UDF, 6*UDF::SZ, BTreeLock> btree("foo");
+
+        for(std::string::size_type i = 0; i < test.size();  i++)
+		{
+			// std::cout << "[" << test[i] << "," << test[i] << "]" << std::endl;
+			btree.set( test[i], test[i] );
+		}
+
+		BOOST_CHECK_EQUAL( btree.count(), test.size() );
+
+		std::vector< std::pair<char,UDF> > res;
+		btree.range(char(32),char(126), res);
+
+		//		for(int i = 0; i < res.size();  ++i)
+		//			std::cout << "[" << res[i].first << "," << res[i].second << "]" << std::endl;
+		//		std::cout << std::endl;
+
+		BOOST_CHECK_EQUAL( btree.count(), res.size() );
+
+		std::cout << std::endl;
+		btree.dump() ;
+		std::cout << std::endl;
+
+        for(std::string::size_type i = 0; i < test.size(); i++)
+		{
+			UDF k;
+			BOOST_CHECK( btree.get(test[i],k) );
+			std::string s; s += test[i];
+			BOOST_CHECK_EQUAL( (std::string)(k) , s );
+		}
+	}
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_random_char_fixedstring )
+{
+	std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+	{
+//		std::random_shuffle( test.begin(),test.end() );
+
+		unlink("foo");
+
+        BTree< char, FixedString<16>, 156, BTreeLock> btree("foo");
+
+        for(std::string::size_type i = 0; i < test.size();  i++)
+		{
+			// std::cout << "[" << test[i] << "," << test[i] << "]" << std::endl;
+			std::string s ("foo"); s +=test[i];
+			btree.set( test[i], s );
+		}
+
+		BOOST_CHECK_EQUAL( btree.count(), test.size() );
+
+        std::vector< std::pair<char,FixedString<16> > > res;
+		btree.range(char(32),char(126), res);
+
+		//		for(int i = 0; i < res.size();  ++i)
+		//			std::cout << "[" << res[i].first << "," << res[i].second << "]" << std::endl;
+		//		std::cout << std::endl;
+
+		BOOST_CHECK_EQUAL( btree.count(), res.size() );
+
+		std::cout << std::endl;
+		btree.dump() ;
+		std::cout << std::endl;
+
+        for(std::string::size_type i = 0; i < test.size(); i++)
+		{
+			FixedString<16> v;
+			BOOST_CHECK( btree.get(test[i],v) );
+			std::string s ("foo"); s +=test[i];
+			BOOST_CHECK_EQUAL( s , v.asString() );
+		}
+	}
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_random_char_int )
+{
+	std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+	for(int j = 0; j < 100 ; j++)
+	{
+		std::random_shuffle( test.begin(),test.end() );
+
+		unlink("foo");
+
+        BTree<char,int, 128, BTreeLock> btree("foo");
+
+        for(std::string::size_type i = 0; i < test.size();  i++)
+		{
+			// std::cout << "[" << test[i] << "," << -int(i) << "]" << std::endl;
+			btree.set(test[i],-int(i));
+		}
+
+		BOOST_CHECK_EQUAL( btree.count(), test.size() );
+
+		std::vector< std::pair<char,int> > res;
+		btree.range(char(32),char(126), res);
+
+		//		for(int i = 0; i < res.size();  ++i)
+		//			std::cout << "[" << res[i].first << "," << res[i].second << "]" << std::endl;
+		//		std::cout << std::endl;
+
+		BOOST_CHECK_EQUAL( btree.count(), res.size() );
+
+//		std::cout << std::endl;
+//		btree.dump() ;
+//		std::cout << std::endl;
+
+        for(std::string::size_type i = 0; i < test.size(); i++)
+		{
+			int k;
+			BOOST_CHECK( btree.get(test[i],k) );
+			BOOST_CHECK_EQUAL( k , (-int(i)) );
+		}
+	}
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_fixedsttring_int_nolocking )
+{
+    std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+    unlink("bar");
+
+    BTree<FixedString<80>,int,1024, BTreeNoLock> btree("bar");
+
+    string f("foo");
+
+    btree.set(f, 22);
+    btree.set("barbar", 44);
+    btree.set("zoulou", 484);
+    btree.set("ZOULOU", 484);
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+    {
+        string s;
+        s += test[i];
+        btree.set(s,-int(i));
+    }
+
+    int k;
+
+    BOOST_CHECK( btree.get("foo",k) );
+    BOOST_CHECK_EQUAL( k, 22 );
+
+    BOOST_CHECK( btree.get("barbar",k) );
+    BOOST_CHECK_EQUAL( k, 44 );
+
+    BOOST_CHECK( btree.get("zoulou",k) );
+    BOOST_CHECK_EQUAL( k, 484 );
+
+    BOOST_CHECK( btree.get("ZOULOU",k) );
+    BOOST_CHECK_EQUAL( k, 484 );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_btree_fixedsttring_int_locking )
+{
+    std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+    unlink("bar");
+
+    BTree<FixedString<80>,int,1024, BTreeLock> btree("bar");
+
+    string f("foo");
+
+    btree.set(f, 22);
+    btree.set("barbar", 44);
+    btree.set("zoulou", 484);
+    btree.set("ZOULOU", 484);
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+    {
+        string s;
+        s += test[i];
+        btree.set(s,-int(i));
+    }
+
+    int k;
+
+    BOOST_CHECK( btree.get("foo",k) );
+    BOOST_CHECK_EQUAL( k, 22 );
+
+    BOOST_CHECK( btree.get("barbar",k) );
+    BOOST_CHECK_EQUAL( k, 44 );
+
+    BOOST_CHECK( btree.get("zoulou",k) );
+    BOOST_CHECK_EQUAL( k, 484 );
+
+    BOOST_CHECK( btree.get("ZOULOU",k) );
+    BOOST_CHECK_EQUAL( k, 484 );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_fixedsttring_fixedsttring )
+{
+	std::string test = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+	unlink("bar");
+
+    BTree< FixedString<80>, FixedString<256>, 2048, BTreeLock> btree("bar");
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+	{
+		string s;
+		s += test[i];
+		btree.set(s,s);
+	}
+
+	string f("foo");
+	btree.set(f,f);
+
+    for(std::string::size_type i = 0; i < test.size(); ++i)
+	{
+		string s;
+		s += test[i];
+		s += std::string("_bar");
+		btree.set(s,s);
+	}
+
+	FixedString<256> z;
+
+	BOOST_CHECK( btree.get(f, z) );
+	BOOST_CHECK_EQUAL( z, "foo" );
+
+	//	btree.dump();
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/container/test_cache_lru.cc b/eckit/src/tests/container/test_cache_lru.cc
new file mode 100644
index 0000000..4ad25fd
--- /dev/null
+++ b/eckit/src/tests/container/test_cache_lru.cc
@@ -0,0 +1,193 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <string>
+
+#define BOOST_TEST_MODULE eckit_test_cache_lru
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/container/CacheLRU.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static size_t purgeCalls = 0;
+
+static void purge(std::string& key, size_t& value) {
+    BOOST_TEST_MESSAGE( "Purged key " << key << " with value " << value );
+    ++purgeCalls;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_cache_lru )
+
+BOOST_AUTO_TEST_CASE( test_cache_lru_basic ) {
+    eckit::CacheLRU<std::string,size_t> cache(3);
+
+    BOOST_CHECK_EQUAL( cache.size() , 0 );
+    BOOST_CHECK_EQUAL( cache.capacity() , 3 );
+
+    // Add 2 items, check they're in the cache
+
+    BOOST_CHECK( !cache.insert("ddd", 40) );
+    BOOST_CHECK( !cache.insert("aaa", 5) );
+    BOOST_CHECK( cache.insert("aaa", 10) );
+
+    BOOST_CHECK_EQUAL( cache.size() , 2 );
+
+    BOOST_CHECK( cache.exists("ddd") );
+    BOOST_CHECK( cache.exists("aaa") );
+    BOOST_CHECK( !cache.exists("bbb") );
+
+    BOOST_CHECK_EQUAL( cache.access("aaa") , 10 );
+    BOOST_CHECK_EQUAL( cache.access("ddd") , 40 );
+
+    // Add 3 more items, displacing first 2
+
+    BOOST_CHECK( !cache.insert("ccc", 30) );
+    BOOST_CHECK( !cache.insert("eee", 50) );
+    BOOST_CHECK( !cache.insert("bbb", 20) );
+
+    BOOST_CHECK_EQUAL( cache.size() , 3 );
+
+    BOOST_CHECK( cache.exists("bbb") );
+    BOOST_CHECK( cache.exists("ccc") );
+    BOOST_CHECK( cache.exists("eee") );
+
+    BOOST_CHECK_EQUAL( cache.access("eee") , 50 );
+    BOOST_CHECK_EQUAL( cache.access("bbb") , 20 );
+    BOOST_CHECK_EQUAL( cache.access("ccc") , 30 );
+
+    BOOST_CHECK_THROW( cache.access("ddd"), eckit::OutOfRange );
+    BOOST_CHECK_THROW( cache.access("aaa"), eckit::OutOfRange );
+
+    // Increase capacity to 5, add 2 more items
+
+    cache.capacity(5);
+    BOOST_CHECK_EQUAL( cache.size() , 3 );
+    BOOST_CHECK_EQUAL( cache.capacity() , 5 );
+
+    BOOST_CHECK( !cache.insert("ddd", 40) );
+    BOOST_CHECK( !cache.insert("aaa", 10) );
+
+    BOOST_CHECK_EQUAL( cache.size() , 5 );
+
+    BOOST_CHECK( cache.exists("aaa") );
+    BOOST_CHECK( cache.exists("bbb") );
+    BOOST_CHECK( cache.exists("ccc") );
+    BOOST_CHECK( cache.exists("ddd") );
+    BOOST_CHECK( cache.exists("eee") );
+
+    BOOST_CHECK_EQUAL( cache.access("aaa") , 10 );
+    BOOST_CHECK_EQUAL( cache.access("bbb") , 20 );
+    BOOST_CHECK_EQUAL( cache.access("ccc") , 30 );
+    BOOST_CHECK_EQUAL( cache.access("ddd") , 40 );
+    BOOST_CHECK_EQUAL( cache.access("eee") , 50 );
+
+    // Reduce capacity to 3, 2 items are purged
+
+    cache.capacity(3);
+    BOOST_CHECK_EQUAL( cache.size() , 3 );
+    BOOST_CHECK_EQUAL( cache.capacity() , 3 );
+
+    BOOST_CHECK( !cache.exists("aaa") );
+    BOOST_CHECK( !cache.exists("bbb") );
+    BOOST_CHECK( cache.exists("ccc") );
+    BOOST_CHECK( cache.exists("ddd") );
+    BOOST_CHECK( cache.exists("eee") );
+
+    BOOST_CHECK_THROW( cache.access("aaa"), eckit::OutOfRange );
+    BOOST_CHECK_THROW( cache.access("bbb"), eckit::OutOfRange );
+    BOOST_CHECK_EQUAL( cache.access("ccc") , 30 );
+    BOOST_CHECK_EQUAL( cache.access("ddd") , 40 );
+    BOOST_CHECK_EQUAL( cache.access("eee") , 50 );
+
+    // Extract item
+
+    BOOST_CHECK_EQUAL( cache.extract("ccc") , 30 );
+    BOOST_CHECK_EQUAL( cache.size() , 2 );
+
+    BOOST_CHECK( !cache.exists("ccc") );
+    BOOST_CHECK( cache.exists("ddd") );
+    BOOST_CHECK( cache.exists("eee") );
+
+    BOOST_CHECK_THROW( cache.access("ccc"), eckit::OutOfRange );
+    BOOST_CHECK_EQUAL( cache.access("ddd") , 40 );
+    BOOST_CHECK_EQUAL( cache.access("eee") , 50 );
+
+    // Remove item
+
+    BOOST_CHECK( !cache.remove("ccc") );
+    BOOST_CHECK( cache.remove("ddd") );
+    BOOST_CHECK_EQUAL( cache.size() , 1 );
+
+    BOOST_CHECK( !cache.exists("ddd") );
+    BOOST_CHECK_THROW( cache.access("ddd"), eckit::OutOfRange );
+
+    // Clear the cache
+
+    BOOST_CHECK_NO_THROW( cache.clear() );
+    BOOST_CHECK_EQUAL( cache.size() , 0 );
+    BOOST_CHECK_EQUAL( cache.capacity() , 3 );
+
+    BOOST_CHECK( !cache.exists("eee") );
+
+    BOOST_CHECK_THROW( cache.access("eee"), eckit::OutOfRange );
+}
+
+BOOST_AUTO_TEST_CASE( test_cache_lru_purge ) {
+    eckit::CacheLRU<std::string,size_t> cache(4, purge);
+    BOOST_CHECK_EQUAL( purgeCalls , 0 );
+
+    BOOST_CHECK( !cache.insert("aaa", 10) );
+    BOOST_CHECK( !cache.insert("bbb", 20) );
+    BOOST_CHECK( !cache.insert("ccc", 30) );
+    BOOST_CHECK( !cache.insert("ddd", 40) );
+
+    BOOST_CHECK_EQUAL( purgeCalls , 0 );
+
+    // Reducing capacity purges
+
+    cache.capacity(3);
+    BOOST_CHECK_EQUAL( purgeCalls , 1 );
+
+    // Extract does not purge
+
+    BOOST_CHECK_EQUAL( cache.extract("bbb"), 20 );
+    BOOST_CHECK_EQUAL( purgeCalls , 1 );
+
+    // Remove purges
+
+    BOOST_CHECK( cache.remove("ccc") );
+    BOOST_CHECK_EQUAL( purgeCalls , 2 );
+
+    // Clear purges
+
+    BOOST_CHECK_NO_THROW( cache.clear() );
+    BOOST_CHECK_EQUAL( purgeCalls , 3 );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckittest
diff --git a/eckit/src/tests/container/test_densemap.cc b/eckit/src/tests/container/test_densemap.cc
new file mode 100755
index 0000000..a894e74
--- /dev/null
+++ b/eckit/src/tests/container/test_densemap.cc
@@ -0,0 +1,191 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE eckit_test_densemap
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/container/DenseMap.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_container_densemap )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_densemap_0 ) {
+
+   DenseMap<std::string,int> m;
+
+   BOOST_CHECK_NO_THROW( m.insert( "two",  2 ) );
+   BOOST_CHECK_NO_THROW( m.insert( "four", 4 ) );
+   BOOST_CHECK_NO_THROW( m.insert( "nine", 9 ) );
+
+   BOOST_CHECK_EQUAL( m.size(), 3 );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_map_string_int ) {
+    DenseMap<std::string,int> m;
+
+    //
+
+    m.insert( "two",  2 );
+
+	BOOST_CHECK( !m.sorted() );
+
+	BOOST_CHECK( m.size() == 1 );
+
+    m.sort();
+
+	BOOST_CHECK( m.sorted() );
+
+	BOOST_CHECK( m.get("two") == 2 );
+
+    //
+
+    m.insert( "four", 4 );
+    m.insert( "nine", 9 );
+
+	BOOST_CHECK( !m.sorted() );
+
+    m.sort();
+
+	BOOST_CHECK( m.sorted() );
+
+	BOOST_CHECK( m.size() == 3 );
+
+	BOOST_CHECK( m.get("two") == 2 );
+	BOOST_CHECK( m.get("four") == 4 );
+	BOOST_CHECK( m.get("nine") == 9 );
+
+    // failed find
+
+	BOOST_CHECK( ! m.has("one") );
+
+    // replace an existing value
+
+    m.replace( "four", 44 );  
+    m.replace( "nine", 99 );
+
+	BOOST_CHECK( m.sorted() ); // still sorted
+
+	BOOST_CHECK( m.size() == 3 );
+
+	BOOST_CHECK( m.get("two") == 2 );
+	BOOST_CHECK( m.get("four") == 44 );
+	BOOST_CHECK( m.get("nine") == 99 );
+
+    // replace into non-existing
+
+	BOOST_CHECK( m.size() == 3 );
+
+    m.replace( "five", 555 );  
+
+	BOOST_CHECK( !m.sorted() );
+    m.sort();
+
+	BOOST_CHECK( m.get("two") == 2 );
+	BOOST_CHECK( m.get("four") == 44 );
+	BOOST_CHECK( m.get("nine") == 99 );
+	BOOST_CHECK( m.get("five") == 555 );
+
+	BOOST_CHECK( m.size() == 4 );
+
+    //
+    // std::cout << m << std::endl;
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_map_int_string ) {
+    DenseMap<unsigned,std::string> m;
+
+    //
+
+    m.insert( 2, "two" );
+
+	BOOST_CHECK( !m.sorted() );
+
+	BOOST_CHECK( m.size() == 1 );
+
+    m.sort();
+
+	BOOST_CHECK( m.sorted() );
+
+	BOOST_CHECK( m.get(2) == "two" );
+
+    //
+
+    m.insert( 4, "four" );
+    m.insert( 9, "nine" );
+
+	BOOST_CHECK( !m.sorted() );
+
+    m.sort();
+
+	BOOST_CHECK( m.sorted() );
+
+	BOOST_CHECK( m.size() == 3 );
+
+	BOOST_CHECK( m.get(2) == "two" );
+	BOOST_CHECK( m.get(4) == "four" );
+	BOOST_CHECK( m.get(9) == "nine" );
+
+    // failed find
+
+	BOOST_CHECK( m.find(1) == m.end() );
+
+    // replace an existing value
+
+    m.replace( 4, "FOUR" );  
+    m.replace( 9, "NINE" );
+
+	BOOST_CHECK( m.sorted() ); // still sorted
+
+	BOOST_CHECK( m.size() == 3 );
+
+	BOOST_CHECK( m.get(2) == "two" );
+	BOOST_CHECK( m.get(4) == "FOUR" );
+	BOOST_CHECK( m.get(9) == "NINE" );
+
+    // replace into non-existing
+
+	BOOST_CHECK( m.size() == 3 );
+
+    m.replace( 5, "five" );  
+
+	BOOST_CHECK( !m.sorted() );
+    m.sort();
+
+	BOOST_CHECK( m.get(2) == "two" );
+	BOOST_CHECK( m.get(5) == "five" );
+	BOOST_CHECK( m.get(4) == "FOUR" );
+	BOOST_CHECK( m.get(9) == "NINE" );
+
+	BOOST_CHECK( m.size() == 4 );
+
+    // std::cout << m << std::endl;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/container/test_sharedmemarray.cc b/eckit/src/tests/container/test_sharedmemarray.cc
new file mode 100755
index 0000000..e598cb9
--- /dev/null
+++ b/eckit/src/tests/container/test_sharedmemarray.cc
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE eckit_test_sharedmemarray
+
+#include "ecbuild/boost_test_framework.h"
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "eckit/types/Types.h"
+#include "eckit/types/FixedString.h"
+#include "eckit/container/SharedMemArray.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/thread/AutoLock.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+struct ShmCleanup : Setup {
+    ~ShmCleanup() {
+        SYSCALL(::shm_unlink("/baz_hosts"));
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE( ShmCleanup );
+
+BOOST_AUTO_TEST_SUITE( test_eckit_sharedmemarray )
+
+struct Host {
+    eckit::FixedString<64> hostname_;
+    size_t connetions_;
+};
+
+BOOST_AUTO_TEST_CASE( test_eckit_sharedmemarray_construction )
+{
+    SharedMemArray<Host> hosts("~/etc/baz/hosts", "/baz_hosts", 128);
+
+    AutoLock< SharedMemArray<Host> > lock(hosts);
+
+    hosts[0].hostname_   = std::string("calvin");
+    hosts[0].connetions_ = 1;
+
+    hosts[1].hostname_ = std::string("hobbes");
+    hosts[1].connetions_ = 2;
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_sharedmemarray_checkvalues )
+{
+    SharedMemArray<Host> hosts("~/etc/baz/hosts", "/baz_hosts", 128);
+
+    AutoLock< SharedMemArray<Host> > lock(hosts);
+
+    BOOST_CHECK_EQUAL( hosts[0].hostname_ , std::string("calvin"));
+    BOOST_CHECK_EQUAL( hosts[0].connetions_ , 1);
+
+    BOOST_CHECK_EQUAL( hosts[1].hostname_ , std::string("hobbes"));
+    BOOST_CHECK_EQUAL( hosts[1].connetions_ , 2);
+
+    hosts[1].connetions_++;
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_sharedmemarray_add_more )
+{
+    SharedMemArray<Host> hosts("~/etc/baz/hosts", "/baz_hosts", 128);
+
+    AutoLock< SharedMemArray<Host> > lock(hosts);
+
+    BOOST_CHECK_EQUAL( hosts[1].hostname_ , std::string("hobbes"));
+    BOOST_CHECK_EQUAL( hosts[1].connetions_ , 3);
+
+    eckit::Translator<size_t,std::string> toStr;
+
+    for(size_t i = 0; i < hosts.size(); ++i) {
+        std::string h("node");
+        h += toStr(i);
+        hosts[i].hostname_ = h;
+        hosts[i].connetions_ = 2*i;
+    }
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_sharedmemarray_checkvalues_2 )
+{
+    SharedMemArray<Host> hosts("~/etc/baz/hosts", "/baz_hosts", 128);
+
+    AutoLock< SharedMemArray<Host> > lock(hosts);
+
+    eckit::Translator<size_t,std::string> toStr;
+
+    for(size_t i = 0; i < hosts.size(); ++i) {
+        std::string h("node");
+        h += toStr(i);
+        BOOST_CHECK_EQUAL( hosts[i].hostname_ , h);
+        BOOST_CHECK_EQUAL( hosts[i].connetions_ , 2*i);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
+
+
diff --git a/eckit/src/tests/filesystem/CMakeLists.txt b/eckit/src/tests/filesystem/CMakeLists.txt
new file mode 100644
index 0000000..1247863
--- /dev/null
+++ b/eckit/src/tests/filesystem/CMakeLists.txt
@@ -0,0 +1,22 @@
+# aio system calls are know to fail on the Cray
+# see https://sourceware.org/bugzilla/show_bug.cgi?id=11787
+
+ecbuild_add_test( TARGET      eckit_test_multihandle
+                  SOURCES     test_multihandle.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_aiohandle
+                  CONDITION   NOT DEFINED ENV{CRAYOS_VERSION}
+                  SOURCES     test_aiohandle.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_localpathname
+                  BOOST
+                  SOURCES     test_localpathname.cc
+                  LIBS        eckit )
+
+
+ecbuild_add_test( TARGET      eckit_test_restarthandle
+                  BOOST
+                  SOURCES     test_restarthandle.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/filesystem/test_aiohandle.cc b/eckit/src/tests/filesystem/test_aiohandle.cc
new file mode 100644
index 0000000..1a29896
--- /dev/null
+++ b/eckit/src/tests/filesystem/test_aiohandle.cc
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/AIOHandle.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/types/Types.h"
+
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestAIOHandle : public Tool {
+public:
+
+    TestAIOHandle(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestAIOHandle() {}
+
+    virtual void run();
+
+    void setup();
+    void teardown();
+
+    void test_write();
+    void test_append();
+
+    PathName path_;
+};
+
+
+void TestAIOHandle::test_write() {
+    ScopedPtr<DataHandle> aioh(new AIOHandle(path_));
+
+    aioh->openForWrite(0);
+
+    const char buf [] = "74e1feb8d0b1d328cbea63832c2dcfb2b4fa1adf";
+
+    aioh->write(buf,sizeof(buf));
+
+    aioh->close();
+
+    ScopedPtr<DataHandle> fh(path_.fileHandle());
+
+    fh->openForRead();
+
+    Buffer buf2(1024);
+
+    fh->read(buf2,buf2.size());
+    fh->close();
+
+    ASSERT( buf == std::string(buf2) );
+}
+
+
+void TestAIOHandle::test_append() {
+    ScopedPtr<DataHandle> aioh(new AIOHandle(path_));
+
+    aioh->openForAppend(0);
+
+    const char buf [] = "53d50e63a50fba73f0151028a695a238ff06491c";
+
+    aioh->write(buf,sizeof(buf));
+
+    aioh->close();
+
+    ScopedPtr<DataHandle> fh(path_.fileHandle());
+
+    fh->openForRead();
+
+    fh->seek( sizeof(buf) );
+
+    Buffer buf2(1024);
+
+    fh->read(buf2,buf2.size());
+    fh->close();
+
+    ASSERT( buf == std::string(buf2) );
+}
+
+
+void TestAIOHandle::setup() {
+    std::string base = Resource<std::string>("$TMPDIR", "/tmp");
+    path_ = PathName::unique( base + "/lolo" );
+    path_ += ".dat";
+}
+
+void TestAIOHandle::teardown() {
+    path_.unlink();
+}
+
+
+void TestAIOHandle::run() {
+    setup();
+
+    test_write();
+    test_append();
+
+    teardown();
+}
+
+} // namespace test
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit::test::TestAIOHandle app(argc,argv);
+    return app.start();
+}
diff --git a/eckit/src/tests/filesystem/test_localpathname.cc b/eckit/src/tests/filesystem/test_localpathname.cc
new file mode 100644
index 0000000..4600612
--- /dev/null
+++ b/eckit/src/tests/filesystem/test_localpathname.cc
@@ -0,0 +1,199 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_filesystem
+
+#include <string>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/filesystem/FileSystemSize.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( TestLocalPathName )
+
+BOOST_AUTO_TEST_CASE( test_constructors )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_constructors");
+
+   LocalPathName p;
+   BOOST_CHECK_MESSAGE(p == "/","Expected '/'");
+
+   LocalPathName p1("/fred/bill");
+   BOOST_CHECK_MESSAGE(p1 == "/fred/bill","Expected '/fred/bill' but found " << p);
+
+   LocalPathName p2(p1);
+   BOOST_CHECK_MESSAGE(p1 == p2,"Expected p1 == p2");
+}
+
+BOOST_AUTO_TEST_CASE( test_assignment )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_assignment");
+
+   LocalPathName p;
+   LocalPathName pd;
+   p = pd;
+   BOOST_CHECK_MESSAGE(p == "/","Expected '/'");
+
+   LocalPathName p1("/fred/bill");
+   LocalPathName p2;
+   p2 = p1;
+   BOOST_CHECK_MESSAGE(p2 == "/fred/bill","Expected '/fred/bill' but found " << p);
+
+   LocalPathName p3;
+   p3 = "/fred";
+   BOOST_CHECK_MESSAGE(p3 == "/fred","Expected '/fred'");
+
+   LocalPathName p4;
+   p4 = std::string("/fredd");
+   BOOST_CHECK_MESSAGE(p4 == "/fredd","Expected '/fredd'");
+}
+
+BOOST_AUTO_TEST_CASE( test_operators )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_operators");
+
+   LocalPathName p;
+
+   p += "fred";
+
+   BOOST_CHECK_MESSAGE(p == "/fred","Expected '/fred'  but found " << p);
+
+   p += "/joe/90";
+
+   BOOST_CHECK_MESSAGE(p == "/fred/joe/90","Expected '/fred/joe/90'  but found " << p);
+
+   p += '/';
+
+   BOOST_CHECK_MESSAGE(p == "/fred/joe/90/","Expected '/fred/joe/90/'  but found " << p);
+}
+
+
+BOOST_AUTO_TEST_CASE( test_dir_name )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_dir_name");
+
+   LocalPathName p("/fred/bill");
+   BOOST_CHECK_MESSAGE(p.dirName() == "/fred","Expected dirName to be '/fred' but found " << p.dirName());
+   BOOST_CHECK_MESSAGE(p.fullName() == "/fred/bill","Expected fullName to be '/fred/bill' but found " << p.fullName());
+
+   // when no leading '/' on pathname, we append cwd to path, for fullName()
+   LocalPathName p2("fred");
+   LocalPathName expected = LocalPathName::cwd() + "/" + "fred";
+   BOOST_CHECK_MESSAGE(p2.fullName() == expected,"Expected " << expected << " but found " << p2.fullName());
+}
+
+BOOST_AUTO_TEST_CASE( test_exists )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_exists");
+
+   LocalPathName cwd = LocalPathName::cwd();
+
+   BOOST_CHECK_MESSAGE(cwd.exists(),"Current working directory must exist");
+   BOOST_CHECK_MESSAGE(cwd.isDir(),"Must be a directory");
+
+   LocalPathName acwd = LocalPathName::cwd();
+   BOOST_CHECK_MESSAGE(cwd.sameAs(acwd),"The directories must be the same");
+
+   BOOST_TEST_MESSAGE("cwd " << cwd);
+   BOOST_TEST_MESSAGE("cwd.mountPoint() " << cwd.mountPoint());
+   BOOST_TEST_MESSAGE("cwd.realName() " << cwd.realName());
+   BOOST_TEST_MESSAGE("cwd.node() " << cwd.node());
+   BOOST_TEST_MESSAGE("cwd.path() " << cwd.path());
+   BOOST_TEST_MESSAGE("size " << cwd.size());
+   BOOST_TEST_MESSAGE("lastAccess " << cwd.lastAccess());
+   BOOST_TEST_MESSAGE("lastModified " << cwd.lastModified());
+   BOOST_TEST_MESSAGE("created " << cwd.created());
+}
+
+
+BOOST_AUTO_TEST_CASE( test_basename )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_basename");
+
+   LocalPathName p1;
+   BOOST_CHECK_MESSAGE(p1.baseName(false) == "","Expected empty string  but found " << p1.baseName(false));
+   BOOST_CHECK_MESSAGE(p1.baseName(true) == "","Expected empty string  but found " << p1.baseName(true));
+
+   LocalPathName p2("fred");
+   BOOST_CHECK_MESSAGE(p2.baseName(false) == "fred","Expected 'fred' but found " << p2.baseName(false));
+   BOOST_CHECK_MESSAGE(p2.baseName(true) == "fred","Expected 'fred' but found " << p2.baseName(true));
+
+   LocalPathName p("/a/made/up/path/that/does/not/exist/file.ok");
+   BOOST_CHECK_MESSAGE(p.baseName(false) == "file","Expected 'file'  but found " << p.baseName(false));
+   BOOST_CHECK_MESSAGE(p.baseName(true) == "file.ok","Expected 'file.ok'  but found " << p.baseName(true));
+}
+
+BOOST_AUTO_TEST_CASE( test_extension )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_extension");
+
+   LocalPathName p1;
+   BOOST_CHECK_MESSAGE(p1.extension() == "", "Expected empty string  but found " << p1.extension());
+
+   LocalPathName p2("fred");
+   BOOST_CHECK_MESSAGE(p2.extension() == "", "Expected empty string  but found " << p2.extension());
+   LocalPathName p3("/path/to/fred");
+   BOOST_CHECK_MESSAGE(p3.extension() == "", "Expected empty string  but found " << p3.extension());
+   LocalPathName p4("/path/with.dot/to/fred");
+   BOOST_CHECK_MESSAGE(p4.extension() == "", "Expected empty string  but found " << p4.extension());
+
+   LocalPathName p5("fred.");
+   BOOST_CHECK_MESSAGE(p5.extension() == ".", "Expected '.'  but found " << p5.extension());
+   LocalPathName p6("/path/to/fred.ext");
+   BOOST_CHECK_MESSAGE(p6.extension() == ".ext", "Expected '.ext'  but found " << p6.extension());
+   LocalPathName p7("/path/with.dot/to/fred.ext");
+   BOOST_CHECK_MESSAGE(p7.extension() == ".ext", "Expected '.ext'  but found " << p7.extension());
+}
+
+BOOST_AUTO_TEST_CASE( test_fileSystemSize )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_fileSystemSize");
+
+   LocalPathName cwd = LocalPathName::cwd();
+   FileSystemSize fs;
+   cwd.fileSystemSize(fs);
+
+   BOOST_TEST_MESSAGE("cwd " << cwd);
+   BOOST_TEST_MESSAGE("fs.available " << fs.available);
+   BOOST_TEST_MESSAGE("fs.total " << fs.total);
+
+   BOOST_CHECK_MESSAGE(fs.available > 0,"Expected file system available  > 0 ");
+   BOOST_CHECK_MESSAGE(fs.total > 0,"Expected file system total  > 0 ");
+}
+
+BOOST_AUTO_TEST_CASE( test_unique )
+{
+   BOOST_TEST_MESSAGE("eckit::filesystem:: ...test_unique");
+
+   LocalPathName unique = LocalPathName::unique(LocalPathName::cwd());
+
+   unique.mkdir();
+
+   BOOST_CHECK_MESSAGE(unique.exists(),"Expected '" << unique << "' to exist ");
+
+   unique.rmdir();
+   BOOST_CHECK_MESSAGE(!unique.exists(),"Expected '" << unique << "' to be removed ");
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/filesystem/test_multihandle.cc b/eckit/src/tests/filesystem/test_multihandle.cc
new file mode 100644
index 0000000..3cc2a9c
--- /dev/null
+++ b/eckit/src/tests/filesystem/test_multihandle.cc
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/MultiHandle.h"
+#include "eckit/io/PartFileHandle.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/types/Types.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestMHHandle : public Tool {
+public:
+
+    TestMHHandle(int argc, char **argv): Tool(argc, argv) {}
+
+    ~TestMHHandle() {}
+
+    virtual void run();
+
+    void setup();
+    void teardown();
+
+    void test_write();
+
+    PathName path1_;
+    PathName path2_;
+    PathName path3_;
+
+};
+
+
+void TestMHHandle::test_write()
+{
+    const char buf1 [] = "abcdefghijklmnopqrstuvwxyz";
+    const char buf2 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    char expect[26 * 2];
+
+    // setformat(std::cout, Log::fullFormat);
+
+    {
+        FileHandle f1(path1_);
+        f1.openForWrite(0);
+        f1.write(buf1, sizeof(buf1));
+        f1.close();
+
+        std::cout << path1_ << std::endl;
+    }
+
+     {
+        FileHandle f2(path2_);
+        f2.openForWrite(0);
+        f2.write(buf2, sizeof(buf2));
+        f2.close();
+
+        std::cout << path2_ << std::endl;
+
+    }
+
+    {
+        MultiHandle mh1;
+
+        char* e = expect;
+        for(int i = 0; i < 26; i++) {
+            mh1 += new PartFileHandle(path1_, i, 1);
+            mh1 += new PartFileHandle(path2_, i, 1);
+
+            *e++ = buf1[i];
+            *e++ = buf2[i];
+        }
+
+        std::cout << mh1 << std::endl;
+        std::cout << mh1.estimate() << std::endl;
+
+        mh1.compress();
+
+        std::cout << mh1 << std::endl;
+        std::cout << mh1.estimate() << std::endl;
+
+        FileHandle f3(path3_);
+        mh1.saveInto(f3);
+
+    }
+
+    DataHandle* fh = path3_.fileHandle();
+
+    fh->openForRead();
+
+    Buffer result(1024);
+
+    ASSERT(fh->read(result, result.size()) == 52);
+    fh->close();
+
+    delete fh;
+
+    ASSERT( ::memcmp(expect, result, 52) == 0 );
+}
+
+
+
+void TestMHHandle::setup()
+{
+    std::string base = Resource<std::string>("$TMPDIR", "/tmp");
+    path1_ = PathName::unique( base + "/path1" );
+    path1_ += ".dat";
+
+    path2_ = PathName::unique( base + "/path2" );
+    path2_ += ".dat";
+
+    path3_ = PathName::unique( base + "/path3" );
+    path3_ += ".dat";
+}
+
+void TestMHHandle::teardown()
+{
+    path1_.unlink();
+    path2_.unlink();
+    path3_.unlink();
+}
+
+
+void TestMHHandle::run()
+{
+    setup();
+
+    test_write();
+
+    teardown();
+}
+
+} // namespace test
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+    eckit::test::TestMHHandle app(argc, argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/filesystem/test_restarthandle.cc b/eckit/src/tests/filesystem/test_restarthandle.cc
new file mode 100644
index 0000000..ccbe433
--- /dev/null
+++ b/eckit/src/tests/filesystem/test_restarthandle.cc
@@ -0,0 +1,270 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/MultiHandle.h"
+#include "eckit/io/PartFileHandle.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/types/Types.h"
+#include "eckit/io/HandleHolder.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+
+class Restart : public DataHandle, public HandleHolder {
+    Length total_;
+    Length next_;
+
+public:
+
+    Restart(DataHandle* h): HandleHolder(h), total_(0) {
+        next_ = 4 * 1024 * 1024  + 1;
+    }
+
+    virtual Length openForRead() {
+        NOTIMP;
+    }
+
+    virtual void openForWrite(const Length& len) {
+        return handle().openForWrite(len);
+    }
+
+    virtual void openForAppend(const Length& len) {
+        NOTIMP;
+    }
+
+    virtual long read(void* buffer, long len) {
+        NOTIMP;
+    }
+
+    virtual long write(const void* buffer, long len) {
+        if (total_ > next_) {
+            next_ += len + 4 * 1024 * 1024 + 1;
+            throw RestartTransfer(total_ - Length(12345));
+        }
+        total_ += len;
+        return handle().write(buffer, len);
+    }
+
+    virtual void close() {
+        handle().close();
+    }
+
+    virtual void flush() {
+        NOTIMP;
+    }
+
+    virtual void rewind() {
+        NOTIMP;
+    }
+
+    virtual void print(std::ostream& os) const {
+        os << "Restart";
+    }
+
+    virtual void skip(const Length&) {
+        NOTIMP;
+    }
+
+    virtual Offset seek(const Offset&) {
+        NOTIMP;
+    }
+
+    virtual Length estimate() {
+        NOTIMP;
+    }
+
+    virtual Offset position() {
+        NOTIMP;
+    }
+
+    virtual DataHandle* clone() const {
+        NOTIMP;
+    }
+
+    virtual void restartReadFrom(const Offset& offset) {
+        handle().restartReadFrom(offset);
+    }
+
+    virtual void restartWriteFrom(const Offset& offset) {
+        handle().restartWriteFrom(offset);
+    }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestMHHandle : public Tool {
+public:
+
+    TestMHHandle(int argc, char **argv): Tool(argc, argv) {}
+
+    ~TestMHHandle() {}
+
+    virtual void run();
+
+    void setup();
+    void teardown();
+
+    void test_write();
+
+    PathName path1_;
+    PathName path2_;
+    PathName path3_;
+
+};
+
+
+void TestMHHandle::test_write()
+{
+    const char buf1 [] = "abcdefghijklmnopqrstuvwxyz";
+    const char buf2 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+    const size_t N = 1024 * 1024 * 64;
+
+    // setformat(std::cout, Log::fullFormat);
+
+
+    {
+
+        Buffer b1(N * 26);
+        char *p = b1;
+        for (size_t i = 0; i < N; i++) {
+            memcpy(p, buf1, 26);
+            p += 26;
+        }
+
+        FileHandle f1(path1_);
+        f1.openForWrite(0);
+        f1.write(b1, b1.size());
+
+        f1.close();
+
+        std::cout << path1_ << std::endl;
+    }
+
+    {
+        Buffer b2(N * 26);
+        char *p = b2;
+        for (size_t i = 0; i < N; i++) {
+            memcpy(p, buf2, 26);
+            p += 26;
+        }
+
+
+        FileHandle f2(path2_);
+        f2.openForWrite(0);
+        f2.write(b2, b2.size());
+
+        f2.close();
+
+        std::cout << path2_ << std::endl;
+
+    }
+
+    std::cout << "-----" << std::endl;
+
+    MultiHandle mh1;
+
+    {
+
+        for (int i = 0; i < 26; i++) {
+
+            mh1 += new PartFileHandle(path1_, i * N, N);
+            mh1 += new PartFileHandle(path2_, i * N, N);
+
+        }
+
+        std::cout << mh1 << std::endl;
+        std::cout << mh1.estimate() << std::endl;
+
+        // mh1.compress();
+
+        // std::cout << mh1 << std::endl;
+        // std::cout << mh1.estimate() << std::endl;
+
+        Restart f3(path3_.fileHandle());
+        mh1.saveInto(f3);
+
+    }
+
+    DataHandle* fh = path3_.fileHandle();
+    ASSERT(fh->compare(mh1));
+    delete fh;
+
+    // fh->openForRead();
+
+    // Buffer result((26 * 2) * N);
+
+    // ASSERT(fh->read(result, result.size()) == (26 * 2) * N);
+    // fh->close();
+
+    // delete fh;
+
+    // ASSERT( ::memcmp(expect, result, (26 * 2) * N) == 0 );
+}
+
+
+
+void TestMHHandle::setup()
+{
+    std::string base = Resource<std::string>("$TMPDIR", "/tmp");
+    path1_ = PathName::unique( base + "/path1" );
+    path1_ += ".dat";
+
+    path2_ = PathName::unique( base + "/path2" );
+    path2_ += ".dat";
+
+    path3_ = PathName::unique( base + "/path3" );
+    path3_ += ".dat";
+}
+
+void TestMHHandle::teardown()
+{
+    if (path1_.exists()) path1_.unlink();
+    if (path2_.exists()) path2_.unlink();
+    if (path3_.exists()) path3_.unlink();
+}
+
+
+void TestMHHandle::run()
+{
+    setup();
+
+    try {
+        test_write();
+    } catch (...) {
+        teardown();
+        throw;
+    }
+
+    teardown();
+}
+
+} // namespace test
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+    eckit::test::TestMHHandle app(argc, argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/geometry/CMakeLists.txt b/eckit/src/tests/geometry/CMakeLists.txt
new file mode 100644
index 0000000..0c7dd24
--- /dev/null
+++ b/eckit/src/tests/geometry/CMakeLists.txt
@@ -0,0 +1,6 @@
+ecbuild_add_test(
+    TARGET    eckit_test_kdtree
+    LIBS      eckit_geometry
+    BOOST
+    SOURCES   test_kdtree.cc
+    )
diff --git a/eckit/src/tests/geometry/test_kdtree.cc b/eckit/src/tests/geometry/test_kdtree.cc
new file mode 100755
index 0000000..8a2b384
--- /dev/null
+++ b/eckit/src/tests/geometry/test_kdtree.cc
@@ -0,0 +1,131 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <list>
+
+#define BOOST_TEST_MODULE test_eckit_geometry
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/container/KDTree.h"
+#include "eckit/geometry/Point2.h"
+#include "eckit/os/Semaphore.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::geometry;
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+struct TestTreeTrait {
+    typedef Point2   Point;
+    typedef double   Payload;
+};
+
+}
+
+//-----------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_container_kdtree )
+
+BOOST_AUTO_TEST_CASE( test_eckit_container_kdtree_constructor )
+{
+    typedef KDTreeMemory<TestTreeTrait> Tree;
+
+    Tree kd;
+    typedef Tree::PointType Point;
+
+    std::vector<Tree::Value> points;
+
+    // test it for single closest point
+
+    for (unsigned int i = 0; i < 10; i++) {
+        for (unsigned int j = 0; j < 10; j++) {
+            Point p = Point(double(i), double(j));
+            Tree::Value v(p, 99.9);
+            points.push_back(v);
+        }
+    }
+
+    kd.build(points.begin(), points.end());
+
+    // pick some point from the vector
+    Point refPoint = points[points.size() / 2].point();
+
+    // perturb it a little
+    Point delta(0.1, 0.1);
+    Point testPoint = Point::add(refPoint, delta);
+    BOOST_TEST_MESSAGE( "testPoint perturb " << testPoint.x(0) << ", " << testPoint.x(1) );
+
+    Point nr = kd.nearestNeighbour(testPoint).point();
+
+    // we should find the same point
+    for (unsigned int i = 0; i < Point::dimensions(); i++)
+        BOOST_CHECK_EQUAL(nr.x(i) , refPoint.x(i));
+
+
+    // test exact match to a point
+
+    nr = kd.nearestNeighbour(refPoint).point();
+    for (unsigned int i = 0; i < Point::dimensions(); i++)
+        BOOST_CHECK_EQUAL(nr.x(i) , refPoint.x(i));
+
+
+    // test "off the scale" - i.e. not within a group of points
+    delta = Point(1000.0, 0.0);
+    // add it to the last point
+    testPoint = Point::add(points.back().point(), delta);
+    nr = kd.nearestNeighbour(testPoint).point();
+
+    for (unsigned int i = 0; i < Point::dimensions(); i++)
+        BOOST_CHECK_EQUAL(nr.x(i) , points.back().point().x(i));
+
+    // and negatively
+    //
+    delta = Point(-1000.0, 0.0);
+    // add it to the point() point
+    testPoint = Point::add(points.front().point(), delta);
+    nr = kd.nearestNeighbour(testPoint).point();
+
+    for (unsigned int i = 0; i < Point::dimensions(); i++)
+        BOOST_CHECK_EQUAL(nr.x(i) , points.front().point().x(i));
+
+
+    // test N nearest
+    refPoint = points[points.size() / 2].point();
+    // move this point so it lies between four equally
+    delta = Point(0.5, 0.5);
+    testPoint = Point::add(refPoint, delta);
+
+    Tree::NodeList nn = kd.kNearestNeighbours(testPoint, 4);
+    for (Tree::NodeList::iterator it = nn.begin(); it != nn.end(); ++it)
+    {
+        Point diff = Point::sub(it->point(), testPoint);
+        // make sure we differ by 0.5 along each axis
+        for (unsigned int i = 0; i < Point::dimensions(); ++i)
+        {
+            BOOST_TEST_MESSAGE( "distance along point " << Point::distance(Point(0.0, 0.0), diff, i) );
+            BOOST_CHECK_EQUAL(Point::distance(Point(0.0, 0.0), diff, i) , 0.5);
+        }
+
+    }
+
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/io/CMakeLists.txt b/eckit/src/tests/io/CMakeLists.txt
new file mode 100644
index 0000000..7b3632a
--- /dev/null
+++ b/eckit/src/tests/io/CMakeLists.txt
@@ -0,0 +1,9 @@
+ecbuild_add_test( TARGET   eckit_test_filepool
+                  BOOST
+                  SOURCES  test_filepool.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET  eckit_test_datablob
+                  BOOST
+                  SOURCES test_datablob.cc
+                  LIBS    eckit )
diff --git a/eckit/src/tests/io/test_datablob.cc b/eckit/src/tests/io/test_datablob.cc
new file mode 100755
index 0000000..82972df
--- /dev/null
+++ b/eckit/src/tests/io/test_datablob.cc
@@ -0,0 +1,107 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+#include <iostream>
+#include <string>
+
+#define BOOST_TEST_MODULE test_eckit_io
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/io/DataBlob.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/types/Metadata.h"
+
+using namespace std;
+using namespace eckit;
+
+// ----------------------------------------------------------------------------------------
+
+namespace {
+
+    // A trivial do-nothing metadata object
+    class TestMetadata : public Metadata {
+    public: // methods
+        TestMetadata() {}
+        virtual ~TestMetadata() {}
+        virtual std::vector<std::string> keywords() const { NOTIMP; };
+        virtual bool has(const std::string& name) const { NOTIMP; }
+        virtual void get(const std::string& name, std::string& value) const { NOTIMP; }
+        virtual void get(const std::string& name, long& value) const { NOTIMP; }
+        virtual void get(const std::string& name, double& value) const { NOTIMP; }
+        friend std::ostream& operator<<(std::ostream& s, const TestMetadata& p) {
+            p.print(s);
+            return s;
+        }
+    protected: // methods
+        virtual void print(std::ostream& os) const { os << "TestMetadata()"; }
+    };
+
+    //
+    // A null datablob for testing the factories
+    class TestDataBlob : public DataBlob {
+
+    public: // methods
+
+        TestDataBlob(const void* data, size_t length) : DataBlob(data, length) {}
+        TestDataBlob(DataHandle& dh, size_t length) : DataBlob(dh, length) {}
+
+        virtual ~TestDataBlob() {}
+
+        virtual const Metadata& metadata() const { return metadata_; }
+
+    private: // methods
+
+        virtual void print(std::ostream& os) const { os << "TestDataBlob()"; }
+
+    private: // members
+
+        TestMetadata metadata_;
+    };
+
+    DataBlobBuilder<TestDataBlob> dbBuilder("test");
+}
+
+// ----------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_io_datablob )
+
+BOOST_AUTO_TEST_CASE( test_eckit_io_datablob_factory_generate )
+{
+    ScopedPtr<DataBlob> blob(DataBlobFactory::build("test", NULL, 0));
+
+    // Check that we generate a blob of the correct type (and implicitly that the factory
+    // is correctly registered).
+    TestDataBlob * testBlob = dynamic_cast<TestDataBlob*>(blob.get());
+    BOOST_CHECK(testBlob);
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_io_datablob_factory_list )
+{
+    // DataBlobFactory::list appends the results to a ostream&, so we need to extract them.
+    std::stringstream ss;
+    DataBlobFactory::list(ss);
+
+    // Extract the seperate components from the string stream into a vector
+    std::vector<std::string> bits;
+    Tokenizer(" ,")(ss.str(), bits);
+
+    // We expect the file and MultIO factories to be in there too...
+    BOOST_CHECK(std::find(bits.begin(), bits.end(), "json") != bits.end());
+    BOOST_CHECK(std::find(bits.begin(), bits.end(), "test") != bits.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+// ----------------------------------------------------------------------------------------
+
diff --git a/eckit/src/tests/io/test_filepool.cc b/eckit/src/tests/io/test_filepool.cc
new file mode 100755
index 0000000..d5e9c24
--- /dev/null
+++ b/eckit/src/tests/io/test_filepool.cc
@@ -0,0 +1,243 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE eckit_test_filepool
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/FilePool.h"
+#include "eckit/thread/ThreadPool.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const size_t BUF_SIZE = 1024;
+
+static const char* files[] = {"foo.data", "bar.data", "baz.data", "marco.data", "polo.data"};
+
+class FilePoolUser : public ThreadPoolTask {
+public:
+    FilePoolUser(FilePool& pool, int id) : pool_(pool), id_(id) {}
+private:
+    virtual void execute() {
+        std::vector<char> buffer(BUF_SIZE, id_);
+        DataHandle* foo = pool_.checkout("foo.data");
+        foo->write(&buffer[0], buffer.size());
+        pool_.checkin(foo);
+    }
+
+    FilePool& pool_;
+    int id_;
+};
+
+struct Setup : public testing::Setup {
+    ~Setup() {
+        for (size_t i = 0; i < 5; ++i) {
+            PathName path(files[i]);
+            if (path.exists()) path.unlink();
+        }
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE( Setup );
+
+BOOST_AUTO_TEST_SUITE( test_eckit_io_filepool )
+
+BOOST_AUTO_TEST_CASE( test_eckit_io_filepool_threads ) {
+    const size_t nThreads = Resource<size_t>("$ECKIT_TEST_THREADS", 16);
+
+    ThreadPool threads("filepool", nThreads);
+    FilePool pool(1);
+    for (size_t i = 0; i < nThreads; ++i) {
+        threads.push(new FilePoolUser(pool, i + 1));
+    }
+    threads.waitForThreads();
+
+    DataHandle* foo = pool.checkout("foo.data");
+    BOOST_CHECK_GE( foo->openForRead(), Length(nThreads * BUF_SIZE) );
+
+    // Check we have nThreads blocks of BUF_SIZE with Bytes 1 to nThreads
+    std::vector<bool> found(nThreads);
+    for (size_t i = 0; i < nThreads; ++i) {
+        char buffer[BUF_SIZE];
+        foo->read(buffer, BUF_SIZE);
+        const char c = buffer[0];
+        BOOST_CHECK_GT( c, 0 );
+        BOOST_CHECK( !found[c-1] );
+        std::vector<char> expect(BUF_SIZE, c);
+        BOOST_CHECK_EQUAL_COLLECTIONS( buffer, buffer + BUF_SIZE, expect.begin(), expect.end() );
+        found[c-1] = true;
+    }
+    for (size_t i = 0; i < nThreads; ++i) {
+        BOOST_CHECK( found[i] );
+    }
+
+    pool.checkin(foo);
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  1 );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_io_filepool_0 ) {
+    Buffer buffer(BUF_SIZE);
+    ::memset(buffer, 0, buffer.size());
+
+    FilePool pool(1);
+
+    // pool sized 1
+
+    BOOST_CHECK_EQUAL( pool.capacity(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),     0 );
+
+    pool.capacity(3);
+
+    // pool is larger
+
+    BOOST_CHECK_EQUAL( pool.capacity(), 3 );
+    BOOST_CHECK_EQUAL( pool.size(),     0 );
+
+    DataHandle* foo = pool.checkout("foo.data");
+    foo->write(buffer, buffer.size());
+
+    // foo is in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),  0 );
+
+    DataHandle* bar = pool.checkout("bar.data");
+    bar->write(buffer, buffer.size());
+
+    // foo + bar are in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 2 );
+    BOOST_CHECK_EQUAL( pool.size(),  0 );
+
+    BOOST_TEST_MESSAGE( pool );
+
+    pool.checkin(foo);
+    pool.checkin(bar);
+
+    BOOST_TEST_MESSAGE( pool );
+
+    // foo + bar are in pool, none in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  2 );
+
+    DataHandle* foo2 = pool.checkout("foo.data");
+    foo2->write(buffer, buffer.size());
+
+    // foo in use again, bar in pool
+
+    BOOST_CHECK_EQUAL( pool.usage(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),  1 );
+
+    pool.checkin(foo);
+
+    // foo + bar are again in pool, none in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  2 );
+
+    DataHandle* baz = pool.checkout("baz.data");
+    baz->write(buffer, buffer.size());
+
+    // foo + bar in pool, baz in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),  2 );
+
+    pool.checkin(baz);
+
+    // baz + foo + bar in pool, none in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  3 );
+
+    DataHandle* marco = pool.checkout("marco.data");
+    marco->write(buffer, buffer.size());
+
+    // baz + foo + bar in pool, marco in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),  3 );
+
+    pool.checkin(marco);
+
+    // marco + foo + baz in pool, none in use, bar was purged (closed)
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  3 );
+
+    BOOST_TEST_MESSAGE( pool );
+
+    pool.capacity(1);
+
+    // marco in pool, none in use, foo + baz were purged (closed) due to reduced capacity
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  1 );
+
+    BOOST_TEST_MESSAGE( pool );
+
+    DataHandle* polo = pool.checkout("polo.data");
+    polo->write(buffer, buffer.size());
+
+    // marco in pool, polo is in use
+
+    BOOST_CHECK_EQUAL( pool.usage(), 1 );
+    BOOST_CHECK_EQUAL( pool.size(),  1 );
+
+    BOOST_TEST_MESSAGE( pool );
+
+    pool.checkin(polo);
+
+    // polo pushes marco out and remains alone in pool
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  1 );
+
+    BOOST_TEST_MESSAGE( pool );
+
+    // remove polo and marco (latter has no effect but is legal)
+
+    BOOST_CHECK( pool.remove("polo.data") );
+    BOOST_CHECK( !pool.remove("marco.data") );
+
+    BOOST_CHECK_EQUAL( pool.usage(), 0 );
+    BOOST_CHECK_EQUAL( pool.size(),  0 );
+
+    // checking in a never checked out DataHandle throws
+
+    BOOST_CHECK_THROW( pool.checkin(new FileHandle("foo.data")), eckit::SeriousBug );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/large_file/CMakeLists.txt b/eckit/src/tests/large_file/CMakeLists.txt
new file mode 100644
index 0000000..e7e368d
--- /dev/null
+++ b/eckit/src/tests/large_file/CMakeLists.txt
@@ -0,0 +1,3 @@
+ecbuild_add_test( TARGET      eckit_test_large_file
+                  SOURCES     test_large_file.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/large_file/test_large_file.cc b/eckit/src/tests/large_file/test_large_file.cc
new file mode 100644
index 0000000..498070d
--- /dev/null
+++ b/eckit/src/tests/large_file/test_large_file.cc
@@ -0,0 +1,88 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cassert>
+#include <iostream>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+
+#include <limits>
+#include <cmath>
+
+#include <sys/mman.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/filesystem/PathName.h"
+
+#define SIZE_2G 2147483648    // 2^31
+#define SIZE_1M    1048576    // 2^20
+
+#define TOTAL_SIZE  (SIZE_2G+SIZE_1M) // 2Gb + 1Mb file
+
+// #define TOTAL_SIZE  2*SIZE_2G // 4Gb file
+
+using namespace std;
+using namespace eckit;
+
+int main(int argc, char *argv[])
+{
+  std::cout << "off_t can index " <<  Bytes( std::pow( 2.0, (int) sizeof(off_t)*8 ) ) << std::endl;
+
+  // make temporary file name
+  int fd;
+  char filename [] = "large.XXXXXX";
+  if( ( fd = ::mkstemp( filename )) == -1 )
+    ::perror("cannot create temporary file"), ::exit(EXIT_FAILURE);
+
+  std::cout << "openning file '" << filename << "'" << std::endl;
+
+  // file is opened in mkstemp, so this is not needed
+  //    if( ( fd = ::open(filename, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600) ) == -1 )
+  //      perror("Error opening file"),  exit(EXIT_FAILURE);
+    
+  // stretch the file size
+  if( (long long) ::lseek(fd, TOTAL_SIZE-1, SEEK_SET) < 0 )
+    ::perror("Error calling lseek() to 'stretch' the file"), ::exit(EXIT_FAILURE);
+  
+  // write something to the end of the file to actually resize it correctly
+  if( ::write(fd, "", 1) != 1 )
+    ::close(fd), perror("Error writing last byte of the file"), ::exit(EXIT_FAILURE);
+
+  // lock the region beyond the 2GB limit until the EOF
+
+  struct flock fl;
+
+  fl.l_type   = F_WRLCK;   // F_RDLCK, F_WRLCK, F_UNLCK
+  fl.l_whence = SEEK_SET;  // SEEK_SET, SEEK_CUR, SEEK_END
+  fl.l_start  = SIZE_2G;   // Offset from l_whence
+  fl.l_len    = 0;         // length, 0 = to EOF
+  fl.l_pid    = getpid();  // PID
+
+  fcntl(fd, F_SETLKW, &fl);
+
+  // unlock the region
+  fl.l_type   = F_UNLCK;
+  fcntl(fd, F_SETLK, &fl);
+
+  eckit::PathName file(filename);
+
+  file.unlink();
+
+  return 0;
+}
diff --git a/eckit/src/tests/linalg/CMakeLists.txt b/eckit/src/tests/linalg/CMakeLists.txt
new file mode 100644
index 0000000..1076078
--- /dev/null
+++ b/eckit/src/tests/linalg/CMakeLists.txt
@@ -0,0 +1,95 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+ecbuild_add_test( TARGET   eckit_test_la_factory
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_la_factory.cc
+                  LIBS     eckit_linalg )
+
+ecbuild_add_test( TARGET   eckit_test_la_linalg
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_la_linalg.cc util.h
+                  LIBS     eckit_linalg )
+
+ecbuild_add_test( TARGET     eckit_test_la_linalg_armadillo
+                  BOOST
+                  COMMAND    eckit_test_la_linalg
+                  CONDITION  ECKIT_HAVE_ARMADILLO
+                  ARGS       --log_level=message -linearAlgebraBackend armadillo )
+
+# This test seems to have a system call exit with 1 even though tests pass.
+# Ignore system errors, see also http://stackoverflow.com/a/20360334/396967
+ecbuild_add_test( TARGET     eckit_test_la_linalg_cuda
+                  BOOST
+                  COMMAND    eckit_test_la_linalg
+                  CONDITION  ECKIT_HAVE_CUDA
+                  ARGS       --log_level=message --catch_system_errors=no -linearAlgebraBackend cuda )
+
+ecbuild_add_test( TARGET     eckit_test_la_linalg_eigen
+                  BOOST
+                  COMMAND    eckit_test_la_linalg
+                  CONDITION  ECKIT_HAVE_EIGEN
+                  ARGS       --log_level=message -linearAlgebraBackend eigen )
+
+ecbuild_add_test( TARGET     eckit_test_la_linalg_mkl
+                  BOOST
+                  COMMAND    eckit_test_la_linalg
+                  CONDITION  ECKIT_HAVE_MKL
+                  ARGS       --log_level=message -linearAlgebraBackend mkl )
+
+ecbuild_add_test( TARGET     eckit_test_la_linalg_viennacl
+                  BOOST
+                  COMMAND    eckit_test_la_linalg
+                  CONDITION  ECKIT_HAVE_VIENNACL
+                  ARGS       --log_level=message -linearAlgebraBackend viennacl )
+
+ecbuild_add_test( TARGET   eckit_test_la_sparse
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_la_sparse.cc util.h
+                  LIBS     eckit_linalg )
+
+ecbuild_add_test( TARGET     eckit_test_la_sparse_armadillo
+                  BOOST
+                  COMMAND    eckit_test_la_sparse
+                  CONDITION  ECKIT_HAVE_ARMADILLO
+                  ARGS       --log_level=message -linearAlgebraBackend armadillo )
+
+# This test seems to have a system call exit with 1 even though tests pass.
+# Ignore system errors, see also http://stackoverflow.com/a/20360334/396967
+ecbuild_add_test( TARGET     eckit_test_la_sparse_cuda
+                  BOOST
+                  COMMAND    eckit_test_la_sparse
+                  CONDITION  ECKIT_HAVE_CUDA
+                  ARGS       --log_level=message --catch_system_errors=no -linearAlgebraBackend cuda )
+
+ecbuild_add_test( TARGET     eckit_test_la_sparse_eigen
+                  BOOST
+                  COMMAND    eckit_test_la_sparse
+                  CONDITION  ECKIT_HAVE_EIGEN
+                  ARGS       --log_level=message -linearAlgebraBackend eigen )
+
+ecbuild_add_test( TARGET     eckit_test_la_sparse_mkl
+                  BOOST
+                  COMMAND    eckit_test_la_sparse
+                  CONDITION  ECKIT_HAVE_MKL
+                  ARGS       --log_level=message -linearAlgebraBackend mkl )
+
+ecbuild_add_test( TARGET     eckit_test_la_sparse_viennacl
+                  BOOST
+                  COMMAND    eckit_test_la_sparse
+                  CONDITION  ECKIT_HAVE_VIENNACL
+                  ARGS       --log_level=message -linearAlgebraBackend viennacl )
+
+ecbuild_add_test( TARGET   eckit_test_la_streaming
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_la_streaming.cc util.h
+                  LIBS     eckit_linalg )
diff --git a/eckit/src/tests/linalg/test_la_factory.cc b/eckit/src/tests/linalg/test_la_factory.cc
new file mode 100644
index 0000000..6c9adc3
--- /dev/null
+++ b/eckit/src/tests/linalg/test_la_factory.cc
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   test_la_factory.cc
+/// @author Florian Rathgeber
+/// @date   July 2015
+
+#define BOOST_TEST_MODULE test_eckit_la_factory
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/eckit_config.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/linalg/LinearAlgebra.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit::linalg;
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+/// Test linear algebra factory
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE(test_eckit_la_factory)
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE(test_list) {
+    std::ostringstream oss;
+    LinearAlgebra::list(oss);
+    BOOST_TEST_MESSAGE("Available linear algebra backends: " << oss.str());
+    BOOST_CHECK_NE(oss.str().find("generic"), std::string::npos);
+#ifdef ECKIT_HAVE_ARMADILLO
+    BOOST_CHECK_NE(oss.str().find("armadillo"), std::string::npos);
+#endif
+#ifdef ECKIT_HAVE_CUDA
+    BOOST_CHECK_NE(oss.str().find("cuda"), std::string::npos);
+#endif
+#ifdef ECKIT_HAVE_EIGEN
+    BOOST_CHECK_NE(oss.str().find("eigen"), std::string::npos);
+#endif
+#ifdef ECKIT_HAVE_MKL
+    BOOST_CHECK_NE(oss.str().find("mkl"), std::string::npos);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE(test_backend) {
+    LinearAlgebra::backend();
+    LinearAlgebra::backend("generic");
+    LinearAlgebra::backend();
+#ifdef ECKIT_HAVE_ARMADILLO
+    LinearAlgebra::backend("armadillo");
+    LinearAlgebra::backend();
+#endif
+#ifdef ECKIT_HAVE_CUDA
+    LinearAlgebra::backend("cuda");
+    LinearAlgebra::backend();
+#endif
+#ifdef ECKIT_HAVE_EIGEN
+    LinearAlgebra::backend("eigen");
+    LinearAlgebra::backend();
+#endif
+#ifdef ECKIT_HAVE_MKL
+    LinearAlgebra::backend("mkl");
+    LinearAlgebra::backend();
+#endif
+    BOOST_CHECK_THROW(LinearAlgebra::backend("foo"), BadParameter);
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}  // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/linalg/test_la_linalg.cc b/eckit/src/tests/linalg/test_la_linalg.cc
new file mode 100644
index 0000000..41b3a16
--- /dev/null
+++ b/eckit/src/tests/linalg/test_la_linalg.cc
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_la_linalg
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/runtime/Main.h"
+
+#include "eckit/linalg/LinearAlgebra.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/Vector.h"
+#include "util.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit::testing;
+using namespace eckit::linalg;
+
+namespace eckit {
+namespace test {
+
+// Set linear algebra backend
+struct Setup : testing::Setup {
+    Setup() : testing::Setup() {
+        LinearAlgebra::backend(Resource<std::string>("-linearAlgebraBackend", "generic"));
+    }
+};
+
+struct Fixture {
+
+    Fixture() : x(V(3, 1., 2., 4.)),
+                A(M(2, 2, 1., -2., -4., 2.)),
+                linalg(LinearAlgebra::backend()) {}
+
+    Vector x;
+    Matrix A;
+   const LinearAlgebra& linalg;
+};
+
+
+template <class T>
+void test(const T& v, const T& r) {
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.data(), v.data() + v.size(), r.data(), r.data() + r.size());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Test linear algebra interface
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_FIXTURE_TEST_SUITE(test_eckit_la_linalg, Fixture)
+
+BOOST_AUTO_TEST_CASE(test_dot) {
+    BOOST_CHECK_EQUAL(linalg.dot(x, x), 21.);
+    BOOST_TEST_MESSAGE("dot of vectors of different sizes should fail");
+    BOOST_CHECK_THROW(linalg.dot(x, Vector(2)), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_gemv) {
+    Vector y(2);
+    linalg.gemv(A, V(2, -1., -2.), y);
+    test(y, V(2, 3., 0.));
+    BOOST_TEST_MESSAGE("gemv of matrix and vector of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.gemv(A, x, y), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_gemm) {
+    Matrix B(2, 2);
+    linalg.gemm(A, A, B);
+    test(B, M(2, 2, 9., -6., -12., 12.));
+    BOOST_TEST_MESSAGE("gemm of matrices of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.gemm(A, Matrix(1, 2), B), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckittest
diff --git a/eckit/src/tests/linalg/test_la_sparse.cc b/eckit/src/tests/linalg/test_la_sparse.cc
new file mode 100644
index 0000000..8e16d05
--- /dev/null
+++ b/eckit/src/tests/linalg/test_la_sparse.cc
@@ -0,0 +1,312 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_la_linalg
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/linalg/LinearAlgebra.h"
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+
+#include "util.h"
+
+#include "eckit/testing/Setup.h"
+
+
+
+using namespace eckit::linalg;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+SparseMatrix S(Size rows, Size cols, Size nnz, ...) {
+    va_list args;
+    va_start(args, nnz);
+    std::vector<Triplet> triplets;
+    for (Size n = 0; n < nnz; ++n) {
+        Size row = Size( va_arg(args, int) );
+        Size col = Size( va_arg(args, int) );
+        Scalar v = va_arg(args, Scalar);
+        triplets.push_back(Triplet(row, col, v));
+    }
+    va_end(args);
+
+    SparseMatrix mat(rows, cols, triplets);
+
+//    ECKIT_DEBUG_VAR(mat.nonZeros());
+//    ECKIT_DEBUG_VAR(mat.rows());
+//    ECKIT_DEBUG_VAR(mat.cols());
+
+    return mat;
+}
+
+
+
+// Set linear algebra backend
+struct Setup : testing::Setup {
+    Setup() : testing::Setup() {
+        LinearAlgebra::backend(Resource<std::string>("-linearAlgebraBackend", "generic"));
+    }
+};
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+
+
+struct Fixture {
+
+    Fixture() : A(S(3, 3, 4,
+                    0, 0, 2.,
+                    0, 2, -3.,
+                    1, 1, 2.,
+                    2, 2, 2.)),
+                A2(S(2, 3, 4,
+                     0, 0, 1.,
+                     0, 2, 2.,
+                     1, 0, 3.,
+                     1, 1, 4.)),
+                x(V(3, 1., 2., 3.)),
+                linalg(LinearAlgebra::backend()) {}
+
+    // A = 2 0 -3
+    //     0 2  0
+    //     0 0  2
+    SparseMatrix A;
+    // A2 = 1. 0. 2.
+    //      3. 4. 0.
+    SparseMatrix A2;
+    Vector x;
+    const LinearAlgebra& linalg;
+};
+
+
+
+template <class T>
+void test(const T& v, const T& r) {
+    const size_t s = std::min(v.size(), r.size());
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.data(), v.data() + s, r.data(), r.data() + s);
+}
+
+template <typename T>
+void test(T* v, T* r, size_t s) {
+    BOOST_CHECK_EQUAL_COLLECTIONS(v, v + s, r, r + s);
+}
+
+void test(const SparseMatrix& A, const Index* outer, const Index* inner, const Scalar* data) {
+    test(A.outer(), outer, A.rows()+1);
+    test(A.inner(), inner, A.nonZeros());
+    test(A.data(), data, A.nonZeros());
+}
+
+
+/// Test linear algebra interface
+
+BOOST_FIXTURE_TEST_SUITE(test_eckit_la_sparse, Fixture)
+
+BOOST_AUTO_TEST_CASE(test_set_from_triplets) {
+
+    {
+        BOOST_CHECK_EQUAL(A.nonZeros(), 4);
+
+        Index outer[4] = {0, 2, 3, 4};
+        Index inner[4] = {0, 2, 1, 2};
+        Scalar data[4] = {2., -3., 2., 2.};
+        test(A, outer, inner, data);
+    }
+
+    // Pathological case with empty rows
+    {
+        Index outer[7] = {0, 0, 1, 1, 2, 2, 2};
+        Index inner[2] = {0, 3};
+        Scalar data[2] = {1., 2.};
+        test(S(6, 6, 2, 1, 0, 1., 3, 3, 2.), outer, inner, data);
+    }
+
+    // Rows in wrong order (not triggering right now since triplets are sorted)
+    //BOOST_CHECK_THROW(S(2, 2, 2, 1, 1, 1., 0, 0, 1.), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_identity) {
+
+    {
+        Vector y1(3);
+
+        SparseMatrix B;
+        B.setIdentity(3, 3);
+
+        linalg.spmv(B, x, y1);
+        test(y1, x);
+    }
+
+    {
+        SparseMatrix C;
+        C.setIdentity(6, 3);
+
+        Vector y2(6);
+        linalg.spmv(C, x, y2);
+        test(y2, x);
+        test(y2.data()+3, V(3, 0., 0., 0.).data(), 3);
+    }
+
+    {
+        SparseMatrix D;
+        D.setIdentity(2, 3);
+
+        Vector y3(2);
+
+        linalg.spmv(D, x, y3);
+        test(y3, x);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(test_prune) {
+
+    SparseMatrix A(S(3, 3, 5,
+                     0, 0, 0.,
+                     0, 2, 1.,
+                     1, 0, 0.,
+                     1, 1, 2.,
+                     2, 2, 0.));
+
+    A.prune();
+    BOOST_CHECK_EQUAL(A.nonZeros(), 2);
+    Index outer[4] = {0, 1, 2, 2};
+    Index inner[2] = {2, 1};
+    Scalar data[2] = {1., 2.};
+    test(A, outer, inner, data);
+}
+
+BOOST_AUTO_TEST_CASE(test_iterator) {
+
+    SparseMatrix A(S(3, 3, 5,
+                     0, 0, 0.,
+                     1, 0, 0.,
+                     1, 1, 0.,
+                     1, 2, 1.,
+                     2, 2, 2.));
+
+    A.prune();
+    BOOST_CHECK_EQUAL(A.nonZeros(), 2);
+
+    //  data    [ 1 2 ]
+    //  outer   [ 0 0 1 2 ]
+    //  inner   [ 2 2 ]
+
+    Scalar data[2] = {1., 2.};
+    Index outer[4] = {0, 0, 1, 2};
+    Index inner[2] = {2, 2};
+    test(A, outer, inner, data);
+
+    SparseMatrix::const_iterator it = A.begin();
+
+    // check entry #1
+    BOOST_CHECK_EQUAL(it.row(), 1);
+    BOOST_CHECK_EQUAL(it.col(), 2);
+    BOOST_CHECK(*it == 1.);
+
+    // check entry #2
+    ++it;
+
+    BOOST_CHECK_EQUAL(it.row(), 2);
+    BOOST_CHECK_EQUAL(it.col(), 2);
+    BOOST_CHECK(*it == 2.);
+
+    // go past the end
+    BOOST_CHECK(it != A.end());
+
+    ++it;
+
+    BOOST_CHECK(it == A.end());
+    BOOST_CHECK(!it);
+
+    // go back and re-check entry #1
+    // (row 0 is empty, should relocate to row 1)
+    it = A.begin();
+    BOOST_CHECK(it);
+
+    BOOST_CHECK_EQUAL(it.row(), 1);
+    BOOST_CHECK_EQUAL(it.col(), 2);
+    BOOST_CHECK(*it == 1.);
+
+    // go way past the end
+    it = A.begin(42);
+    BOOST_CHECK(!it);
+
+}
+
+
+BOOST_AUTO_TEST_CASE(test_transpose_square) {
+    Index outer[4] = {0, 1, 2, 4};
+    Index inner[4] = {0, 1, 0, 2};
+    Scalar data[4] = {2., 2., -3., 2.};
+    test(A.transpose(), outer, inner, data);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_transpose_nonsquare) {
+    Index outer[4] = {0, 2, 3, 4};
+    Index inner[4] = {0, 1, 1, 0};
+    Scalar data[4] = {1., 3., 4., 2.};
+    test(A2.transpose(), outer, inner, data);
+}
+
+BOOST_AUTO_TEST_CASE(test_spmv) {
+    Vector y(3);
+    linalg.spmv(A, x, y);
+    test(y, V(3, -7., 4., 6.));
+    BOOST_TEST_MESSAGE("spmv of sparse matrix and vector of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.spmv(A, Vector(2), y), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_spmm) {
+    Matrix C(3, 2);
+    linalg.spmm(A, M(3, 2, 1., 2., 3., 4., 5., 6.), C);
+    test(C, M(3, 2, -13., -14., 6., 8., 10., 12.));
+    BOOST_TEST_MESSAGE("spmm of sparse matrix and matrix of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.spmm(A, Matrix(2, 2), C), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_dsptd_square) {
+    SparseMatrix B;
+    linalg.dsptd(x, A, x, B);
+    Index outer[4] = {0, 1, 2, 4};
+    Index inner[4] = {0, 1, 0, 2};
+    Scalar data[4] = {2., 8., -9., 18.};
+    test(B, outer, inner, data);
+    BOOST_TEST_MESSAGE("dsptd with vectors of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.dsptd(x, A, Vector(2), B), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_CASE(test_dsptd_nonsquare) {
+    SparseMatrix B;
+    linalg.dsptd(x, A2, V(2, 1., 2.), B);
+    Index outer[4] = {0, 2, 3, 4};
+    Index inner[4] = {0, 1, 1, 0};
+    Scalar data[4] = {1., 6., 16., 6.};
+    test(B, outer, inner, data);
+    BOOST_TEST_MESSAGE("dsptd with vectors of nonmatching sizes should fail");
+    BOOST_CHECK_THROW(linalg.dsptd(x, A2, x, B), AssertionFailed);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/linalg/test_la_streaming.cc b/eckit/src/tests/linalg/test_la_streaming.cc
new file mode 100644
index 0000000..bc8550b
--- /dev/null
+++ b/eckit/src/tests/linalg/test_la_streaming.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_la_streaming
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/serialisation/FileStream.h"
+
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/SparseMatrix.h"
+#include "eckit/linalg/Vector.h"
+#include "util.h"
+
+using namespace eckit::linalg;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template <typename T>
+void test(const T& v, const T& r) {
+    BOOST_CHECK_EQUAL(v.rows(), r.rows());
+    BOOST_CHECK_EQUAL(v.cols(), r.cols());
+    BOOST_CHECK_EQUAL(v.size(), r.size());
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.begin(), v.end(), r.begin(), r.end());
+}
+
+void test(const SparseMatrix& v, const SparseMatrix& r) {
+    BOOST_CHECK_EQUAL(v.rows(), r.rows());
+    BOOST_CHECK_EQUAL(v.cols(), r.cols());
+    BOOST_CHECK_EQUAL(v.nonZeros(), r.nonZeros());
+    const Size nnz = v.nonZeros();
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.outer(), v.outer()+v.rows()+1, r.outer(), r.outer()+r.rows()+1);
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.inner(), v.inner()+nnz, r.inner(), r.inner()+nnz);
+    BOOST_CHECK_EQUAL_COLLECTIONS(v.data(), v.data()+nnz, r.data(), r.data()+nnz);
+}
+
+template<typename T>
+void stream_test(const T& t) {
+    PathName filename = PathName::unique( "data" );
+    {
+        FileStream sout( filename, "w" );
+        sout << t;
+    }
+    {
+        FileStream sin( filename, "r" );
+        T out(sin);
+        test(t, out);
+    }
+    if (filename.exists()) filename.unlink();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Test linear algebra interface
+
+BOOST_AUTO_TEST_SUITE(test_eckit_la_streaming)
+
+BOOST_AUTO_TEST_CASE(test_stream_vector) {
+    stream_test(V(5, 1., 2., 3., 4., 5.));
+}
+
+BOOST_AUTO_TEST_CASE(test_stream_matrix) {
+    stream_test(M(3, 3, 1., 2., 3., 4., 5., 6., 7., 8., 9.));
+}
+
+BOOST_AUTO_TEST_CASE(test_stream_sparsematrix) {
+
+    std::vector<Triplet> triplets;
+
+    triplets.push_back(Triplet(0, 0, 2.));
+    triplets.push_back(Triplet(0, 2, -3.));
+    triplets.push_back(Triplet(1, 1, 2.));
+    triplets.push_back(Triplet(2, 2, 2.));
+
+    SparseMatrix smat(3, 3, triplets);
+
+    stream_test(smat);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/linalg/util.h b/eckit/src/tests/linalg/util.h
new file mode 100644
index 0000000..6a9c833
--- /dev/null
+++ b/eckit/src/tests/linalg/util.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#ifndef eckit_test_la_util_h
+#define eckit_test_la_util_h
+
+#include <cstdarg>
+
+#include "eckit/linalg/Matrix.h"
+#include "eckit/linalg/Vector.h"
+
+using namespace eckit::linalg;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Vector V(Size s, ...) {
+    Vector vec(s);
+    va_list args;
+    va_start(args, s);
+    for (Size i = 0; i < s; ++i) vec[i] = va_arg(args, Scalar);
+    va_end(args);
+    return vec;
+}
+
+
+Matrix M(Matrix::Size rows, Matrix::Size cols, ...) {
+    Matrix mat(rows, cols);
+    va_list args;
+    va_start(args, cols);
+    for (Matrix::Size r = 0; r < rows; ++r)
+        for (Matrix::Size c = 0; c < cols; ++c)
+            mat(r, c) = va_arg(args, Scalar);
+    va_end(args);
+    return mat;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+}  // namespace test
+} // namespace eckit
+
+#endif
diff --git a/eckit/src/tests/log/CMakeLists.txt b/eckit/src/tests/log/CMakeLists.txt
new file mode 100644
index 0000000..6df5d51
--- /dev/null
+++ b/eckit/src/tests/log/CMakeLists.txt
@@ -0,0 +1,27 @@
+ecbuild_add_test( TARGET      eckit_test_log
+                  SOURCES     test_log.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_log_channels
+                  SOURCES     test_log_channels.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_log_callback
+                  SOURCES     test_log_callback.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_log_threads
+                  SOURCES     test_log_threads.cc
+                  ENVIRONMENT _TEST_ECKIT_HOME=/tmp/$ENV{USER}
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_colour
+                  SOURCES     test_colour.cc
+                  LIBS        eckit )
+
+
+ecbuild_add_test( TARGET      eckit_test_log_user_channels
+                  BOOST
+                  ENABLED     OFF
+                  SOURCES     test_log_user_channels.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/log/test_colour.cc b/eckit/src/tests/log/test_colour.cc
new file mode 100644
index 0000000..418e260
--- /dev/null
+++ b/eckit/src/tests/log/test_colour.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/log/Colour.h"
+#include "eckit/log/Log.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+class TestColour : public Tool {
+public:
+
+    TestColour(int argc, char **argv): Tool(argc, argv) {}
+
+    virtual void run();
+};
+
+void TestColour::run()
+{
+
+    Log::info() << "Hello, worlds" << std::endl;
+
+    std::cout << Colour::red << "Red" << Colour::reset << std::endl;
+    std::cout << Colour::off;
+    std::cout << Colour::red << "Red" << Colour::reset << std::endl;
+    std::cout << Colour::on;
+    std::cout << Colour::blue << "Red" << Colour::reset << std::endl;
+    std::cout << Colour::red << Colour::bold << "Red" << Colour::reset << std::endl;
+    std::cout << Colour::yellow << Colour::underline << "Red" << Colour::reset << std::endl;
+
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+    eckit_test::TestColour app(argc, argv);
+    return app.start();
+}
diff --git a/eckit/src/tests/log/test_log.cc b/eckit/src/tests/log/test_log.cc
new file mode 100644
index 0000000..2f1eaf8
--- /dev/null
+++ b/eckit/src/tests/log/test_log.cc
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/log/Log.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit_test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestLog : public Tool {
+public:
+
+    TestLog(int argc,char **argv): Tool(argc,argv) {}
+
+    virtual void run();
+
+    void test_debug();
+    void test_info();
+    void test_warning();
+    void test_error();
+    void test_panic();
+    void test_strerr();
+};
+
+
+void TestLog::test_debug()
+{
+    Log::debug()         << "debug message 1" << std::endl;
+}
+
+void TestLog::test_info()
+{
+    Log::info()         << "info message 1" << std::endl;
+}
+
+void TestLog::test_warning()
+{
+    Log::warning()         << "warning message 1" << std::endl;
+}
+
+void TestLog::test_error()
+{
+    Log::error()         << "error message 1" << std::endl;
+}
+
+void TestLog::test_panic()
+{
+    Log::panic()         << "panic message 1" << std::endl;
+}
+
+void TestLog::test_strerr()
+{
+    LocalPathName p("/tmp/edfpmjq3480hfnsribnzasdfibv");
+    p.unlink();
+}
+
+void TestLog::run()
+{
+    test_debug();
+    test_info();
+    test_warning();
+    test_error();
+    test_panic();
+    test_strerr();
+}
+
+
+} // namespace eckit_test
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestLog app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/log/test_log_callback.cc b/eckit/src/tests/log/test_log_callback.cc
new file mode 100644
index 0000000..b4e8c03
--- /dev/null
+++ b/eckit/src/tests/log/test_log_callback.cc
@@ -0,0 +1,102 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/os/BackTrace.h"
+#include "eckit/runtime/Main.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit_test {
+
+struct CTxt { std::string name_; };
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+static void output_callback_noctxt( void* ctxt, const char* msg )
+{
+//    std::cout << "[DEBUG] -- " << Here() << "\n" << BackTrace::dump() << "\n" << std::endl;
+    std::cout << "[FORWARD OUT] -- " << msg ;
+}
+
+
+static void output_callback_withctxt( void* ctxt, const char* msg )
+{
+//    std::cout << "[DEBUG] -- " << Here() << "\n" << BackTrace::dump() << "\n" << std::endl;
+    std::cout << "[FORWARD OUT] -- CTXT [" << static_cast<CTxt*>(ctxt)->name_ << "] -- " << msg ;
+}
+
+
+
+/// tests without callback
+void test_callback_none()
+{
+    Log::info().clear();
+
+    Log::info()          << "info message 1" << std::endl;
+
+    Log::warning()       << "warning message 1" << std::endl;
+
+    Log::error()         << "error message 1" << std::endl;
+}
+
+
+
+/// tests with null context
+void test_callback_noctxt()
+{
+    Log::info().setCallback(&output_callback_noctxt);
+
+    Log::info()          << "info message 1" << std::endl;
+
+    Log::warning()       << "warning message 1" << std::endl;
+
+    Log::error()         << "error message 1" << std::endl;
+
+    Log::info().clear();
+}
+
+
+/// tests with context
+void test_callback_withctxt()
+{
+    CTxt ctxt;
+    ctxt.name_ = "MyTest";
+
+    Log::info().setCallback(&output_callback_withctxt, &ctxt);
+
+    Log::info()          << "info message 1" << std::endl;
+
+    Log::warning()       << "warning message 1" << std::endl;
+
+    Log::error()         << "error message 1" << std::endl;
+
+    Log::info().clear();
+}
+
+} // namespace eckit_test
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+int main(int argc, char **argv)
+{
+
+
+    test_callback_none();
+    test_callback_noctxt();
+    test_callback_withctxt();
+
+    return 0;
+}
diff --git a/eckit/src/tests/log/test_log_channels.cc b/eckit/src/tests/log/test_log_channels.cc
new file mode 100644
index 0000000..49e12cf
--- /dev/null
+++ b/eckit/src/tests/log/test_log_channels.cc
@@ -0,0 +1,180 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <csignal>
+#include <cstdlib>
+#include <vector>
+#include <map>
+#include <iostream>
+#include <sstream>
+#include <locale>
+#include <fstream>
+#include <cassert>
+
+#include "eckit/os/BackTrace.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+#include "eckit/log/Channel.h"
+#include "eckit/log/ColouringTarget.h"
+#include "eckit/log/FileTarget.h"
+#include "eckit/log/CallbackTarget.h"
+#include "eckit/log/WrapperTarget.h"
+#include "eckit/log/OStreamTarget.h"
+
+using namespace std;
+using namespace eckit;
+
+
+#if 1
+    #define DEBUG_H
+    #define DEBUG_(x)
+#else
+    #define DEBUG_H     std::cerr << " DEBUG @ " << __FILE__ << " +" << __LINE__ << std::endl;
+    #define DEBUG_(x)   std::cerr << #x << " : [" << x << "] @ " <<  __FILE__ << " +" << __LINE__ << std::endl;
+#endif
+
+namespace eckit_test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Example of a wrapper target that capitalizes all output
+
+class CapitalizerTarget : public WrapperTarget {
+public:
+
+    CapitalizerTarget(LogTarget* target) : WrapperTarget(target) {}
+
+private:
+
+    virtual void write(const char* start, const char* end) {
+
+        std::string::size_type length = std::distance(start, end);
+        buffer_.resize(length);
+
+        std::locale loc;
+        const char* p = start;
+        for (std::string::size_type i = 0; i< length; ++i, ++p) {
+          buffer_[i] = std::toupper(*p,loc);
+        }
+
+        target_->write(buffer_.c_str(), buffer_.c_str() + length);
+    }
+
+    virtual void writePrefix(){}
+    virtual void writeSuffix(){}
+
+    void print(std::ostream& s) const { s << "CapitalizerTarget()"; }
+
+    std::string buffer_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static void callback_ctxt( void* ctxt, const char* msg )
+{
+    std::cout << "[" << *((int*)ctxt) << "] : -- " << msg << std::endl ;
+}
+
+static void callback_noctxt( void* , const char* msg )
+{
+    std::cout << "[CALLBACK OUT] : -- " << msg << std::endl ;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestApp : public Tool {
+public:
+
+    TestApp(int argc,char **argv) : Tool(argc,argv) {}
+
+    ~TestApp() {}
+
+    virtual void run()
+    {
+        test_multi_targets();
+        test_multi_colouring();
+    }
+
+    void test_multi_targets() {
+        std::cout << "---> test_multi_targets()" << std::endl;
+
+        int t = 0;
+
+        Channel mychannel;
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        mychannel.addFile("test.txt");
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        std::ofstream of ("test.txt.2");
+        mychannel.addStream(of);
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        mychannel.addStream(std::cout);
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        mychannel.addStream(std::cerr);
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        std::ostringstream oss;
+        mychannel.addStream(oss);
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        mychannel.addCallback(&callback_noctxt,0);
+        mychannel.addCallback(&callback_ctxt, &t);
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        // mychannel.addLogTarget(new CapitalizerTarget(new FileTarget(PathName("capitals.txt"))));
+
+        mychannel << "testing [" << t++ << "]" << std::endl;
+
+        mychannel << "Final test" << std::endl;
+    }
+
+    void test_multi_colouring() {
+#if 0
+        Log::info().setLogTarget(    new ColouringTarget(new OStreamTarget(std::cout), &Colour::green));
+        Log::warning().setLogTarget( new ColouringTarget(new OStreamTarget(std::cerr), &Colour::yellow));
+        Log::error().setLogTarget(   new ColouringTarget(new OStreamTarget(std::cerr), &Colour::red));
+
+        Log::info()    << "Log::info() is green"     << std::endl;
+        Log::warning() << "Log::warning() is yellow" << std::endl;
+        Log::error()   << "Log::error() is red"      << std::endl;
+#endif
+    }
+};
+
+} // namespace eckit_test
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void on_signal_dumpbacktrace(int signum)
+{
+    printf("Caught signal %d\n",signum);
+    std::cerr << BackTrace::dump() << std::endl;
+    ::abort();
+}
+
+int main(int argc,char **argv)
+{
+    signal(SIGSEGV, on_signal_dumpbacktrace );
+
+    eckit_test::TestApp app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/log/test_log_threads.cc b/eckit/src/tests/log/test_log_threads.cc
new file mode 100644
index 0000000..b7b05f4
--- /dev/null
+++ b/eckit/src/tests/log/test_log_threads.cc
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/filesystem/LocalPathName.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/log/Channel.h"
+#include "eckit/log/CallbackTarget.h"
+
+#include "eckit/os/BackTrace.h"
+
+#include "eckit/runtime/Tool.h"
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/StaticMutex.h"
+#include "eckit/thread/Thread.h"
+#include "eckit/thread/ThreadControler.h"
+
+using namespace std;
+using namespace eckit;
+
+
+namespace eckit_test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static StaticMutex static_mutex;
+
+static void callback_logger( void* ctxt, const char* msg )
+{
+    AutoLock<StaticMutex> lock(static_mutex); ///< usually global resources like this need to be protected by mutex
+
+    std::cout << "[TEST] -- " << msg ;
+}
+
+static void callback_special( void* ctxt, const char* msg )
+{
+    std::cout << ">>> " << msg ;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#define LOOPS 3
+#define WAIT  10000
+
+template< int N >
+class TLog : public Thread
+{
+    void run()
+    {
+        // this shows how you can change the callback per thread
+        if( N == 2 ) {
+            Log::info().setCallback(&callback_special);
+        }
+
+        for( int i = 0; i < LOOPS*N; ++i )
+        {
+           ::usleep(N*WAIT);
+
+           Log::info() << "thread [" << N << "] -- " << i << std::endl;
+        }
+
+        Log::info() << "thread [" << N << "] -- done !" << std::endl;
+    }
+};
+
+
+class TestApp : public eckit::Tool {
+public:
+
+    TestApp(int argc,char **argv) : Tool(argc,argv) {}
+
+    virtual ~TestApp() {}
+
+    virtual void run()
+    {
+        Log::info().setCallback(&callback_logger);
+
+        Log::info() << ">>> starting ... " << std::endl;
+
+        ThreadControler t1( new TLog<1>(), false );
+        ThreadControler t2( new TLog<2>(), false );
+        ThreadControler t3( new TLog<3>(), false );
+
+        t1.start();
+        t2.start();
+        t3.start();
+
+        t1.wait();
+        t2.wait();
+        t3.wait();
+
+        Log::info() << ">>> finished!" << std::endl;
+
+    }
+};
+
+} // namespace eckit_test
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestApp app(argc,argv);
+    return app.start();
+}
diff --git a/eckit/src/tests/log/test_log_user_channels.cc b/eckit/src/tests/log/test_log_user_channels.cc
new file mode 100644
index 0000000..4d629ab
--- /dev/null
+++ b/eckit/src/tests/log/test_log_user_channels.cc
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_user_channels
+
+#include <iostream>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/log/Channel.h"
+#include "eckit/log/ColouringTarget.h"
+#include "eckit/log/OStreamTarget.h"
+#include "eckit/runtime/Main.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE(test_eckit_user_log_channels)
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE(test_eckit_user_log_channel_registration) {
+
+    // FIXME: re-enable this test
+
+//    BOOST_CHECK_NO_THROW( Log::registerChannel("mychannel", new Channel(new ColouringTarget(new OStreamTarget(std::cout), &Colour::green))) );
+
+//    BOOST_CHECK_NO_THROW( Log::channel("mychannel") << "TEST MY VERY OWN CHANNEL" << std::endl );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/maths/CMakeLists.txt b/eckit/src/tests/maths/CMakeLists.txt
new file mode 100644
index 0000000..dea8bdf
--- /dev/null
+++ b/eckit/src/tests/maths/CMakeLists.txt
@@ -0,0 +1,11 @@
+ecbuild_add_test( TARGET      eckit_test_eigen
+                  BOOST
+                  SOURCES     test_eigen.cc
+                  CONDITION   HAVE_EIGEN
+                  LIBS        eckit_maths eckit
+)
+ecbuild_add_test( TARGET      eckit_test_matrix
+                  BOOST
+                  SOURCES     test_matrix.cc
+                  LIBS        eckit_maths eckit
+)
diff --git a/eckit/src/tests/maths/test_eigen.cc b/eckit/src/tests/maths/test_eigen.cc
new file mode 100644
index 0000000..27bd10e
--- /dev/null
+++ b/eckit/src/tests/maths/test_eigen.cc
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_maths_eigen
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/maths/Eigen.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {}
+
+//-----------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_resource )
+
+BOOST_AUTO_TEST_CASE( test_default )
+{
+    Eigen::Vector3d v1;
+    Eigen::Vector3d v2;
+    Eigen::Vector3d r;
+
+    r = v2 - v1;
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/maths/test_matrix.cc b/eckit/src/tests/maths/test_matrix.cc
new file mode 100644
index 0000000..015dfe3
--- /dev/null
+++ b/eckit/src/tests/maths/test_matrix.cc
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_matrix
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/maths/Matrix.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {}
+
+//-----------------------------------------------------------------------------
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_matrix )
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_determinant )
+{
+  typedef eckit::maths::Matrix<double> Matrix;
+
+  Matrix m(5,5);
+  m(0,0) =  9.;  m(0,1) =  6.; m(0,2) =  2.; m(0,3) =  0.; m(0,4) =  3.;
+  m(1,0) =  3.;  m(1,1) =  6.; m(1,2) =  8.; m(1,3) = 10.; m(1,4) = 12.;
+  m(2,0) =  4.;  m(2,1) =  8.; m(2,2) =  2.; m(2,3) =  6.; m(2,4) =  9.;
+  m(3,0) =  1.;  m(3,1) =  5.; m(3,2) =  5.; m(3,3) =  3.; m(3,4) =  2.;
+  m(4,0) =  1.;  m(4,1) =  3.; m(4,2) =  6.; m(4,3) =  8.; m(4,4) = 10.;
+
+  BOOST_CHECK_CLOSE( m.determinant(), 1124, 1.e-10 );
+}
+
+//-----------------------------------------------------------------------------
+BOOST_AUTO_TEST_CASE( test_inverse )
+{
+  typedef eckit::maths::Matrix<double> Matrix;
+
+  Matrix m(5,5);
+  m(0,0) =  9.;  m(0,1) =  6.; m(0,2) =  2.; m(0,3) =  0.; m(0,4) =  3.;
+  m(1,0) =  3.;  m(1,1) =  6.; m(1,2) =  8.; m(1,3) = 10.; m(1,4) = 12.;
+  m(2,0) =  4.;  m(2,1) =  8.; m(2,2) =  2.; m(2,3) =  6.; m(2,4) =  9.;
+  m(3,0) =  1.;  m(3,1) =  5.; m(3,2) =  5.; m(3,3) =  3.; m(3,4) =  2.;
+  m(4,0) =  1.;  m(4,1) =  3.; m(4,2) =  6.; m(4,3) =  8.; m(4,4) = 10.;
+
+
+  // Expected inverse
+  Matrix minv(5,5);
+  minv(0,0) = 0.03024911;  minv(0,1) =  0.78113879; minv(0,2) = -0.1405694; minv(0,3) = -0.29181495; minv(0,4) = -0.76156584;
+  minv(1,0) = -0.01067616;  minv(1,1) = -0.33451957; minv(1,2) = 0.16725979; minv(1,3) = 0.22064057; minv(1,4) = 0.20996441;
+  minv(2,0) = 0.12366548;  minv(2,1) = -0.54181495; minv(2,2) = -0.10409253; minv(2,3) = 0.27758007; minv(2,4) = 0.65124555;
+  minv(3,0) = -0.31939502;  minv(3,1) = 2.07562278; minv(3,2) = -0.16281139; minv(3,3) = -0.5658363; minv(3,4) = -2.13523132;
+  minv(4,0) = 0.18149466;  minv(4,1) = -1.31316726; minv(4,2) = 0.15658363; minv(4,3) = 0.24911032; minv(4,4) = 1.43060498;
+
+  // Compute inverse
+  Matrix mi(5,5);
+  mi.noalias() = m.inverse();
+
+  // Compute identity matrix
+  Matrix I(5,5);
+  I = m*mi;
+
+  // Verify results
+  for( int i=0; i<m.rows(); ++i) {
+    for( int j=0; j<m.cols(); ++j) {
+      BOOST_CHECK_CLOSE(mi(i,j),minv(i,j),1.e-4);
+      if( i==j ) BOOST_CHECK_CLOSE( I(i,j),1.,1.e-8);
+      else BOOST_CHECK_SMALL( I(i,j), 1.e-10 );
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/memory/CMakeLists.txt b/eckit/src/tests/memory/CMakeLists.txt
new file mode 100644
index 0000000..1f382e3
--- /dev/null
+++ b/eckit/src/tests/memory/CMakeLists.txt
@@ -0,0 +1,19 @@
+ecbuild_add_test( TARGET      eckit_test_memory_counted
+                  BOOST
+                  SOURCES     test_counted.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_memory_factory
+                  BOOST
+                  SOURCES     test_factory.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_memory_scoped_ptr
+                  BOOST
+                  SOURCES     test_scoped_ptr.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_memory_shared_ptr
+                  BOOST
+                  SOURCES     test_shared_ptr.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/memory/test_counted.cc b/eckit/src/tests/memory/test_counted.cc
new file mode 100644
index 0000000..79b0333
--- /dev/null
+++ b/eckit/src/tests/memory/test_counted.cc
@@ -0,0 +1,223 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "eckit/eckit_config.h"
+
+#define BOOST_TEST_MODULE test_eckit_memory_counted
+
+#include "ecbuild/boost_test_framework.h"
+
+#include <boost/test/test_case_template.hpp>
+#include <boost/mpl/list.hpp>
+
+#include "eckit/memory/Counted.h"
+#include "eckit/memory/Owned.h"
+#include "eckit/memory/SharedPtr.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+struct FooLock : public OwnedLock
+{
+    typedef SharedPtr<FooLock> ptype;
+
+    FooLock( int in ) : i(in) {}
+    int i;
+};
+
+struct FooNoLock : public OwnedNoLock
+{
+    typedef SharedPtr<FooNoLock> ptype;
+
+    FooNoLock( int in ) : i(in) {}
+    int i;
+};
+
+}
+
+typedef boost::mpl::list< eckit_test::FooLock, eckit_test::FooNoLock > test_types;
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_memory_counted )
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_default, F, test_types )
+{
+    typename F::ptype p;
+
+    BOOST_CHECK( !p );
+    BOOST_CHECK( p.get() == 0 );
+
+    p.reset( new F(10) );
+
+    BOOST_CHECK( p );
+    BOOST_CHECK( p.unique() );
+
+    p.release();
+
+    BOOST_CHECK( !p );
+    BOOST_CHECK_EQUAL( p.owners() , 0 );
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_copy, F, test_types )
+{
+    typename F::ptype p1;
+    typename F::ptype p2;
+    typename F::ptype p3;
+
+    BOOST_CHECK( !p1 );
+    BOOST_CHECK_EQUAL( p1.owners() , 0 );
+
+    p1.reset( new F(10) );
+
+    BOOST_CHECK( p1->i == 10 );
+
+    p1->i = 20;
+
+    BOOST_CHECK( p1 != p2 );
+    BOOST_CHECK( p1 != p3 );
+    BOOST_CHECK( p2 == p3 );
+
+    BOOST_CHECK(  p1 );
+    BOOST_CHECK( !p2 );
+    BOOST_CHECK( !p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 1 );
+    BOOST_CHECK_EQUAL( p2.owners() , 0 );
+    BOOST_CHECK_EQUAL( p3.owners() , 0 );
+
+    p2.reset(p1);
+
+    BOOST_CHECK_EQUAL( p1->i , 20 );
+    BOOST_CHECK_EQUAL( p2->i , 20 );
+
+    p1->i = 30;
+
+    BOOST_CHECK_EQUAL( p1->i , 30 );
+    BOOST_CHECK_EQUAL( p2->i , 30 );
+
+    BOOST_CHECK( p1 == p2 );
+    BOOST_CHECK( p1 != p3 );
+    BOOST_CHECK( p2 != p3 );
+
+    BOOST_CHECK(  p1 );
+    BOOST_CHECK(  p2 );
+    BOOST_CHECK( !p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 2 );
+    BOOST_CHECK_EQUAL( p2.owners() , 2 );
+    BOOST_CHECK_EQUAL( p3.owners() , 0 );
+
+    p3 = p1;
+
+    p1->i = 40;
+
+    BOOST_CHECK_EQUAL( p1->i , 40 );
+    BOOST_CHECK_EQUAL( p2->i , 40 );
+    BOOST_CHECK_EQUAL( p3->i , 40 );
+
+    BOOST_CHECK( p1 == p2 );
+    BOOST_CHECK( p1 == p3 );
+    BOOST_CHECK( p2 == p3 );
+
+    BOOST_CHECK( p1 );
+    BOOST_CHECK( p2 );
+    BOOST_CHECK( p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 3 );
+    BOOST_CHECK_EQUAL( p2.owners() , 3 );
+    BOOST_CHECK_EQUAL( p3.owners() , 3 );
+
+    p1.release();
+
+    BOOST_CHECK( !p1 );
+    BOOST_CHECK(  p2 );
+    BOOST_CHECK(  p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 0 );
+    BOOST_CHECK_EQUAL( p2.owners() , 2 );
+    BOOST_CHECK_EQUAL( p3.owners() , 2 );
+
+    p2.release();
+
+    BOOST_CHECK( !p1 );
+    BOOST_CHECK( !p2 );
+    BOOST_CHECK(  p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 0 );
+    BOOST_CHECK_EQUAL( p2.owners() , 0 );
+    BOOST_CHECK_EQUAL( p3.owners() , 1 );
+
+    p3.release();
+
+    BOOST_CHECK( !p1 );
+    BOOST_CHECK( !p2 );
+    BOOST_CHECK( !p3 );
+
+    BOOST_CHECK_EQUAL( p1.owners() , 0 );
+    BOOST_CHECK_EQUAL( p2.owners() , 0 );
+    BOOST_CHECK_EQUAL( p3.owners() , 0 );
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_release, F, test_types )
+{
+    typename F::ptype p;
+
+    BOOST_CHECK( !p );
+
+    p.release();
+
+    BOOST_CHECK( !p );
+
+    p.reset( new F(10) );
+
+    BOOST_CHECK( p );
+
+    p.release();
+
+    BOOST_CHECK( !p );
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE( test_swap, F, test_types )
+{
+    typename F::ptype p1( new F(10) );
+    typename F::ptype p2( new F(5) );
+
+    BOOST_CHECK( p1 );
+    BOOST_CHECK( p2 );
+
+    BOOST_CHECK( p1.unique() );
+    BOOST_CHECK( p2.unique() );
+
+
+    BOOST_CHECK_EQUAL( p1->i , 10 );
+    BOOST_CHECK_EQUAL( p2->i , 5 );
+
+    p1.swap(p2);
+
+    BOOST_CHECK_EQUAL( p1->i , 5 );
+    BOOST_CHECK_EQUAL( p2->i , 10 );
+
+    BOOST_CHECK( p1.unique() );
+    BOOST_CHECK( p2.unique() );
+
+    BOOST_CHECK( p1 );
+    BOOST_CHECK( p2 );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/memory/test_factory.cc b/eckit/src/tests/memory/test_factory.cc
new file mode 100644
index 0000000..22e6037
--- /dev/null
+++ b/eckit/src/tests/memory/test_factory.cc
@@ -0,0 +1,275 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE test_eckit_memory_factory
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/memory/Owned.h"
+#include "eckit/memory/SharedPtr.h"
+#include "eckit/memory/Builder.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/value/Properties.h"
+
+using namespace std;
+using namespace eckit;
+
+/// These tests are similar to the test for boost scoped_ptr and shared ptrs
+/// This allows as in the future to drop out, our own home grown managed
+/// ptr's in favour of the standards.
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+class Base0 : public Owned {
+public:
+
+	typedef BuilderT0<Base0> builder_t;
+
+	typedef SharedPtr<Base0> Ptr;
+
+	static std::string className() { return "eckit_test.Base0"; }
+
+	virtual ~Base0() {}
+	virtual std::string foo() const = 0;
+
+};
+
+class A0 : public Base0 {
+public:
+
+	static std::string className() { return "eckit_test.A0"; }
+
+	A0() : s1_( "A.0" ) {}
+
+	virtual std::string foo() const { return className() + "." + s1_; }
+
+private:
+
+	std::string s1_;
+};
+
+class B0 : public Base0 {
+public:
+
+	static std::string className() { return "eckit_test.B0"; }
+
+	B0() : s2_( "B.0") {}
+
+	virtual std::string foo() const { return className() + "." + s2_; }
+
+private:
+
+	std::string s2_;
+};
+
+ConcreteBuilderT0<Base0,A0> builder_A0;
+ConcreteBuilderT0<Base0,B0> builder_B0;
+
+}
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+class Base1 : public Owned {
+public:
+
+	typedef BuilderT1<Base1> builder_t;
+	typedef const Params& ARG1;
+
+	typedef SharedPtr<Base1> Ptr;
+
+	static std::string className() { return "eckit_test.Base1"; }
+
+	virtual ~Base1() {}
+	virtual std::string foo() const = 0;
+
+};
+
+class A1 : public Base1 {
+public:
+
+	static std::string className() { return "eckit_test.A1"; }
+
+	A1( const Params& p ) : s1_( p["mystr"].as<string>() + ".1") {}
+
+	virtual std::string foo() const { return className() + "." + s1_; }
+
+private:
+
+	std::string s1_;
+};
+
+class B1 : public Base1 {
+public:
+
+	static std::string className() { return "eckit_test.B1"; }
+
+	B1( const Params& p ) : s2_( p["mystr"].as<string>() + ".2") {}
+
+	virtual std::string foo() const { return className() + "." + s2_; }
+
+private:
+
+	std::string s2_;
+};
+
+ConcreteBuilderT1<Base1,A1> builder_A1;
+ConcreteBuilderT1<Base1,B1> builder_B1;
+
+}
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+class Base2 : public Owned {
+public:
+
+	typedef BuilderT2<Base2> builder_t;
+	typedef std::string   ARG1;
+	typedef int           ARG2;
+
+	typedef SharedPtr<Base2> Ptr;
+
+	static std::string className() { return "eckit_test.Base2"; }
+
+	virtual ~Base2() {}
+	virtual std::string foo() const = 0;
+
+};
+
+class A2 : public Base2 {
+public:
+
+	static std::string className() { return "eckit_test.A2"; }
+
+	A2( std::string s, int i ) : s1_( s + "." + Translator<int,string>()(i) ) {}
+
+	virtual std::string foo() const { return className() + "." + s1_; }
+
+private:
+
+	std::string s1_;
+};
+
+class B2 : public Base2 {
+public:
+
+	static std::string className() { return "eckit_test.B2"; }
+
+	B2( std::string s, int i ) :  s2_( s + "." + Translator<int,string>()(i) ) {}
+
+	virtual std::string foo() const { return className() + "." + s2_; }
+
+private:
+
+	std::string s2_;
+};
+
+ConcreteBuilderT2<Base2,A2> builder_A2;
+ConcreteBuilderT2<Base2,B2> builder_B2;
+
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_memory_factory )
+
+BOOST_AUTO_TEST_CASE( test_eckit_memory_factory_0 )
+{
+	using namespace eckit_test;
+
+//	std::cout << Factory<Base0>::instance() << std::endl;
+
+	BOOST_CHECK_EQUAL( Factory<Base0>::build_type(), "eckit_test.Base0" );
+
+	BOOST_CHECK_EQUAL( Factory<Base0>::instance().size() , 2 );
+
+	BOOST_CHECK( Factory<Base0>::instance().exists( "eckit_test.A0" ) );
+	BOOST_CHECK( Factory<Base0>::instance().exists( "eckit_test.B0" ) );
+
+	BOOST_CHECK_EQUAL( Factory<Base0>::instance().get( "eckit_test.A0" ).name() , "eckit_test.A0" );
+	BOOST_CHECK_EQUAL( Factory<Base0>::instance().get( "eckit_test.B0" ).name() , "eckit_test.B0" );
+
+	Base0::Ptr p1 ( Factory<Base0>::instance().get( "eckit_test.A0" ).create() );
+	Base0::Ptr p2 ( Factory<Base0>::instance().get( "eckit_test.B0" ).create() );
+
+	BOOST_CHECK( p1 );
+	BOOST_CHECK( p2 );
+
+	BOOST_CHECK_EQUAL( p1->foo(), "eckit_test.A0.A.0" );
+	BOOST_CHECK_EQUAL( p2->foo(), "eckit_test.B0.B.0" );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_memory_factory_1 )
+{
+	using namespace eckit_test;
+
+//	std::cout << Factory<Base1>::instance() << std::endl;
+
+	BOOST_CHECK_EQUAL( Factory<Base1>::build_type(), "eckit_test.Base1" );
+
+	BOOST_CHECK_EQUAL( Factory<Base1>::instance().size() , 2 );
+
+	BOOST_CHECK( Factory<Base1>::instance().exists( "eckit_test.A1" ) );
+	BOOST_CHECK( Factory<Base1>::instance().exists( "eckit_test.B1" ) );
+
+	BOOST_CHECK_EQUAL( Factory<Base1>::instance().get( "eckit_test.A1" ).name() , "eckit_test.A1" );
+	BOOST_CHECK_EQUAL( Factory<Base1>::instance().get( "eckit_test.B1" ).name() , "eckit_test.B1" );
+
+	Properties p;
+	p.set("mystr","lolo");
+
+	Base1::Ptr p1 ( Factory<Base1>::instance().get( "eckit_test.A1" ).create(Params(p)) );
+	Base1::Ptr p2 ( Factory<Base1>::instance().get( "eckit_test.B1" ).create(Params(p)) );
+
+	BOOST_CHECK( p1 );
+	BOOST_CHECK( p2 );
+
+	BOOST_CHECK_EQUAL( p1->foo(), "eckit_test.A1.lolo.1" );
+	BOOST_CHECK_EQUAL( p2->foo(), "eckit_test.B1.lolo.2" );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_memory_factory_2 )
+{
+	using namespace eckit_test;
+
+//	std::cout << Factory<Base2>::instance() << std::endl;
+
+	BOOST_CHECK_EQUAL( Factory<Base2>::build_type(), "eckit_test.Base2" );
+
+	BOOST_CHECK_EQUAL( Factory<Base2>::instance().size() , 2 );
+
+	BOOST_CHECK( Factory<Base2>::instance().exists( "eckit_test.A2" ) );
+	BOOST_CHECK( Factory<Base2>::instance().exists( "eckit_test.B2" ) );
+
+	BOOST_CHECK_EQUAL( Factory<Base2>::instance().get( "eckit_test.A2" ).name() , "eckit_test.A2" );
+	BOOST_CHECK_EQUAL( Factory<Base2>::instance().get( "eckit_test.B2" ).name() , "eckit_test.B2" );
+
+	string s("lolo");
+
+	Base2::Ptr p1 ( Factory<Base2>::instance().get( "eckit_test.A2" ).create(s,42) );
+	Base2::Ptr p2 ( Factory<Base2>::instance().get( "eckit_test.B2" ).create(s,42) );
+
+	BOOST_CHECK( p1 );
+	BOOST_CHECK( p2 );
+
+	BOOST_CHECK_EQUAL( p1->foo(), "eckit_test.A2.lolo.42" );
+	BOOST_CHECK_EQUAL( p2->foo(), "eckit_test.B2.lolo.42" );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/memory/test_scoped_ptr.cc b/eckit/src/tests/memory/test_scoped_ptr.cc
new file mode 100644
index 0000000..1bf7188
--- /dev/null
+++ b/eckit/src/tests/memory/test_scoped_ptr.cc
@@ -0,0 +1,135 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+#include <iostream>
+
+#define BOOST_TEST_MODULE test_eckit_memory_scoped_ptr
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/memory/Owned.h"
+#include "eckit/memory/ScopedPtr.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+
+using namespace std;
+using namespace eckit;
+
+/// These tests are similar to the test for boost scoped_ptr and shared ptrs
+/// This allows as in the future to drop out, our own home grown managed
+/// ptr's in favour of the standards.
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+template<class T>
+void ck( const T* v1, T v2 ) { BOOST_CHECK_EQUAL( *v1, v2 ); }
+
+namespace {
+   static int UDT_use_count = 0;  // independent of pointer maintained counts
+}
+
+//-----------------------------------------------------------------------------
+//  user defined type
+
+class UDT {
+   long value_;
+public:
+   explicit UDT( long value=0 ) : value_(value) { ++UDT_use_count; }
+   ~UDT() {
+      --UDT_use_count;
+//      std::cout << "   UDT with value " << value_ << " being destroyed\n";
+   }
+   long value() const { return value_; }
+   void value( long v ) { value_ = v; }
+};
+
+//-----------------------------------------------------------------------------
+//  tests on incomplete types
+
+//  Certain smart pointer operations are specified to work on incomplete types,
+//  and some uses depend upon this feature.  These tests verify compilation
+//  only - the functions aren't actually invoked.
+
+class Incomplete;
+
+Incomplete * check_incomplete( ScopedPtr<Incomplete>& incomplete )
+{
+   return incomplete.get();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+// TODO issues:
+// o/ test shared ptr, in STL containers
+// o/ <Not applicable> weak shared ptr
+// o/ <Not applicable> enable_shared_from_this/ not required
+// o/ <Not applicable> custom deletor
+// o/ <Not applicable> ? static pointer cast
+
+using namespace eckit_test;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_memory_scope_ptr )
+
+BOOST_AUTO_TEST_CASE( test_scoped_ptr_empty_constructor )
+{
+  ScopedPtr<long> sp;
+
+  BOOST_CHECK_EQUAL( sp.get(), ScopedPtr<long>::pointer_type(0) );
+
+  BOOST_CHECK_EQUAL( sp.release(), ScopedPtr<long>::pointer_type(0) );
+
+  BOOST_CHECK( !sp );
+
+  BOOST_CHECK_NO_THROW( sp.reset() );
+}
+
+BOOST_AUTO_TEST_CASE( test_scoped_ptr )
+{
+//   std::cout << "test ScopedPtr with a built-in type\n";
+   {
+      long * lp = new long;
+      ScopedPtr<long> sp ( lp );
+      BOOST_CHECK( sp );
+      BOOST_CHECK_EQUAL( sp.get(), lp );
+      BOOST_CHECK_EQUAL( lp, sp.get() );
+      BOOST_CHECK_EQUAL( &*sp, lp );
+
+      *sp = 1234568901L;
+      BOOST_CHECK_EQUAL( *sp, 1234568901L );
+      BOOST_CHECK_EQUAL( *lp, 1234568901L );
+      ck( static_cast<long*>(sp.get()), 1234568901L );
+      ck( lp, *sp );
+
+      sp.reset();
+
+      BOOST_CHECK_EQUAL( sp.get(), ScopedPtr<long>::pointer_type(0) );
+   }
+
+//   std::cout << "test ScopedPtr with a user defined type\n";
+   {
+      ScopedPtr<UDT> udt_sp ( new UDT( 999888777 ) );
+      BOOST_CHECK_EQUAL( udt_sp->value(), 999888777 );
+      udt_sp.reset();
+      udt_sp.reset( new UDT( 111222333 ) );
+      BOOST_CHECK_EQUAL( udt_sp->value(), 111222333 );
+      udt_sp.reset( new UDT( 333222111 ) );
+      BOOST_CHECK_EQUAL( udt_sp->value(), 333222111 );
+
+      udt_sp.reset();
+      BOOST_CHECK_EQUAL( udt_sp.get(), ScopedPtr<UDT>::pointer_type(0) );
+   }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/memory/test_shared_ptr.cc b/eckit/src/tests/memory/test_shared_ptr.cc
new file mode 100644
index 0000000..ec2ecc4
--- /dev/null
+++ b/eckit/src/tests/memory/test_shared_ptr.cc
@@ -0,0 +1,209 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE test_eckit_memory_shared_ptr
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/memory/Owned.h"
+#include "eckit/memory/SharedPtr.h"
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+
+using namespace std;
+using namespace eckit;
+
+/// These tests are similar to the test for boost scoped_ptr and shared ptrs
+/// This allows as in the future to drop out, our own home grown managed
+/// ptr's in favour of the standards.
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+class Incomplete;
+
+Incomplete * get_ptr(  eckit::SharedPtr<Incomplete>& incomplete )
+{
+  return incomplete.get();
+}
+
+template<class T>
+void ck( const T* v1, T v2 ) { BOOST_CHECK( *v1 == v2 ); }
+
+namespace {
+   int UDT_use_count;  // independent of pointer maintained counts
+}
+
+//-----------------------------------------------------------------------------
+//  user defined type
+
+class UDT : public OwnedNoLock {
+public:
+    typedef SharedPtr<UDT> ptype;
+
+    UDT( long in ) : value_(in) {  ++UDT_use_count; }
+    ~UDT() {
+       --UDT_use_count;
+//       std::cout << "   UDT with value " << value_ << " being destroyed\n";
+    }
+
+    long value() const { return value_; }
+    void value( long v ) { value_ = v;; }
+private:
+    long value_;
+};
+
+//-----------------------------------------------------------------------------
+//  tests on incomplete types
+
+//  Certain smart pointer operations are specified to work on incomplete types,
+//  and some uses depend upon this feature.  These tests verify compilation
+//  only - the functions aren't actually invoked.
+
+class Incomplete;
+
+Incomplete * check_incomplete( eckit::SharedPtr<Incomplete>& incomplete,
+                                 eckit::SharedPtr<Incomplete>& i2 )
+{
+   incomplete.swap(i2);
+   //std::cout << incomplete.use_count() << ' ' << incomplete.unique() << '\n';
+   return incomplete.get();
+}
+
+//-----------------------------------------------------------------------------
+
+template <typename T >
+eckit::SharedPtr<T> add_another_shareptr( T* p )
+{
+	return eckit::SharedPtr<T>( p );
+}
+
+} // namespace eckit_test
+
+using namespace eckit_test;
+
+//-----------------------------------------------------------------------------
+
+// TODO issues:
+// o/ test shared ptr, in STL containers
+// o/ <Not applicable> weak shared ptr
+// o/ <Not applicable> enable_shared_from_this/ not required
+// o/ <Not applicable> custom deletor
+// o/ <Not applicable> ? static pointer cast
+//
+// eckit::SharedPtr has null() member function, this does not exist on the standard/boost
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_memory_shared_ptr )
+
+BOOST_AUTO_TEST_CASE( test_intrusive_shared_ptr )
+{
+
+//   std::cout << "test SharedPtr with a user defined type\n";
+   {
+      UDT_use_count = 0;
+      UDT * up = new UDT(0);
+      BOOST_CHECK( up->value() == 0 );
+
+	  eckit::SharedPtr<UDT> sup1 ( up );
+	  BOOST_CHECK( up == sup1.get() );
+	  BOOST_CHECK( sup1.use_count() == 1 );
+	  BOOST_CHECK( sup1.unique());
+
+	  sup1->value( 54321 ) ;
+	  BOOST_CHECK( sup1->value() == 54321 );
+      BOOST_CHECK( up->value() == 54321 );
+
+      eckit::SharedPtr<UDT> sup2;
+	  sup2 = sup1;
+      BOOST_CHECK( sup2->value() == 54321 );
+	  BOOST_CHECK( sup1.use_count() == 2 );
+      BOOST_CHECK( sup2.use_count() == 2 );
+      BOOST_CHECK( !sup2.unique());
+	  BOOST_CHECK( !sup1.unique());
+
+//      cout << "eckit::SharedPtr check self assignment\n";
+      sup2 = sup2;
+      BOOST_CHECK( sup2->value() == 54321 );
+	  BOOST_CHECK( sup1.use_count() == 2 );
+      BOOST_CHECK( sup2.use_count() == 2 );
+
+	  // check return creates from function
+
+	  eckit::SharedPtr<UDT> sup3 = add_another_shareptr( up );
+	  BOOST_CHECK( sup3->value() == 54321 );
+	  BOOST_CHECK( sup1.use_count() == 3 );
+	  BOOST_CHECK( sup2.use_count() == 3 );
+	  BOOST_CHECK( sup3.use_count() == 3 );
+
+   }
+
+    BOOST_CHECK_EQUAL( UDT_use_count , 0 );
+
+   {
+//      std::cout << "test SharedPtr swap\n";
+	  eckit::SharedPtr<UDT> sup1 ( new UDT(0) );
+	  BOOST_CHECK(sup1.get() != 0);
+	  BOOST_CHECK(sup1.use_count() == 1 );
+	  BOOST_CHECK(sup1.unique());
+
+      eckit::SharedPtr<UDT> sup2;
+      BOOST_CHECK(sup2.use_count() == 0 );
+      BOOST_CHECK(sup2.get() == 0);
+
+	  sup1.swap(sup2);
+
+      BOOST_CHECK(sup2.get() != 0);
+      BOOST_CHECK(sup2.use_count() == 1 );
+      BOOST_CHECK(sup2.unique());
+
+	  BOOST_CHECK(sup1.use_count() == 0 );
+	  BOOST_CHECK(sup1.get() == 0);
+   }
+
+   BOOST_CHECK_EQUAL( UDT_use_count , 0 );
+
+
+   //   std::cout << "test SharedPtr with a user defined type in std::vector\n";
+   {
+      std::vector< eckit::SharedPtr<UDT> > vec;
+      vec.push_back(eckit::SharedPtr<UDT>(new UDT(0)));
+      vec.push_back(eckit::SharedPtr<UDT>(new UDT(1)));
+      vec.push_back(eckit::SharedPtr<UDT>(new UDT(2)));
+   }
+   BOOST_CHECK_EQUAL( UDT_use_count , 0 );
+
+   //   std::cout << "test SharedPtr with a user defined type in std::set\n";
+   {
+      std::set< eckit::SharedPtr<UDT> > vec;
+      vec.insert(eckit::SharedPtr<UDT>(new UDT(0)));
+      vec.insert(eckit::SharedPtr<UDT>(new UDT(1)));
+      vec.insert(eckit::SharedPtr<UDT>(new UDT(3)));
+   }
+   BOOST_CHECK_EQUAL( UDT_use_count , 0 );
+
+   //   std::cout << "test SharedPtr with a user defined type in std::map\n";
+   {
+      std::map<std::string, eckit::SharedPtr<UDT> > map;
+      map.insert(std::make_pair(std::string("first"),eckit::SharedPtr<UDT>(new UDT(0))));
+      map.insert(std::make_pair(std::string("secon"),eckit::SharedPtr<UDT>(new UDT(1))));
+      map.insert(std::make_pair(std::string("third"),eckit::SharedPtr<UDT>(new UDT(2))));
+   }
+   BOOST_CHECK_EQUAL( UDT_use_count , 0 );
+
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/memory_map/CMakeLists.txt b/eckit/src/tests/memory_map/CMakeLists.txt
new file mode 100644
index 0000000..9ecabb1
--- /dev/null
+++ b/eckit/src/tests/memory_map/CMakeLists.txt
@@ -0,0 +1,3 @@
+ecbuild_add_test( TARGET      eckit_test_memory_map
+                  SOURCES     test_memory_map.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/memory_map/test_memory_map.cc b/eckit/src/tests/memory_map/test_memory_map.cc
new file mode 100644
index 0000000..ee85c45
--- /dev/null
+++ b/eckit/src/tests/memory_map/test_memory_map.cc
@@ -0,0 +1,119 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Bytes.h"
+
+int get_pagesize()
+{
+    int pagesize;
+    errno = 0ll;
+    if ((pagesize = sysconf(_SC_PAGE_SIZE)) == -1)
+    {
+       if (errno == 0)
+           printf("PAGE_SIZE not supported by sysconf implementation\n");
+       else
+           perror("sysconf error.");
+
+       exit(EXIT_FAILURE);
+    }
+    return pagesize;
+}
+
+// |--------------------------------|--------------------------------|
+//                                  ^2G limit                        ^4Gb
+//                              ^--------^ mapped region
+
+#define SIZE_2G  2147483648   // 2^31
+#define TOTAL_SIZE  2*SIZE_2G // 4Gb file
+
+#define ELEMS_2G   (SIZE_2G/sizeof(int))
+
+#define NELEMS   8*1024
+#define MAP_SIZE  (NELEMS*sizeof(int))
+
+#define ELEM_LW    (ELEMS_2G - NELEMS/2)
+#define ELEM_UP    (ELEM_LW  + NELEMS)
+
+#define SIZE_UP  (ELEM_UP*sizeof(int))
+#define SIZE_LW  (ELEM_LW*sizeof(int))
+
+using namespace std;
+using namespace eckit;
+
+int main(int argc, char *argv[])
+{
+    // compute the 2GB limit and its number of pages
+    int pagesize = get_pagesize();
+    size_t nbpages_2gb = SIZE_2G / pagesize;
+    ASSERT( nbpages_2gb * pagesize == SIZE_2G );
+
+    std::cout << Bytes(SIZE_2G) << " are " <<  nbpages_2gb << " memory pages" << std::endl;
+
+    // mmapped array of int's
+    int *map;
+
+    // make temporary file name
+    int fd;
+    char filename [] = "mmaped.XXXXXX";
+    if( (fd = ::mkstemp( filename )) == -1 ) 
+      perror("cannot create temporary file"), exit(EXIT_FAILURE);
+
+// file is opened in mkstemp, so this is not needed
+//    if( ( fd = ::open(filename, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600) ) == -1 )
+//      perror("Error opening file"),  exit(EXIT_FAILURE);
+
+    // stretch the file size to the size of the (mmapped) array of ints
+    if( (long long) ::lseek(fd, TOTAL_SIZE-1, SEEK_SET) < 0 )
+      perror("Error calling lseek() to 'stretch' the file"), exit(EXIT_FAILURE);
+
+    // write something to the end of the file to actually resize it correctly
+    if( ::write(fd, "", 1) != 1 ) 
+      close(fd), perror("Error writing last byte of the file"), exit(EXIT_FAILURE);
+
+    // map the file
+    std::cout << "mapping " << Bytes(MAP_SIZE) << " from " << Bytes(SIZE_LW) << " to " << Bytes(SIZE_UP) << std::endl;
+
+    if( (map = (int*) ::mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ELEM_LW)) ==  MAP_FAILED)
+      close(fd), perror("Error mmapping the file"), exit(EXIT_FAILURE);
+
+    // write to file as if it were memory
+    for (int i = 0; i < NELEMS; ++i)
+      map[i] = 2 * i;
+
+    // free the mmapped memory
+    if (munmap(map, MAP_SIZE) == -1)
+      perror("Error un-mmapping the file"), exit(EXIT_FAILURE);
+
+    // close the file
+    if( close(fd) == -1 ) 
+      perror("Error closing file descriptot"), exit(EXIT_FAILURE);
+
+    // test that the contents of the file are correct
+
+    // remove the file -- its large!
+    if( ::unlink(filename) == -1 )
+      perror("Error removing the file"), exit(EXIT_FAILURE);
+
+    return 0;
+}
diff --git a/eckit/src/tests/mpi/CMakeLists.txt b/eckit/src/tests/mpi/CMakeLists.txt
new file mode 100644
index 0000000..38035d7
--- /dev/null
+++ b/eckit/src/tests/mpi/CMakeLists.txt
@@ -0,0 +1,18 @@
+ecbuild_add_test(
+    TARGET      eckit_test_mpi
+    SOURCES     eckit_test_mpi.cc
+    CONDITION   HAVE_MPI
+    LIBS eckit_mpi
+    MPI 4
+    BOOST
+)
+
+ ecbuild_add_test(
+    TARGET      eckit_test_mpi_addcomm
+    SOURCES     eckit_test_mpi_addcomm.cc
+    CONDITION   HAVE_MPI
+    LIBS eckit_mpi
+    MPI 4
+)
+
+    
diff --git a/eckit/src/tests/mpi/eckit_test_mpi.cc b/eckit/src/tests/mpi/eckit_test_mpi.cc
new file mode 100644
index 0000000..b8202f8
--- /dev/null
+++ b/eckit/src/tests/mpi/eckit_test_mpi.cc
@@ -0,0 +1,556 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/types/Types.h"
+#include "eckit/mpi/Comm.h"
+
+#define BOOST_TEST_MODULE eckit_test_mpi
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/testing/Setup.h"
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// Teach boost how to write a std::vector<T>
+namespace boost {
+
+template <typename T>
+inline boost::wrap_stringstream& operator<<(boost::wrap_stringstream& wrapped, std::vector<T> const& v)
+{
+    wrapped << '[';
+    for (int j=0; j<v.size(); ++j) {
+        wrapped << (j!=0 ? "," : "") << v[j];
+    }
+    return wrapped << ']';
+}
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE( Setup );
+
+BOOST_AUTO_TEST_CASE( test_rank_size )
+{
+    BOOST_CHECK_NO_THROW( mpi::comm().size() );
+    BOOST_CHECK_NO_THROW( mpi::comm().rank() );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_broadcast )
+{
+  size_t root = 0;
+
+  int d[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  BOOST_TEST_CHECKPOINT("Test value");
+  {
+    float val;
+    if( mpi::comm().rank() == root ) {
+      val = 3.14f;
+    }
+
+    float* pval = &val;
+    BOOST_CHECK_NO_THROW( mpi::comm().broadcast(val, root) );
+    BOOST_CHECK_NO_THROW( mpi::comm().broadcast(pval, pval+1, root) );
+
+    // check results
+    BOOST_CHECK_CLOSE( val, 3.14f, 0.0001 );
+  }
+
+  BOOST_TEST_CHECKPOINT("Test vector");
+  {
+    std::vector<int> data(10);
+    if(mpi::comm().rank() == root) {
+      data.assign(d,d+10);
+    }
+    BOOST_CHECK_NO_THROW( mpi::comm().broadcast(data, root) );
+
+    // check results
+    BOOST_CHECK_EQUAL( data.size(), 10u );
+    BOOST_CHECK_EQUAL_COLLECTIONS(data.begin(),data.end(),d,d+10);
+  }
+
+  BOOST_TEST_CHECKPOINT("Test raw data");
+  {
+    std::vector<int> data(10);
+    if( mpi::comm().rank() == root )
+    {
+      data.assign(d,d+10);
+    }
+
+    BOOST_CHECK_NO_THROW( mpi::comm().broadcast(data.begin(), data.end(), root) );
+
+    // check results
+    BOOST_CHECK_EQUAL_COLLECTIONS(data.begin(), data.end(), d, d+10);
+  }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_gather_scalar )
+{
+    size_t size = mpi::comm().size();
+    size_t rank = mpi::comm().rank();
+
+    std::vector<size_t> recv(size);
+
+    size_t send = 777 + rank;
+
+    size_t root = 0; /* master */
+
+    BOOST_CHECK_NO_THROW( mpi::comm().gather(send, recv, root) );
+
+    if(rank == root) {
+        std::vector<size_t> expected(size);
+        for(size_t j = 0; j < recv.size(); ++j) {
+            expected[j] = 777 + j;
+        }
+
+        BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_gather_nscalars )
+{
+    for(size_t N = 1; N < 10; ++N) {
+
+        size_t size = mpi::comm().size();
+        size_t rank = mpi::comm().rank();
+
+        std::vector<long> recv(size*N);
+
+        std::vector<long> send(N);
+        for(size_t n = 0; n < N; ++n) {
+            send[n] = long(rank*2 + n + 1);
+        }
+
+        std::vector<long> expected(size*N);
+        for(size_t j = 0; j < size; ++j) {
+            for(size_t n = 0; n < N; ++n) {
+                expected[j*N+n] = long(j*2 + n + 1);
+            }
+        }
+
+        size_t root = 0; /* master */
+
+        BOOST_CHECK_NO_THROW( mpi::comm().gather(send, recv, root) );
+
+        if(rank == root) {
+            BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+        }
+
+        std::vector<long> recv2(size*N);
+
+        BOOST_CHECK_NO_THROW( mpi::comm().gather(send.begin(), send.end(), recv2.begin(), recv2.end(), root) );
+
+        if(rank == root) {
+            BOOST_CHECK_EQUAL_COLLECTIONS(recv2.begin(), recv2.end(), expected.begin(), expected.end());
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_gatherv_equal_stride )
+{
+    size_t root = 0;
+    size_t size = mpi::comm().size();
+    size_t rank = mpi::comm().rank();
+
+    size_t stride = 100;
+
+    std::vector<long> send(stride);
+
+    for(size_t i = 0; i < stride; ++i) {
+        send[i] = long(rank * i);
+    }
+
+    std::vector<long> recv(size * stride);
+
+    std::vector<int> displs(size);
+    std::vector<int> recvcounts(size);
+
+    for(size_t i = 0; i < size; ++i) {
+        displs[i]     = int(i*stride);
+        recvcounts[i] = int(stride);
+    }
+
+    BOOST_CHECK_NO_THROW( mpi::comm().gatherv(send, recv, recvcounts, displs, root) );
+
+    std::vector<long> expected(size * stride);
+    for(size_t i = 0; i < size; ++i) {
+        for(size_t j = 0; j < stride; ++j) {
+            expected[i*stride + j] = long(i*j);
+        }
+    }
+
+    if(rank == root) {
+        BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_gatherv_unequal_stride )
+{
+    size_t root = 0;
+    size_t size = mpi::comm().size();
+    size_t rank = mpi::comm().rank();
+
+    size_t stride = 10 * rank;
+
+    std::vector<long> send(stride);
+
+    for(size_t i = 0; i < stride; ++i) {
+        send[i] = long(rank * i);
+    }
+
+    std::vector<int> displs(size);
+    std::vector<int> recvcounts(size);
+
+    for(size_t i = 0; i < size; ++i) {
+        displs[i]     = int( i ? displs[i-1] + stride : 0 );
+        recvcounts[i] = int(stride);
+    }
+
+    size_t recvsize = size_t( std::accumulate(recvcounts.begin(), recvcounts.end(), 0) );
+
+    std::vector<long> recv(recvsize);
+
+    BOOST_CHECK_NO_THROW( mpi::comm().gatherv(send, recv, recvcounts, displs, root) );
+
+    size_t e = 0;
+    std::vector<long> expected(recvsize);
+    for(size_t i = 0; i < size; ++i) {
+        for(size_t j = 0; j < stride; ++j, ++e) {
+            expected[e] = long(i*j);
+        }
+    }
+
+    if(rank == root) {
+        BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_scatter_scalar )
+{
+    size_t size = mpi::comm().size();
+    std::vector<long> send(size);
+    for(size_t j = 0; j < send.size(); ++j) {
+            send[j] = long(j*j) - 1;
+    }
+
+    long recv = -999;
+
+    size_t root = 0; /* master */
+
+    BOOST_CHECK_NO_THROW( mpi::comm().scatter(send, recv, root) );
+
+    size_t rank = mpi::comm().rank();
+
+    BOOST_CHECK_EQUAL(recv, rank*rank - 1);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_scatter_nscalars )
+{
+    for(size_t N = 1; N < 4; ++N) {
+
+        size_t size = mpi::comm().size();
+        std::vector<long> send(size*N);
+        for(size_t j = 0; j < send.size() / N; ++j) {
+            for(size_t n = 0; n < N; ++n) {
+                send[j*N+n] = long(j*j - n);
+            }
+        }
+
+        std::vector<long> recv(N);
+
+        size_t root = 0; /* master */
+
+        BOOST_CHECK_NO_THROW( mpi::comm().scatter(send, recv, root) );
+
+        size_t rank = mpi::comm().rank();
+
+        // check results
+        std::vector<long> expected (N);
+        for(size_t n = 0; n < N; ++n) {
+            expected[n] = long(rank*rank - n);
+        }
+
+        BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_scatterv )
+{
+     /// TODO
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_allReduce )
+{
+  int d = int(mpi::comm().rank()) + 1;
+
+  std::pair<double,int> v(-d, mpi::comm().rank());
+  std::cout << "v : " << v << std::endl;
+  std::cout << std::flush;
+  mpi::comm().barrier();
+
+  // check results
+  int s=0;
+  int p=1;
+  for( size_t j=0; j<mpi::comm().size(); ++j ) {
+    s += (j+1);
+    p *= (j+1);
+  }
+
+  BOOST_TEST_CHECKPOINT("Testing all_reduce");
+  {
+    int sum;
+    int prod;
+    int max;
+    int min;
+
+    std::pair<double,int> maxloc;
+    std::pair<double,int> minloc;
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(d, sum,    mpi::sum())   );
+
+    BOOST_CHECK_EQUAL( sum, s );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(d, prod,   mpi::prod()) );
+
+    BOOST_CHECK_EQUAL( prod, p );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(d, max,    mpi::max())   );
+
+    BOOST_CHECK_EQUAL( size_t(max), mpi::comm().size() );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(d, min,    mpi::min())   );
+
+    BOOST_CHECK_EQUAL( min, 1 );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(v, maxloc, mpi::maxloc())   );
+
+    BOOST_CHECK_EQUAL( maxloc.first, -double(1) );
+    BOOST_CHECK_EQUAL( maxloc.second, 0 );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduce(v, minloc, mpi::minloc())   );
+
+    BOOST_CHECK_EQUAL( minloc.first, -double(mpi::comm().size()) );
+    BOOST_CHECK_EQUAL( size_t(minloc.second), mpi::comm().size()-1 );
+  }
+
+  std::vector<float> arr(5, mpi::comm().rank()+1);
+  std::cout << "arr : " << arr << std::endl;
+
+  std::cout << std::flush;
+  mpi::comm().barrier();
+
+  BOOST_TEST_CHECKPOINT("Testing all_reduce inplace");
+  {
+    int sum = d;
+    int prod = d;
+    int max = d;
+    int min = d;
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(sum, mpi::sum())   );
+    BOOST_CHECK_EQUAL( sum, s );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(prod, mpi::prod()) );
+    BOOST_CHECK_EQUAL( prod, p );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(max, mpi::max())   );
+    BOOST_CHECK_EQUAL( size_t(max), mpi::comm().size() );
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(min, mpi::min())   );
+    BOOST_CHECK_EQUAL( min, 1 );
+
+    std::vector<float> expected;
+
+    expected = std::vector<float>(5, mpi::comm().size());
+    std::vector<float> maxvec = arr;
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(maxvec.begin(), maxvec.end(), mpi::max()) );
+    BOOST_CHECK_EQUAL_COLLECTIONS(maxvec.begin(),maxvec.end(),expected.begin(),expected.end());
+
+    expected = std::vector<float>(5,1);
+    std::vector<float> minvec = arr;
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(minvec.begin(),minvec.end(), mpi::min()) );
+    BOOST_CHECK_EQUAL_COLLECTIONS(minvec.begin(),minvec.end(),expected.begin(),expected.end());
+
+    expected = std::vector<float>(5,s);
+    std::vector<float> sumvec = arr;
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(sumvec.begin(),sumvec.end(), mpi::sum()) );
+    BOOST_CHECK_EQUAL_COLLECTIONS(sumvec.begin(),sumvec.end(),expected.begin(),expected.end());
+
+    expected = std::vector<float>(5,p);
+    std::vector<float> prodvec = arr;
+    BOOST_CHECK_NO_THROW( mpi::comm().allReduceInPlace(prodvec.begin(),prodvec.end(), mpi::prod()) );
+    BOOST_CHECK_EQUAL_COLLECTIONS(prodvec.begin(),prodvec.end(),expected.begin(),expected.end());
+  }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_allGather )
+{
+    // Scalar
+
+    int send = mpi::comm().rank();
+    std::vector<int> recv(mpi::comm().size());
+
+    BOOST_CHECK_NO_THROW( mpi::comm().allGather(send, recv.begin(), recv.end()) );
+
+    std::vector<int> expected(mpi::comm().size());
+    for(size_t j = 0; j < expected.size(); ++j) {
+        expected[j] = int(j);
+    }
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_allGatherv )
+{
+  std::vector<int> send(mpi::comm().rank(), mpi::comm().rank());
+  mpi::Buffer<int> recv(mpi::comm().size());
+
+  BOOST_CHECK_NO_THROW( mpi::comm().allGatherv(send.begin(), send.end(), recv) );
+
+  // check results
+  std::vector<int> expected;
+  for(size_t j=0; j<mpi::comm().size(); ++j )
+  {
+    for( size_t i=0; i<j; ++i )
+      expected.push_back(j);
+  }
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(recv.begin(), recv.end(), expected.begin(), expected.end());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_allToAll )
+{
+  std::vector< std::vector<int> > send(mpi::comm().size(), std::vector<int>(1, mpi::comm().rank()));
+  std::vector< std::vector<int> > recv(mpi::comm().size());
+
+  BOOST_CHECK_NO_THROW( mpi::comm().allToAll(send, recv) );
+
+  // check results
+  std::vector< std::vector<int> > expected(mpi::comm().size());
+  for(size_t j=0; j<mpi::comm().size(); ++j) {
+    expected[j] = std::vector<int>(1,int(j));
+  }
+
+  BOOST_CHECK_EQUAL(recv.size(), expected.size());
+  for (size_t i = 0; i < mpi::comm().size(); ++i) {
+      BOOST_CHECK_EQUAL_COLLECTIONS(recv[i].begin(), recv[i].end(), expected[i].begin(), expected[i].end());
+  }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_nonblocking_send_receive )
+{
+  mpi::Comm& comm = mpi::comm("world");
+  int tag = 99;
+  mpi::Request sendreq;
+  mpi::Request recvreq;
+  double send;
+  double recv = 1.;
+
+  // Post a receive request
+  if( comm.rank() == comm.size()-1 ) {
+    recvreq = comm.iReceive(recv,0,tag);
+  }
+
+  // Post a send request
+  if( comm.rank() == 0 ) {
+    send = 0.5;
+    sendreq = comm.iSend(send,comm.size()-1,tag);
+  }
+
+  // Wait for receiving to finish
+  if( comm.rank() == comm.size()-1 ) {
+    mpi::Status recvstatus = comm.wait(recvreq);
+    BOOST_CHECK_CLOSE(recv,0.5,1.e-9);
+  }
+  else {
+    BOOST_CHECK_CLOSE(recv,1.,1.e-9);
+  }
+
+  // Wait for sending to finish
+  if( comm.rank() == 0 ) {
+    mpi::Status sendstatus = comm.wait(sendreq);
+  }
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_blocking_send_receive )
+{
+  mpi::Comm& comm = mpi::comm("world");
+  int tag = 99;
+  double send1, send2;
+  double recv1 = 1.;
+  double recv2 = 1.;
+
+  // Send1
+  if( comm.rank() == 0 ) {
+    send1 = 0.1;
+    comm.send(send1,comm.size()-1,tag);
+    send1 = 0.; // should not matter, as send() copies to internal mpi buffer
+  }
+
+  // Send2
+  if( comm.rank() == 0 ) {
+    send2 = 0.2;
+    comm.send(send2,comm.size()-1,tag);
+  }
+
+  // Receive1
+  if( comm.rank() == comm.size()-1 ) {
+    mpi::Status status = comm.receive(recv1,0,tag);
+    BOOST_CHECK_CLOSE(recv1,0.1,1.e-9);
+  }
+
+  // Receive2
+  if( comm.rank() == comm.size()-1 ) {
+    mpi::Status status = comm.receive(recv2,0,tag);
+    BOOST_CHECK_CLOSE(recv2,0.2,1.e-9);
+  }
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+BOOST_AUTO_TEST_CASE( test_allToAllv )
+{
+    // TODO
+}
diff --git a/eckit/src/tests/mpi/eckit_test_mpi_addcomm.cc b/eckit/src/tests/mpi/eckit_test_mpi_addcomm.cc
new file mode 100644
index 0000000..f39ffd8
--- /dev/null
+++ b/eckit/src/tests/mpi/eckit_test_mpi_addcomm.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <mpi.h>
+
+#include "eckit/mpi/Comm.h"
+
+int main( int argc, char** argv )
+{
+    MPI_Init(&argc, &argv);
+
+    MPI_Fint fortranComm = MPI_Comm_c2f(MPI_COMM_WORLD);
+
+    eckit::mpi::addComm("fort.1", fortranComm);
+
+    eckit::mpi::setCommDefault("fort.1");
+
+    MPI_Finalize();
+
+    return 0;
+}
diff --git a/eckit/src/tests/option/CMakeLists.txt b/eckit/src/tests/option/CMakeLists.txt
new file mode 100644
index 0000000..a2dd1a8
--- /dev/null
+++ b/eckit/src/tests/option/CMakeLists.txt
@@ -0,0 +1,16 @@
+foreach( TESTCASE RANGE 1 12 )
+  ecbuild_add_test(
+    TARGET      eckit_test_option_cmdargs_${TESTCASE}
+    SOURCES     eckit_test_option_cmdargs.cc
+    LIBS        eckit_option
+    DEFINITIONS TESTCASE=${TESTCASE}
+    BOOST
+  )
+endforeach()
+
+ecbuild_add_test(
+    TARGET      eckit_test_option_factory
+    SOURCES     eckit_test_option_factory.cc
+    LIBS        eckit_option
+    BOOST
+)
diff --git a/eckit/src/tests/option/eckit_test_option_cmdargs.cc b/eckit/src/tests/option/eckit_test_option_cmdargs.cc
new file mode 100644
index 0000000..e27abfe
--- /dev/null
+++ b/eckit/src/tests/option/eckit_test_option_cmdargs.cc
@@ -0,0 +1,234 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_option
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/option/CmdArgs.h"
+#include "eckit/option/SimpleOption.h"
+#include "eckit/option/VectorOption.h"
+#include "eckit/runtime/Main.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/types/Types.h"
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit;
+using namespace eckit::option;
+
+/// Test the options parser
+/// @note The options parser normall calls ::exit(1) if an error occurs. All the constructors accept an additional
+///       option to turn this behaviour off, and throw a UserError instead.
+/// @note Main can only be initialised once, so we need to generate a binary per test case via ifdefs
+
+namespace {
+
+    /// A local function to satisfy the CmdArg details
+    void usage(const std::string&) {
+        // Empty
+    }
+
+    void init(int nargs, const char* global_args[]) {
+        Main::initialise(nargs, const_cast<char**>(global_args));
+        CmdArgs(&usage, 1, 0, true);
+    }
+
+    void init(int nargs, const char* global_args[], std::vector<Option*>& options, int args_count = 0) {
+        Main::initialise(nargs, const_cast<char**>(global_args));
+        CmdArgs(&usage, options, args_count, 0, true);
+    }
+}
+
+BOOST_AUTO_TEST_SUITE( test_eckit_option_cmdargs )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE >= 1 and TESTCASE <= 3
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_numbered_args_required ) {
+    // Argument parser will succeed when passed exactly one unnamed argument.
+    // Note that argument 0 is always the executable name.
+
+#if TESTCASE == 1
+    const char* args1[] = {"exe"};
+    BOOST_CHECK_THROW(init(1, args1), UserError);
+#endif
+
+#if TESTCASE == 2
+    const char* args2[] = {"exe", "a1"};
+    BOOST_CHECK_NO_THROW(init(2, args2));
+#endif
+
+#if TESTCASE == 3
+    const char* args3[] = {"exe", "a1", "a2"};
+    BOOST_CHECK_THROW(init(3, args3), UserError);
+#endif
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE >= 4 and TESTCASE <= 6
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_numbered_args_required_with_options ) {
+    std::vector<Option*> options;
+    options.push_back(new SimpleOption<std::string>("arg1", ""));
+
+    // Argument parser will succeed when passed exactly one unnamed argument.
+    // Note that argument 0 is always the executable name.
+
+#if TESTCASE == 4
+    const char* args1[] = {"exe"};
+    BOOST_CHECK_THROW(init(1, args1, options, 1), UserError);
+#endif
+
+#if TESTCASE == 5
+    const char* args2[] = {"exe", "a1"};
+    BOOST_CHECK_NO_THROW(init(2, args2, options, 1));
+#endif
+
+#if TESTCASE == 6
+    const char* args3[] = {"exe", "a1", "a2"};
+    BOOST_CHECK_THROW(init(3, args3, options, 1), UserError);
+#endif
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 7
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_simple_argument_string ) {
+    // Set up he parser to accept two named arguments, one integer and one string
+    // n.b. Option* are deleted inside CmdArgs.
+    std::vector<Option*> options;
+    options.push_back(new SimpleOption<std::string>("arg1", ""));
+    options.push_back(new SimpleOption<long>("arg2", ""));
+
+    const char* input[] = {"exe", "--arg1=testing"};
+    Main::initialise(2, const_cast<char**>(input));
+
+    CmdArgs args(&usage, options, 0, 0, true);
+    BOOST_CHECK(args.has("arg1"));
+    BOOST_CHECK(!args.has("arg2"));
+
+    std::string tmpstr;
+    args.get("arg1", tmpstr);
+    BOOST_CHECK_EQUAL(tmpstr, "testing");
+    BOOST_CHECK_EQUAL(args.getString("arg1"), "testing");
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 8
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_simple_argument_integer ) {
+    // Set up the parser to accept two named arguments, one integer and one string
+    // n.b. Option* are deleted inside CmdArgs.
+    std::vector<Option*> options;
+    options.push_back(new SimpleOption<std::string>("arg1", ""));
+    options.push_back(new SimpleOption<long>("arg2", ""));
+
+    const char* input[] = {"exe", "--arg2=12345"};
+    Main::initialise(2, const_cast<char**>(input));
+
+    CmdArgs args(&usage, options, 0, 0, true);
+    BOOST_CHECK(args.has("arg2"));
+    BOOST_CHECK(!args.has("arg1"));
+
+    long tmpi;
+    args.get("arg2", tmpi);
+    BOOST_CHECK_EQUAL(tmpi, 12345);
+    BOOST_CHECK_EQUAL(args.getLong("arg2"), 12345);
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 9
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_simple_argument_missing ) {
+    std::vector<Option*> options;
+    options.push_back(new SimpleOption<std::string>("arg1", ""));
+    options.push_back(new SimpleOption<long>("arg2", ""));
+
+    const char* input[] = {"exe", "--arg3=12345"};
+    BOOST_CHECK_THROW(init(2, input, options), UserError);
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 10
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_integer_vector ) {
+    // Set up the parser to accept two named arguments, one integer and one string
+    // n.b. Option* are deleted inside CmdArgs.
+    std::vector<Option*> options;
+    options.push_back(new VectorOption<long>("arg", "", 3));
+
+    const char* input[] = {"exe", "--arg=-12345/678/-123"};
+    Main::initialise(2, const_cast<char**>(input));
+
+    CmdArgs args(&usage, options, 0, 0, true);
+    BOOST_CHECK(args.has("arg"));
+
+    std::vector<long> tmpv;
+    args.get("arg", tmpv);
+    BOOST_CHECK_EQUAL(tmpv.size(), 3);
+    BOOST_CHECK_EQUAL(tmpv[0], -12345);
+    BOOST_CHECK_EQUAL(tmpv[1], 678);
+    BOOST_CHECK_EQUAL(tmpv[2], -123);
+
+    // Check equality directly to avoid exciting operator<< gubbins within BOOST_CHECK_EQUAL.
+    BOOST_CHECK(tmpv == args.getLongVector("arg"));
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 11
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_double_vector ) {
+    // Set up the parser to accept two named arguments, one integer and one string
+    // n.b. Option* are deleted inside CmdArgs.
+    std::vector<Option*> options;
+    options.push_back(new VectorOption<double>("arg", "", 4));
+
+    const char* input[] = {"exe", "--arg=-123.45/67.8/90/-123.0"};
+    Main::initialise(2, const_cast<char**>(input));
+
+    CmdArgs args(&usage, options, 0, 0, true);
+    BOOST_CHECK(args.has("arg"));
+
+    std::vector<double> tmpv;
+    args.get("arg", tmpv);
+    BOOST_CHECK_EQUAL(tmpv.size(), 4);
+    BOOST_CHECK_CLOSE(tmpv[0], -123.45, 1.0e-8);
+    BOOST_CHECK_CLOSE(tmpv[1], 67.8, 1.0e-8);
+    BOOST_CHECK_CLOSE(tmpv[2], 90, 1.0e-8);
+    BOOST_CHECK_CLOSE(tmpv[3], -123, 1.0e-8);
+
+    // Check equality directly to avoid exciting operator<< gubbins within BOOST_CHECK_EQUAL.
+    BOOST_CHECK(tmpv == args.getDoubleVector("arg"));
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if TESTCASE == 12
+BOOST_AUTO_TEST_CASE( test_eckit_option_cmdargs_vector_size_check ) {
+    std::vector<Option*> options;
+    options.push_back(new VectorOption<long>("arg", "", 4));
+
+    const char* input[] = {"exe", "--arg=1/2/3"};
+    BOOST_CHECK_THROW(init(2, input, options), UserError);
+}
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/option/eckit_test_option_factory.cc b/eckit/src/tests/option/eckit_test_option_factory.cc
new file mode 100644
index 0000000..bb2643a
--- /dev/null
+++ b/eckit/src/tests/option/eckit_test_option_factory.cc
@@ -0,0 +1,94 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_option
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/option/FactoryOption.h"
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Build some generic factories to demonstrate functionality.
+/// n.b. This does not worry about thread safety in the factory for the purpose of these unit tests.
+///      Only one thread...
+
+namespace {
+
+/// A general base type, for objects to be built from.
+class ObjectBase {
+public:
+    ObjectBase() {}
+};
+
+/// A general object factory.
+class ObjectFactory {
+    std::string name_;
+    virtual ObjectBase* make() const = 0;
+    static std::map<std::string, ObjectFactory*>& factory() {
+        static std::map<std::string, ObjectFactory*> m;
+        return m;
+    }
+protected:
+    ObjectFactory(const std::string& nm) : name_(nm) { factory()[nm] = this; }
+    virtual ~ObjectFactory() { factory().erase(name_); }
+public:
+    static void list(std::ostream& out) {
+        const char* sep = "";
+        for (std::map<std::string, ObjectFactory*>::const_iterator j = factory().begin(); j != factory().end(); ++j) {
+            out << sep << (*j).first;
+            sep = ", ";
+        }
+    }
+    static ObjectBase * build(const std::string& nm) { return factory()[nm]->make(); }
+};
+
+
+/// Templated class to self-register, and build, objects
+template <class T>
+class ObjectBuilder : public ObjectFactory {
+    virtual ObjectBase * make() const { return new T; }
+public:
+    ObjectBuilder(const std::string& name) : ObjectFactory(name) {}
+};
+
+
+/// Register the types
+class Object1 : public ObjectBase { };
+class Object2 : public ObjectBase { };
+
+ObjectBuilder<Object1> factory1("obj1");
+ObjectBuilder<Object2> factory2("obj2");
+
+} // anonymous namespace
+
+//----------------------------------------------------------------------------------------------------------------------
+
+using namespace eckit;
+using namespace eckit::option;
+
+BOOST_AUTO_TEST_SUITE( test_eckit_option_factory )
+
+BOOST_AUTO_TEST_CASE( test_eckit_option_factory_list) {
+
+    FactoryOption<ObjectFactory> opt("arg1", "description");
+
+    // Extract the output
+    std::stringstream ss;
+    ss << opt;
+    std::string opt_str(ss.str());
+
+    std::string cmp = "   --arg1=name (description)\n     Values are: obj1, obj2";
+    BOOST_CHECK_EQUAL(opt_str, cmp);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/parser/CMakeLists.txt b/eckit/src/tests/parser/CMakeLists.txt
new file mode 100644
index 0000000..9d63fbc
--- /dev/null
+++ b/eckit/src/tests/parser/CMakeLists.txt
@@ -0,0 +1,14 @@
+ecbuild_add_test( TARGET   eckit_test_json
+                  BOOST
+                  SOURCES  test_json.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_json_metadata
+                  BOOST
+                  SOURCES  test_json_metadata.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_stream_parser
+                  BOOST
+                  SOURCES  test_stream_parser.cc
+                  LIBS     eckit )
diff --git a/eckit/src/tests/parser/test_json.cc b/eckit/src/tests/parser/test_json.cc
new file mode 100644
index 0000000..b912e6d
--- /dev/null
+++ b/eckit/src/tests/parser/test_json.cc
@@ -0,0 +1,138 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_parser
+
+#include "ecbuild/boost_test_framework.h"
+
+//#include "eckit/log/Log.h"
+#include "eckit/parser/JSONParser.h"
+#include "eckit/parser/JSON.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_parser_json )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_parse_to_value ) {
+    std::istringstream in("{ \"a\" : [true, false, 3], \"b\" : 42.3 , \"c\" : null, \"d\" : \"y\n\tr\rh\", \"e\" : \"867017db84f4bc2b5078ca56ffd3b9b9\"}");
+    JSONParser p(in);
+
+    Value v = p.parse();
+
+//    Log::info() << "json " << v << std::endl;
+
+    BOOST_TEST_MESSAGE( v );
+    BOOST_TEST_MESSAGE( v["a"] );
+    BOOST_TEST_MESSAGE( v["a"][2] );
+
+    JSON j(cout);
+    j << v;
+
+    BOOST_CHECK( v.isMap() );
+    BOOST_CHECK_EQUAL( v.as<ValueMap>().size(), 5 );
+
+    BOOST_CHECK( v["a"].isList() );
+    BOOST_CHECK_EQUAL( v["a"].as<ValueList>().size(), 3 );
+
+
+    BOOST_CHECK( v["a"][0].isBool() );
+    BOOST_CHECK_EQUAL( v["a"][0].as<bool>(), true );
+
+    BOOST_CHECK( v["a"][1].isBool() );
+    BOOST_CHECK_EQUAL( v["a"][1].as<bool>(), false );
+
+    BOOST_CHECK( v["a"][2].isNumber() );
+    BOOST_CHECK_EQUAL( (int) v["a"][2], 3 );
+
+    BOOST_CHECK( v["b"].isDouble() );
+    BOOST_CHECK_LT( v["b"].as<double>() - 42.3, 1E-12 );
+
+    BOOST_CHECK( v["c"].isNil() );
+
+    BOOST_CHECK( v["d"].isString() );
+
+    BOOST_CHECK( v["e"].isString() );
+    BOOST_CHECK_EQUAL( v["e"].as<string>(), "867017db84f4bc2b5078ca56ffd3b9b9" );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_parse_to_set ) {
+    istringstream in("[ \"a\" , \"b\", \"c\" ]" );
+    JSONParser p(in);
+    Value v = p.parse();
+
+//    Log::info() << "json " << v << std::endl;
+
+
+    BOOST_TEST_MESSAGE( v );
+
+    BOOST_CHECK( v.isList() );
+    BOOST_CHECK_EQUAL( v.as<ValueList>().size(), 3 );
+
+    BOOST_CHECK( v[0].isString() );
+    BOOST_CHECK_EQUAL( v[0].as<string>(), "a" );
+
+    BOOST_CHECK( v[1].isString() );
+    BOOST_CHECK_EQUAL( v[1].as<string>(), "b" );
+
+    BOOST_CHECK( v[2].isString() );
+    BOOST_CHECK_EQUAL( v[2].as<string>(), "c" );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_parse_to_map ) {
+    istringstream in("{ \"a\" : \"AAA\", \"b\" : 0.0 , \"c\" : \"null\", \"d\" : \"\"}" );
+    JSONParser p(in);
+    Value v = p.parse();
+
+//    Log::info() << "json " << v << std::endl;
+
+
+    BOOST_TEST_MESSAGE( v );
+
+    BOOST_CHECK( v.isMap() );
+    BOOST_CHECK_EQUAL( v.as<ValueMap>().size(), 4 );
+
+    BOOST_CHECK( v["a"].isString() );
+    BOOST_CHECK_EQUAL( v["a"].as<string>(), "AAA" );
+
+    BOOST_CHECK( v["b"].isDouble() );
+    BOOST_CHECK_EQUAL( v["b"].as<double>(), 0.0 );
+
+    BOOST_CHECK( v["c"].isString() );
+    BOOST_CHECK_EQUAL( v["c"].as<string>(), "null" );
+
+    BOOST_CHECK( v["d"].isString() );
+    BOOST_CHECK_EQUAL( v["d"].as<string>(), "" );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_eof ) {
+    istringstream in("");
+    JSONParser p(in);
+
+    BOOST_CHECK_THROW(p.next(), StreamParser::Error);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/parser/test_json_metadata.cc b/eckit/src/tests/parser/test_json_metadata.cc
new file mode 100644
index 0000000..414afed
--- /dev/null
+++ b/eckit/src/tests/parser/test_json_metadata.cc
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_json_metadata
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/parser/JSONMetadata.h"
+#include "eckit/io/Buffer.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_json_metadata )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_params ) {
+    std::string json_str = "{\"key1\": 1, \"key2\": 99.5, \"key3\": []}";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    std::vector<std::string> params(md.keywords());
+
+    BOOST_CHECK_EQUAL(params.size(), 3);
+    BOOST_CHECK(std::find(params.begin(), params.end(), "key1") != params.end());
+    BOOST_CHECK(std::find(params.begin(), params.end(), "key2") != params.end());
+    BOOST_CHECK(std::find(params.begin(), params.end(), "key3") != params.end());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_params_nondict ) {
+    // If a valid json is supplied that is not an object/dict, there should be no
+    // internal parameters to find
+
+    std::string json_str = "[1, 2, 44.5]";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    std::vector<std::string> params(md.keywords());
+
+    BOOST_CHECK_EQUAL(params.size(), 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_has ) {
+    std::string json_str = "{\"key1\": 1, \"key2\": 99.5, \"key3\": []}";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    BOOST_CHECK(md.has("key1"));
+    BOOST_CHECK(md.has("key2"));
+    BOOST_CHECK(md.has("key3"));
+    BOOST_CHECK(!md.has("keyunused"));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_has_nondict ) {
+    // If a valid json is supplied that is not an object/dict, there should be no
+    // internal parameters to find
+
+    std::string json_str = "[1, 2, 44.5]";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    BOOST_CHECK(!md.has("key1"));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_get_string ) {
+    std::string json_str = "{\"key1\": 1, \"key2\": \"testing string\", \"key3\": []}";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    std::string str_tmp;
+    md.get("key2", str_tmp);
+
+    // Gets value correctly
+    BOOST_CHECK_EQUAL(str_tmp, "testing string");
+
+    // Throws on incorrect type
+    BOOST_CHECK_THROW(md.get("key3", str_tmp), BadCast);
+
+    // Throws on missing key
+    BOOST_CHECK_THROW(md.get("keyunused", str_tmp), OutOfRange);
+
+    // Throws on non-dictionary root-value
+    std::string json_str2 = "[1234]";
+    Buffer buf2(json_str2.c_str(), json_str2.length());
+    JSONMetadata md2(buf2);
+
+    BOOST_CHECK_THROW(md2.get("key1", str_tmp), AssertionFailed);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_get_double ) {
+    std::string json_str = "{\"key1\": 123.45, \"key2\": \"testing string\", \"key3\": [], \"key4\": 123}";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    double dbl_tmp;
+    md.get("key1", dbl_tmp);
+    BOOST_CHECK_CLOSE(dbl_tmp, 123.45, 1.0e-6);
+
+    // N.b. this should also work for integers.
+    md.get("key4", dbl_tmp);
+    BOOST_CHECK_CLOSE(dbl_tmp, 123, 1.0e-6);
+
+    // Throws on incorrect type
+    BOOST_CHECK_THROW(md.get("key3", dbl_tmp), BadCast);
+
+    // Throws on missing key
+    BOOST_CHECK_THROW(md.get("keyunused", dbl_tmp), OutOfRange);
+
+    // Throws on non-dictionary root-value
+    std::string json_str2 = "[1234]";
+    Buffer buf2(json_str2.c_str(), json_str2.length());
+    JSONMetadata md2(buf2);
+
+    BOOST_CHECK_THROW(md2.get("key1", dbl_tmp), AssertionFailed);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_json_metadata_get_long ) {
+    std::string json_str = "{\"key1\": 123.45, \"key2\": \"testing string\", \"key3\": [], \"key4\": 123}";
+    Buffer buf(json_str.c_str(), json_str.length());
+
+    JSONMetadata md(buf);
+
+    long long_tmp;
+    md.get("key4", long_tmp);
+    BOOST_CHECK_EQUAL(long_tmp, 123);
+
+    // Throws on incorrect type
+    BOOST_CHECK_THROW(md.get("key3", long_tmp), BadCast);
+
+    // Throws on missing key
+    BOOST_CHECK_THROW(md.get("keyunused", long_tmp), OutOfRange);
+
+    // Throws on non-dictionary root-value
+    std::string json_str2 = "[1234]";
+    Buffer buf2(json_str2.c_str(), json_str2.length());
+    JSONMetadata md2(buf2);
+
+    BOOST_CHECK_THROW(md2.get("key1", long_tmp), AssertionFailed);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/parser/test_stream_parser.cc b/eckit/src/tests/parser/test_stream_parser.cc
new file mode 100644
index 0000000..fa85728
--- /dev/null
+++ b/eckit/src/tests/parser/test_stream_parser.cc
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_parser_stream
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/parser/StreamParser.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_parser_stream )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_next ) {
+    std::stringstream ss;
+    ss << "1234567890abcdefgh";
+
+    eckit::StreamParser parser(ss);
+
+    BOOST_CHECK_EQUAL(parser.next(), '1');
+    BOOST_CHECK_EQUAL(parser.next(), '2');
+    for (int i = 0; i < 15; i++)
+        parser.next();
+    BOOST_CHECK_EQUAL(parser.next(), 'h');
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_next_spaces ) {
+    std::stringstream ss;
+    ss << "1 3  6    ab def h";
+
+    eckit::StreamParser parser(ss);
+
+    BOOST_CHECK_EQUAL(parser.next(), '1');
+    BOOST_CHECK_EQUAL(parser.next(), '3');  // n.b. by default spaces=false
+    BOOST_CHECK_EQUAL(parser.next(true), ' ');
+    BOOST_CHECK_EQUAL(parser.next(true), ' ');
+    BOOST_CHECK_EQUAL(parser.next(true), '6');
+    BOOST_CHECK_EQUAL(parser.next(true), ' ');
+    BOOST_CHECK_EQUAL(parser.next(false), 'a');
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_next_eof ) {
+    // cf. commit: 7224cc431
+
+    std::stringstream ss;
+    ss << "12";
+
+    eckit::StreamParser parser(ss);
+
+    BOOST_CHECK_EQUAL(parser.next(), '1');
+    BOOST_CHECK_EQUAL(parser.next(), '2');
+    BOOST_CHECK_THROW(parser.next(), StreamParser::Error);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_peek ) {
+    std::stringstream ss;
+    ss << " 2   5";
+
+    eckit::StreamParser parser(ss);
+
+    BOOST_CHECK_EQUAL(parser.peek(), '2'); // n.b. by default spaces=false
+    BOOST_CHECK_EQUAL(parser.peek(), '2');
+    BOOST_CHECK_EQUAL(parser.next(), '2');
+    BOOST_CHECK_EQUAL(parser.peek(true), ' ');
+    BOOST_CHECK_EQUAL(parser.peek(false), '5');
+    BOOST_CHECK_EQUAL(parser.peek(true), '5');
+    BOOST_CHECK_EQUAL(parser.next(true), '5');
+
+    // And check eof behaviour
+    BOOST_CHECK_EQUAL(parser.peek(), 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_consume_char ) {
+    std::stringstream ss;
+    ss << " 2  567 ";
+
+    eckit::StreamParser parser(ss);
+
+    // n.b. by default the StreamParser ignores spaces.
+    parser.consume('2');
+    parser.consume('5');
+
+    // Check that it throws on mismatch
+    BOOST_CHECK_THROW(parser.consume('7'), StreamParser::Error); // 6 != 7
+    parser.consume('7');
+
+    // Check that it throws on eof
+    BOOST_CHECK_THROW(parser.consume('8'), StreamParser::Error);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_parser_stream_consume_string ) {
+    std::stringstream ss;
+    ss << " 234  789 0a";
+
+    eckit::StreamParser parser(ss);
+
+    // n.b. by default the StreamParser ignores spaces.
+    parser.consume("234");
+
+    // Check that it throws on mismatched contents
+    BOOST_CHECK_THROW(parser.consume("779"), StreamParser::Error);
+
+    // Check that it throws on eof
+    BOOST_CHECK_THROW(parser.consume("0ab"), StreamParser::Error);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/runtime/CMakeLists.txt b/eckit/src/tests/runtime/CMakeLists.txt
new file mode 100644
index 0000000..a79e39d
--- /dev/null
+++ b/eckit/src/tests/runtime/CMakeLists.txt
@@ -0,0 +1,19 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+ecbuild_add_test( TARGET  eckit_test_producer
+                  SOURCES test_producer.cc
+                  ENVIRONMENT _TEST_ECKIT_HOME=/tmp/$ENV{USER}
+                  LIBS    eckit
+)
+
+ecbuild_add_test( TARGET  eckit_test_context
+                  BOOST
+                  SOURCES test_context.cc
+                  LIBS    eckit
+)
diff --git a/eckit/src/tests/runtime/boost_auto_param.h b/eckit/src/tests/runtime/boost_auto_param.h
new file mode 100644
index 0000000..c2a9762
--- /dev/null
+++ b/eckit/src/tests/runtime/boost_auto_param.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. In applying
+ * this licence, ECMWF does not waive the privileges and immunities granted to it by virtue
+ * of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
+ */
+
+// source: https://stackoverflow.com/a/17890914/396967
+
+#ifndef BOOST_AUTO_PARAM_H
+#define BOOST_AUTO_PARAM_H
+
+#include <boost/test/unit_test_suite.hpp>
+#include <boost/test/parameterized_test.hpp>
+
+#define BOOST_FIXTURE_PARAM_TEST_CASE( test_name, F, P, mbegin, mend )  \
+struct test_name : public F                                             \
+{                                                                       \
+    typedef P param_t;                                                  \
+    void test_method(const param_t &);                                  \
+};                                                                      \
+                                                                        \
+void BOOST_AUTO_TC_INVOKER( test_name )(const test_name::param_t &param) \
+{                                                                       \
+    test_name t;                                                        \
+    t.test_method(param);                                               \
+}                                                                       \
+                                                                        \
+BOOST_AUTO_TU_REGISTRAR( test_name )(                                   \
+    boost::unit_test::make_test_case(                                   \
+       &BOOST_AUTO_TC_INVOKER( test_name ), #test_name,                 \
+       (mbegin), (mend)));                                              \
+                                                                        \
+void test_name::test_method(const param_t &param)                       \
+
+// *******
+
+#define BOOST_AUTO_PARAM_TEST_CASE( test_name, param_type, mbegin, mend )           \
+   BOOST_FIXTURE_PARAM_TEST_CASE( test_name,                            \
+                                  BOOST_AUTO_TEST_CASE_FIXTURE,         \
+                                  param_type,                           \
+                                  mbegin, mend)
+
+#endif // BOOST_AUTO_PARAM_H
diff --git a/eckit/src/tests/runtime/test_context.cc b/eckit/src/tests/runtime/test_context.cc
new file mode 100644
index 0000000..6535ee8
--- /dev/null
+++ b/eckit/src/tests/runtime/test_context.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_runtime
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Main.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+//-----------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_resource )
+
+BOOST_AUTO_TEST_CASE( test_default )
+{
+    /* log before context build */
+
+    Log::info() << "logging before calling Context" << std::endl;
+
+    /* setting context another time */
+
+
+    Log::info()   << "logging after resetting behavior" << std::endl;
+    Log::debug()  << "logging after resetting behavior" << std::endl;
+    Log::warning()<< "logging after resetting behavior" << std::endl;
+    Log::error()  << "logging after resetting behavior" << std::endl;
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/runtime/test_producer.cc b/eckit/src/tests/runtime/test_producer.cc
new file mode 100644
index 0000000..303528f
--- /dev/null
+++ b/eckit/src/tests/runtime/test_producer.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/runtime/Tool.h"
+#include "eckit/runtime/ProducerConsumer.h"
+#include "eckit/utils/Translator.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+    class TestProducer : public Tool {
+        virtual void run();
+
+    public:
+
+        TestProducer(int argc, char** argv): Tool(argc,argv) {}
+
+    };
+
+
+    struct C : public Consumer<string> {
+
+        virtual void consume(string& s) {
+            Log::info() << "Consume " << s << std::endl;
+            ::usleep(10000);
+        }
+
+    };
+
+    struct P : public Producer<string> {
+
+        int count_;
+
+        virtual bool done() {
+            return count_ <= 0;
+        }
+
+        virtual void produce(string& s) {
+            Log::info() << "Produce " << count_ << std::endl;
+            ::usleep(count_*10000);
+            ASSERT(count_); s = string("Hello, world! ") + Translator<int,string>()(count_); count_--;
+        }
+
+        P() : count_(5) {}
+    };
+
+    void TestProducer::run()
+    {
+        P p;
+        C c;
+        ProducerConsumer<string> pc;
+        pc.execute(p, c);
+    }
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestProducer app(argc,argv);
+    return app.start();
+}
diff --git a/eckit/src/tests/serialisation/CMakeLists.txt b/eckit/src/tests/serialisation/CMakeLists.txt
new file mode 100644
index 0000000..b3cfbeb
--- /dev/null
+++ b/eckit/src/tests/serialisation/CMakeLists.txt
@@ -0,0 +1,11 @@
+ecbuild_add_test( TARGET   eckit_test_file_stream
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_file_stream.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_streamable
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_streamable.cc
+                  LIBS     eckit )
diff --git a/eckit/src/tests/serialisation/test_file_stream.cc b/eckit/src/tests/serialisation/test_file_stream.cc
new file mode 100644
index 0000000..0a8d62e
--- /dev/null
+++ b/eckit/src/tests/serialisation/test_file_stream.cc
@@ -0,0 +1,185 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cstdlib>
+#include <cstdio>
+
+#define BOOST_TEST_MODULE TestStreamable
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/serialisation/FileStream.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+namespace {
+
+const char               i_char('a');
+const unsigned char      i_uchar('b');
+const bool               i_bool(true);
+const int                i_int(-20000000);
+const unsigned int       i_uint(30000000);
+const short              i_short(-4000);
+const unsigned short     i_ushort(5000);
+const long               i_long(-60000000);
+const unsigned long      i_ulong(70000000);
+const long long          i_longlong(-80000000);
+const unsigned long long i_ulonglong(90000000);
+//const float              i_float(300000);
+const double             i_double(100000000);
+const string             i_string("abcdefghijklmnopqrstuvwx");
+const char*              i_charp("cdefghijklmnopqrstuvwxyz");
+
+char               v_char;
+unsigned char      v_uchar;
+bool               v_bool;
+int                v_int;
+unsigned int       v_uint;
+short              v_short;
+unsigned short     v_ushort;
+long               v_long;
+unsigned long      v_ulong;
+long long          v_longlong;
+unsigned long long v_ulonglong;
+//float              v_float;
+double             v_double;
+string             v_string;
+string             v_charp;
+
+}  // anonymous namespace
+
+struct F {
+    ~F()
+    {
+        if (filename.exists()) filename.unlink();
+    }
+
+    static PathName filename;
+};
+
+PathName F::filename = PathName::unique( "data" );
+
+
+BOOST_GLOBAL_FIXTURE( F );
+
+BOOST_AUTO_TEST_SUITE( TestFileStream )
+
+BOOST_AUTO_TEST_CASE( write_data )
+{
+    BOOST_TEST_MESSAGE("Write to FileStream");
+    FileStream sout( F::filename, "w" );
+
+    sout << i_char
+         << i_uchar
+         << i_bool
+         << i_int
+         << i_uint
+         << i_short
+         << i_ushort
+         << i_long
+         << i_ulong
+         << i_longlong
+         << i_ulonglong
+//         << i_float
+         << i_double
+         << i_string
+         << i_charp
+    ;
+}
+
+BOOST_AUTO_TEST_CASE( read_data )
+{
+    BOOST_TEST_MESSAGE("Read from FileStream");
+    FileStream sin( F::filename, "r" );
+
+    sin
+        >> v_char
+        >> v_uchar
+        >> v_bool
+        >> v_int
+        >> v_uint
+        >> v_short
+        >> v_ushort
+        >> v_long
+        >> v_ulong
+        >> v_longlong
+        >> v_ulonglong
+    //  >> v_float
+        >> v_double
+        >> v_string
+        >> v_charp
+    ;
+}
+
+BOOST_AUTO_TEST_CASE( check_data )
+{
+    BOOST_CHECK_EQUAL( v_char,      i_char );
+    BOOST_CHECK_EQUAL( v_uchar,     i_uchar );
+    BOOST_CHECK_EQUAL( v_bool,      i_bool );
+    BOOST_CHECK_EQUAL( v_int,       i_int );
+    BOOST_CHECK_EQUAL( v_uint,      i_uint );
+    BOOST_CHECK_EQUAL( v_short,     i_short );
+    BOOST_CHECK_EQUAL( v_ushort,    i_ushort );
+    BOOST_CHECK_EQUAL( v_long,      i_long );
+    BOOST_CHECK_EQUAL( v_ulong,     i_ulong );
+    BOOST_CHECK_EQUAL( v_longlong,  i_longlong );
+    BOOST_CHECK_EQUAL( v_ulonglong, i_ulonglong );
+//  BOOST_CHECK_EQUAL( v_float,     i_float );
+    BOOST_CHECK_EQUAL( v_double,    i_double );
+    BOOST_CHECK_EQUAL( v_string,    i_string );
+    BOOST_CHECK_EQUAL( v_charp,     i_charp );
+}
+
+BOOST_AUTO_TEST_CASE( stream_object ) {
+    BOOST_TEST_MESSAGE("Stream an object");
+    const std::string k("key");
+    const std::string v("value");
+    {
+        FileStream sout( F::filename, "w" );
+        sout.startObject();
+        sout << k;
+        sout << v;
+        sout.endObject();
+    }
+    {
+        FileStream sin( F::filename, "r" );
+        BOOST_CHECK( sin.next() );
+        std::string s;
+        sin >> s;
+        BOOST_CHECK_EQUAL( s, k );
+        sin >> s;
+        BOOST_CHECK_EQUAL( s, v );
+        BOOST_CHECK( sin.endObjectFound() );
+        BOOST_CHECK( !sin.next() );
+    }
+}
+
+BOOST_AUTO_TEST_CASE( stream_string ) {
+    BOOST_TEST_MESSAGE("Stream a string");
+    {
+        FileStream sout( F::filename, "w" );
+        sout << i_string;
+    }
+    {
+        FileStream sin( F::filename, "r" );
+        std::string s;
+        BOOST_CHECK( sin.next(s) );
+        BOOST_CHECK_EQUAL( s, i_string );
+        BOOST_CHECK( !sin.next() );
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/serialisation/test_streamable.cc b/eckit/src/tests/serialisation/test_streamable.cc
new file mode 100644
index 0000000..a957e11
--- /dev/null
+++ b/eckit/src/tests/serialisation/test_streamable.cc
@@ -0,0 +1,221 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. In applying
+ * this licence, ECMWF does not waive the privileges and immunities granted to it by virtue
+ * of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
+ */
+
+/// @file test_streamable.cc
+/// @date Jan 2015
+/// @author Florian Rathgeber
+
+// NOTE: cannot include cstdint due to backwards compatbility with gcc 4.5
+// which requires enabling c++0x mode for including cstdint
+#include <stdint.h>
+#include <limits>
+
+#define BOOST_TEST_MODULE TestStreamable
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/serialisation/Streamable.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+template <typename T>
+class TestItem : public Streamable {
+
+public:
+	TestItem(const T& s) : payload_(s) {}
+
+	TestItem(Stream& s) : Streamable(s), payload_() {
+		s >> payload_;
+	}
+
+	// From Streamble
+	virtual void encode(eckit::Stream& s) const {
+		Streamable::encode(s);
+		s << payload_;
+	}
+	virtual const eckit::ReanimatorBase& reanimator() const;
+
+	// Class members
+	static const eckit::ClassSpec& classSpec() { return classSpec_; }
+
+	T payload_;
+
+protected:
+
+	virtual void print(std::ostream& s) const {
+		s << "TestItem " << payload_;
+	}
+
+private:
+	// -- Class members
+	static eckit::ClassSpec            classSpec_;
+
+	// -- Friends
+	friend std::ostream& operator<<(std::ostream& s,const TestItem& p) {
+		p.print(s); return s;
+	}
+};
+
+template <typename T>
+const ReanimatorBase& TestItem<T>::reanimator() const
+{
+	static Reanimator< TestItem<T> > reanimator;
+	return reanimator;
+}
+
+typedef unsigned char uchar;
+typedef long long llong;
+#ifndef ulong
+typedef unsigned long ulong;
+#endif
+typedef unsigned long long ullong;
+
+/// Partially specialise ClassSpecs since they must have unique names
+template <>
+ClassSpec TestItem<char>::classSpec_ = {&Streamable::classSpec(),"TestItemChar",};
+template <>
+ClassSpec TestItem<uchar>::classSpec_ = {&Streamable::classSpec(),"TestItemUChar",};
+template <>
+ClassSpec TestItem<bool>::classSpec_ = {&Streamable::classSpec(),"TestItemBool",};
+template <>
+ClassSpec TestItem<int>::classSpec_ = {&Streamable::classSpec(),"TestItemInt",};
+template <>
+ClassSpec TestItem<uint>::classSpec_ = {&Streamable::classSpec(),"TestItemUInt",};
+template <>
+ClassSpec TestItem<short>::classSpec_ = {&Streamable::classSpec(),"TestItemShort",};
+template <>
+ClassSpec TestItem<ushort>::classSpec_ = {&Streamable::classSpec(),"TestItemUShort",};
+template <>
+ClassSpec TestItem<long>::classSpec_ = {&Streamable::classSpec(),"TestItemLong",};
+template <>
+ClassSpec TestItem<ulong>::classSpec_ = {&Streamable::classSpec(),"TestItemULong",};
+template <>
+ClassSpec TestItem<llong>::classSpec_ = {&Streamable::classSpec(),"TestItemLLong",};
+template <>
+ClassSpec TestItem<ullong>::classSpec_ = {&Streamable::classSpec(),"TestItemULLong",};
+// NOTE: float is not implemented!
+// template <>
+// ClassSpec TestItem<float>::classSpec_ = {&Streamable::classSpec(),"TestItemFloat",};
+template <>
+ClassSpec TestItem<double>::classSpec_ = {&Streamable::classSpec(),"TestItemDouble",};
+template <>
+ClassSpec TestItem<string>::classSpec_ = {&Streamable::classSpec(),"TestItemString",};
+
+#define test_decode(TYPE, INITIAL, SUFFIX) \
+BOOST_AUTO_TEST_CASE( test_decode_##TYPE##_##SUFFIX ) \
+{ \
+	BOOST_TEST_MESSAGE("Manually (de)serialise Streamable with " #TYPE " member"); \
+	PathName filename = PathName::unique( "data" ); \
+	std::string filepath = filename.asString(); \
+	TestItem<TYPE> t(INITIAL); \
+	{ \
+		FileStream sout( filepath.c_str(), "w" ); \
+		t.encode(sout); \
+	} \
+	{ \
+		FileStream sin( filepath.c_str(), "r" ); \
+		TestItem<TYPE> t2(sin); \
+		BOOST_TEST_MESSAGE("original: " << t.payload_); \
+		BOOST_TEST_MESSAGE("streamed: " << t2.payload_); \
+		BOOST_CHECK(t.payload_ == t2.payload_); \
+	} \
+	if (filename.exists()) filename.unlink(); \
+}
+
+#define test_reanimate(TYPE, INITIAL, SUFFIX) \
+BOOST_AUTO_TEST_CASE( test_reanimate_##TYPE##_##SUFFIX ) \
+{ \
+	BOOST_TEST_MESSAGE("(de)serialise Streamable with " #TYPE " member via Reanimator"); \
+	PathName filename = PathName::unique( "data" ); \
+	std::string filepath = filename.asString(); \
+	TestItem<TYPE> t(INITIAL); \
+	{ \
+		FileStream sout( filepath.c_str(), "w" ); \
+		sout << t; \
+	} \
+	{ \
+		FileStream sin( filepath.c_str(), "r" ); \
+		TestItem<TYPE>* t2 = eckit::Reanimator< TestItem<TYPE> >::reanimate(sin); \
+		BOOST_TEST_MESSAGE("orginal: " << t.payload_); \
+		BOOST_TEST_MESSAGE("streamed: " << t2->payload_); \
+		BOOST_CHECK(t.payload_ == t2->payload_); \
+	} \
+	if (filename.exists()) filename.unlink(); \
+}
+
+BOOST_AUTO_TEST_SUITE( TestStreamable )
+
+test_decode(char, 'A', max)
+test_decode(char, 'z', min)
+test_reanimate(char, 'A', max)
+test_reanimate(char, 'z', min)
+
+test_decode(uchar, 'A', max)
+test_reanimate(uchar, 'A', max)
+
+test_decode(bool, true, true)
+test_reanimate(bool, true, true)
+
+test_decode(int, numeric_limits<int32_t>::max(), max)
+test_decode(int, numeric_limits<int32_t>::min(), min)
+test_reanimate(int, numeric_limits<int32_t>::max(), max)
+test_reanimate(int, numeric_limits<int32_t>::min(), min)
+
+test_decode(uint, numeric_limits<uint32_t>::max(), max)
+test_reanimate(uint, numeric_limits<uint32_t>::max(), max)
+
+test_decode(short, numeric_limits<short>::max(), max)
+test_decode(short, numeric_limits<short>::min(), min)
+test_reanimate(short, numeric_limits<short>::max(), max)
+test_reanimate(short, numeric_limits<short>::min(), min)
+
+test_decode(ushort, numeric_limits<ushort>::max(), max)
+test_reanimate(ushort, numeric_limits<ushort>::max(), max)
+
+// NOTE: long in eckit is always 32 bit!
+
+test_decode(long, numeric_limits<int32_t>::max(), max)
+test_decode(long, numeric_limits<int32_t>::min(), min)
+test_reanimate(long, numeric_limits<int32_t>::max(), max)
+test_reanimate(long, numeric_limits<int32_t>::min(), min)
+
+test_decode(ulong, numeric_limits<uint32_t>::max(), max)
+test_reanimate(ulong, numeric_limits<uint32_t>::max(), max)
+
+test_decode(llong, numeric_limits<llong>::max(), max)
+test_decode(llong, numeric_limits<llong>::min(), min)
+test_reanimate(llong, numeric_limits<llong>::max(), max)
+test_reanimate(llong, numeric_limits<llong>::min(), min)
+
+test_decode(ullong, numeric_limits<ullong>::max(), max)
+test_reanimate(ullong, numeric_limits<ullong>::max(), max)
+
+// NOTE: float is not implemented!
+// test_decode(float, numeric_limits<float>::max(), max)
+// test_decode(float, numeric_limits<float>::min(), min)
+// test_reanimate(float, numeric_limits<float>::max(), max)
+// test_reanimate(float, numeric_limits<float>::min(), min)
+
+test_decode(double, numeric_limits<double>::max(), max)
+test_decode(double, numeric_limits<double>::min(), min)
+test_reanimate(double, numeric_limits<double>::max(), max)
+test_reanimate(double, numeric_limits<double>::min(), min)
+
+test_decode(string, "Hello, World!", _)
+test_reanimate(string, "Hello, World!", _)
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace hermes
diff --git a/eckit/src/tests/system/CMakeLists.txt b/eckit/src/tests/system/CMakeLists.txt
new file mode 100644
index 0000000..3ec927f
--- /dev/null
+++ b/eckit/src/tests/system/CMakeLists.txt
@@ -0,0 +1,8 @@
+# file accessed in the test
+
+file( WRITE "${CMAKE_BINARY_DIR}/etc/eckit/test/test.cfg" "Just for testing" )
+
+ecbuild_add_test(   TARGET      eckit_test_system
+					BOOST
+                    SOURCES     test_system.cc
+					LIBS        eckit )
diff --git a/eckit/src/tests/system/test_system.cc b/eckit/src/tests/system/test_system.cc
new file mode 100755
index 0000000..be7cc03
--- /dev/null
+++ b/eckit/src/tests/system/test_system.cc
@@ -0,0 +1,92 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+#include <cstdlib>
+
+#define BOOST_TEST_MODULE test_eckit_system
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/exception/Exceptions.h"
+
+#include "eckit/system/ResourceUsage.h"
+#include "eckit/system/SystemInfo.h"
+#include "eckit/system/Library.h"
+#include "eckit/filesystem/LocalPathName.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_resource_usage )
+
+BOOST_AUTO_TEST_CASE( test_eckit_resource_usage_0 )
+{
+    size_t chunk = 20*1024*1024;
+    for(size_t i = 1; i < 5; ++i) {
+
+        size_t before = system::ResourceUsage().maxResidentSetSize();
+
+        void *m = ::malloc(chunk);
+        ::memset(m,0,chunk);
+
+        size_t after = system::ResourceUsage().maxResidentSetSize();
+
+        ::free(m);
+
+        BOOST_TEST_MESSAGE( "Memory usage " << after );
+
+        BOOST_REQUIRE( before <= after );
+    }
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_system_info )
+{
+    eckit::LocalPathName execPath;
+    BOOST_CHECK_NO_THROW( execPath = eckit::system::SystemInfo::instance().executablePath() );
+    BOOST_CHECK( std::string(execPath).size() );
+
+    Log::info() << "execPath is " << execPath << std::endl;
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_system_library )
+{
+    using eckit::system::Library;
+
+    std::vector<std::string> libs = Library::list();
+
+    std::string libpath;
+
+    for(std::vector<std::string>::const_iterator libname = libs.begin(); libname != libs.end(); ++libname ) {
+
+        BOOST_CHECK_NO_THROW( Library::lookup(*libname) );
+
+        const Library& lib = Library::lookup(*libname);
+
+        BOOST_CHECK_NO_THROW( lib.prefixDirectory() );
+
+        Log::info() << "Library " << lib.name() << " @ " << lib.prefixDirectory() << std::endl;
+        Log::info() << lib << std::endl;
+    }
+
+    // this exercises the tilde expansion
+
+    BOOST_CHECK_NO_THROW( LocalPathName("~eckit/etc").exists() );
+    BOOST_CHECK_NO_THROW( LocalPathName("~eckit/etc/eckit/test/test.cfg").exists() );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/eckit/src/tests/thread/CMakeLists.txt b/eckit/src/tests/thread/CMakeLists.txt
new file mode 100644
index 0000000..2107692
--- /dev/null
+++ b/eckit/src/tests/thread/CMakeLists.txt
@@ -0,0 +1,3 @@
+ecbuild_add_test( TARGET      eckit_test_mutex
+                  SOURCES     test_mutex.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/thread/test_mutex.cc b/eckit/src/tests/thread/test_mutex.cc
new file mode 100644
index 0000000..2d9c92e
--- /dev/null
+++ b/eckit/src/tests/thread/test_mutex.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/thread/Mutex.h"
+#include "eckit/runtime/Tool.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+class TestMutex : public Tool {
+public:
+
+    TestMutex(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestMutex() {}
+
+    virtual void run();
+
+protected:
+    
+    void test_constructor();
+    
+};
+
+//-----------------------------------------------------------------------------
+
+void TestMutex::test_constructor()
+{
+    Mutex* m = new Mutex();
+    
+    m->lock();
+    
+    m->unlock();
+    
+    delete m;   
+}
+
+//-----------------------------------------------------------------------------
+            
+void TestMutex::run()
+{
+    test_constructor();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestMutex app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/types/CMakeLists.txt b/eckit/src/tests/types/CMakeLists.txt
new file mode 100644
index 0000000..b70b3ce
--- /dev/null
+++ b/eckit/src/tests/types/CMakeLists.txt
@@ -0,0 +1,40 @@
+ecbuild_add_test( TARGET      eckit_test_cache
+                  SOURCES     test_cache.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_doublecompare
+                  BOOST
+                  SOURCES     test_doublecompare.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_floatcompare
+                  BOOST
+                  SOURCES     test_floatcompare.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_uuid
+                  BOOST
+                  SOURCES     test_uuid.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_print_vector
+                  BOOST
+                  SOURCES     test_print_vector.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_fixedstring
+                  BOOST
+                  SOURCES     test_fixedstring.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_fraction
+                  BOOST
+                  SOURCES     test_fraction.cc
+                  LIBS        eckit )
+
+# performance test
+
+ecbuild_add_test( TARGET      eckit-test-double-compare-speed
+                  SOURCES     test-double-compare-speed.cc
+                  LIBS        eckit )
+
diff --git a/eckit/src/tests/types/test-double-compare-speed.cc b/eckit/src/tests/types/test-double-compare-speed.cc
new file mode 100644
index 0000000..5cc8d2c
--- /dev/null
+++ b/eckit/src/tests/types/test-double-compare-speed.cc
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <stdlib.h>
+
+#include "eckit/log/Timer.h"
+#include "eckit/log/BigNum.h"
+#include "eckit/types/FloatCompare.h"
+#include "eckit/runtime/Tool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestDoubleCompareSpeed : public Tool {
+public:
+
+    TestDoubleCompareSpeed(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestDoubleCompareSpeed() {}
+
+    virtual void run();
+    
+    void setup();
+    void teardown();
+
+    void compare(size_t n);
+    
+};
+
+void TestDoubleCompareSpeed::compare(size_t n)
+{
+    for(size_t i = 0; i < n; ++i) {
+        double x = (double) ::rand() / (double) RAND_MAX;
+        eckit::types::is_approximately_equal(x, double(0.5));
+    }
+}
+
+
+void TestDoubleCompareSpeed::setup() {
+}
+
+void TestDoubleCompareSpeed::teardown() {
+}
+
+void TestDoubleCompareSpeed::run()
+{
+    const size_t n = 30000000; // with this data set, on a modern cpu we expect > 25E6 /s
+
+    setup();
+
+    eckit::Timer t;
+    
+    compare(n);
+
+    eckit::Log::info() << "Double compare speed: " << eckit::BigNum(n / t.elapsed()) << " /s" << std::endl;
+    
+    teardown();
+}
+
+} // namespace test
+} // namespace eckit
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit::test::TestDoubleCompareSpeed app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/types/test_cache.cc b/eckit/src/tests/types/test_cache.cc
new file mode 100644
index 0000000..ffb3b40
--- /dev/null
+++ b/eckit/src/tests/types/test_cache.cc
@@ -0,0 +1,223 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+#include "eckit/types/Types.h"
+
+#include "eckit/container/Cache.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit_test {
+
+class TestCache : public Tool {
+public:
+
+    TestCache(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestCache() {}
+
+    virtual void run();
+
+    void test_contructor();
+    void test_expire();
+    void test_purge();
+    void test_update();
+    void test_fetch();
+    void test_insert();
+
+};
+
+//-----------------------------------------------------------------------------
+
+struct Obj
+{
+    Obj() : s_(),d_(0) {}
+    Obj(const string& s, const unsigned long long& d) : s_(s),d_(d) {}
+    string s_;
+    unsigned long long d_;
+    friend std::ostream& operator<<(std::ostream& s,const Obj& x) { s << x.s_ << ":" << x.d_; return s; }    
+};
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_contructor()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+    
+//    cache.print(std::cout);
+    
+    ASSERT( cache.valid("a") );
+    ASSERT( cache.valid("b") );
+    ASSERT( cache.valid("c") );
+    
+    cache.clear();
+
+    ASSERT( ! cache.valid("a") );
+    ASSERT( ! cache.valid("b") );
+    ASSERT( ! cache.valid("c") );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_expire()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+    
+    cache.expire("b");
+
+//    cache.print(std::cout);
+    
+    ASSERT(  cache.size() == 3 );
+    ASSERT(  cache.valid("a") );
+    ASSERT( !cache.valid("b") );
+    ASSERT(  cache.valid("c") );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_purge()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+    ASSERT( cache.insert("d", Obj("ddd",44444) ) );
+    
+    ASSERT(  cache.size() == 4 );
+
+    ASSERT( cache.expire("b") );
+    ASSERT( cache.expire("d") );
+    
+    cache.purge();
+    
+    cache.print(std::cout);
+
+    ASSERT(  cache.size() == 2 );
+    
+    ASSERT(  cache.valid("a") );
+    ASSERT( !cache.valid("b") );
+    ASSERT(  cache.valid("c") );
+    ASSERT( !cache.valid("d") );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_update()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+
+    Obj o1( "ddd", 44444);
+
+    ASSERT( !cache.update("d",o1) );
+
+    Obj o2( "BBB", 2);
+    
+    ASSERT( cache.update("b",o2) );
+    
+    Obj o3;
+    
+    ASSERT( cache.fetch("d",o3) );
+    
+    ASSERT( o3.s_ == "ddd"  );
+    ASSERT( o3.d_ ==  44444 );
+
+    ASSERT( cache.fetch("b",o3) );
+    
+    ASSERT( o3.s_ == "BBB"  );
+    ASSERT( o3.d_ ==  2     );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_fetch()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+    ASSERT( cache.insert("d", Obj("ddd",44444) ) );
+
+    Obj o;
+
+    ASSERT( !cache.fetch("f",o) ); // no obj f exists
+    
+    ASSERT( cache.fetch("b",o) );  // obj b exists
+
+    ASSERT( o.s_ == "bbb" );
+    ASSERT( o.d_ ==  22222  );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestCache::test_insert()
+{
+    Cache<string,Obj> cache;
+    
+    ASSERT( cache.insert("a", Obj("aaa",11111) ) );
+    ASSERT( cache.insert("b", Obj("bbb",22222) ) );
+    ASSERT( cache.insert("c", Obj("ccc",33333) ) );
+
+    // double insert fails
+    
+    ASSERT( !cache.insert("a", Obj("AAA",1) ) ); 
+
+    Obj o;
+    
+    ASSERT( cache.fetch("a",o) );
+    ASSERT( o.s_ != "AAA" );
+    ASSERT( o.d_ !=  1    );
+    ASSERT( o.s_ == "aaa" );
+    ASSERT( o.d_ ==  11111);
+}
+
+//-----------------------------------------------------------------------------
+            
+void TestCache::run()
+{
+    test_contructor();
+    test_expire();
+    test_purge();
+    test_update();
+    test_fetch();
+    test_insert();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestCache app(argc,argv);
+    return app.start();
+}
diff --git a/eckit/src/tests/types/test_doublecompare.cc b/eckit/src/tests/types/test_doublecompare.cc
new file mode 100644
index 0000000..f53b127
--- /dev/null
+++ b/eckit/src/tests/types/test_doublecompare.cc
@@ -0,0 +1,344 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE test_eckit_types
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/FloatCompare.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace {
+
+bool is_equal(double a, double b, double epsilon, int maxUlps) {
+    return eckit::types::is_approximately_equal(a, b, epsilon, maxUlps);
+}
+
+bool is_equal(double a, double b, double epsilon) {
+    return eckit::types::is_approximately_equal(a, b, epsilon);
+}
+
+bool is_equal(double a, double b) {
+    return eckit::types::is_approximately_equal(a, b, 0.00001);
+}
+
+const double dEps = std::numeric_limits<double>::epsilon();
+const double dInf = std::numeric_limits<double>::infinity();
+const double sMin = std::numeric_limits<double>::denorm_min();
+const double dMin = std::numeric_limits<double>::min();
+const double dMax = std::numeric_limits<double>::max();
+const double qNaN = std::numeric_limits<double>::quiet_NaN();
+const double sNaN = std::numeric_limits<double>::signaling_NaN();
+
+};
+
+//-----------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_doublecompare )
+
+BOOST_AUTO_TEST_CASE( test_large_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_large_numbers" );
+
+   BOOST_CHECK(  is_equal(1000000,       1000000      ));
+   BOOST_CHECK(  is_equal(1000000,       1000000.00001));
+   BOOST_CHECK(  is_equal(1000000.00001, 1000000      ));
+
+   BOOST_CHECK(! is_equal(1000000.0,     1000001.0    ));
+   BOOST_CHECK(! is_equal(1000001.0,     1000000.0    ));
+
+   // -----------------------------------------------
+   BOOST_CHECK(is_equal(dMax, dMax));
+   BOOST_CHECK(is_equal(dMax, dMax, dEps));
+
+   BOOST_CHECK(is_equal(dMin, dMin));
+   BOOST_CHECK(is_equal(dMin, dMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_negative_large_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_negative_large_numbers " << dMin );
+
+   BOOST_CHECK(  is_equal(-1000000,       -1000000      ));
+   BOOST_CHECK(  is_equal(-1000000,       -1000000.00001));
+   BOOST_CHECK(  is_equal(-1000000.00001, -1000000      ));
+
+   BOOST_CHECK(! is_equal(-1000000.0,     -1000001.0    ));
+   BOOST_CHECK(! is_equal(-1000001.0,     -1000000.0    ));
+
+   // -----------------------------------------------
+   BOOST_CHECK(is_equal(-dMax, -dMax      ));
+   BOOST_CHECK(is_equal(-dMax, -dMax, dEps));
+
+   BOOST_CHECK(is_equal(-dMin, -dMin      ));
+   BOOST_CHECK(is_equal(-dMin, -dMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_large_numbers_of_opposite_sign )
+{
+    BOOST_CHECK(! is_equal(-1000000,       1000000      ));
+    BOOST_CHECK(! is_equal(-1000000,       1000000.00001));
+    BOOST_CHECK(! is_equal(-1000000.00001, 1000000      ));
+
+    BOOST_CHECK(! is_equal(-1000000.0,     1000001.0    ));
+    BOOST_CHECK(! is_equal(-1000001.0,     1000000.0    ));
+
+    // -----------------------------------------------
+    BOOST_CHECK(! is_equal(-dMax, dMax      ));
+    BOOST_CHECK(! is_equal(-dMax, dMax, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_ulp_around_one )
+{
+   BOOST_TEST_MESSAGE( "test_ulp_around_one" );
+
+   // ULP distances up to 10 are equal
+   // Going right from 1 by eps increases distance by 1
+   // Going left from 1 by eps increases distance by 2
+   for (int i = 0; i <= 10; ++i) {
+       BOOST_CHECK(is_equal(1.0 + i * dEps,   1.0,              dEps));
+       BOOST_CHECK(is_equal(1.0,              1.0 + i * dEps,   dEps));
+       BOOST_CHECK(is_equal(1.0 - i * dEps/2, 1.0,              dEps));
+       BOOST_CHECK(is_equal(1.0,              1.0 - i * dEps/2, dEps));
+   }
+   // ULP distances greater 10 are not equal
+   BOOST_CHECK(! is_equal(1.0 + 11 * dEps,   1.0,               dEps));
+   BOOST_CHECK(! is_equal(1.0,               1.0 + 11 * dEps,   dEps));
+   BOOST_CHECK(! is_equal(1.0 - 11 * dEps/2, 1.0,               dEps));
+   BOOST_CHECK(! is_equal(1.0,               1.0 - 11 * dEps/2, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_around_one )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_around_one" );
+
+   BOOST_CHECK(  is_equal(1.0000001, 1.0000002));
+   BOOST_CHECK(  is_equal(1.0000002, 1.0000001));
+
+   BOOST_CHECK(  is_equal(1.12345,   1.12346  ));
+   BOOST_CHECK(  is_equal(1.12345,   1.12344, 0.001));
+
+   BOOST_CHECK(! is_equal(1.0001,    1.0002   ));
+   BOOST_CHECK(! is_equal(1.0002,    1.0001   ));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_around_negative_one )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_around_negative_one" );
+
+   BOOST_CHECK(  is_equal(-1.0000001, -1.0000002));
+   BOOST_CHECK(  is_equal(-1.0000002, -1.0000001));
+
+   BOOST_CHECK(! is_equal(-1.0001,    -1.0002   ));
+   BOOST_CHECK(! is_equal(-1.0002,    -1.0001   ));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_between_one_and_zero )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_between_one_and_zero" );
+
+   BOOST_CHECK(  is_equal(0.000000001000001, 0.000000001000002));
+   BOOST_CHECK(  is_equal(0.000000001000002, 0.000000001000001));
+
+   BOOST_CHECK(! is_equal(0.00102,           0.00101          ));
+   BOOST_CHECK(! is_equal(0.00101,           0.00102          ));
+
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_between_minusone_and_zero )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_between_minusone_and_zero" );
+
+   BOOST_CHECK(  is_equal(-0.000000001000001, -0.000000001000002));
+   BOOST_CHECK(  is_equal(-0.000000001000002, -0.000000001000001));
+
+   BOOST_CHECK(! is_equal(-0.00102,           -0.00101          ));
+   BOOST_CHECK(! is_equal(-0.00101,           -0.00102          ));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_involving_zero" );
+
+   BOOST_CHECK(  is_equal(0.0,     0.0   ));
+   BOOST_CHECK(  is_equal(0.0,    -0.0   ));
+   BOOST_CHECK(  is_equal(-0.0,   -0.0   ));
+
+   BOOST_CHECK(! is_equal(0.0001,  0.0   ));
+   BOOST_CHECK(! is_equal(0.0,     0.0001));
+   BOOST_CHECK(! is_equal(-0.0001, 0.0   ));
+   BOOST_CHECK(! is_equal(0.0,    -0.0001));
+
+   BOOST_CHECK(  is_equal(0.0,     1e-40, 0.01 ));
+   BOOST_CHECK(  is_equal(1e-40,   0.0,   0.01 ));
+   BOOST_CHECK(! is_equal(1e-40,   0.0,   1e-41));
+   BOOST_CHECK(! is_equal(0.0,     1e-40, 1e-41));
+
+   BOOST_CHECK(  is_equal(0.0,    -1e-40, 0.1  ));
+   BOOST_CHECK(  is_equal(-1e-40,  0.0,   0.1  ));
+   BOOST_CHECK(! is_equal(-1e-40,  0.0,   1e-41));
+   BOOST_CHECK(! is_equal(0.0,    -1e-40, 1e-41));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_infinity )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_involving_infinity" );
+
+   if (std::numeric_limits<double>::has_infinity) {
+
+      BOOST_CHECK(is_equal( dInf,  dInf));
+      BOOST_CHECK(is_equal(-dInf, -dInf));
+      BOOST_CHECK(! is_equal( dInf,  dMax));
+      BOOST_CHECK(! is_equal( dMax,  dInf));
+      BOOST_CHECK(! is_equal(-dInf, -dMax));
+      BOOST_CHECK(! is_equal(-dMax, -dInf));
+   } else {
+      BOOST_TEST_MESSAGE( "test_comparisons_involving_infinity NOT VALID on this platform" );
+   }
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_nan )
+{
+    BOOST_TEST_MESSAGE( "test_comparisons_involving_nan" );
+
+    // The value NaN (Not a Number) is used to represent a value that does not represent a real number.
+    // NaN's are represented by a bit pattern with an exponent of all 1s and a non-zero fraction. T
+    // there are two categories of NaN: QNaN (Quiet NaN) and SNaN (Signalling NaN).
+    //
+    // A QNaN is a NaN with the most significant fraction bit set.
+    // QNaN's propagate freely through most arithmetic operations.
+    // These values pop out of an operation when the result is not mathematically defined.
+
+    // An SNaN is a NaN with the most significant fraction bit clear.
+    // It is used to signal an exception when used in operations.
+    // SNaN's can be handy to assign to uninitialized variables to trap premature usage.
+
+    // Semantically, QNaN's denote indeterminate operations, while SNaN's denote invalid operations.
+
+    BOOST_CHECK(! is_equal( qNaN,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  0.0 ));
+    BOOST_CHECK(! is_equal(-0.0 ,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -0.0 ));
+    BOOST_CHECK(! is_equal( 0.0 ,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dInf));
+    BOOST_CHECK(! is_equal( dInf,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dMax));
+    BOOST_CHECK(! is_equal( dMax,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -dMax));
+    BOOST_CHECK(! is_equal(-dMax,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dMin));
+    BOOST_CHECK(! is_equal( dMin,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -dMin));
+    BOOST_CHECK(! is_equal(-dMin,  qNaN));
+
+    BOOST_CHECK(! is_equal( sNaN,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  0.0 ));
+    BOOST_CHECK(! is_equal(-0.0 ,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -0.0 ));
+    BOOST_CHECK(! is_equal( 0.0 ,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dInf));
+    BOOST_CHECK(! is_equal( dInf,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dMax));
+    BOOST_CHECK(! is_equal( dMax,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -dMax));
+    BOOST_CHECK(! is_equal(-dMax,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dMin));
+    BOOST_CHECK(! is_equal( dMin,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -dMin));
+    BOOST_CHECK(! is_equal(-dMin,  sNaN));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_opposite_side_of_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_opposite_side_of_zero" );
+
+   BOOST_CHECK(! is_equal(1.000000001, -1.0        ));
+   BOOST_CHECK(! is_equal(-1.0,         1.000000001));
+   BOOST_CHECK(! is_equal(-1.000000001, 1.0        ));
+   BOOST_CHECK(! is_equal(1.0,         -1.000000001));
+
+   BOOST_CHECK(  is_equal(   10.0 * dMin,    10.0 * -dMin));
+   BOOST_CHECK(  is_equal(10000   * dMin, 10000   * -dMin));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_very_close_to_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_very_close_to_zero" );
+
+   BOOST_CHECK(  is_equal( dMin, -dMin, dEps));
+   BOOST_CHECK(  is_equal(-dMin,  dMin, dEps));
+   BOOST_CHECK(  is_equal( dMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     dMin, dEps));
+   BOOST_CHECK(  is_equal(-dMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -dMin, dEps));
+
+
+   BOOST_CHECK(  is_equal( 0.000000001, -dMin       ));
+   BOOST_CHECK(  is_equal( 0.000000001,  dMin       ));
+   BOOST_CHECK(  is_equal( dMin,         0.000000001));
+   BOOST_CHECK(  is_equal(-dMin,         0.000000001));
+
+
+   BOOST_CHECK(! is_equal( 0.000000001, -dMin,        1e-10));
+   BOOST_CHECK(! is_equal( 0.000000001,  dMin,        1e-10));
+   BOOST_CHECK(! is_equal( dMin,         0.000000001, 1e-10));
+   BOOST_CHECK(! is_equal(-dMin,         0.000000001, 1e-10));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_with_denormal_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_with_denormal_numbers" );
+
+   BOOST_CHECK(  is_equal( sMin, -sMin, dEps));
+   BOOST_CHECK(  is_equal(-sMin,  sMin, dEps));
+   BOOST_CHECK(  is_equal( sMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     sMin, dEps));
+   BOOST_CHECK(  is_equal(-sMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -sMin, dEps));
+
+   const double lMin = dMin - sMin;  // largest denormal number
+   BOOST_CHECK(  is_equal( lMin, -lMin, dEps));
+   BOOST_CHECK(  is_equal(-lMin,  lMin, dEps));
+   BOOST_CHECK(  is_equal( lMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     lMin, dEps));
+   BOOST_CHECK(  is_equal(-lMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -lMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_ulps )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_ulps" );
+
+   BOOST_CHECK(  is_equal( dMin, -dMin, 0, 2));
+   BOOST_CHECK(  is_equal(-dMin,  dMin, 0, 2));
+   BOOST_CHECK(  is_equal( dMin,  0   , 0, 1));
+   BOOST_CHECK(  is_equal( 0,     dMin, 0, 1));
+   BOOST_CHECK(  is_equal(-dMin,  0   , 0, 1));
+   BOOST_CHECK(  is_equal( 0,    -dMin, 0, 1));
+
+   BOOST_CHECK(! is_equal( dMin, -dMin, 0, 1));
+   BOOST_CHECK(! is_equal(-dMin,  dMin, 0, 1));
+   BOOST_CHECK(! is_equal( dMin,  0   , 0, 0));
+   BOOST_CHECK(! is_equal( 0,     dMin, 0, 0));
+   BOOST_CHECK(! is_equal(-dMin,  0   , 0, 0));
+   BOOST_CHECK(! is_equal( 0,    -dMin, 0, 0));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//-----------------------------------------------------------------------------
diff --git a/eckit/src/tests/types/test_fixedstring.cc b/eckit/src/tests/types/test_fixedstring.cc
new file mode 100644
index 0000000..9ef49c0
--- /dev/null
+++ b/eckit/src/tests/types/test_fixedstring.cc
@@ -0,0 +1,485 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_types
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/FixedString.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_types_fixedstring )
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Start by testing the constructors
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_constructor_zero ) {
+    // What happens if we create a length-zero FixedString?
+
+    FixedString<0> fs;
+
+    BOOST_CHECK_EQUAL(fs.length(), 0);
+    BOOST_CHECK_EQUAL(fs.size(), 0);
+    BOOST_CHECK_EQUAL(fs.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs), "");
+
+    FixedString<0> fs2("");
+    BOOST_CHECK_EQUAL(fs2.length(), 0);
+    BOOST_CHECK_EQUAL(fs2.size(), 0);
+    BOOST_CHECK_EQUAL(fs2.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs2), "");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_constructor_default ) {
+    FixedString<8> fs;
+
+    BOOST_CHECK_EQUAL(fs.length(), 0);
+    BOOST_CHECK_EQUAL(fs.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs), "");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_constructor_string ) {
+    // Construct with a string of the correct length
+
+    std::string str1("12345678");
+    FixedString<8> fs1(str1);
+
+    BOOST_CHECK_EQUAL(fs1.size(), 8);
+    BOOST_CHECK_EQUAL(fs1.length(), 8);
+    BOOST_CHECK_EQUAL(fs1.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1), "12345678");
+
+    // Construct with a string that is too short
+
+    std::string str2("1234");
+    FixedString<8> fs2(str2);
+
+    BOOST_CHECK_EQUAL(fs2.size(), 8);
+    BOOST_CHECK_EQUAL(fs2.length(), 4);
+    BOOST_CHECK_EQUAL(fs2.asString(), "1234");
+    BOOST_CHECK_EQUAL(std::string(fs2), "1234");
+
+    // Construct with a string that is too long
+
+    // n.b. use functor to make exception thrown in constructor palatable to BOOST_CHECK_THROW
+    struct fs3_allocate {
+        void operator()() {
+            std::string str3("1234567890");
+            FixedString<8> fs3(str3);
+        }
+    };
+    BOOST_CHECK_THROW(fs3_allocate()(), AssertionFailed);
+
+    // Construct with a zero length string
+
+    std::string str4;
+    FixedString<8> fs4(str4);
+
+    BOOST_CHECK_EQUAL(fs4.size(), 8);
+    BOOST_CHECK_EQUAL(fs4.length(), 0);
+    BOOST_CHECK_EQUAL(fs4.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs4), "");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_constructor_char ) {
+    // Construct with a string of the correct length
+
+    FixedString<8> fs1("12345678");
+
+    BOOST_CHECK_EQUAL(fs1.size(), 8);
+    BOOST_CHECK_EQUAL(fs1.length(), 8);
+    BOOST_CHECK_EQUAL(fs1.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1), "12345678");
+
+    // Construct with a string that is too short
+
+    FixedString<8> fs2("1234");
+
+    BOOST_CHECK_EQUAL(fs2.size(), 8);
+    BOOST_CHECK_EQUAL(fs2.length(), 4);
+    BOOST_CHECK_EQUAL(fs2.asString(), "1234");
+    BOOST_CHECK_EQUAL(std::string(fs2), "1234");
+
+    // Construct with a string that is too long
+
+    // n.b. use functor to make exception thrown in constructor palatable to BOOST_CHECK_THROW
+    struct fs3_allocate {
+        void operator()() {
+            FixedString<8> fs3("1234567890");
+        }
+    };
+    BOOST_CHECK_THROW(fs3_allocate()(), AssertionFailed);
+
+    // Construct with a zero length string
+
+    FixedString<8> fs4("");
+
+    BOOST_CHECK_EQUAL(fs4.size(), 8);
+    BOOST_CHECK_EQUAL(fs4.length(), 0);
+    BOOST_CHECK_EQUAL(fs4.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs4), "");
+
+    // Construct with a null pointer
+
+    struct fs5_allocate {
+        void operator()() {
+            FixedString<8> fs5(0);
+        }
+    };
+    BOOST_CHECK_THROW(fs5_allocate()(), AssertionFailed);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_constructor_fixedstring ) {
+    // Construct with a string of the correct length
+
+    /// @note for all of these assignments, except for the <8> --> <8> the std::string constructor is used. It might
+    ///       be better for the long term if a template <int SIZE2> FixedString(const FixedString<SIZE2>& )
+    ///       constructor existed.
+
+    std::string str1("12345678");
+    FixedString<8> fs1(str1);
+    FixedString<8> fs1a(fs1);
+
+    BOOST_CHECK_EQUAL(fs1a.size(), 8);
+    BOOST_CHECK_EQUAL(fs1a.length(), 8);
+    BOOST_CHECK_EQUAL(fs1a.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1a), "12345678");
+
+    // Construct with a string that is too short
+
+    std::string str2("1234");
+    FixedString<4> fs2(str2);
+    FixedString<8> fs2a(fs2);
+
+    BOOST_CHECK_EQUAL(fs2a.size(), 8);
+    BOOST_CHECK_EQUAL(fs2a.length(), 4);
+    BOOST_CHECK_EQUAL(fs2a.asString(), "1234");
+    BOOST_CHECK_EQUAL(std::string(fs2a), "1234");
+
+    // Construct with a string that is too long
+
+    // n.b. use functor to make exception thrown in constructor palatable to BOOST_CHECK_THROW
+    struct fs3_allocate {
+        void operator()() {
+            std::string str3("1234567890");
+            FixedString<10> fs3(str3);
+            FixedString<8> fs3a(fs3);
+        }
+    };
+    BOOST_CHECK_THROW(fs3_allocate()(), AssertionFailed);
+
+    // Construct with a zero length string
+
+    std::string str4;
+    FixedString<0> fs4(str4);
+    FixedString<8> fs4a(fs4);
+
+    BOOST_CHECK_EQUAL(fs4a.size(), 8);
+    BOOST_CHECK_EQUAL(fs4a.length(), 0);
+    BOOST_CHECK_EQUAL(fs4a.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs4a), "");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_assignment_string ) {
+    // Construct with a string of the correct length
+
+    FixedString<8> fs1 = std::string("12345678");
+
+    BOOST_CHECK_EQUAL(fs1.size(), 8);
+    BOOST_CHECK_EQUAL(fs1.length(), 8);
+    BOOST_CHECK_EQUAL(fs1.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1), "12345678");
+
+    // Construct with a string that is too short
+
+    FixedString<8> fs2 = std::string("1234");
+
+    BOOST_CHECK_EQUAL(fs2.size(), 8);
+    BOOST_CHECK_EQUAL(fs2.length(), 4);
+    BOOST_CHECK_EQUAL(fs2.asString(), "1234");
+    BOOST_CHECK_EQUAL(std::string(fs2), "1234");
+
+    // Construct with a string that is too long
+
+    // n.b. use functor to make exception thrown in assignment palatable to BOOST_CHECK_THROW
+    struct fs3_assign {
+        void operator()() {
+            FixedString<8> fs3 = std::string("1234567890");
+            (void) fs3;
+        }
+    };
+    BOOST_CHECK_THROW(fs3_assign()(), AssertionFailed);
+
+    // Construct with a zero length string
+
+    FixedString<8> fs4 = std::string();
+
+    BOOST_CHECK_EQUAL(fs4.size(), 8);
+    BOOST_CHECK_EQUAL(fs4.length(), 0);
+    BOOST_CHECK_EQUAL(fs4.asString(), "");
+    BOOST_CHECK_EQUAL(std::string(fs4), "");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_assignment_fixedstring ) {
+    // Construct with a string of the correct length
+
+    /// @note for all of these assignments, except for the <8> --> <8> the std::string constructor is used. It might
+    ///       be better for the long term if a template <int SIZE2> FixedString(const FixedString<SIZE2>& )
+    ///       constructor existed.
+
+    FixedString<8> fs1("12345678");
+    FixedString<8> fs1a = fs1;
+
+    BOOST_CHECK_EQUAL(fs1a.size(), 8);
+    BOOST_CHECK_EQUAL(fs1a.length(), 8);
+    BOOST_CHECK_EQUAL(fs1a.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1a), "12345678");
+
+    /// Mismatched size assignments are (correctly) compile-time disallowed.
+
+    /// // Construct with a string that is too short
+
+    /// FixedString<4> fs2("1234");
+    /// FixedString<8> fs2a = fs2;
+
+    /// BOOST_CHECK_EQUAL(fs2a.size(), 8);
+    /// BOOST_CHECK_EQUAL(fs2a.length(), 4);
+    /// BOOST_CHECK_EQUAL(fs2a.asString(), "1234");
+    /// BOOST_CHECK_EQUAL(std::string(fs2a), "1234");
+
+    /// // Construct with a string that is too long
+
+    /// // n.b. use functor to make exception thrown in constructor palatable to BOOST_CHECK_THROW
+    /// struct fs3_allocate {
+    ///     void operator()() {
+    ///         FixedString<10> fs3("1234567890");
+    ///         FixedString<8> fs3a = fs3;
+    ///     }
+    /// };
+    /// BOOST_CHECK_THROW(fs3_allocate()(), AssertionFailed);
+
+    /// // Construct with a zero length string
+
+    /// FixedString<0> fs4;
+    /// FixedString<8> fs4a = fs4;
+
+    /// BOOST_CHECK_EQUAL(fs4a.size(), 8);
+    /// BOOST_CHECK_EQUAL(fs4a.length(), 0);
+    /// BOOST_CHECK_EQUAL(fs4a.asString(), "");
+    /// BOOST_CHECK_EQUAL(std::string(fs4a), "");
+
+    // What happens if we assign to ourself?
+
+    fs1a = fs1a;
+
+    BOOST_CHECK_EQUAL(fs1a.size(), 8);
+    BOOST_CHECK_EQUAL(fs1a.length(), 8);
+    BOOST_CHECK_EQUAL(fs1a.asString(), "12345678");
+    BOOST_CHECK_EQUAL(std::string(fs1a), "12345678");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test comparison operators
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+/// @note Comparisons between different FixedString<N>, FixedString<M> not permitted at compile time.
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_operators ) {
+    FixedString<8> fs1("12345678");
+    FixedString<8> fs2("12345678");
+    FixedString<8> fs3("abcdefgh");
+
+    // ==
+
+    BOOST_CHECK(fs1 == fs1);
+    BOOST_CHECK(fs1 == fs2);
+    BOOST_CHECK(!(fs1 == fs3));
+
+    // !=
+
+    BOOST_CHECK(!(fs1 != fs1));
+    BOOST_CHECK(!(fs1 != fs2));
+    BOOST_CHECK(fs1 != fs3);
+
+    // <
+
+    BOOST_CHECK(!(fs1 < fs1));
+    BOOST_CHECK(!(fs1 < fs2));
+    BOOST_CHECK(fs1 < fs3);
+    BOOST_CHECK(!(fs3 < fs1));
+
+    // >
+
+    BOOST_CHECK(!(fs1 > fs1));
+    BOOST_CHECK(!(fs1 > fs2));
+    BOOST_CHECK(!(fs1 > fs3));
+    BOOST_CHECK(fs3 > fs1);
+
+    // <=
+
+    BOOST_CHECK(fs1 <= fs1);
+    BOOST_CHECK(fs1 <= fs2);
+    BOOST_CHECK(fs1 <= fs3);
+    BOOST_CHECK(!(fs3 <= fs1));
+
+    // >=
+
+    BOOST_CHECK(fs1 >= fs1);
+    BOOST_CHECK(fs1 >= fs2);
+    BOOST_CHECK(!(fs1 >= fs3));
+    BOOST_CHECK(fs3 >= fs1);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_operators_zero ) {
+    FixedString<0> fs1;
+    FixedString<0> fs2;
+
+    // ==
+
+    BOOST_CHECK(fs1 == fs1);
+    BOOST_CHECK(fs1 == fs2);
+
+    // !=
+
+    BOOST_CHECK(!(fs1 != fs1));
+    BOOST_CHECK(!(fs1 != fs2));
+
+    // <
+
+    BOOST_CHECK(!(fs1 < fs1));
+    BOOST_CHECK(!(fs1 < fs2));
+
+    // >
+
+    BOOST_CHECK(!(fs1 > fs1));
+    BOOST_CHECK(!(fs1 > fs2));
+
+    // <=
+
+    BOOST_CHECK(fs1 <= fs1);
+    BOOST_CHECK(fs1 <= fs2);
+
+    // >=
+
+    BOOST_CHECK(fs1 >= fs1);
+    BOOST_CHECK(fs1 >= fs2);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test extraneous bits
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_byte_size ) {
+    FixedString<0> fs0;
+    FixedString<1> fs1;
+    FixedString<4> fs4;
+    FixedString<8> fs8;
+
+    BOOST_CHECK_EQUAL(sizeof(fs0), 0);
+    BOOST_CHECK_EQUAL(sizeof(fs1), 1);
+    BOOST_CHECK_EQUAL(sizeof(fs4), 4);
+    BOOST_CHECK_EQUAL(sizeof(fs8), 8);
+
+    BOOST_CHECK_EQUAL(sizeof(FixedString<0>), 0);
+    BOOST_CHECK_EQUAL(sizeof(FixedString<1>), 1);
+    BOOST_CHECK_EQUAL(sizeof(FixedString<4>), 4);
+    BOOST_CHECK_EQUAL(sizeof(FixedString<8>), 8);
+
+    BOOST_CHECK_EQUAL(fs0.size(), 0);
+    BOOST_CHECK_EQUAL(fs1.size(), 1);
+    BOOST_CHECK_EQUAL(fs4.size(), 4);
+    BOOST_CHECK_EQUAL(fs8.size(), 8);
+
+    BOOST_CHECK_EQUAL(FixedString<0>::static_size(), 0);
+    BOOST_CHECK_EQUAL(FixedString<1>::static_size(), 1);
+    BOOST_CHECK_EQUAL(FixedString<4>::static_size(), 4);
+    BOOST_CHECK_EQUAL(FixedString<8>::static_size(), 8);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_data_access ) {
+    FixedString<8> fs("12345678");
+
+    // Check that const and non-const pointers give access to same data...
+
+    char* d = fs.data();
+    const char* cd = fs.data();
+
+    // BOOST_CHECK_EQUAL does a string comparison for char*
+    BOOST_CHECK(d == cd);
+
+    // Check that if we insert a \0 character appropriately, the accessible strings and lengths adjust correctly
+
+    BOOST_CHECK_EQUAL(std::string(fs), "12345678");
+    BOOST_CHECK_EQUAL(fs.length(), 8);
+
+    d[4] = '\0';
+    BOOST_CHECK_EQUAL(std::string(fs), "1234");
+    BOOST_CHECK_EQUAL(fs.length(), 4);
+
+    d[6] = '\0';
+    d[4] = 'F';
+    BOOST_CHECK_EQUAL(std::string(fs), "1234F6");
+    BOOST_CHECK_EQUAL(fs.length(), 6);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_fixedstring_reassign_shorter_string ) {
+
+    ///< @see ECKIT-182
+
+    FixedString<64> fs;
+
+    fs = std::string("calvin");
+
+    BOOST_CHECK_EQUAL(fs, std::string("calvin")); /* this worked */
+
+    fs = std::string("calvin & hobbes");
+
+    BOOST_CHECK_EQUAL(fs, std::string("calvin & hobbes")); /* assinging longer string also worked */
+
+    fs = std::string("susie");
+
+    BOOST_CHECK_EQUAL(fs, std::string("susie")); /* but then assigning shorter did not, resulted "susien & hobbes" */
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/types/test_floatcompare.cc b/eckit/src/tests/types/test_floatcompare.cc
new file mode 100644
index 0000000..30dd598
--- /dev/null
+++ b/eckit/src/tests/types/test_floatcompare.cc
@@ -0,0 +1,343 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#define BOOST_TEST_MODULE test_eckit_types
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/FloatCompare.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace {
+
+bool is_equal(float a, float b, float epsilon, int maxUlps) {
+    return eckit::types::is_approximately_equal(a, b, epsilon, maxUlps);
+}
+
+bool is_equal(float a, float b, float epsilon) {
+    return eckit::types::is_approximately_equal(a, b, epsilon);
+}
+
+bool is_equal(float a, float b) {
+    return eckit::types::is_approximately_equal(a, b, 0.00001f);
+}
+
+const float dEps = std::numeric_limits<float>::epsilon();
+const float dInf = std::numeric_limits<float>::infinity();
+const float sMin = std::numeric_limits<float>::denorm_min();
+const float dMin = std::numeric_limits<float>::min();
+const float dMax = std::numeric_limits<float>::max();
+const float qNaN = std::numeric_limits<float>::quiet_NaN();
+const float sNaN = std::numeric_limits<float>::signaling_NaN();
+
+};
+
+//-----------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_floatcompare )
+
+BOOST_AUTO_TEST_CASE( test_large_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_large_numbers" );
+
+   BOOST_CHECK(  is_equal(1000000,       1000000      ));
+   BOOST_CHECK(  is_equal(1000000,       1000000.00001));
+   BOOST_CHECK(  is_equal(1000000.00001, 1000000      ));
+
+   BOOST_CHECK(! is_equal(1000000.0,     1000001.0    ));
+   BOOST_CHECK(! is_equal(1000001.0,     1000000.0    ));
+
+   // -----------------------------------------------
+   BOOST_CHECK(is_equal(dMax, dMax));
+   BOOST_CHECK(is_equal(dMax, dMax, dEps));
+
+   BOOST_CHECK(is_equal(dMin, dMin));
+   BOOST_CHECK(is_equal(dMin, dMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_negative_large_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_negative_large_numbers " << dMin );
+
+   BOOST_CHECK(  is_equal(-1000000,       -1000000      ));
+   BOOST_CHECK(  is_equal(-1000000,       -1000000.00001));
+   BOOST_CHECK(  is_equal(-1000000.00001, -1000000      ));
+
+   BOOST_CHECK(! is_equal(-1000000.0,     -1000001.0    ));
+   BOOST_CHECK(! is_equal(-1000001.0,     -1000000.0    ));
+
+   // -----------------------------------------------
+   BOOST_CHECK(is_equal(-dMax, -dMax      ));
+   BOOST_CHECK(is_equal(-dMax, -dMax, dEps));
+
+   BOOST_CHECK(is_equal(-dMin, -dMin      ));
+   BOOST_CHECK(is_equal(-dMin, -dMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_large_numbers_of_opposite_sign )
+{
+    BOOST_CHECK(! is_equal(-1000000,       1000000      ));
+    BOOST_CHECK(! is_equal(-1000000,       1000000.00001));
+    BOOST_CHECK(! is_equal(-1000000.00001, 1000000      ));
+
+    BOOST_CHECK(! is_equal(-1000000.0,     1000001.0    ));
+    BOOST_CHECK(! is_equal(-1000001.0,     1000000.0    ));
+
+    // -----------------------------------------------
+    BOOST_CHECK(! is_equal(-dMax, dMax      ));
+    BOOST_CHECK(! is_equal(-dMax, dMax, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_ulp_around_one )
+{
+   BOOST_TEST_MESSAGE( "test_ulp_around_one" );
+
+   // ULP distances up to 10 are equal
+   // Going right from 1 by eps increases distance by 1
+   // Going left from 1 by eps increases distance by 2
+   for (int i = 0; i <= 10; ++i) {
+       BOOST_CHECK(is_equal(1.0 + i * dEps,   1.0,              dEps));
+       BOOST_CHECK(is_equal(1.0,              1.0 + i * dEps,   dEps));
+       BOOST_CHECK(is_equal(1.0 - i * dEps/2, 1.0,              dEps));
+       BOOST_CHECK(is_equal(1.0,              1.0 - i * dEps/2, dEps));
+   }
+   // ULP distances greater 10 are not equal
+   BOOST_CHECK(! is_equal(1.0 + 11 * dEps,   1.0,               dEps));
+   BOOST_CHECK(! is_equal(1.0,               1.0 + 11 * dEps,   dEps));
+   BOOST_CHECK(! is_equal(1.0 - 11 * dEps/2, 1.0,               dEps));
+   BOOST_CHECK(! is_equal(1.0,               1.0 - 11 * dEps/2, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_around_one )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_around_one" );
+
+   BOOST_CHECK(  is_equal(1.0000001, 1.0000002));
+   BOOST_CHECK(  is_equal(1.0000002, 1.0000001));
+
+   BOOST_CHECK(  is_equal(1.123456,  1.123457 ));
+   BOOST_CHECK(  is_equal(1.12345,   1.12344, 0.001));
+
+   BOOST_CHECK(! is_equal(1.0001,    1.0002   ));
+   BOOST_CHECK(! is_equal(1.0002,    1.0001   ));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_around_negative_one )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_around_negative_one" );
+
+   BOOST_CHECK(  is_equal(-1.0000001, -1.0000002));
+   BOOST_CHECK(  is_equal(-1.0000002, -1.0000001));
+
+   BOOST_CHECK(! is_equal(-1.0001,    -1.0002   ));
+   BOOST_CHECK(! is_equal(-1.0002,    -1.0001   ));
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_between_one_and_zero )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_between_one_and_zero" );
+
+   BOOST_CHECK(  is_equal(0.000000001000001, 0.000000001000002));
+   BOOST_CHECK(  is_equal(0.000000001000002, 0.000000001000001));
+
+   BOOST_CHECK(! is_equal(0.0012,            0.0011           ));
+   BOOST_CHECK(! is_equal(0.0011,            0.0012           ));
+
+}
+
+BOOST_AUTO_TEST_CASE( test_numbers_between_minusone_and_zero )
+{
+   BOOST_TEST_MESSAGE( "test_numbers_between_minusone_and_zero" );
+
+   BOOST_CHECK(  is_equal(-0.000000001000001, -0.000000001000002));
+   BOOST_CHECK(  is_equal(-0.000000001000002, -0.000000001000001));
+
+   BOOST_CHECK(! is_equal(-0.0012,            -0.0011           ));
+   BOOST_CHECK(! is_equal(-0.0011,            -0.0012           ));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_involving_zero" );
+
+   BOOST_CHECK(  is_equal(0.0,     0.0   ));
+   BOOST_CHECK(  is_equal(0.0,    -0.0   ));
+   BOOST_CHECK(  is_equal(-0.0,   -0.0   ));
+
+   BOOST_CHECK(! is_equal(0.0001,  0.0   ));
+   BOOST_CHECK(! is_equal(0.0,     0.0001));
+   BOOST_CHECK(! is_equal(-0.0001, 0.0   ));
+   BOOST_CHECK(! is_equal(0.0,    -0.0001));
+
+   BOOST_CHECK(  is_equal(0.0,     1e-30, 0.01 ));
+   BOOST_CHECK(  is_equal(1e-30,   0.0,   0.01 ));
+   BOOST_CHECK(! is_equal(1e-30,   0.0,   1e-31));
+   BOOST_CHECK(! is_equal(0.0,     1e-30, 1e-31));
+
+   BOOST_CHECK(  is_equal(0.0,    -1e-30, 0.1  ));
+   BOOST_CHECK(  is_equal(-1e-30,  0.0,   0.1  ));
+   BOOST_CHECK(! is_equal(-1e-30,  0.0,   1e-31));
+   BOOST_CHECK(! is_equal(0.0,    -1e-30, 1e-31));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_infinity )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_involving_infinity" );
+
+   if (std::numeric_limits<float>::has_infinity) {
+      BOOST_CHECK(  is_equal( dInf,  dInf));
+      BOOST_CHECK(  is_equal(-dInf, -dInf));
+      BOOST_CHECK(! is_equal( dInf,  dMax));
+      BOOST_CHECK(! is_equal( dMax,  dInf));
+      BOOST_CHECK(! is_equal(-dInf, -dMax));
+      BOOST_CHECK(! is_equal(-dMax, -dInf));
+   } else {
+      BOOST_TEST_MESSAGE( "test_comparisons_involving_infinity NOT VALID on this platform" );
+   }
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_involving_nan )
+{
+    BOOST_TEST_MESSAGE( "test_comparisons_involving_nan" );
+
+    // The value NaN (Not a Number) is used to represent a value that does not represent a real number.
+    // NaN's are represented by a bit pattern with an exponent of all 1s and a non-zero fraction. T
+    // there are two categories of NaN: QNaN (Quiet NaN) and SNaN (Signalling NaN).
+    //
+    // A QNaN is a NaN with the most significant fraction bit set.
+    // QNaN's propagate freely through most arithmetic operations.
+    // These values pop out of an operation when the result is not mathematically defined.
+
+    // An SNaN is a NaN with the most significant fraction bit clear.
+    // It is used to signal an exception when used in operations.
+    // SNaN's can be handy to assign to uninitialized variables to trap premature usage.
+
+    // Semantically, QNaN's denote indeterminate operations, while SNaN's denote invalid operations.
+
+    BOOST_CHECK(! is_equal( qNaN,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  0.0 ));
+    BOOST_CHECK(! is_equal(-0.0 ,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -0.0 ));
+    BOOST_CHECK(! is_equal( 0.0 ,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dInf));
+    BOOST_CHECK(! is_equal( dInf,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dMax));
+    BOOST_CHECK(! is_equal( dMax,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -dMax));
+    BOOST_CHECK(! is_equal(-dMax,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN,  dMin));
+    BOOST_CHECK(! is_equal( dMin,  qNaN));
+    BOOST_CHECK(! is_equal( qNaN, -dMin));
+    BOOST_CHECK(! is_equal(-dMin,  qNaN));
+
+    BOOST_CHECK(! is_equal( sNaN,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  0.0 ));
+    BOOST_CHECK(! is_equal(-0.0 ,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -0.0 ));
+    BOOST_CHECK(! is_equal( 0.0 ,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dInf));
+    BOOST_CHECK(! is_equal( dInf,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dMax));
+    BOOST_CHECK(! is_equal( dMax,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -dMax));
+    BOOST_CHECK(! is_equal(-dMax,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN,  dMin));
+    BOOST_CHECK(! is_equal( dMin,  sNaN));
+    BOOST_CHECK(! is_equal( sNaN, -dMin));
+    BOOST_CHECK(! is_equal(-dMin,  sNaN));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_opposite_side_of_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_opposite_side_of_zero" );
+
+   BOOST_CHECK(! is_equal( 1.0000001, -1.0      ));
+   BOOST_CHECK(! is_equal(-1.0,        1.0000001));
+   BOOST_CHECK(! is_equal(-1.0000001,  1.0      ));
+   BOOST_CHECK(! is_equal( 1.0,       -1.0000001));
+
+   BOOST_CHECK(  is_equal(   10.0 * dMin,    10.0 * -dMin));
+   BOOST_CHECK(  is_equal(10000   * dMin, 10000   * -dMin));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_very_close_to_zero )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_very_close_to_zero" );
+
+   BOOST_CHECK(  is_equal( dMin, -dMin, dEps));
+   BOOST_CHECK(  is_equal(-dMin,  dMin, dEps));
+   BOOST_CHECK(  is_equal( dMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     dMin, dEps));
+   BOOST_CHECK(  is_equal(-dMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -dMin, dEps));
+
+
+   BOOST_CHECK(  is_equal( 0.000000001, -dMin       ));
+   BOOST_CHECK(  is_equal( 0.000000001,  dMin       ));
+   BOOST_CHECK(  is_equal( dMin,         0.000000001));
+   BOOST_CHECK(  is_equal(-dMin,         0.000000001));
+
+
+   BOOST_CHECK(! is_equal( 0.000000001, -dMin,        1e-10));
+   BOOST_CHECK(! is_equal( 0.000000001,  dMin,        1e-10));
+   BOOST_CHECK(! is_equal( dMin,         0.000000001, 1e-10));
+   BOOST_CHECK(! is_equal(-dMin,         0.000000001, 1e-10));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_with_denormal_numbers )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_with_denormal_numbers" );
+
+   BOOST_CHECK(  is_equal( sMin, -sMin, dEps));
+   BOOST_CHECK(  is_equal(-sMin,  sMin, dEps));
+   BOOST_CHECK(  is_equal( sMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     sMin, dEps));
+   BOOST_CHECK(  is_equal(-sMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -sMin, dEps));
+
+   const float lMin = dMin - sMin;  // largest denormal number
+   BOOST_CHECK(  is_equal( lMin, -lMin, dEps));
+   BOOST_CHECK(  is_equal(-lMin,  lMin, dEps));
+   BOOST_CHECK(  is_equal( lMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,     lMin, dEps));
+   BOOST_CHECK(  is_equal(-lMin,  0   , dEps));
+   BOOST_CHECK(  is_equal( 0,    -lMin, dEps));
+}
+
+BOOST_AUTO_TEST_CASE( test_comparisons_ulps )
+{
+   BOOST_TEST_MESSAGE( "test_comparisons_ulps" );
+
+   BOOST_CHECK(  is_equal( dMin, -dMin, 0, 2));
+   BOOST_CHECK(  is_equal(-dMin,  dMin, 0, 2));
+   BOOST_CHECK(  is_equal( dMin,  0   , 0, 1));
+   BOOST_CHECK(  is_equal( 0,     dMin, 0, 1));
+   BOOST_CHECK(  is_equal(-dMin,  0   , 0, 1));
+   BOOST_CHECK(  is_equal( 0,    -dMin, 0, 1));
+
+   BOOST_CHECK(! is_equal( dMin, -dMin, 0, 1));
+   BOOST_CHECK(! is_equal(-dMin,  dMin, 0, 1));
+   BOOST_CHECK(! is_equal( dMin,  0   , 0, 0));
+   BOOST_CHECK(! is_equal( 0,     dMin, 0, 0));
+   BOOST_CHECK(! is_equal(-dMin,  0   , 0, 0));
+   BOOST_CHECK(! is_equal( 0,    -dMin, 0, 0));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//-----------------------------------------------------------------------------
diff --git a/eckit/src/tests/types/test_fraction.cc b/eckit/src/tests/types/test_fraction.cc
new file mode 100644
index 0000000..06b134c
--- /dev/null
+++ b/eckit/src/tests/types/test_fraction.cc
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_fraction
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/Fraction.h"
+
+#include "eckit/testing/Setup.h"
+
+
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE( Setup );
+
+BOOST_AUTO_TEST_SUITE( test_eckit_fraction )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_fraction )
+{
+    // 0
+
+    BOOST_CHECK_EQUAL( Fraction(0, 1), Fraction() );
+
+    BOOST_CHECK_THROW( Fraction(0, 0), std::exception ); // this prints a backtrace wi Assertion failed
+
+    // negative number
+
+    BOOST_CHECK_EQUAL( Fraction(-1, 2), Fraction(3, -6) );
+
+    // decimals
+
+    BOOST_CHECK_EQUAL( Fraction(0.16), Fraction(16, 100) );
+    BOOST_CHECK_EQUAL( Fraction(0.1616), Fraction(1616, 10000) );
+
+    // 5 / 7
+
+    BOOST_CHECK( Fraction(0.714285) != Fraction(5, 7) );
+
+    BOOST_CHECK( Fraction(0.7142857142) != Fraction(5, 7) );
+
+    BOOST_CHECK_EQUAL( Fraction(0.71428571428),  Fraction(5, 7) );
+    BOOST_CHECK_EQUAL( Fraction(0.714285714285), Fraction(5, 7) );
+    BOOST_CHECK_EQUAL( Fraction(0.714285714286), Fraction(5, 7) );
+
+    BOOST_CHECK_EQUAL( Fraction(0.714285714285714285), Fraction(5, 7) );
+
+    // 1 / 6
+
+    BOOST_CHECK( Fraction(0.166) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.1666) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.16666) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.166666) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.1666666) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.16666666) != Fraction(1, 6) );
+    BOOST_CHECK( Fraction(0.166666666) != Fraction(1, 6) );
+
+    BOOST_CHECK_EQUAL( Fraction(0.16666666666), Fraction(1, 6) );
+    BOOST_CHECK_EQUAL( Fraction(0.166666666666), Fraction(1, 6) );
+    BOOST_CHECK_EQUAL( Fraction(0.1666666666666), Fraction(1, 6) );
+    BOOST_CHECK_EQUAL( Fraction(0.16666666666666), Fraction(1, 6) );
+    BOOST_CHECK_EQUAL( Fraction(0.166666666666666), Fraction(1, 6) );
+    BOOST_CHECK_EQUAL( Fraction(0.1666666666666666), Fraction(1, 6) );
+
+    // 1 / 3
+
+    BOOST_CHECK_EQUAL( Fraction(0.3333333333), Fraction(1, 3) );
+
+    BOOST_CHECK_EQUAL( Fraction(0.3333333333333333), Fraction(1, 3) );
+
+    // 1 / 1
+
+    BOOST_CHECK_EQUAL( Fraction(0.9999999999999999), Fraction(1, 1) );
+    BOOST_CHECK_EQUAL( Fraction(0.9999999999999999), Fraction(10, 10) );
+
+    // 7 / 10..
+
+    BOOST_CHECK_EQUAL( Fraction(0.7), Fraction(7, 10) );
+    BOOST_CHECK_EQUAL( Fraction(0.07), Fraction(7, 100) );
+    BOOST_CHECK_EQUAL( Fraction(0.0000007), Fraction(7, 10000000) );
+
+    // operations
+
+    BOOST_CHECK_EQUAL( Fraction(1, 3) + Fraction(2, 3), Fraction(10, 10) );
+    BOOST_CHECK_EQUAL( Fraction(1, 3) - Fraction(2, 6), Fraction(0, 10) );
+
+    BOOST_CHECK_EQUAL( Fraction(1, 3) * 3, Fraction(1) );
+
+    BOOST_CHECK_EQUAL( -Fraction(1, 3), Fraction(1, -3) );
+
+    BOOST_CHECK_EQUAL( 2 * Fraction(1, 3) , Fraction(2, 3) );
+
+    Fraction a(1, 3);
+    Fraction b(3, 28);
+    BOOST_CHECK_EQUAL(a + b, Fraction(37, 84));
+    BOOST_CHECK_EQUAL(a - b, Fraction(19, 84));
+    BOOST_CHECK_EQUAL(a * b, Fraction(1, 28));
+    BOOST_CHECK_EQUAL(a / b, Fraction(28, 9));
+
+    BOOST_CHECK_EQUAL(a > b, true);
+    BOOST_CHECK_EQUAL(a != b, true);
+    BOOST_CHECK_EQUAL(a < b, false);
+    BOOST_CHECK_EQUAL(a == b, false);
+
+    BOOST_CHECK_EQUAL(Fraction("1/3"), Fraction(1, 3));
+    BOOST_CHECK_EQUAL(double(Fraction("1/3")), 1.0 / 3.0);
+
+    BOOST_CHECK_EQUAL(Fraction("1"), Fraction(1));
+    BOOST_CHECK_EQUAL(Fraction("1.2"), Fraction(12, 10));
+    BOOST_CHECK_EQUAL(Fraction("1e-6"), Fraction(1, 1000000));
+    BOOST_CHECK_EQUAL(Fraction("1e+6"), Fraction(1000000, 1));
+    BOOST_CHECK_EQUAL(Fraction("1.2e+6"), Fraction(1200000, 1));
+
+    {
+        Fraction west(-12), east(1.2), increment(1.2);
+
+        Fraction f(west);
+        while (f < east) {
+            f += increment;
+        }
+        BOOST_CHECK_EQUAL(f, east);
+    }
+
+    {
+        Fraction west("-77"), east("7"), increment("0.7");
+
+        Fraction f(west);
+        while (f < east) {
+            f += increment;
+        }
+        BOOST_CHECK_EQUAL(f, east);
+    }
+
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/types/test_print_vector.cc b/eckit/src/tests/types/test_print_vector.cc
new file mode 100644
index 0000000..82cd59e
--- /dev/null
+++ b/eckit/src/tests/types/test_print_vector.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_types
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/Types.h"
+
+#include "eckit/log/Log.h"
+
+
+using namespace std;
+using namespace eckit;
+
+// ------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_print_vector )
+
+BOOST_AUTO_TEST_CASE( test_eckit_print_vector_string )
+{
+    vector<string> vstr;
+    vstr.push_back("Hello");
+    vstr.push_back("World");
+    vstr.push_back("test");
+    vstr.push_back("case");
+
+    stringstream s;
+    s << vstr;
+
+    BOOST_CHECK_EQUAL("[Hello,World,test,case]", s.str());
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_print_vector_pair )
+{
+    vector<pair<string, int> > vpair;
+    vpair.push_back(make_pair("k1", 123));
+    vpair.push_back(make_pair("k1", 124));
+    vpair.push_back(make_pair("k1", 125));
+    vpair.push_back(make_pair("k2", 125));
+
+    stringstream s;
+    s << vpair;
+
+    BOOST_CHECK_EQUAL("[<k1,123>,<k1,124>,<k1,125>,<k2,125>]", s.str());
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_print_vector_ints )
+{
+    vector<int> vint;
+    vint.push_back(123);
+    vint.push_back(124);
+    vint.push_back(125);
+    vint.push_back(126);
+    vint.push_back(127);
+    vint.push_back(129);
+    vint.push_back(131);
+    vint.push_back(133);
+    vint.push_back(135);
+    vint.push_back(135);
+    vint.push_back(135);
+    vint.push_back(135);
+    vint.push_back(1);
+
+    stringstream s;
+    s << vint;
+
+    BOOST_CHECK_EQUAL("[123-127,129-135-2,3*135,1]", s.str());
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_print_vector_doubs )
+{
+    // These should not contract into ranges.
+    vector<double> vdoub;
+    vdoub.push_back(123.0);
+    vdoub.push_back(124.0);
+    vdoub.push_back(125.0);
+    vdoub.push_back(126.0);
+    vdoub.push_back(127.0);
+    vdoub.push_back(129.0);
+    vdoub.push_back(131.0);
+    vdoub.push_back(133.0);
+    vdoub.push_back(135.0);
+    vdoub.push_back(135.0);
+    vdoub.push_back(135.0);
+    vdoub.push_back(135.0);
+    vdoub.push_back(1.0);
+
+    stringstream s;
+    s << vdoub;
+
+    BOOST_CHECK_EQUAL("[123,124,125,126,127,129,131,133,135,135,135,135,1]", s.str());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+// ------------------------------------------------------------------------------------------------
diff --git a/eckit/src/tests/types/test_uuid.cc b/eckit/src/tests/types/test_uuid.cc
new file mode 100644
index 0000000..944275e
--- /dev/null
+++ b/eckit/src/tests/types/test_uuid.cc
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_types
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/types/UUID.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( test_eckit_types_uuid )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_uuid_default_constructor ) {
+    UUID uuid;
+
+    BOOST_CHECK_EQUAL( uuid.size() , 16 );
+
+    BOOST_CHECK( uuid.isNil() );
+
+    std::string res ("00000000000000000000000000000000");
+
+    BOOST_CHECK_EQUAL( res , uuid.asString() );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_uuid_constructor_string ) {
+    std::string s ("4b4053dc93e0b52a6f028cb36649d229");
+
+    UUID uuid( s );
+
+    BOOST_CHECK_EQUAL( uuid.size() , 16 );
+
+    BOOST_CHECK( ! uuid.isNil() );
+
+    BOOST_CHECK_EQUAL( s, uuid.asString() );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_uuid_fromstring ) {
+    std::string s ("4b4053dc93e0b52a6f028cb36649d229");
+
+    UUID uuid;
+
+    BOOST_CHECK( uuid.isNil() );
+
+    BOOST_CHECK_NO_THROW( uuid.fromString(s) );
+
+    BOOST_CHECK( ! uuid.isNil() );
+
+    BOOST_CHECK_EQUAL( s, uuid.asString() );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_types_uuid_constructor_string_trow ) {
+    std::string s ("4b405");
+
+    BOOST_CHECK_THROW( UUID uuid( s ), eckit::AssertionFailed );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/utils/CMakeLists.txt b/eckit/src/tests/utils/CMakeLists.txt
new file mode 100644
index 0000000..92ce735
--- /dev/null
+++ b/eckit/src/tests/utils/CMakeLists.txt
@@ -0,0 +1,47 @@
+ecbuild_add_test( TARGET      eckit_test_string_tools
+                  SOURCES     test_string_tools.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_translator
+                  BOOST
+                  SOURCES     test_translator.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_tokenizer
+                  SOURCES     test_tokenizer.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_md5
+                  BOOST
+                  SOURCES     test_md5.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_rendezvoushash
+                  BOOST
+                  SOURCES     test_rendezvoushash.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_sha1
+                  BOOST
+                  CONDITION   ECKIT_HAVE_SSL
+                  INCLUDES    "${OPENSSL_INCLUDE_DIR}"
+                  SOURCES     test_sha1.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_md4
+                  BOOST
+                  CONDITION   ECKIT_HAVE_SSL
+                  INCLUDES    "${OPENSSL_INCLUDE_DIR}"
+                  SOURCES     test_md4.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      eckit_test_xxhash
+                  BOOST
+                  CONDITION   ECKIT_HAVE_XXHASH
+                  INCLUDES    "${XXHASH_INCLUDE_DIRS}"
+                  SOURCES     test_xxhash.cc
+                  LIBS        eckit )
+
+ecbuild_add_test( TARGET      hash-performance
+                  SOURCES     hash-performance.cc
+                  LIBS        eckit )
diff --git a/eckit/src/tests/utils/hash-performance.cc b/eckit/src/tests/utils/hash-performance.cc
new file mode 100644
index 0000000..0beb316
--- /dev/null
+++ b/eckit/src/tests/utils/hash-performance.cc
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+
+#include "eckit/log/Timer.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+#include "eckit/utils/Hash.h"
+#include "eckit/memory/ScopedPtr.h"
+
+using namespace std;
+using namespace eckit;
+
+template <int N, int M>
+void timeAdd(Hash& hash, eckit::Buffer& buffer, eckit::Timer& timer) {
+
+    timer.start();
+
+    for(int i = 0; i < N; ++i ) {
+        hash.reset();
+        for(size_t j=0; j < M; ++j) {
+            hash.add(buffer, buffer.size());
+        }
+        hash.digest();
+    }
+
+    timer.stop();
+
+    std::cout << " - add()     rate " << Bytes(N*M*buffer.size(), timer) << std::endl;
+}
+
+template <int N>
+void timeCompute(Hash& hash, eckit::Buffer& buffer, eckit::Timer& timer) {
+
+    timer.start();
+
+    for(int i = 0; i < N; ++i ) {
+            std::string s = hash.compute(buffer, buffer.size());
+        }
+
+    timer.stop();
+
+    std::cout << " - compute() rate " << Bytes(N*buffer.size(), timer) << std::endl;
+}
+
+
+int main(int argc, char const *argv[])
+{
+    eckit::Buffer buffer(4*1024*1024);
+    eckit::Buffer buffer2(64*1024*1024);
+    eckit::Timer timer;
+
+    const char *hashes[4] =
+    {
+        "xxHash",
+        "MD4",
+        "MD5",
+        "SHA1"
+    };
+
+    for(int i = 0; i < 4; ++i) {
+
+        std::string name = hashes[i];
+
+        if(eckit::HashFactory::has(name)) {
+
+            std::cout << name << std::endl;
+
+            eckit::ScopedPtr<eckit::Hash> hash( eckit::HashFactory::build(name) );
+
+            timeAdd<20,1>  (*hash, buffer, timer);
+            timeCompute<5> (*hash, buffer2, timer);
+
+        }
+    }
+
+    return 0;
+}
+
diff --git a/eckit/src/tests/utils/test_md4.cc b/eckit/src/tests/utils/test_md4.cc
new file mode 100644
index 0000000..abf3f43
--- /dev/null
+++ b/eckit/src/tests/utils/test_md4.cc
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_utils
+
+#include <iostream>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/utils/MD4.h"
+#include "eckit/log/Timer.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+using namespace std;
+using namespace eckit;
+
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_utils_md4 )
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md4_constructor )
+{
+    MD4 hash;
+
+        const char* msg = "The quick brown fox jumps over the lazy dog";
+
+        hash.add(msg,strlen(msg));
+
+        std::string res ("1bee69a46ba811185c194762abaeae90");
+
+        BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md4_constructor_string )
+{
+    MD4 hash( "The quick brown fox jumps over the lazy cog" );
+
+    std::string res ("b86e130ce7028da59e672d56ad0113df");
+
+    BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+// original test suite from RFC-1320
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md4_test_suite_from_rfc1320 )
+{
+    BOOST_CHECK_EQUAL( MD4("").digest(), std::string("31d6cfe0d16ae931b73c59d7e0c089c0"));
+    BOOST_CHECK_EQUAL( MD4("a").digest(), std::string("bde52cb31de33e46245e05fbdbd6fb24"));
+    BOOST_CHECK_EQUAL( MD4("abc").digest(), std::string("a448017aaf21d8525fc10ae87aa6729d"));
+    BOOST_CHECK_EQUAL( MD4("message digest").digest(), std::string("d9130a8164549fe818874806e1c7014b"));
+    BOOST_CHECK_EQUAL( MD4("abcdefghijklmnopqrstuvwxyz").digest(), std::string("d79e1c308aa5bbcdeea8ed63df412da9"));
+    BOOST_CHECK_EQUAL( MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").digest(), std::string("043f8582f241db351ce627e153e7f0e4"));
+    BOOST_CHECK_EQUAL( MD4("12345678901234567890123456789012345678901234567890123456789012345678901234567890").digest(), std::string("e33b4ddc9c38f2199c3e7b164fcc0536"));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md4_compute )
+{
+    std::string msg ( "The quick brown fox jumps over the lazy cog" );
+
+    std::string res ("b86e130ce7028da59e672d56ad0113df");
+
+    MD4 hash;
+    BOOST_CHECK_EQUAL( res , hash.compute(msg.c_str(), msg.size()));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md4_reset )
+{
+    MD4 hash( "FOOBAR" );
+
+    hash.reset(); // reset initial state
+
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    hash.add(msg.c_str(), msg.size());
+
+    std::string res ("1bee69a46ba811185c194762abaeae90");
+
+    BOOST_CHECK_EQUAL( res , hash.digest());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // end namespace test
+} // end namespace eckit
diff --git a/eckit/src/tests/utils/test_md5.cc b/eckit/src/tests/utils/test_md5.cc
new file mode 100644
index 0000000..3d1c872
--- /dev/null
+++ b/eckit/src/tests/utils/test_md5.cc
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_utils
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/utils/MD5.h"
+
+using namespace std;
+using namespace eckit;
+
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_utils_md5 )
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_constructor )
+{
+    MD5 md5;
+
+	const char* msg = "The quick brown fox jumps over the lazy dog";
+
+	md5.add(msg,strlen(msg));
+
+	std::string res ("9e107d9d372bb6826bd81d3542a419d6");
+
+	BOOST_CHECK_EQUAL( res , md5.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_constructor_string )
+{
+    MD5 md5( "Few quips galvanized the mock jury box" );
+
+	std::string res ("01190cddf60f758278c728e768d218ff");
+
+	BOOST_CHECK_EQUAL( res , md5.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_double_add )
+{
+    MD5 md5;
+
+	const char* msg = "The quick brown fox jumps over the lazy dog";
+
+	md5.add(msg,strlen(msg));
+	md5.add(msg,strlen(msg));
+
+	std::string res ("d27c6d8bcaa695e377d32387e115763c");
+
+	BOOST_CHECK_EQUAL( res , md5.digest() );
+}
+
+// original test suite from RFC-1321
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_test_suite_from_rfc1321 )
+{
+    BOOST_CHECK_EQUAL( MD5("").digest(),
+                       "d41d8cd98f00b204e9800998ecf8427e" );
+
+    BOOST_CHECK_EQUAL( MD5("a").digest(),
+                       "0cc175b9c0f1b6a831c399e269772661" );
+
+    BOOST_CHECK_EQUAL( MD5("abc").digest(),
+                       "900150983cd24fb0d6963f7d28e17f72" );
+
+    BOOST_CHECK_EQUAL( MD5("message digest").digest(),
+                       "f96b697d7cb7938d525a2f31aaf161d0" );
+
+    BOOST_CHECK_EQUAL( MD5("abcdefghijklmnopqrstuvwxyz").digest(),
+                       "c3fcd3d76192e4007dfb496cca67e13b" );
+
+    BOOST_CHECK_EQUAL( MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").digest(),
+                       "d174ab98d277d9f5a5611c2c9f419d9f" );
+
+    BOOST_CHECK_EQUAL( MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890").digest(),
+                       "57edf4a22be3c955ac49da2e2107b67a" );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_compute )
+{
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    std::string res ("9e107d9d372bb6826bd81d3542a419d6");
+
+    MD5 hash;
+    BOOST_CHECK_EQUAL( res , hash.compute(msg.c_str(), msg.size()));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_md5_reset )
+{
+    MD5 hash( "FOOBAR" );
+
+    hash.reset(); // reset initial state
+
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    hash.add(msg.c_str(), msg.size());
+
+    std::string res ("9e107d9d372bb6826bd81d3542a419d6");
+
+    BOOST_CHECK_EQUAL( res , hash.digest());
+}
+
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // end namespace test
+} // end namespace eckit
diff --git a/eckit/src/tests/utils/test_rendezvoushash.cc b/eckit/src/tests/utils/test_rendezvoushash.cc
new file mode 100644
index 0000000..77e1dae
--- /dev/null
+++ b/eckit/src/tests/utils/test_rendezvoushash.cc
@@ -0,0 +1,233 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_utils
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/types/Types.h"
+#include "eckit/utils/RendezvousHash.h"
+#include "eckit/utils/Translator.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_GLOBAL_FIXTURE( Setup );
+
+BOOST_AUTO_TEST_SUITE( test_eckit_rendezvous_hash )
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_rendezvous_hash_constructor )
+{
+    BOOST_TEST_MESSAGE( Here() );
+
+    std::set<std::string> nodes;
+
+    nodes.insert("node01");
+    nodes.insert("node02");
+    nodes.insert("node03");
+    nodes.insert("node04");
+
+    eckit::RendezvousHash rendezvous(nodes, &RendezvousHash::md5);
+
+    std::map<std::string, std::string> dict;
+
+    dict["class"]  = "od";
+    dict["stream"] = "oper";
+    dict["type"]   = "fc";
+    dict["level"]  = "1";
+
+    std::string n = rendezvous.selectNode(dict);
+
+    BOOST_TEST_MESSAGE( n );
+
+    BOOST_CHECK_EQUAL( nodes.count(n), 1 );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_rendezvous_hash_distribution )
+{
+    BOOST_TEST_MESSAGE( Here() );
+
+    eckit::Translator<size_t,std::string> toStr;
+
+    std::vector<std::string> params;
+
+    params.push_back( "2t" );
+    params.push_back( "2d" );
+    params.push_back( "z" );
+    params.push_back( "u" );
+    params.push_back( "v" );
+    params.push_back( "130.128" );
+    params.push_back( "138.128" );
+
+
+    std::set<std::string> nodes;
+
+    nodes.insert("node01");
+    nodes.insert("node02");
+    nodes.insert("node03");
+    nodes.insert("node04");
+    nodes.insert("node05");
+    nodes.insert("node06");
+    nodes.insert("node07");
+
+    eckit::RendezvousHash rendezvous(nodes);
+
+    std::map<std::string, std::string> dict;
+    dict["class"]  = "od";
+    dict["stream"] = "oper";
+    dict["type"]   = "fc";
+
+    std::map<std::string, size_t> counts;
+
+
+    for(std::vector<std::string>::iterator param = params.begin(); param != params.end(); ++param ) {
+
+        dict["param"] = *param;
+
+        for(size_t step = 0; step < 240; ++step) {
+
+            dict["step"]  = toStr(step);
+
+            for( size_t level = 0; level < 10; ++level ) {
+
+                dict["level"]  = toStr(level);
+
+                counts[ rendezvous.selectNode(dict) ]++;
+
+            }
+        }
+
+    }
+
+    BOOST_TEST_MESSAGE( counts );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_rendezvous_hash_empty_dict )
+{
+    BOOST_TEST_MESSAGE( Here() );
+
+    std::map<std::string, std::string> dict;
+
+    eckit::RendezvousHash rendezvous;
+    rendezvous.addNode("node01");
+    rendezvous.addNode("node02");
+
+    BOOST_CHECK_NO_THROW(rendezvous.selectNode(dict)); /* don't throw on empty dictionary */
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_rendezvous_hash_throws_empty_node_list )
+{
+    BOOST_TEST_MESSAGE( Here() );
+
+    std::map<std::string, std::string> dict;
+
+    eckit::RendezvousHash rendezvous(&RendezvousHash::md5);
+    dict["class"]  = "od";
+    dict["stream"] = "oper";
+    dict["type"]   = "fc";
+
+    BOOST_CHECK_THROW(rendezvous.selectNode(dict), eckit::BadParameter); /* throw on empty node list */
+
+    rendezvous.addNode("node01");
+    rendezvous.addNode("node02");
+
+    BOOST_CHECK_NO_THROW(rendezvous.selectNode(dict));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_rendezvous_hash_add_node )
+{
+    BOOST_TEST_MESSAGE( Here() );
+
+    eckit::Translator<size_t,std::string> toStr;
+
+    std::vector<std::string> params;
+    params.push_back( "2t" );
+    params.push_back( "2d" );
+    params.push_back( "z" );
+    params.push_back( "w" );
+
+    std::map<std::string, std::string> dict;
+    dict["class"]  = "od";
+    dict["stream"] = "oper";
+    dict["type"]   = "fc";
+
+    eckit::RendezvousHash rendezvous;
+
+    rendezvous.addNode("node01");
+    rendezvous.addNode("node02");
+
+
+    const size_t nsteps  = 25;
+    const size_t nlevels = 100;
+
+
+    std::map<std::string, size_t> counts;
+
+    for(std::vector<std::string>::iterator param = params.begin(); param != params.end(); ++param ) {
+
+        dict["param"] = *param;
+
+        for(size_t step = 0; step < nsteps; ++step) {
+
+            dict["step"]  = toStr(step);
+
+            for( size_t level = 0; level < nlevels; ++level ) {
+
+                dict["level"]  = toStr(level);
+
+                counts[ rendezvous.selectNode(dict) ]++;
+
+            }
+        }
+    }
+
+    BOOST_TEST_MESSAGE( counts );
+
+    std::map<std::string, size_t> counts2;
+
+    rendezvous.addNode("node03");
+    rendezvous.addNode("node04");
+
+    for(std::vector<std::string>::iterator param = params.begin(); param != params.end(); ++param ) {
+
+        dict["param"] = *param;
+
+        for(size_t step = 0; step < nsteps; ++step) {
+
+            dict["step"]  = toStr(step);
+
+            for( size_t level = 0; level < nlevels; ++level ) {
+
+                dict["level"]  = toStr(level);
+
+                counts2[ rendezvous.selectNode(dict) ]++;
+
+            }
+        }
+    }
+
+    BOOST_TEST_MESSAGE( counts2 );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
+
diff --git a/eckit/src/tests/utils/test_sha1.cc b/eckit/src/tests/utils/test_sha1.cc
new file mode 100644
index 0000000..426210e
--- /dev/null
+++ b/eckit/src/tests/utils/test_sha1.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_utils
+
+#include <iostream>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/utils/SHA1.h"
+#include "eckit/log/Timer.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+using namespace std;
+using namespace eckit;
+
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_utils_sha1 )
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_constructor )
+{
+    SHA1 hash;
+
+        const char* msg = "The quick brown fox jumps over the lazy dog";
+
+        hash.add(msg,strlen(msg));
+
+        std::string res ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
+
+        BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_constructor_string )
+{
+    SHA1 hash( "Few quips galvanized the mock jury box" );
+
+        std::string res ("4c135b5afa7f415e71a509b747c1391811fdc52c");
+
+        BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_double_add )
+{
+    SHA1 hash;
+
+        const char* msg = "The quick brown fox jumps over the lazy dog";
+
+        hash.add(msg,strlen(msg));
+        hash.add(msg,strlen(msg));
+
+        std::string res ("86c842aa0249527aad022bab2f8a9b4d77b82b12");
+
+        BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+// original test suite from RFC-3174
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_test_suite_from_rfc3174 )
+{
+
+    /*
+     *  Define patterns for testing
+     */
+#define TEST1   "abc"
+#define TEST2a  "abcdbcdecdefdefgefghfghighijhi"
+#define TEST2b  "jkijkljklmklmnlmnomnopnopq"
+#define TEST2   TEST2a TEST2b
+#define TEST3   "a"
+#define TEST4a  "01234567012345670123456701234567"
+#define TEST4b  "01234567012345670123456701234567"
+    /* an exact multiple of 512 bits */
+#define TEST4   TEST4a TEST4b
+const char *testarray[4] =
+{
+    TEST1,
+    TEST2,
+    TEST3,
+    TEST4
+};
+
+    const char *resultarray[4] =
+    {
+        "a9993e364706816aba3e25717850c26c9cd0d89d",
+        "84983e441c3bd26ebaae4aa1f95129e5e54670f1",
+        "34aa973cd4c4daa4f61eeb2bdbad27316534016f",
+        "dea356a2cddd90c7a7ecedc5ebb563934f460452"
+    };
+
+    const size_t n[4] = { 1, 1, 1000000, 10 };
+
+    for(int i = 0; i < 4; ++i ) {
+
+        SHA1 hash;
+        for(size_t j=0; j < n[i]; ++j) {
+            hash.add(testarray[i], ::strlen(testarray[i]));
+        }
+
+        BOOST_CHECK_EQUAL(hash.digest(), std::string(resultarray[i]));
+    }
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_compute )
+{
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    std::string res ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
+
+    SHA1 hash;
+    BOOST_CHECK_EQUAL( res , hash.compute(msg.c_str(), msg.size()));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_sha1_reset )
+{
+    SHA1 hash( "FOOBAR" );
+
+    hash.reset(); // reset initial state
+
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    hash.add(msg.c_str(), msg.size());
+
+    std::string res ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
+
+    BOOST_CHECK_EQUAL( res , hash.digest());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // end namespace test
+} // end namespace eckit
diff --git a/eckit/src/tests/utils/test_string_tools.cc b/eckit/src/tests/utils/test_string_tools.cc
new file mode 100644
index 0000000..77c60c0
--- /dev/null
+++ b/eckit/src/tests/utils/test_string_tools.cc
@@ -0,0 +1,183 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/types/Types.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+class TestStringTools : public Tool {
+public:
+
+    TestStringTools(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestStringTools() {}
+
+    virtual void run();
+
+protected:
+    
+    void test_substitute();
+    void test_substituteVariables();
+    void test_startsWith();
+    void test_trim();
+    void test_front_trim();
+    void test_back_trim();
+
+};
+
+//-----------------------------------------------------------------------------
+            
+void TestStringTools::run()
+{
+    test_substitute();
+    test_substituteVariables();
+    test_startsWith();
+    test_trim();
+    test_front_trim();
+    test_back_trim();
+}
+
+void TestStringTools::test_substitute()
+{
+    StringDict m;
+
+    m["class"]  = "od";
+    m["stream"] = "oper";
+
+    string in ( "{class}:none:{stream}" );
+    
+    string out = StringTools::substitute(in,m);
+    
+    ASSERT( out == "od:none:oper" );    
+}
+
+void TestStringTools::test_substituteVariables()
+{
+    string in ( "{class}:none:{stream}" );
+
+    StringList out = StringTools::substituteVariables(in);
+    
+    ASSERT( out.size() == 2 );    
+    ASSERT( out[0] == "class" );    
+    ASSERT( out[1] == "stream" );    
+}
+
+void TestStringTools::test_startsWith()
+{
+    string in ( "_lolo_test" );
+    string s1 ( "_lolo" );
+    string s2 ( "lolo" );
+    string s3 ( "_lolo_test_bigger" );
+
+    ASSERT( !StringTools::startsWith(in,"") );
+    ASSERT(  StringTools::startsWith(in,in) );
+    ASSERT(  StringTools::startsWith(in,s1) );
+    ASSERT(  StringTools::startsWith(in,"_") );
+    ASSERT( !StringTools::startsWith(in,s2) );
+    ASSERT( !StringTools::startsWith(in,s3) );
+}
+
+void TestStringTools::test_trim()
+{
+    string t1 ( "   lolo_test    " );
+    ASSERT(  StringTools::trim(t1) == string("lolo_test") );
+
+    string t2 ( "   lolo_test" );
+    ASSERT(  StringTools::trim(t2) == string("lolo_test") );
+
+    string t3 ( "lolo_test   " );
+    ASSERT(  StringTools::trim(t3) == string("lolo_test") );
+
+    string t4 ( "" );
+    ASSERT(  StringTools::trim(t4) == string("") );
+
+    string t5 ( "nothing_here" );
+    ASSERT(  StringTools::trim(t5) == string("nothing_here") );
+
+    string t6 ( "XXXXXXusefullXXXXX" );
+    ASSERT(  StringTools::trim(t6,"X") == string("usefull") );
+
+    string t7 ( "0000010" );
+    ASSERT(  StringTools::trim(t7,"0") == string("1") );
+}
+
+void TestStringTools::test_front_trim()
+{
+    string t1 ( "   lolo_test    " );
+    ASSERT(  StringTools::front_trim(t1) == string("lolo_test    ") );
+
+    string t2 ( "   lolo_test" );
+    ASSERT(  StringTools::front_trim(t2) == string("lolo_test") );
+
+    string t3 ( "lolo_test   " );
+    ASSERT(  StringTools::front_trim(t3) == string("lolo_test   ") );
+
+    string t4 ( "" );
+    ASSERT(  StringTools::front_trim(t4) == string("") );
+
+    string t5 ( "nothing_here" );
+    ASSERT(  StringTools::front_trim(t5) == string("nothing_here") );
+
+    string t6 ( "XXXXXXusefullXXXXX" );
+    ASSERT(  StringTools::front_trim(t6,"X") == string("usefullXXXXX") );
+
+    string t7 ( "0000010" );
+    ASSERT(  StringTools::front_trim(t7,"0") == string("10") );
+}
+
+void TestStringTools::test_back_trim()
+{
+    string t1 ( "   lolo_test    " );
+
+    Log::info() << StringTools::back_trim(t1) << std::endl;
+
+    ASSERT(  StringTools::back_trim(t1) == string("   lolo_test") );
+
+    string t2 ( "   lolo_test" );
+    ASSERT(  StringTools::back_trim(t2) == string("   lolo_test") );
+
+    string t3 ( "lolo_test   " );
+    ASSERT(  StringTools::back_trim(t3) == string("lolo_test") );
+
+    string t4 ( "" );
+    ASSERT(  StringTools::back_trim(t4) == string("") );
+
+    string t5 ( "nothing_here" );
+    ASSERT(  StringTools::back_trim(t5) == string("nothing_here") );
+
+    string t6 ( "XXXXXXusefullXXXXX" );
+    ASSERT(  StringTools::back_trim(t6,"X") == string("XXXXXXusefull") );
+
+    string t7 ( "0000010" );
+    ASSERT(  StringTools::back_trim(t7,"0") == string("000001") );
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestStringTools app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/utils/test_tokenizer.cc b/eckit/src/tests/utils/test_tokenizer.cc
new file mode 100644
index 0000000..e321bfc
--- /dev/null
+++ b/eckit/src/tests/utils/test_tokenizer.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/types/Types.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+class TestTokenizer : public Tool {
+public:
+
+    TestTokenizer(int argc,char **argv): Tool(argc,argv) {}
+
+    ~TestTokenizer() {}
+
+    virtual void run();
+
+    template< class Container >  void test_single();
+    template< class Container >  void test_multi();
+
+    void test_keep_empty_list();
+    void test_keep_empty_set();
+};
+
+//-----------------------------------------------------------------------------
+
+template< class Container >
+void TestTokenizer::test_single()
+{
+    std::string source (":lolo1:lolo2::lolo3");
+    Container  target;
+
+    Tokenizer parse(":");
+
+    parse(source,target);
+
+    ASSERT( target.size() == 3 );
+
+    size_t c = 1;
+    for( typename Container::const_iterator i = target.begin(); i != target.end(); ++i, ++c )
+    {
+        ostringstream s;
+        s << "lolo" << c;
+//        std::cout << *i << " ??? " << s.str() << std::endl;
+        ASSERT( *i == s.str() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+template< class Container >
+void TestTokenizer::test_multi()
+{
+    Container  target;
+
+    Tokenizer parse("-:;");
+
+    std::string source1 ("-lolo0-lolo1-lolo2;lolo3:-lolo4-");
+    parse(source1,target);
+
+    ASSERT( target.size() == 5 );
+
+    size_t c = 0;
+    for( typename Container::const_iterator i = target.begin(); i != target.end(); ++i, ++c )
+    {
+        ostringstream s;
+        s << "lolo" << c;
+//        std::cout << *i << " ??? " << s.str() << std::endl;
+        ASSERT( *i == s.str() );
+    }
+
+    std::string source2 ("-lolo5-lolo6-lolo7;lolo8:lolo9-");
+    parse(source2,target);
+
+    ASSERT( target.size() == 10 );
+
+    c = 0;
+    for( typename Container::const_iterator i = target.begin(); i != target.end(); ++i, ++c )
+    {
+        ostringstream s;
+        s << "lolo" << c;
+//        std::cout << *i << " ??? " << s.str() << std::endl;
+        ASSERT( *i == s.str() );
+    }
+}
+
+//-----------------------------------------------------------------------------
+
+void TestTokenizer::test_keep_empty_list()
+{
+    StringList  target;
+
+    Tokenizer parse(":",true);
+
+    std::string source1 (":4i:2100:::01:::::.");
+    parse(source1,target);
+
+    Log::info() << target << std::endl;
+
+    ASSERT( target.size() == 11 );
+}
+
+//-----------------------------------------------------------------------------
+
+void TestTokenizer::test_keep_empty_set()
+{
+    StringSet  target;
+
+    Tokenizer parse(":",true);
+
+    std::string source1 (":4i:2100:::01:::::.");
+    parse(source1,target);
+
+    Log::info() << target << std::endl;
+
+    ASSERT( target.size() == 5 ); // 5 uniqe elments
+}
+
+//-----------------------------------------------------------------------------
+            
+void TestTokenizer::run()
+{
+    test_single< StringList >();
+    test_multi < StringList >();
+    test_keep_empty_list();
+
+    test_single< StringSet >();
+    test_multi < StringSet >();
+    test_keep_empty_set();
+}
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+//-----------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    eckit_test::TestTokenizer app(argc,argv);
+    return app.start();
+}
+
diff --git a/eckit/src/tests/utils/test_translator.cc b/eckit/src/tests/utils/test_translator.cc
new file mode 100644
index 0000000..03ab32c
--- /dev/null
+++ b/eckit/src/tests/utils/test_translator.cc
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <string>
+
+#include "eckit/eckit_config.h"
+
+#define BOOST_TEST_MODULE TestTranslator
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/utils/Translator.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+BOOST_AUTO_TEST_SUITE( TestTranslator )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_translator_string_double ) {
+    double half = 0.5;
+    double zero = 0.0;
+    double hund = 100.0;
+    double ten  = 10.;
+
+    Translator<string,double> t;
+
+    BOOST_CHECK( half == t( "0.5" ) );
+
+    BOOST_CHECK( hund == t( "1e2"  ) );
+    BOOST_CHECK( hund == t( "1E2"  ) );
+    BOOST_CHECK( hund == t( "1.e2"  ) );
+    BOOST_CHECK( hund == t( "1.E2"  ) );
+    BOOST_CHECK( hund == t( "1.0e2"  ) );
+    BOOST_CHECK( hund == t( "1.0E2"  ) );
+    BOOST_CHECK( hund == t( "1e002"  ) );
+    BOOST_CHECK( hund == t( "1E002"  ) );
+    BOOST_CHECK( hund == t( "1.0e002"  ) );
+    BOOST_CHECK( hund == t( "1.0E002"  ) );
+
+    BOOST_CHECK( zero == t( "0"       ) );
+    BOOST_CHECK( zero == t( "0."      ) );
+    BOOST_CHECK( zero == t( "0.0"     ) );
+    BOOST_CHECK( zero == t( "0.00"    ) );
+    BOOST_CHECK( zero == t( "0.0e0"   ) );
+    BOOST_CHECK( zero == t( "0.0e-0"  ) );
+    BOOST_CHECK( zero == t( "0.0e+0"  ) );
+    BOOST_CHECK( zero == t( "+0"      ) );
+    BOOST_CHECK( zero == t( "+0."     ) );
+    BOOST_CHECK( zero == t( "+0.0"    ) );
+    BOOST_CHECK( zero == t( "+0.00"   ) );
+    BOOST_CHECK( zero == t( "+0.0e0"  ) );
+    BOOST_CHECK( zero == t( "+0.0e-0" ) );
+    BOOST_CHECK( zero == t( "+0.0e+0" ) );
+    BOOST_CHECK( zero == t( "-0"      ) );
+    BOOST_CHECK( zero == t( "-0."     ) );
+    BOOST_CHECK( zero == t( "-0.0"    ) );
+    BOOST_CHECK( zero == t( "-0.00"   ) );
+    BOOST_CHECK( zero == t( "-0.0e0"  ) );
+    BOOST_CHECK( zero == t( "-0.0e-0" ) );
+    BOOST_CHECK( zero == t( "-0.0e+0" ) );
+
+    /// weird cases that actually pass
+
+    BOOST_CHECK_NO_THROW( t( "inf" ) ); // inf is acceptable, case insensitive
+    BOOST_CHECK_NO_THROW( t( "INF" ) ); // INF is acceptable, case insensitive
+    BOOST_CHECK_NO_THROW( t( "infinity" ) ); // infinity is acceptable, case insensitive
+    BOOST_CHECK_NO_THROW( t( "INFINITY" ) ); // INFINITY is acceptable, case insensitive
+
+    BOOST_CHECK_NO_THROW( t( "nan" ) ); // nan is acceptable, case insensitive
+    BOOST_CHECK_NO_THROW( t( "NAN" ) ); // NAN is acceptable, case insensitive
+
+    BOOST_CHECK( zero == t( "0x0" ) );   // hexadecimal is acceptable
+    BOOST_CHECK( ten  == t( "0xA" ) );   // hexadecimal is acceptable
+
+    /// these should fail ...
+
+    BOOST_CHECK_THROW( t( "" ), BadParameter ); // empty string
+
+    BOOST_CHECK_THROW( t("0.5 "), BadParameter ); // no spaces accepted -- we are being extra strict
+    BOOST_CHECK_THROW( t(" 0.5"), BadParameter ); // no spaces accepted -- we are being extra strict
+    BOOST_CHECK_THROW( t(" 0.5   "), BadParameter ); // no spaces accepted -- we are being extra strict
+
+    BOOST_CHECK_THROW( t( "1e+10000" ), BadParameter ); // overflow,  max ~ 1.79769e+308
+    BOOST_CHECK_THROW( t( "1e-10000" ), BadParameter ); // underflow, min ~ 2.22507e-308
+
+    BOOST_CHECK_THROW( t( "0.5a"   ), BadParameter );
+    BOOST_CHECK_THROW( t( "foobar"  ), BadParameter );
+    BOOST_CHECK_THROW( t( "foo555bar" ), BadParameter );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/eckit/src/tests/utils/test_xxhash.cc b/eckit/src/tests/utils/test_xxhash.cc
new file mode 100644
index 0000000..47d0a68
--- /dev/null
+++ b/eckit/src/tests/utils/test_xxhash.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_utils
+
+#include <iostream>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/utils/xxHash.h"
+#include "eckit/log/Timer.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Seconds.h"
+
+using namespace std;
+using namespace eckit;
+
+
+namespace eckit {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_utils_xxHash )
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_xxHash_constructor )
+{
+    xxHash hash;
+
+        const char* msg = "The quick brown fox jumps over the lazy dog";
+
+        hash.add(msg,strlen(msg));
+
+        std::string res ("0b242d361fda71bc");
+
+        BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_xxHash_constructor_string )
+{
+    xxHash hash( "Nobody inspects the spammish repetition" );
+
+    std::string res ("fbcea83c8a378bf1");
+
+    BOOST_CHECK_EQUAL( res , hash.digest() );
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_xxHash_test_suite )
+{
+    BOOST_CHECK_EQUAL( xxHash("").digest(), std::string("ef46db3751d8e999"));
+    BOOST_CHECK_EQUAL( xxHash("a").digest(), std::string("d24ec4f1a98c6e5b"));
+    BOOST_CHECK_EQUAL( xxHash("abc").digest(), std::string("44bc2cf5ad770999"));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_xxhash_compute )
+{
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    std::string res ("0b242d361fda71bc");
+
+    xxHash hash;
+    BOOST_CHECK_EQUAL( res , hash.compute(msg.c_str(), msg.size()));
+}
+
+BOOST_AUTO_TEST_CASE( test_eckit_utils_xxhash_reset )
+{
+    xxHash hash( "FOOBAR" );
+
+    hash.reset(); // reset initial state
+
+    std::string msg ( "The quick brown fox jumps over the lazy dog" );
+
+    hash.add(msg.c_str(), msg.size());
+
+    std::string res ("0b242d361fda71bc");
+
+    BOOST_CHECK_EQUAL( res , hash.digest());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+} // end namespace test
+} // end namespace eckit
diff --git a/eckit/src/tests/value/AnyKeyParams.cc b/eckit/src/tests/value/AnyKeyParams.cc
new file mode 100644
index 0000000..3cf33f8
--- /dev/null
+++ b/eckit/src/tests/value/AnyKeyParams.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "AnyKeyParams.h"
+
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+AnyKeyParams::AnyKeyParams(std::string payload) : payload_(payload) {}
+
+AnyKeyParams::AnyKeyParams(Stream& s) {
+  s >> payload_;
+}
+
+Params::value_t getValue(const AnyKeyParams &, const Params::key_t &)
+{
+  return Params::value_t("foo");
+}
+
+void print(const AnyKeyParams &, std::ostream &) {}
+
+void encode(const AnyKeyParams & p, Stream & s) {
+  s << p.payload_;
+}
+
+//-----------------------------------------------------------------------------
+
+Params::Factory<AnyKeyParams> anyKeyParamsFactory;
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
diff --git a/eckit/src/tests/value/AnyKeyParams.h b/eckit/src/tests/value/AnyKeyParams.h
new file mode 100644
index 0000000..54a62fc
--- /dev/null
+++ b/eckit/src/tests/value/AnyKeyParams.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file AnyKeyParams.h
+/// @author Florian Rathgeber
+/// @date March 2015
+
+#ifndef eckit_test_AnyKeyParams_H
+#define eckit_test_AnyKeyParams_H
+
+#include "eckit/serialisation/Stream.h"
+#include "eckit/value/Params.h"
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+struct AnyKeyParams {
+    AnyKeyParams(std::string payload);
+    AnyKeyParams( eckit::Stream& s );
+    static const char* className() { return "AnyKeyParams"; }
+private:
+    friend void encode( const AnyKeyParams&, eckit::Stream& );
+    std::string payload_;
+};
+
+eckit::Params::value_t getValue( const AnyKeyParams&, const eckit::Params::key_t& );
+
+void print( const AnyKeyParams&, std::ostream& );
+void encode( const AnyKeyParams& p, eckit::Stream& s );
+
+//-----------------------------------------------------------------------------
+
+} // namespace eckit_test
+
+#endif
diff --git a/eckit/src/tests/value/CMakeLists.txt b/eckit/src/tests/value/CMakeLists.txt
new file mode 100644
index 0000000..f99df3f
--- /dev/null
+++ b/eckit/src/tests/value/CMakeLists.txt
@@ -0,0 +1,22 @@
+ecbuild_add_library( TARGET     eckit_test_custom_params
+                     NOINSTALL
+                     SOURCES    AnyKeyParams.cc
+                     LIBS       eckit )
+
+ecbuild_add_test( TARGET   eckit_test_value_params
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_value_params.cc
+                  LIBS     eckit eckit_test_custom_params )
+
+ecbuild_add_test( TARGET   eckit_test_value_properties
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_value_properties.cc
+                  LIBS     eckit )
+
+ecbuild_add_test( TARGET   eckit_test_value
+                  ARGS     --log_level=message
+                  BOOST
+                  SOURCES  test_value.cc
+                  LIBS     eckit )
diff --git a/eckit/src/tests/value/test_value.cc b/eckit/src/tests/value/test_value.cc
new file mode 100644
index 0000000..67fd893
--- /dev/null
+++ b/eckit/src/tests/value/test_value.cc
@@ -0,0 +1,2624 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_value
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/value/Value.h"
+
+#include "eckit/testing/Setup.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+namespace eckit {
+namespace test {
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// eckit::Value offloads its functionality onto various subclasses of eckit::Content, which are
+// internally allocated on the heap and managed by the Value objects. This allows the behaviour to
+// depend on the type of content stored, with a consistent exposed interface.
+//
+// As a result of this, eckit::Value has an extremely thick interface, with potentially NxN possibilities for each
+// action (N possible types of argument, N possible types of content) - e.g. ::as<double>() will happily return a
+// (casted) double if the content type is a long...
+//
+// --> it is not realistic to catch all of this behaviour in the tests.
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace {
+
+    // Helper functions, so that we can put operator expressions inside callables that can be passed into
+    // BOOST_CHECK_THROW
+
+    Value ValueAdd(const Value& lhs, const Value& rhs) { return lhs + rhs; }
+    Value ValueSub(const Value& lhs, const Value& rhs) { return lhs - rhs; }
+    Value ValueMul(const Value& lhs, const Value& rhs) { return lhs * rhs; }
+    Value ValueDiv(const Value& lhs, const Value& rhs) { return lhs / rhs; }
+    Value ValueMod(const Value& lhs, const Value& rhs) { return lhs % rhs; }
+
+    Value ValueAddSelf(Value& lhs, const Value& rhs) { return lhs += rhs; }
+    Value ValueSubSelf(Value& lhs, const Value& rhs) { return lhs -= rhs; }
+    Value ValueMulSelf(Value& lhs, const Value& rhs) { return lhs *= rhs; }
+    Value ValueDivSelf(Value& lhs, const Value& rhs) { return lhs /= rhs; }
+    Value ValueModSelf(Value& lhs, const Value& rhs) { return lhs %= rhs; }
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// TODO:
+// - Tests for Stream
+// - Test json, print, encode
+
+
+BOOST_AUTO_TEST_SUITE( test_eckit_value )
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of bools first
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_cast ) {
+    Value val_true(true);
+    Value val_false(false);
+
+    //
+    // Access and conversion of bools
+    //
+
+    BOOST_CHECK_EQUAL(bool(val_true), true);
+    BOOST_CHECK_EQUAL(bool(val_false), false);
+    BOOST_CHECK_EQUAL(val_true.as<bool>(), true);
+    BOOST_CHECK_EQUAL(val_false.as<bool>(), false);
+
+    // Integer type conversions
+    BOOST_CHECK_EQUAL(int(val_true), 1);
+    BOOST_CHECK_EQUAL(int(val_false), 0);
+    BOOST_CHECK_EQUAL(val_true.as<long long>(), 1);
+    BOOST_CHECK_EQUAL(val_false.as<long long>(), 0);
+
+    // For pretty printing
+
+    BOOST_CHECK_EQUAL(std::string(val_true), "true");
+    BOOST_CHECK_EQUAL(std::string(val_false), "false");
+    BOOST_CHECK_EQUAL(val_true.as<std::string>(), "true");
+    BOOST_CHECK_EQUAL(val_false.as<std::string>(), "false");
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl_true(val_true.as<ValueList>());
+    ValueList vl_false = val_false;
+    BOOST_CHECK_EQUAL(vl_true.size(), 1);
+    BOOST_CHECK_EQUAL(vl_false.size(), 1);
+    BOOST_CHECK_EQUAL(vl_true[0].as<bool>(), true);
+    BOOST_CHECK_EQUAL(vl_false[0].as<bool>(), false);
+
+    // And all the invalid conversions
+
+    /// For some reason, Value(bool) happily converts to double...
+    /// BOOST_CHECK_THROW(val_false.as<double>(), BadConversion);
+
+    /// Length/Offset are just integers, so bool-->Offset conversion works...!!!
+    /// BOOST_CHECK_THROW(Length(val_false), BadConversion);
+    /// BOOST_CHECK_THROW(Offset(val_false), BadConversion);
+
+    BOOST_CHECK_THROW(val_false.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val_false.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val_false.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val_false.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_type ) {
+    Value val_true(true);
+    Value val_false(false);
+
+    BOOST_CHECK(val_true.isBool());
+    BOOST_CHECK(val_false.isBool());
+
+    BOOST_CHECK(!val_true.isNil());
+    BOOST_CHECK(!val_true.isNumber());
+    BOOST_CHECK(!val_true.isDouble());
+    BOOST_CHECK(!val_true.isString());
+    BOOST_CHECK(!val_true.isList());
+    BOOST_CHECK(!val_true.isMap());
+    BOOST_CHECK(!val_true.isDate());
+    BOOST_CHECK(!val_true.isTime());
+    BOOST_CHECK(!val_true.isDateTime());
+
+    BOOST_CHECK(!val_false.isNil());
+    BOOST_CHECK(!val_false.isNumber());
+    BOOST_CHECK(!val_false.isDouble());
+    BOOST_CHECK(!val_false.isString());
+    BOOST_CHECK(!val_false.isList());
+    BOOST_CHECK(!val_false.isMap());
+    BOOST_CHECK(!val_false.isDate());
+    BOOST_CHECK(!val_false.isTime());
+    BOOST_CHECK(!val_false.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_comparisons ) {
+    Value val_true1(true);
+    Value val_true2(true);
+    Value val_false1(false);
+    Value val_false2(false);
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+
+    // ************************
+    // WARNING: This logic is inverted from all the other Value types. Should probably be checked.
+    // ************************
+
+    BOOST_CHECK(val_true1.compare(val_true1) == -1);
+    BOOST_CHECK(val_true1.compare(val_true2) == -1);
+    BOOST_CHECK(val_false1.compare(val_false1) == 1);
+    BOOST_CHECK(val_false1.compare(val_false2) == 1);
+
+    BOOST_CHECK(val_true1.compare(val_false1) == 0);
+    BOOST_CHECK(val_false2.compare(val_true2) == 0);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val_true1.compare(Value(1234)) > 0); // Only need 1 integral test, they are all the same.
+    BOOST_CHECK(val_true1.compare(Value(1234.5)) > 0);
+    BOOST_CHECK(val_true1.compare(Value("test str")) > 0);
+    BOOST_CHECK(val_true1.compare(Value(std::string("testing string"))) > 0);
+    BOOST_CHECK(val_true1.compare(Value(ValueMap())) > 0);
+    BOOST_CHECK(val_true1.compare(Value(Date(2016, 3, 30))) > 0);
+    BOOST_CHECK(val_true1.compare(ValueList()) > 0);
+
+    BOOST_CHECK(Value(1234).compare(val_false1) < 0); // Only need 1 integral test, they are all the same.
+    BOOST_CHECK(Value(1234.5).compare(val_false1) < 0);
+    BOOST_CHECK(Value("test str").compare(val_false1) < 0);
+    BOOST_CHECK(Value(std::string("testing string")).compare(val_false1) < 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val_false1) < 0);
+    BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val_false1) < 0);
+    BOOST_CHECK(Value(ValueList()).compare(val_false1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_index_operator ) {
+    // No indexing operations should work on a bool...
+
+    Value val_true(true);
+    Value val_false(false);
+
+    BOOST_CHECK_THROW(val_true["idx"], BadOperator);
+    BOOST_CHECK_THROW(val_true[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val_true[123], BadOperator);
+    BOOST_CHECK_THROW(val_true[Value(123)], BadOperator);
+
+    BOOST_CHECK_THROW(val_false["idx"], BadOperator);
+    BOOST_CHECK_THROW(val_false[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val_false[123], BadOperator);
+    BOOST_CHECK_THROW(val_false[Value(123)], BadOperator);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK_THROW(val_true.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val_true.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val_true.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val_true.contains(Value(123)), BadOperator);
+
+    BOOST_CHECK_THROW(val_false.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val_false.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val_false.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val_false.contains(Value(123)), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_add_operator ) {
+    // There are no valid boolean addition operations.
+
+    Value val(true);
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_subtract_operator ) {
+    // There are no valid boolean subtraction operations.
+
+    Value val(true);
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_multiply_operator ) {
+    // There are no valid boolean multiplication operations.
+
+    Value val(true);
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_divide_operator ) {
+    // There are no valid boolean division operations.
+
+    Value val(true);
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_modulo_operator ) {
+    // There are no valid boolean modulo operations.
+
+    Value val(true);
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_bool_head_tail ) {
+    Value val(true);
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of integers next. Note that all integral types are treated identically.
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_cast ) {
+    // Note that _all_ the integer types are stored as a signed long long
+    // --> There are constraints on the size that can be stored
+    Value val_zero(0U);
+    Value val_int(12345);
+    Value val_long(-2147483647);
+
+    // Integer conversions
+
+    BOOST_CHECK_EQUAL(val_zero.as<long long>(), 0);
+    BOOST_CHECK_EQUAL(val_int.as<long long>(), 12345);
+    BOOST_CHECK_EQUAL(val_long.as<long long>(), -2147483647);
+
+    BOOST_CHECK_EQUAL(int(val_zero), 0);
+
+    BOOST_CHECK_EQUAL(int(val_int), 12345);
+    BOOST_CHECK_EQUAL((unsigned int)(val_int), 12345);
+    BOOST_CHECK_EQUAL(short(val_int), 12345);
+    BOOST_CHECK_EQUAL((unsigned short)(val_int), 12345);
+    BOOST_CHECK_EQUAL(long(val_int), 12345);
+    BOOST_CHECK_EQUAL((unsigned long)(val_int), 12345);
+    BOOST_CHECK_EQUAL((long long)(val_int), 12345);
+    BOOST_CHECK_EQUAL((unsigned long long)(val_int), 12345);
+
+    // NOTE that using an unsigned variable does NOT cause the check to fail, unless there is an overflow. The
+    // compiler will assume that everything is fine, and just do a bitwise check...
+    BOOST_CHECK_EQUAL(int(val_long), -2147483647);
+    BOOST_CHECK_EQUAL((unsigned int)(val_long), -2147483647);
+//    BOOST_CHECK(short(val_long) != -2147483647);             // a short ranges [−32767, +32767] so this is always true
+//    BOOST_CHECK((unsigned short)(val_long) != -2147483647);  // unsigned is always positive
+    BOOST_CHECK_EQUAL(long(val_long), -2147483647);
+    BOOST_CHECK_EQUAL((unsigned long)(val_long), -2147483647);
+    BOOST_CHECK_EQUAL((long long)(val_long), -2147483647);
+    BOOST_CHECK_EQUAL((unsigned long long)(val_long), -2147483647);
+
+    // Check boolean conversion
+
+    BOOST_CHECK(!val_zero);
+    BOOST_CHECK(val_int);
+    BOOST_CHECK(val_long);
+    BOOST_CHECK(!val_zero.as<bool>());
+    BOOST_CHECK(val_int.as<bool>());
+    BOOST_CHECK(val_long.as<bool>());
+
+    // Check double conversion
+
+    BOOST_CHECK_CLOSE(double(val_zero), 0.0, 1.0e-6);
+    BOOST_CHECK_CLOSE(double(val_int), 12345.0, 1.0e-6);
+    BOOST_CHECK_CLOSE(double(val_long), -2147483647.0, 1.0e-6);
+    BOOST_CHECK_CLOSE(val_zero.as<double>(), 0.0, 1.0e-6);
+    BOOST_CHECK_CLOSE(val_int.as<double>(), 12345.0, 1.0e-6);
+    BOOST_CHECK_CLOSE(val_long.as<double>(), -2147483647.0, 1.0e-6);
+
+    // Check pretty printing
+
+    BOOST_CHECK_EQUAL(std::string(val_zero), "0");
+    BOOST_CHECK_EQUAL(std::string(val_int), "12345");
+    BOOST_CHECK_EQUAL(std::string(val_long), "-2147483647");
+    BOOST_CHECK_EQUAL(val_zero.as<std::string>(), "0");
+    BOOST_CHECK_EQUAL(val_int.as<std::string>(), "12345");
+    BOOST_CHECK_EQUAL(val_long.as<std::string>(), "-2147483647");
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl(val_int.as<ValueList>());
+    BOOST_CHECK_EQUAL(vl.size(), 1);
+    BOOST_CHECK_EQUAL(vl[0].as<long long>(), 12345);
+
+    // And the conversions to Length/Offset, oooer.
+    // It would be nicer if these didn't work...
+
+    Length len = val_int; // Avoid ambiguities. Gah.
+    Offset off = val_int;
+    BOOST_CHECK_EQUAL(len, Length(12345));
+    BOOST_CHECK_EQUAL(off, Offset(12345));
+
+    // And the invalid conversions
+
+    BOOST_CHECK_THROW(val_int.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val_int.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val_int.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val_int.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_unsigned_overflow ) {
+    // Internally a Value(unsigned long long) is stored as a long long, as with all the other integer types. This should
+    // give very predictable overflow behaviour
+
+    Value val_max(9223372036854775807);
+    Value val_min(-9223372036854775807);
+    Value val_overflow(9223372036854775808U);
+
+    BOOST_CHECK_EQUAL((long long)val_max, 9223372036854775807);
+    BOOST_CHECK_EQUAL((long long)val_min, -9223372036854775807);
+    BOOST_CHECK((unsigned long long)(val_min) != 9223372036854775808U);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_type ) {
+    Value val_int(12345);
+    Value val_long(2147483647);
+    Value val_longlong(-9223372036854775807);
+
+    BOOST_CHECK(val_int.isNumber());
+    BOOST_CHECK(val_long.isNumber());
+    BOOST_CHECK(val_longlong.isNumber());
+
+    BOOST_CHECK(!val_int.isNil());
+    BOOST_CHECK(!val_int.isBool());
+    BOOST_CHECK(!val_int.isDouble());
+    BOOST_CHECK(!val_int.isString());
+    BOOST_CHECK(!val_int.isList());
+    BOOST_CHECK(!val_int.isMap());
+    BOOST_CHECK(!val_int.isDate());
+    BOOST_CHECK(!val_int.isTime());
+    BOOST_CHECK(!val_int.isDateTime());
+
+    BOOST_CHECK(!val_long.isNil());
+    BOOST_CHECK(!val_long.isBool());
+    BOOST_CHECK(!val_long.isDouble());
+    BOOST_CHECK(!val_long.isString());
+    BOOST_CHECK(!val_long.isList());
+    BOOST_CHECK(!val_long.isMap());
+    BOOST_CHECK(!val_long.isDate());
+    BOOST_CHECK(!val_long.isTime());
+    BOOST_CHECK(!val_long.isDateTime());
+
+    BOOST_CHECK(!val_longlong.isNil());
+    BOOST_CHECK(!val_longlong.isBool());
+    BOOST_CHECK(!val_longlong.isDouble());
+    BOOST_CHECK(!val_longlong.isString());
+    BOOST_CHECK(!val_longlong.isList());
+    BOOST_CHECK(!val_longlong.isMap());
+    BOOST_CHECK(!val_longlong.isDate());
+    BOOST_CHECK(!val_longlong.isTime());
+    BOOST_CHECK(!val_longlong.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_comparisons ) {
+    Value val1(1234);
+    Value val2(1234);
+    Value val3(4321);
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+
+    BOOST_CHECK(val1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val2) == 0);
+    BOOST_CHECK(val2.compare(val1) == 0);
+
+    BOOST_CHECK(val1.compare(val3) == -1);
+    BOOST_CHECK(val3.compare(val1) == 1);
+
+    // Check comparisons with floating point values ... these get checked by value
+    Value val_f1(1234.0);
+    Value val_f2(2222.0);
+
+    BOOST_CHECK(val_f1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val_f1) == 0);
+
+    BOOST_CHECK(val1.compare(val_f2) == -1);
+    BOOST_CHECK(val3.compare(val_f2) == 1);
+    BOOST_CHECK(val_f2.compare(val1) == 1);
+    BOOST_CHECK(val_f2.compare(val3) == -1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value("test str")) > 0);
+    BOOST_CHECK(val1.compare(Value(std::string("testing string"))) > 0);
+    BOOST_CHECK(val1.compare(Value(ValueMap())) > 0);
+    BOOST_CHECK(val1.compare(Value(Date(2016, 3, 30))) > 0);
+    BOOST_CHECK(val1.compare(ValueList()) > 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value("test str").compare(val1) < 0);
+    BOOST_CHECK(Value(std::string("testing string")).compare(val1) < 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val1) < 0);
+    BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val1) < 0);
+    BOOST_CHECK(Value(ValueList()).compare(val1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_index_operator ) {
+    // No indexing operations should work on an integer...
+
+    Value val(1234);
+
+    BOOST_CHECK_THROW(val["idx"], BadOperator);
+    BOOST_CHECK_THROW(val[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val[123], BadOperator);
+    BOOST_CHECK_THROW(val[Value(123)], BadOperator);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK_THROW(val.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val.contains(Value(123)), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_add_operator ) {
+    Value val(123);
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAdd(val, 1234).as<long long>(), 1357);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAdd(1234, val).as<long long>(), 1357);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAddSelf(val, 1234).as<long long>(), 1357);;
+    val = Value(123);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_subtract_operator ) {
+    Value val(123);
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueSub(val, 1234).as<long long>(), -1111);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_EQUAL(ValueSub(1234, val).as<long long>(), 1111);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueSubSelf(val, 1234).as<long long>(), -1111);
+    val = Value(123);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_multiply_operator ) {
+    Value val(123);
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueMul(val, 1234).as<long long>(), 151782);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_EQUAL(ValueMul(1234, val).as<long long>(), 151782);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueMulSelf(val, 1234).as<long long>(), 151782);
+    val = Value(123);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_divide_operator ) {
+    Value val(1476);
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueDiv(val, 12).as<long long>(), 123);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_EQUAL(ValueDiv(12, val).as<long long>(), 0);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_EQUAL(ValueDivSelf(val, 12).as<long long>(), 123);
+    val = Value(1476);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_modulo_operator ) {
+    Value val(123);
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_integer_head_tail ) {
+    Value val(12345);
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of doubles
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_cast ) {
+    Value val_zero(0.0);
+    Value val_double(99999999999999999999999999999.9);
+
+    // Double conversions
+
+    BOOST_CHECK_CLOSE(val_zero.as<double>(), 0.0, 1.0e-10);
+    BOOST_CHECK_CLOSE(val_double.as<double>(), 99999999999999999999999999999.9, 1.0e-10);
+
+    BOOST_CHECK_CLOSE(double(val_zero), 0.0, 1.0e-10);
+    BOOST_CHECK_CLOSE(double(val_double), 99999999999999999999999999999.9, 1.0e-10);
+
+    // Check pretty printing
+    /// @note rounding of values for pretty printing
+
+    BOOST_CHECK_EQUAL(std::string(val_zero), "0");
+    BOOST_CHECK_EQUAL(std::string(val_double), "1e+29");
+    BOOST_CHECK_EQUAL(val_zero.as<std::string>(), "0");
+    BOOST_CHECK_EQUAL(val_double.as<std::string>(), "1e+29");
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl(val_double.as<ValueList>());
+    BOOST_CHECK_EQUAL(vl.size(), 1);
+    BOOST_CHECK_CLOSE(vl[0].as<double>(), 99999999999999999999999999999.9, 1.0e-10);
+
+    BOOST_CHECK_THROW(val_double.as<bool>(), BadConversion);
+    BOOST_CHECK_THROW(val_double.as<long long>(), BadConversion);
+    BOOST_CHECK_THROW(val_double.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val_double.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val_double.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val_double.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_type ) {
+    Value val_double(-99999999999999999999999999999.9);
+
+    BOOST_CHECK(val_double.isDouble());
+
+    BOOST_CHECK(!val_double.isNil());
+    BOOST_CHECK(!val_double.isBool());
+    BOOST_CHECK(!val_double.isNumber());
+    BOOST_CHECK(!val_double.isString());
+    BOOST_CHECK(!val_double.isList());
+    BOOST_CHECK(!val_double.isMap());
+    BOOST_CHECK(!val_double.isDate());
+    BOOST_CHECK(!val_double.isTime());
+    BOOST_CHECK(!val_double.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_comparisons ) {
+    Value val1(1234.0);
+    Value val2(1234.0);
+    Value val3(4321.0);
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+
+    BOOST_CHECK(val1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val2) == 0);
+    BOOST_CHECK(val2.compare(val1) == 0);
+
+    BOOST_CHECK(val1.compare(val3) == -1);
+    BOOST_CHECK(val3.compare(val1) == 1);
+
+    // Check comparisons with integer values ... these get checked by value
+    Value val_i1(1234);
+    Value val_i2(2222);
+
+    BOOST_CHECK(val_i1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val_i1) == 0);
+
+    BOOST_CHECK(val1.compare(val_i2) == -1);
+    BOOST_CHECK(val3.compare(val_i2) == 1);
+    BOOST_CHECK(val_i2.compare(val1) == 1);
+    BOOST_CHECK(val_i2.compare(val3) == -1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value("test str")) > 0);
+    BOOST_CHECK(val1.compare(Value(std::string("testing string"))) > 0);
+    BOOST_CHECK(val1.compare(Value(ValueMap())) > 0);
+    BOOST_CHECK(val1.compare(Value(Date(2016, 3, 30))) > 0);
+    BOOST_CHECK(val1.compare(ValueList()) > 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value("test str").compare(val1) < 0);
+    BOOST_CHECK(Value(std::string("testing string")).compare(val1) < 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val1) < 0);
+    BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val1) < 0);
+    BOOST_CHECK(Value(ValueList()).compare(val1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_index_operator ) {
+    // No indexing operations should work on a double...
+
+    Value val(1234.45);
+
+    BOOST_CHECK_THROW(val["idx"], BadOperator);
+    BOOST_CHECK_THROW(val[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val[123], BadOperator);
+    BOOST_CHECK_THROW(val[Value(123)], BadOperator);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK_THROW(val.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val.contains(Value(123)), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_add_operator ) {
+    Value val(123.45);
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueAdd(val, 66.6).as<double>(), 190.05, 1.0e-10);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_CLOSE(ValueAdd(66.6, val).as<double>(), 190.05, 1.0e-10);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueAddSelf(val, 66.6).as<double>(), 190.05, 1.0e-10);
+    val = Value(123.45);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_subtract_operator ) {
+    Value val(123.45);
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueSub(val, 66.6).as<double>(), 56.85, 1.0e-10);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_CLOSE(ValueSub(66.6, val).as<double>(), -56.85, 1.0e-10);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueSubSelf(val, 66.6).as<double>(), 56.85, 1.0e-10);
+    val = Value(123.45);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_multiply_operator ) {
+    Value val(123.45);
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueMul(val, 66.6).as<double>(), 8221.77, 1.0e-10);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_CLOSE(ValueMul(66.6, val).as<double>(), 8221.77, 1.0e-10);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueMulSelf(val, 66.6).as<double>(), 8221.77, 1.0e-10);
+    val = Value(123.45);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_divide_operator ) {
+    Value val(123.45);
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueDiv(val, 66.6).as<double>(), 1.853603604, 1.0e-6);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_CLOSE(ValueDiv(66.6, val).as<double>(), 0.539489671, 1.0e-6);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_CLOSE(ValueDivSelf(val, 66.6).as<double>(), 1.853603604, 1.0e-6);
+    val = Value(123.45);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_modulo_operator ) {
+    Value val(123.45);
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_double_head_tail ) {
+    Value val(123.45);
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of strings
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_cast ) {
+    Value val_null("");
+    Value val_char("test string");
+    Value val_str(std::string("test string 2"));
+
+    // String conversion
+
+    BOOST_CHECK_EQUAL(std::string(val_null), "");
+    BOOST_CHECK_EQUAL(std::string(val_char), "test string");
+    BOOST_CHECK_EQUAL(std::string(val_str), "test string 2");
+
+    BOOST_CHECK_EQUAL(val_null.as<std::string>(), "");
+    BOOST_CHECK_EQUAL(val_char.as<std::string>(), "test string");
+    BOOST_CHECK_EQUAL(val_str.as<std::string>(), "test string 2");
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl(val_str.as<ValueList>());
+    BOOST_CHECK_EQUAL(vl.size(), 1);
+    BOOST_CHECK_EQUAL(vl[0].as<std::string>(), "test string 2");
+
+    // And the invalid conversions
+
+    BOOST_CHECK_THROW(val_str.as<bool>(), BadConversion);
+    /// BOOST_CHECK_THROW(val_str.as<long long>(), BadConversion); // This will return zero, not throw
+    BOOST_CHECK_THROW(val_str.as<double>(), BadParameter); // n.b. BadParameter, not BadConversion
+    BOOST_CHECK_THROW(val_str.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val_str.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val_str.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val_str.as<ValueMap>(), BadConversion);
+
+    BOOST_CHECK_THROW(val_null.as<bool>(), BadConversion);
+    /// BOOST_CHECK_THROW(val_null.as<long long>(), BadConversion); // This will return zero, not throw
+    BOOST_CHECK_THROW(val_null.as<double>(), BadParameter); // n.b. BadParameter, not BadConversion`
+    BOOST_CHECK_THROW(val_null.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val_null.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val_null.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val_null.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_cast_bool ) {
+    // Boolean casting for Value(StringContent) types is a bit complicated, as it allows strings to be used to
+    // represent truthy values, but nothing else
+
+    Value val_true1("true");
+    Value val_true2("on");
+    Value val_true3("yes");
+    Value val_true4("1");
+
+    BOOST_CHECK(bool(val_true1));
+    BOOST_CHECK(bool(val_true2));
+    BOOST_CHECK(bool(val_true3));
+    BOOST_CHECK(bool(val_true4));
+
+    BOOST_CHECK(val_true1.as<bool>());
+    BOOST_CHECK(val_true2.as<bool>());
+    BOOST_CHECK(val_true3.as<bool>());
+    BOOST_CHECK(val_true4.as<bool>());
+
+    Value val_false1("false");
+    Value val_false2("off");
+    Value val_false3("no");
+    Value val_false4("0");
+
+    BOOST_CHECK(!bool(val_false1));
+    BOOST_CHECK(!bool(val_false2));
+    BOOST_CHECK(!bool(val_false3));
+    BOOST_CHECK(!bool(val_false4));
+
+    BOOST_CHECK(!val_false1.as<bool>());
+    BOOST_CHECK(!val_false2.as<bool>());
+    BOOST_CHECK(!val_false3.as<bool>());
+    BOOST_CHECK(!val_false4.as<bool>());
+
+    Value val_other("other");
+
+    BOOST_CHECK_THROW(val_other.as<bool>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_vaule_string_cast_numbers ) {
+    Value val_double("123.45");
+    Value val_int("12345");
+    Value val_other("string");
+
+    // Test conversions to integers
+
+    BOOST_CHECK_EQUAL((int)(val_double), 123);
+    BOOST_CHECK_EQUAL((int)(val_int), 12345);
+
+    BOOST_CHECK_EQUAL(val_double.as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val_int.as<long long>(), 12345);
+
+    /// Integer conversion returns zero, rather than throwing, on no matching input
+    /// BOOST_CHECK_THROW(val_other.as<long long>(), std::exception);
+    BOOST_CHECK_EQUAL(val_other.as<long long>(), 0);
+
+    // Test conversions to doubles
+
+    BOOST_CHECK_CLOSE((double)(val_double), 123.45, 1.0e-10);
+    BOOST_CHECK_CLOSE((double)(val_int), 12345.0, 1.0e-10);
+
+    BOOST_CHECK_CLOSE(val_double.as<double>(), 123.45, 1.0e-10);
+    BOOST_CHECK_CLOSE(val_int.as<double>(), 12345.0, 1.0e-10);
+    BOOST_CHECK_THROW(val_other.as<double>(), BadParameter);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_type ) {
+    Value val_null("");
+    Value val_str("This is a test string");
+
+    BOOST_CHECK(val_null.isString());
+    BOOST_CHECK(val_str.isString());
+
+    BOOST_CHECK(!val_null.isNil());
+    BOOST_CHECK(!val_null.isBool());
+    BOOST_CHECK(!val_null.isNumber());
+    BOOST_CHECK(!val_null.isDouble());
+    BOOST_CHECK(!val_null.isList());
+    BOOST_CHECK(!val_null.isMap());
+    BOOST_CHECK(!val_null.isDate());
+    BOOST_CHECK(!val_null.isTime());
+    BOOST_CHECK(!val_null.isDateTime());
+
+    BOOST_CHECK(!val_str.isNil());
+    BOOST_CHECK(!val_str.isBool());
+    BOOST_CHECK(!val_str.isNumber());
+    BOOST_CHECK(!val_str.isDouble());
+    BOOST_CHECK(!val_str.isList());
+    BOOST_CHECK(!val_str.isMap());
+    BOOST_CHECK(!val_str.isDate());
+    BOOST_CHECK(!val_str.isTime());
+    BOOST_CHECK(!val_str.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_comparisons ) {
+    Value val1("a");
+    Value val2("a");
+    Value val3("b");
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+    // Comparison makes use of strcmp
+
+    BOOST_CHECK(val1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val2) == 0);
+    BOOST_CHECK(val2.compare(val1) == 0);
+
+    BOOST_CHECK(val1.compare(val3) == -1);
+    BOOST_CHECK(val3.compare(val1) == 1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value(123)) < 0);
+    BOOST_CHECK(val1.compare(Value(123.45)) < 0);
+    BOOST_CHECK(val1.compare(Value(ValueMap())) > 0);
+    BOOST_CHECK(val1.compare(Value(Date(2016, 3, 30))) > 0);
+    BOOST_CHECK(val1.compare(ValueList()) > 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value(123).compare(val1) > 0);
+    BOOST_CHECK(Value(123.45).compare(val1) > 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val1) < 0);
+    BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val1) < 0);
+    BOOST_CHECK(Value(ValueList()).compare(val1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_index_operator ) {
+    // No indexing operations should work on a string...
+
+    Value val_char("test string 1");
+    Value val_str(std::string("test string 2"));
+
+    BOOST_CHECK_THROW(val_char["idx"], BadOperator);
+    BOOST_CHECK_THROW(val_char[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val_char[123], BadOperator);
+    BOOST_CHECK_THROW(val_char[Value(123)], BadOperator);
+
+    BOOST_CHECK_THROW(val_str["idx"], BadOperator);
+    BOOST_CHECK_THROW(val_str[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val_str[123], BadOperator);
+    BOOST_CHECK_THROW(val_str[Value(123)], BadOperator);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK_THROW(val_char.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val_char.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val_char.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val_char.contains(Value(123)), BadOperator);
+
+    BOOST_CHECK_THROW(val_str.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val_str.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val_str.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val_str.contains(Value(123)), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_add_operator ) {
+    Value val("test string");
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAdd(val, "hi").as<std::string>(), "test stringhi");
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAdd("hi", val), "hitest string");
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_EQUAL(ValueAddSelf(val, "hi").as<std::string>(), "test stringhi");
+    val = Value("test string");
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_subtract_operator ) {
+    Value val("test string");
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_multiply_operator ) {
+    Value val("test string");
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_divide_operator ) {
+    Value val("test string");
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_modulo_operator ) {
+    Value val("test string");
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_string_head_tail ) {
+    Value val("this is a test string");
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of ValueMaps
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_cast ) {
+    // A ValueMap is just a std::map<Value, Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueMap vm;
+    vm[123] = 123;
+    vm["abc"] = "abc";
+    vm[Value(123.45)] = 123.45;
+    vm[Value(true)] = false;
+
+    Value val(vm);
+
+    // Extract the ValueMap
+    // n.b. We cannot compare eqality of ValueMaps, as the internal Values have been copied, and as a result the
+    //      operator== will return false, as it depends only on the memory address of the internal Content.
+
+    BOOST_CHECK_EQUAL(((ValueMap)val)[123].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val.as<ValueMap>()[123].as<long long>(), 123);
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl(val.as<ValueList>());
+    BOOST_CHECK_EQUAL(vl.size(), 1);
+    BOOST_CHECK_EQUAL(vl[0].as<ValueMap>()[123].as<long long>(), 123);
+
+    // And the invalid conversions
+
+    BOOST_CHECK_THROW(val.as<bool>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<long long>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<double>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<std::string>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<DateTime>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_type ) {
+    // A ValueMap is just a std::map<Value, Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueMap vm;
+    vm[123] = 123;
+    vm["abc"] = "abc";
+    vm[Value(123.45)] = 123.45;
+    vm[Value(true)] = false;
+
+    Value val(vm);
+
+    BOOST_CHECK(val.isMap());
+
+    BOOST_CHECK(!val.isNil());
+    BOOST_CHECK(!val.isBool());
+    BOOST_CHECK(!val.isNumber());
+    BOOST_CHECK(!val.isDouble());
+    BOOST_CHECK(!val.isString());
+    BOOST_CHECK(!val.isList());
+    BOOST_CHECK(!val.isDate());
+    BOOST_CHECK(!val.isTime());
+    BOOST_CHECK(!val.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_comparisons ) {
+    // A ValueMap is just a std::map<Value, Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueMap vm;
+    vm[123] = 123;
+
+    ValueMap vm2;
+    vm2["abc"] = "abc";
+
+    Value val1(vm);
+    Value val2(vm);
+    Value val3(vm2);
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+    // Comparison makes use of strcmp
+
+    BOOST_CHECK(val1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val2) == 0);
+    BOOST_CHECK(val2.compare(val1) == 0);
+
+    BOOST_CHECK(val1.compare(val3) == 1);
+    BOOST_CHECK(val3.compare(val1) == -1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value(123)) < 0);
+    BOOST_CHECK(val1.compare(Value(123.45)) < 0);
+    BOOST_CHECK(val1.compare(Value(std::string("test string"))) < 0);
+    BOOST_CHECK(val1.compare(ValueList()) < 0);
+    BOOST_CHECK(val1.compare(Value(Date(2016, 3, 30))) > 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value(123).compare(val1) > 0);
+    BOOST_CHECK(Value(123.45).compare(val1) > 0);
+    BOOST_CHECK(Value(std::string("test string")).compare(val1) > 0);
+    BOOST_CHECK(Value(ValueList()).compare(val1) > 0);
+
+    /// This is currently correct in MapContent.h
+    /// BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_index_operator ) {
+    // A ValueMap is just a std::map<Value, Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueMap vm;
+    vm[123] = 456;
+    vm["abc"] = "def";
+    vm[Value(123.45)] = 543.21;
+    vm[Value(true)] = false;
+
+    Value val(vm);
+
+    // Check with existent keys of the various types
+
+    BOOST_CHECK_EQUAL(val[123].as<long long>(), 456);
+    BOOST_CHECK_EQUAL(val["abc"].as<std::string>(), "def");
+    BOOST_CHECK_EQUAL(val[std::string("abc")].as<std::string>(), "def");
+
+    /// None of these seem to work with boolean indices
+    /// BOOST_CHECK_EQUAL(val[true].as<bool>(), false);
+
+    BOOST_CHECK_EQUAL(val[Value(123)].as<long long>(), 456);
+    BOOST_CHECK_EQUAL(val[Value("abc")].as<std::string>(), "def");
+    BOOST_CHECK_EQUAL(val[Value(std::string("abc"))].as<std::string>(), "def");
+    BOOST_CHECK_CLOSE(val[Value(123.45)].as<double>(), 543.21, 1.0e-10);
+
+    /// Indexing by Value(bool) doesn't seem to work
+    /// BOOST_CHECK_EQUAL(val[Value(true)].as<bool>(), false);
+
+    // And with values that don't exist
+
+    /// This code should not work!!! const has gone screwey
+    ValueMap vm2;
+    const Value cv(vm2);
+    cv[10];
+    Log::info() << cv << std::endl;
+
+    /// BOOST_CHECK(!cv.contains(10));
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK(val.contains(123));
+    BOOST_CHECK(val.contains("abc"));
+    BOOST_CHECK(val.contains(std::string("abc")));
+    BOOST_CHECK(val.contains(Value(123.45)));
+    /// BOOST_CHECK(val.contains(Value(true)));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_add_operator ) {
+    // There are no valid ValueMap addition operations.
+
+    ValueMap vm;
+    Value val(vm);
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_subtract_operator ) {
+    // There are no valid ValueMap subtraction operations.
+
+    ValueMap vm;
+    Value val(vm);
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_multiply_operator ) {
+    // There are no valid ValueMap multiplication operations.
+
+    ValueMap vm;
+    Value val(vm);
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_divide_operator ) {
+    // There are no valid ValueMap division operations.
+
+    ValueMap vm;
+    Value val(vm);
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_modulo_operator ) {
+    // There are no valid ValueMap modulo operations.
+
+    ValueMap vm;
+    Value val(vm);
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_map_head_tail ) {
+    ValueMap vm;
+    vm[123] = 456;
+
+    Value val(vm);
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of ValueMaps
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_cast ) {
+    // A ValueMap is just a std::list<Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    vl.push_back(1234.56);
+    vl.push_back(false);
+
+    Value val(vl);
+
+    // Extract the ValueList
+    // n.b. We cannot compare eqality of ValueLists, as the internal Values have been copied, and as a result the
+    //      operator== will return false, as it depends only on the memory address of the internal Content.
+
+    ValueList casted_vl = val;
+    BOOST_CHECK_EQUAL(casted_vl[0].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val.as<ValueList>()[0].as<long long>(), 123);
+
+    // And the invalid conversions
+
+    BOOST_CHECK_THROW(val.as<bool>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<long long>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<double>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<std::string>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<Date>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_type ) {
+    // A ValueList is just a std::list<Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    vl.push_back(1234.56);
+    vl.push_back(false);
+
+    Value val(vl);
+
+    BOOST_CHECK(val.isList());
+
+    BOOST_CHECK(!val.isNil());
+    BOOST_CHECK(!val.isBool());
+    BOOST_CHECK(!val.isNumber());
+    BOOST_CHECK(!val.isDouble());
+    BOOST_CHECK(!val.isString());
+    BOOST_CHECK(!val.isMap());
+    BOOST_CHECK(!val.isDate());
+    BOOST_CHECK(!val.isTime());
+    BOOST_CHECK(!val.isDateTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_comparisons ) {
+    // A ValueList is just a std::list<Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueList vl;
+    vl.push_back(123);
+
+    ValueList vl2;
+    vl2.push_back(321);
+
+    Value val1(vl);
+    Value val2(vl);
+    Value val3(vl2);
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+    // Comparison makes use of strcmp
+
+    BOOST_CHECK(val1.compare(val1) == 0);
+    BOOST_CHECK(val1.compare(val2) == 0);
+    BOOST_CHECK(val2.compare(val1) == 0);
+
+    BOOST_CHECK(val1.compare(val3) == -1);
+    BOOST_CHECK(val3.compare(val1) == 1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value(123)) < 0);
+    BOOST_CHECK(val1.compare(Value(123.45)) < 0);
+    BOOST_CHECK(val1.compare(Value(std::string("test string"))) < 0);
+    BOOST_CHECK(val1.compare(ValueMap()) > 0);
+    BOOST_CHECK(val1.compare(Value(Date(2016, 3, 30))) > 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value(123).compare(val1) > 0);
+    BOOST_CHECK(Value(123.45).compare(val1) > 0);
+    BOOST_CHECK(Value(std::string("test string")).compare(val1) > 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val1) < 0);
+
+    /// This is currently correct in MapContent.h
+    /// BOOST_CHECK(Value(Date(2016, 3, 30)).compare(val1) < 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_index_operator ) {
+    // A ValueList is just a std::list<Value>. No point in testing the functionality of stl. Just test what
+    // it does when wrapped.
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    vl.push_back(1234.56);
+    vl.push_back(false);
+
+    Value val(vl);
+
+    // Check with existent keys of the various types
+
+    BOOST_CHECK_EQUAL(val[0].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val[1].as<std::string>(), "abc");
+    BOOST_CHECK_CLOSE(val[2].as<double>(), 1234.56, 1.0e-10);
+    BOOST_CHECK_EQUAL(val[3].as<bool>(), false);
+
+    BOOST_CHECK_EQUAL(int(val[Value(0)]), 123);
+    BOOST_CHECK_EQUAL(std::string(val[Value(1)]), "abc");
+    BOOST_CHECK_CLOSE(double(val[Value(2)]), 1234.56, 1.0e-10);
+    BOOST_CHECK_EQUAL(bool(val[Value(3)]), false);
+
+    // And with values that don't exist
+
+    BOOST_CHECK_THROW(val[-1], AssertionFailed);
+    BOOST_CHECK_THROW(val[4], AssertionFailed);
+    BOOST_CHECK_THROW(val[Value(-1)], AssertionFailed);
+    BOOST_CHECK_THROW(val[Value(4)], AssertionFailed);
+
+    /// Value(std::string) silently casts to 0, which means this returns val[0] spuriously
+    /// BOOST_CHECK_THROW(val["hello"], AssertionFailed);
+    /// BOOST_CHECK_THROW(val[std::string("hello")], AssertionFailed);
+    /// BOOST_CHECK_THROW(val[Value("hello")], AssertionFailed);
+
+    // Value(bool) automagically converts to a long, so these return elements 1, 0 respectively...
+    // BOOST_CHECK_THROW(val[Value(true)], BadConversion);
+    // BOOST_CHECK_THROW(val[Value(false)], BadConversion);
+
+    BOOST_CHECK_THROW(val[Value(666.66)], BadConversion);
+    BOOST_CHECK_THROW(val[Value(ValueList())], BadConversion);
+    BOOST_CHECK_THROW(val[Value(ValueMap())], BadConversion);
+    BOOST_CHECK_THROW(val[Value(Date(2016, 3, 31))], BadConversion);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK(!val.contains(-1));
+    BOOST_CHECK(val.contains(0));
+    BOOST_CHECK(val.contains(1));
+    BOOST_CHECK(val.contains(2));
+    BOOST_CHECK(val.contains(3));
+    BOOST_CHECK(!val.contains(4));
+
+    BOOST_CHECK(!val.contains(Value(-1)));
+    BOOST_CHECK(val.contains(Value(0)));
+    BOOST_CHECK(val.contains(Value(1)));
+    BOOST_CHECK(val.contains(Value(2)));
+    BOOST_CHECK(val.contains(Value(3)));
+    BOOST_CHECK(!val.contains(Value(4)));
+
+    /// Same oddities as above...
+    /// BOOST_CHECK(!val.contains("hello"));
+    /// BOOST_CHECK(!val.contains(std::string("hello")));
+    /// BOOST_CHECK(!val.contains(Value("hello")));
+
+    BOOST_CHECK_THROW(val.contains(Value(666.66)), BadConversion);
+    BOOST_CHECK_THROW(val.contains(Value(ValueList())), BadConversion);
+    BOOST_CHECK_THROW(val.contains(Value(ValueMap())), BadConversion);
+    BOOST_CHECK_THROW(val.contains(Value(Date(2016, 3, 31))), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_add_operator ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    Value val(vl);
+
+    ValueList vl2;
+    vl2.push_back(true);
+    vl2.push_back(Date(2016, 3, 31));
+    Value val2(vl2);
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+
+    Value tmp1 = val + val2;
+    BOOST_CHECK_EQUAL(tmp1.as<ValueList>().size(), 4);
+    BOOST_CHECK_EQUAL(tmp1[0].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(tmp1[1].as<std::string>(), "abc");
+    BOOST_CHECK_EQUAL(tmp1[2].as<bool>(), true);
+    BOOST_CHECK_EQUAL(tmp1[3].as<Date>(), Date(2016, 3, 31));
+
+    Value tmp2 = val2 + val;
+    BOOST_CHECK_EQUAL(tmp2.as<ValueList>().size(), 4);
+    BOOST_CHECK_EQUAL(tmp2[0].as<bool>(), true);
+    BOOST_CHECK_EQUAL(tmp2[1].as<Date>(), Date(2016, 3, 31));
+    BOOST_CHECK_EQUAL(tmp2[2].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(tmp2[3].as<std::string>(), "abc");
+
+    val += val2;
+    BOOST_CHECK_EQUAL(val.as<ValueList>().size(), 4);
+    BOOST_CHECK_EQUAL(val[0].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val[1].as<std::string>(), "abc");
+    BOOST_CHECK_EQUAL(val[2].as<bool>(), true);
+    BOOST_CHECK_EQUAL(val[3].as<Date>(), Date(2016, 3, 31));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_subtract_operator ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    Value val(vl);
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_multiply_operator ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    Value val(vl);
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_divide_operator ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    Value val(vl);
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_modulo_operator ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    Value val(vl);
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_list_head_tail ) {
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back(666.66);
+    vl.push_back(true);
+    vl.push_back("test str");
+
+    Value val(vl);
+
+    Log::info() << "H" << val << " - " << val.head() << std::endl;
+    BOOST_CHECK_EQUAL(val.head().as<long long>(), 123);
+    Log::info() << "T" << val << " - " << val.tail() << std::endl;
+    /// BOOST_CHECK_EQUAL(val.tail().as<std::string>(), "test str");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test the behaviour of Dates
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_cast ) {
+    Value val(Date(2016, 3, 31));
+
+    //
+    // Access and conversion
+    //
+
+    Date tmp = val;
+    BOOST_CHECK_EQUAL(tmp, Date(2016, 3, 31));
+    BOOST_CHECK_EQUAL(val.as<Date>(), Date(2016, 3, 31));
+
+    // ValueList is a bit of an odd one --> it just puts the value in a list of one element...
+
+    ValueList vl = val;
+    BOOST_CHECK_EQUAL(vl.size(), 1);
+    BOOST_CHECK_EQUAL(vl[0].as<Date>(), Date(2016, 3, 31));
+
+    // And all the invalid conversions
+
+    BOOST_CHECK_THROW(val.as<bool>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<double>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<long long>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<std::string>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<Time>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<DateTime>(), BadConversion);
+    BOOST_CHECK_THROW(val.as<ValueMap>(), BadConversion);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_type ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK(val.isDate());
+
+    BOOST_CHECK(!val.isNil());
+    BOOST_CHECK(!val.isBool());
+    BOOST_CHECK(!val.isNumber());
+    BOOST_CHECK(!val.isDouble());
+    BOOST_CHECK(!val.isString());
+    BOOST_CHECK(!val.isList());
+    BOOST_CHECK(!val.isMap());
+    BOOST_CHECK(!val.isDateTime());
+    BOOST_CHECK(!val.isTime());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_comparisons ) {
+    Value val1(Date(2016, 3, 31));
+    Value val2(Date(2016, 3, 31));
+    Value val3(Date(2016, 4, 30));
+
+    // n.b. These comparisons are designed to define a well defined order between different data types
+    // bool [false < true] > number > string > nil > list > map > Date > Time > DateTime
+
+    // Check comparisons with same type of data
+    // Comparison makes use of strcmp
+
+    /// Value(Date) compare function is rather broken...
+    /// BOOST_CHECK(val1.compare(val1) == 0);
+    /// BOOST_CHECK(val1.compare(val2) == 0);
+    /// BOOST_CHECK(val2.compare(val1) == 0);
+
+    /// BOOST_CHECK(val1.compare(val3) == -1);
+    BOOST_CHECK(val3.compare(val1) == 1);
+
+    // Check comparisons with other types of data.
+
+    BOOST_CHECK(val1.compare(Value(true)) < 0);
+    BOOST_CHECK(val1.compare(Value(123)) < 0);
+    BOOST_CHECK(val1.compare(Value(123.45)) < 0);
+    BOOST_CHECK(val1.compare(Value("testing")) < 0);
+    /// There is a bug in the ValueMap implementation, so this would fail
+    /// BOOST_CHECK(val1.compare(Value(ValueMap())) < 0);
+    BOOST_CHECK(val1.compare(ValueList()) < 0);
+
+    BOOST_CHECK(Value(true).compare(val1) > 0);
+    BOOST_CHECK(Value(123).compare(val1) > 0);
+    BOOST_CHECK(Value(123.45).compare(val1) > 0);
+    BOOST_CHECK(Value("testing").compare(val1) > 0);
+    BOOST_CHECK(Value(ValueMap()).compare(val1) > 0);
+    BOOST_CHECK(Value(ValueList()).compare(val1) > 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_index_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(val["idx"], BadOperator);
+    BOOST_CHECK_THROW(val[std::string("idx")], BadOperator);
+    BOOST_CHECK_THROW(val[123], BadOperator);
+    BOOST_CHECK_THROW(val[Value(123)], BadOperator);
+
+    // Test the matching contains() function too
+
+    BOOST_CHECK_THROW(val.contains("idx"), BadOperator);
+    BOOST_CHECK_THROW(val.contains(std::string("idx")), BadOperator);
+    BOOST_CHECK_THROW(val.contains(123), BadOperator);
+    BOOST_CHECK_THROW(val.contains(Value(123)), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_add_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(ValueAdd(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAdd(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueAdd(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueAddSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, Date(2016, 3, 31)), BadOperator);
+    val = Date(2016, 3, 31);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueAddSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_subtract_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(ValueSub(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, "hi"), BadOperator);
+    /// The sign of the following test is wrong. SHOULD be +2
+    BOOST_CHECK_EQUAL(ValueSub(val, Date(2016, 3, 29)).as<long long>(), -2);
+    BOOST_CHECK_THROW(ValueSub(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSub(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub("hi", val), BadOperator);
+    /// The sign of the following test is wrong. SHOULD be -2
+    BOOST_CHECK_EQUAL(ValueSub(Date(2016, 3, 29), val).as<long long>(), 2);
+    BOOST_CHECK_THROW(ValueSub(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueSub(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueSubSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, "hi"), BadOperator);
+    /// The sign of the following test is wrong. SHOULD be +2
+    BOOST_CHECK_EQUAL(ValueSubSelf(val, Date(2016, 3, 29)).as<long long>(), -2);
+    val = Date(2016, 3, 31);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueSubSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_multiply_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(ValueMul(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMul(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMul(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMulSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMulSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_divide_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(ValueDiv(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDiv(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueDiv(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueDivSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueDivSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_modulo_operator ) {
+    Value val(Date(2016, 3, 31));
+
+    BOOST_CHECK_THROW(ValueMod(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(val, ValueMap()), BadOperator);
+
+    BOOST_CHECK_THROW(ValueMod(true, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(1234, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(66.6, val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod("hi", val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(Date(2016, 3, 31), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueList(), val), BadOperator);
+    BOOST_CHECK_THROW(ValueMod(ValueMap(), val), BadOperator);
+
+    BOOST_CHECK_THROW(ValueModSelf(val, true), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 1234), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, 66.6), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, "hi"), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, Date(2016, 3, 31)), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueList()), BadOperator);
+    BOOST_CHECK_THROW(ValueModSelf(val, ValueMap()), BadOperator);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_date_head_tail ) {
+    Value val(Date(2016, 3, 31));
+
+    /// BOOST_CHECK_THROW(val.head(), AssertationError);
+    /// BOOST_CHECK_THROW(val.tail(), AssertationError);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+//
+// Test list/map helper functions
+//
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_makelist ) {
+    // Test the trivial version
+
+    Value val1 = Value::makeList();
+
+    BOOST_CHECK(val1.isList());
+    BOOST_CHECK_EQUAL(val1.as<ValueList>().size(), 0);
+
+    // Can we wrap an arbitrary ValueList?
+
+    ValueList vl;
+    vl.push_back(123);
+    vl.push_back("abc");
+    vl.push_back(1234.56);
+    vl.push_back(false);
+
+    Value val2 = Value::makeList(vl);
+
+    BOOST_CHECK(val2.isList());
+
+    BOOST_CHECK_EQUAL(val2.as<ValueList>().size(), 4);
+    BOOST_CHECK_EQUAL(val2[0].as<long long>(), 123);
+    BOOST_CHECK_EQUAL(val2[1].as<std::string>(), "abc");
+    BOOST_CHECK_CLOSE(val2[2].as<double>(), 1234.56, 1.0e-10);
+    BOOST_CHECK_EQUAL(val2[3].as<bool>(), false);
+
+    // Applied to a value, it puts the element in first
+
+    Value val3 = Value::makeList(val2);
+
+    BOOST_CHECK(val3.isList());
+
+    BOOST_CHECK_EQUAL(val3.as<ValueList>().size(), 1);
+    BOOST_CHECK(val3[0].isList());
+    BOOST_CHECK_EQUAL(val3[0].as<ValueList>()[0].as<long long>(), 123);
+
+    // This should work with all the things that can cast into values
+
+    Value val4 = Value::makeList(1234);
+
+    BOOST_CHECK(val4.isList());
+    BOOST_CHECK_EQUAL(val4.as<ValueList>().size(), 1);
+    BOOST_CHECK_EQUAL(val4[0].as<long long>(), 1234);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_makemap ) {
+    // Test the trivial version
+
+    Value val1 = Value::makeMap();
+
+    BOOST_CHECK(val1.isMap());
+    BOOST_CHECK_EQUAL(val1.as<ValueMap>().size(), 0);
+
+    // Can we wrap an arbitrary ValueMap?
+
+    ValueMap vm;
+    vm[123] = 456;
+    vm["abc"] = "def";
+    vm[1234.56] = 666.66;
+    vm[true] = false;
+
+    Value val2 = Value::makeMap(vm);
+
+    BOOST_CHECK(val2.isMap());
+
+    BOOST_CHECK_EQUAL(val2.as<ValueMap>().size(), 4);
+    BOOST_CHECK_EQUAL(val2[123].as<long long>(), 456);
+    BOOST_CHECK_EQUAL(val2["abc"].as<std::string>(), "def");
+    BOOST_CHECK_CLOSE(val2[Value(1234.56)].as<double>(), 666.66, 1.0e-10);
+    /// Cannot index using bools
+    /// BOOST_CHECK_EQUAL(val2[3].as<bool>(), false);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_makelist_vector ) {
+    // n.b. This is templated, so will work for a std::vector<T> for any T for which a Value can be constructed.
+
+    std::vector<int> vint;
+    for (int i = 99; i > 0; i -= 11)
+        vint.push_back(i);
+
+    Value val = makeVectorValue(vint);
+
+    BOOST_CHECK(val.isList());
+    BOOST_CHECK_EQUAL(val.as<ValueList>().size(), 9);
+    BOOST_CHECK_EQUAL(val[4].as<long long>(), 55);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_makelist_list ) {
+    // n.b. This is templated, so will work for a std::list<T> for any T for which a Value can be constructed.
+
+    std::list<int> lint;
+    for (int i = 99; i > 0; i -= 11)
+        lint.push_back(i);
+
+    Value val = makeVectorValue(lint);
+
+    BOOST_CHECK(val.isList());
+    BOOST_CHECK_EQUAL(val.as<ValueList>().size(), 9);
+    BOOST_CHECK_EQUAL(val[4].as<long long>(), 55);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_CASE( test_eckit_value_wrapper_types ) {
+    // There are some constructors that have been added for easy conversion, that aren't actually part of the
+    // internal interface
+
+    // Length type
+
+    Value val_length(Length(12345));
+
+    BOOST_CHECK(val_length.isNumber());
+
+    BOOST_CHECK(!val_length.isNil());
+    BOOST_CHECK(!val_length.isBool());
+    BOOST_CHECK(!val_length.isDouble());
+    BOOST_CHECK(!val_length.isString());
+    BOOST_CHECK(!val_length.isList());
+    BOOST_CHECK(!val_length.isMap());
+    BOOST_CHECK(!val_length.isDate());
+    BOOST_CHECK(!val_length.isTime());
+    BOOST_CHECK(!val_length.isDateTime());
+
+    BOOST_CHECK_EQUAL(int(val_length), 12345);
+    BOOST_CHECK_EQUAL(val_length.as<long long>(), 12345);
+
+    // Offset type
+
+    Value val_offset(Offset(54321));
+
+    BOOST_CHECK(val_offset.isNumber());
+
+    BOOST_CHECK(!val_offset.isNil());
+    BOOST_CHECK(!val_offset.isBool());
+    BOOST_CHECK(!val_offset.isDouble());
+    BOOST_CHECK(!val_offset.isString());
+    BOOST_CHECK(!val_offset.isList());
+    BOOST_CHECK(!val_offset.isMap());
+    BOOST_CHECK(!val_offset.isDate());
+    BOOST_CHECK(!val_offset.isTime());
+    BOOST_CHECK(!val_offset.isDateTime());
+
+    BOOST_CHECK_EQUAL(int(val_offset), 54321);
+    BOOST_CHECK_EQUAL(val_offset.as<long long>(), 54321);
+
+    // PathName type
+
+    Value val_pathname(PathName("/usr/bin"));
+
+    BOOST_CHECK(val_pathname.isString());
+
+    BOOST_CHECK(!val_pathname.isNil());
+    BOOST_CHECK(!val_pathname.isBool());
+    BOOST_CHECK(!val_pathname.isDouble());
+    BOOST_CHECK(!val_pathname.isNumber());
+    BOOST_CHECK(!val_pathname.isList());
+    BOOST_CHECK(!val_pathname.isMap());
+    BOOST_CHECK(!val_pathname.isDate());
+    BOOST_CHECK(!val_pathname.isTime());
+    BOOST_CHECK(!val_pathname.isDateTime());
+
+    BOOST_CHECK_EQUAL(std::string(val_pathname), "/usr/bin");
+    BOOST_CHECK_EQUAL(val_pathname.as<std::string>(), "/usr/bin");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace test
+} // namespace eckit
diff --git a/eckit/src/tests/value/test_value_params.cc b/eckit/src/tests/value/test_value_params.cc
new file mode 100644
index 0000000..a17f570
--- /dev/null
+++ b/eckit/src/tests/value/test_value_params.cc
@@ -0,0 +1,385 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_value_params
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/value/CompositeParams.h"
+#include "eckit/value/DispatchParams.h"
+#include "eckit/value/Params.h"
+#include "eckit/value/Properties.h"
+#include "eckit/value/ScopeParams.h"
+
+#include "eckit/testing/Setup.h"
+
+#include "AnyKeyParams.h"
+
+using namespace std;
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace eckit_test {
+
+const int imax = numeric_limits<int>::max();
+const unsigned int uimax = numeric_limits<unsigned int>::max();
+const long long llmax = numeric_limits<long long>::max();
+const unsigned long long ullmax = numeric_limits<unsigned long long>::max();
+const double dmax = numeric_limits<double>::max();
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TestParams : public DispatchParams<TestParams> {
+public:
+    TestParams(const std::string& payload) : payload_(payload) {
+        dispatch_["foo"] = &TestParams::getPayload;
+    }
+    TestParams( Stream& s ) {
+        dispatch_["foo"] = &TestParams::getPayload;
+        s >> payload_;
+    }
+
+private:
+    Params::value_t getPayload( const Params::key_t& key ) const {
+        return payload_;
+    }
+
+    friend void encode( const TestParams&, Stream& );
+
+    string payload_;
+};
+
+void encode( const TestParams& p, Stream& s )
+{
+    s << p.payload_;
+}
+
+Params::Factory<TestParams> testParamsFactory;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+struct PropertiesFixture
+{
+    PropertiesFixture()
+      : p( Properties()
+           .set("bool", true)
+           .set("int", imax)
+           .set("unsigned int", uimax)
+           .set("long long", llmax)
+           .set("unsigned long long", ullmax)
+           .set("double", dmax)
+           .set("string", "foo")
+           .set("Length", Length(42))
+           .set("Date", Date(2015, 2, 1))
+           .set("PathName", PathName("/var/tmp")) ) {}
+
+    Params p;
+};
+
+struct CompositeParamsFixture
+{
+    CompositeParamsFixture()
+      : p( CompositeParams()
+           .push_back(Params(Properties().set("bool", true)))
+           .push_back(Params(Properties().set("int", imax)))
+           .push_back(Params(Properties().set("unsigned int", uimax)))
+           .push_back(Params(Properties().set("long long", llmax)))
+           .push_back(Params(Properties().set("unsigned long long", ullmax)))
+           .push_back(Params(Properties().set("double", dmax)))
+           .push_back(Params(Properties().set("string", "foo")))
+           .push_back(Params(Properties().set("Length", Length(42))))
+           .push_back(Params(Properties().set("Date", Date(2015, 2, 1))))
+           .push_back(Params(Properties().set("PathName", PathName("/var/tmp")))) ) {}
+
+    Params p;
+};
+
+struct ListParamsFixture
+{
+    ListParamsFixture()
+      : p( CompositeParams(ListParamsFixture::pl()) ) {}
+
+    static Params::List& pl () {
+        static Params::List l;
+        if (!l.size()) {
+            l.push_back(Params(Properties().set("bool", true)));
+            l.push_back(Params(Properties().set("int", imax)));
+            l.push_back(Params(Properties().set("unsigned int", uimax)));
+            l.push_back(Params(Properties().set("long long", llmax)));
+            l.push_back(Params(Properties().set("unsigned long long", ullmax)));
+            l.push_back(Params(Properties().set("double", dmax)));
+            l.push_back(Params(Properties().set("string", "foo")));
+            l.push_back(Params(Properties().set("Length", Length(42))));
+            l.push_back(Params(Properties().set("Date", Date(2015, 2, 1))));
+            l.push_back(Params(Properties().set("PathName", PathName("/var/tmp"))));
+        }
+        return l;
+    }
+
+    Params p;
+};
+
+struct ScopedParamsFixture
+{
+    ScopedParamsFixture()
+      : p( ScopeParams("scope", Params(Properties().set("foo", "bar"))) ) {}
+
+    Params p;
+};
+
+struct CompositeScopedParamsFixture
+{
+    CompositeScopedParamsFixture()
+      : p( CompositeParams()
+           .push_back(Params(ScopeParams("user",
+                                         Params(Properties().set("resol", 100)))))
+           .push_back(Params(ScopeParams("default",
+                                         Params(Properties().set("resol", 200))))) ) {}
+
+    Params p;
+};
+
+struct DispatchParamsFixture
+{
+    DispatchParamsFixture()
+      : p( TestParams("bar") ) {}
+
+    Params p;
+};
+
+struct AnyKeyParamsFixture
+{
+    AnyKeyParamsFixture()
+      : p( AnyKeyParams("foo") ) {}
+
+    Params p;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void test_keys(const Params& p)
+{
+    BOOST_CHECK( p.has("bool") );
+    BOOST_CHECK( p.has("int") );
+    BOOST_CHECK( p.has("unsigned int") );
+    BOOST_CHECK( p.has("long long") );
+    BOOST_CHECK( p.has("unsigned long long") );
+    BOOST_CHECK( p.has("double") );
+    BOOST_CHECK( p.has("string") );
+    BOOST_CHECK( p.has("Length") );
+    BOOST_CHECK( p.has("Date") );
+    BOOST_CHECK( p.has("PathName") );
+    BOOST_CHECK( !p.has("foo") );
+    BOOST_CHECK_THROW( p["foo"], BadParameter );
+}
+
+void test_vals(const Params& p)
+{
+    BOOST_CHECK_EQUAL((bool)p["bool"], true);
+    BOOST_CHECK_EQUAL((int)p["int"], imax);
+    BOOST_CHECK_EQUAL((unsigned int)p["unsigned int"], uimax);
+    BOOST_CHECK_EQUAL((long long)p["long long"], llmax);
+    BOOST_CHECK_EQUAL((unsigned long long)p["unsigned long long"], ullmax);
+    BOOST_CHECK_EQUAL((double)p["double"], dmax);
+    BOOST_CHECK_EQUAL(p["string"], "foo");
+    BOOST_CHECK_EQUAL(p["Length"], Length(42));
+    BOOST_CHECK(p["Date"].compare(Date(2015, 2, 1))); // FIXME: equality check fails
+    BOOST_CHECK_EQUAL(p["PathName"], PathName("/var/tmp"));
+}
+
+void test_scope(const Params& p)
+{
+    BOOST_CHECK( p.has("scope.foo") );
+    BOOST_CHECK( !p.has("foo") );
+    BOOST_CHECK_EQUAL( p["scope.foo"], "bar" );
+    BOOST_CHECK_THROW( p["foo"], BadParameter );
+}
+
+void test_composite_scope(const Params& p)
+{
+    BOOST_CHECK( p.has("user.resol") );
+    BOOST_CHECK( p.has("default.resol") );
+    BOOST_CHECK( !p.has("resol") );
+    BOOST_CHECK_EQUAL( (uint)p["user.resol"], 100 );
+    BOOST_CHECK_EQUAL( (uint)p["default.resol"], 200 );
+}
+
+void test_dispatch(const Params& p)
+{
+    BOOST_CHECK( p.has("foo") );
+    BOOST_CHECK( !p.has("bar") );
+    BOOST_CHECK_EQUAL( p["foo"], "bar" );
+}
+
+void test_custom(const Params& p)
+{
+    BOOST_CHECK( p.has("foo") );
+    BOOST_CHECK( p.has("bar") );
+    BOOST_CHECK_EQUAL( p["foo"], "foo" );
+    BOOST_CHECK_EQUAL( p["bar"], "foo" );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Params stream_to_from_file(const Params& p)
+{
+    PathName filename = PathName::unique( "data" );
+    std::string filepath = filename.asString();
+    {
+        FileStream sout( filepath.c_str(), "w" );
+        sout << p;
+    }
+    {
+        FileStream sin( filepath.c_str(), "r" );
+        return Params(Params::decode(sin));
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_value_params )
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_properties_params, PropertiesFixture ) {
+    BOOST_TEST_MESSAGE("Initialize Properties");
+    BOOST_TEST_MESSAGE("Params: " << p);
+    test_keys(p);
+    test_vals(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_properties_params_streaming, PropertiesFixture ) {
+    BOOST_TEST_MESSAGE("Stream Properties");
+    BOOST_TEST_MESSAGE("original: " << p);
+    Params params = stream_to_from_file(p);
+    BOOST_TEST_MESSAGE("streamed: " << params);
+    test_keys(params);
+    test_vals(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_params, CompositeParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize CompositeParams from Properties");
+    BOOST_TEST_MESSAGE("Params: " << p);
+    test_keys(p);
+    test_vals(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_params_streaming, CompositeParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream CompositeParams initialised from Properties");
+    BOOST_TEST_MESSAGE("original: " << p);
+    Params params = stream_to_from_file(p);
+    BOOST_TEST_MESSAGE("streamed: " << params);
+    test_keys(params);
+    test_vals(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_params_list, ListParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize CompositeParams from list of Properties");
+    BOOST_TEST_MESSAGE("Params: " << p);
+    test_keys(p);
+    test_vals(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_params_list_streaming, ListParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream CompositeParams initialised from list of Properties");
+    BOOST_TEST_MESSAGE("original: " << p);
+    Params params = stream_to_from_file(p);
+    BOOST_TEST_MESSAGE("streamed: " << params);
+    test_keys(params);
+    test_vals(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_scope_params, ScopedParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize ScopedParams");
+    BOOST_TEST_MESSAGE("Params: " << p);
+    test_scope(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_scope_params_streaming, ScopedParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream ScopedParams");
+    BOOST_TEST_MESSAGE("original: " << p);
+    Params params = stream_to_from_file(p);
+    BOOST_TEST_MESSAGE("streamed: " << params);
+    test_scope(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_scope_params, CompositeScopedParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize CompositeParams from ScopedParams");
+    BOOST_TEST_MESSAGE("Params: " << p);
+    test_composite_scope(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_composite_scope_params_streaming, CompositeScopedParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream CompositeParams initialised from ScopedParams");
+    BOOST_TEST_MESSAGE("original: " << p);
+    Params params = stream_to_from_file(p);
+    BOOST_TEST_MESSAGE("streamed: " << params);
+    test_composite_scope(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_dispatch_params, DispatchParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize DispatchParams");
+    test_dispatch(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_dispatch_params_streaming, DispatchParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream DispatchParams");
+    Params params = stream_to_from_file(p);
+    test_dispatch(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_custom_params, AnyKeyParamsFixture ) {
+    BOOST_TEST_MESSAGE("Initialize custom AnyKeyParams");
+    test_custom(p);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_FIXTURE_TEST_CASE( test_custom_params_streaming, AnyKeyParamsFixture ) {
+    BOOST_TEST_MESSAGE("Stream custom AnyKeyParams");
+    Params params = stream_to_from_file(p);
+    test_custom(params);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace eckit_test
diff --git a/eckit/src/tests/value/test_value_properties.cc b/eckit/src/tests/value/test_value_properties.cc
new file mode 100644
index 0000000..1670ff0
--- /dev/null
+++ b/eckit/src/tests/value/test_value_properties.cc
@@ -0,0 +1,119 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define BOOST_TEST_MODULE test_eckit_value_properties
+
+#include <limits>
+
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/Length.h"
+#include "eckit/serialisation/FileStream.h"
+#include "eckit/types/Date.h"
+#include "eckit/value/Properties.h"
+
+using namespace std;
+using namespace eckit;
+
+//-----------------------------------------------------------------------------
+
+namespace eckit_test {
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( test_eckit_value_properties )
+
+BOOST_AUTO_TEST_CASE( test_serialize )
+{
+    BOOST_TEST_MESSAGE("(de)serialize Properties to/from file");
+    PathName filename = PathName::unique( "data" );
+    std::string filepath = filename.asString();
+    Properties p;
+    p.set("bool", true);
+    p.set("int", numeric_limits<int>::max());
+    p.set("unsigned int", numeric_limits<unsigned int>::max());
+    p.set("long long", numeric_limits<long long>::max());
+    p.set("unsigned long long", numeric_limits<unsigned long long>::max());
+    p.set("double", numeric_limits<double>::max());
+    p.set("string", "foo");
+    p.set("Length", Length(42));
+    p.set("Date", Date(2015, 2, 1));
+    // p.set("Time", Time(11, 59, 59));  <-- not implemented
+    // p.set("DateTime", DateTime(Date(1, 2, 2015), Time(11, 59, 59)));  <-- not implemented
+    p.set("PathName", PathName("/var/tmp"));
+    p.set("Vector", ValueList(5, "string"));
+    ValueMap m;
+    m.insert( std::make_pair("int", numeric_limits<int>::max()) );
+    m.insert( std::make_pair("unsigned int", numeric_limits<unsigned int>::max()) );
+    m.insert( std::make_pair("long long", numeric_limits<long long>::max()) );
+    m.insert( std::make_pair("unsigned long long", numeric_limits<unsigned long long>::max()) );
+    m.insert( std::make_pair("double", numeric_limits<double>::max()) );
+    m.insert( std::make_pair("string", "foo") );
+    m.insert( std::make_pair("Length", Length(42)) );
+    m.insert( std::make_pair("PathName", PathName("/var/tmp")) );
+    m.insert( std::make_pair("Vector", ValueList(5, "string")) );
+    p.set("Map", Value(m) );
+
+    Properties pm;
+    pm.set("int",numeric_limits<int>::max());
+    pm.set("unsigned int", numeric_limits<unsigned int>::max());
+    p.set("Nested", pm );
+
+    std::vector<Properties> property_list(2);
+    property_list[0].set("int",numeric_limits<int>::max());
+    property_list[1].set("string","foo");
+    p.set("list", makeVectorValue(property_list) );
+
+    BOOST_TEST_MESSAGE("encoded Properties: " << p);
+    {
+        FileStream sout( filepath.c_str(), "w" );
+        sout << p;
+    }
+    {
+        FileStream sin( filepath.c_str(), "r" );
+        Properties p2(sin);
+        BOOST_TEST_MESSAGE("decoded Properties: " << p2);
+        BOOST_CHECK_EQUAL((bool)p["bool"], (bool)p2["bool"]);
+        BOOST_CHECK_EQUAL(p["int"], p2["int"]);
+        BOOST_CHECK_EQUAL(p["unsigned int"], p2["unsigned int"]);
+        BOOST_CHECK_EQUAL(p["long long"], p2["long long"]);
+        BOOST_CHECK_EQUAL(p["unsigned long long"], p2["unsigned long long"]);
+        BOOST_CHECK_EQUAL(p["double"], p2["double"]);
+        BOOST_CHECK_EQUAL(p["string"], p2["string"]);
+        BOOST_CHECK_EQUAL(p["Length"], p2["Length"]);
+        BOOST_CHECK(p["Date"].compare(p2["Date"])); // FIXME: equality check fails
+        // BOOST_CHECK_EQUAL(p["Time"], p2["Time"]);  <-- not implemented
+        // BOOST_CHECK_EQUAL(p["DateTime"], p2["DateTime"]); <-- not implemented
+        BOOST_CHECK_EQUAL(p["PathName"], p2["PathName"]);
+        BOOST_CHECK_EQUAL(p["Vector"], p2["Vector"]);
+        BOOST_CHECK_EQUAL(p["Map"], p2["Map"]);
+    }
+    if (filename.exists()) filename.unlink();
+
+    Properties access_nested = p.get("Nested");
+
+    eckit::ValueList access_list = p.get("list");
+    BOOST_TEST_MESSAGE("encoded list: " <<  access_list );
+    std::vector<eckit::Properties> access_property_list(access_list.begin(),access_list.end());
+
+    BOOST_TEST_MESSAGE("encoded Nested: " << access_nested);
+    {
+        FileStream sout( filepath.c_str(), "w" );
+        sout << p;
+    }
+
+}
+
+//-----------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace eckit_test
diff --git a/metkit/.gitignore b/metkit/.gitignore
new file mode 100644
index 0000000..db6c96a
--- /dev/null
+++ b/metkit/.gitignore
@@ -0,0 +1,8 @@
+.tags*
+CMakeLists.txt.user*
+*.autosave
+doc/html
+doc/latex
+*.sublime-workspace
+.*.sw*
+.*un~
diff --git a/metkit/CMakeLists.txt b/metkit/CMakeLists.txt
new file mode 100644
index 0000000..4761ee9
--- /dev/null
+++ b/metkit/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+project( metkit CXX )
+
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 2.0 )
+
+### dependencies
+
+# grib support
+
+ecbuild_add_option( FEATURE GRIB
+                    DEFAULT ON
+                    DESCRIPTION "Add support for GRIB format" )
+
+ecbuild_add_option( FEATURE ECCODES
+                    CONDITION HAVE_GRIB
+                    DEFAULT ON
+                    DESCRIPTION "Use ecCodes instead of grib_api"
+                    REQUIRED_PACKAGES "PROJECT eccodes VERSION 0.14" )
+
+if( HAVE_GRIB )
+
+  if( HAVE_ECCODES )
+    set( GRIB_API_INCLUDE_DIRS ${ECCODES_INCLUDE_DIRS} )
+    set( GRIB_API_LIBRARIES    ${ECCODES_LIBRARIES} )
+    set( GRIB_API_DEFINITIONS  ${ECCODES_DEFINITIONS} )
+    set( grib_api_BASE_DIR     ${eccodes_BASE_DIR} )
+  else()
+    ecbuild_use_package( PROJECT grib_api VERSION 1.14 )
+    if(NOT GRIB_API_FOUND)
+      set(HAVE_GRIB 0)
+    endif()
+  endif()
+
+endif()
+
+# eckit
+
+ecbuild_use_package( PROJECT eckit  VERSION  0.13 REQUIRED )
+
+### start project
+
+ecbuild_declare_project()
+
+### export package info
+
+set( METKIT_INCLUDE_DIRS   ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src )
+set( METKIT_LIBRARIES      metkit )
+
+get_directory_property( METKIT_DEFINITIONS COMPILE_DEFINITIONS )
+
+### sources
+
+include_directories( ${METKIT_INCLUDE_DIRS} )
+
+add_subdirectory( src )
+add_subdirectory( etc )
+
+### finalize project
+
+ecbuild_pkgconfig( NAME metkit
+                   DESCRIPTION "ECMWF Meteorological toolkit"
+                   LIBRARIES metkit )
+
+ecbuild_install_project( NAME ${PROJECT_NAME} )
+
+ecbuild_print_summary()
diff --git a/metkit/VERSION.cmake b/metkit/VERSION.cmake
new file mode 100644
index 0000000..a61bea4
--- /dev/null
+++ b/metkit/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.5.2")
diff --git a/metkit/bamboo/CLANG-env.sh b/metkit/bamboo/CLANG-env.sh
new file mode 100644
index 0000000..a89b187
--- /dev/null
+++ b/metkit/bamboo/CLANG-env.sh
@@ -0,0 +1,8 @@
+# No module environment on the Mac
+[[ $(uname) == "Darwin" ]] && return
+# Initialise module environment if it is not
+if [[ ! $(command -v module > /dev/null 2>&1) ]]; then
+  . /usr/local/apps/module/init/bash
+fi
+module unload grib_api
+module switch gnu clang/3.6.2
diff --git a/metkit/bamboo/INTEL-env.sh b/metkit/bamboo/INTEL-env.sh
new file mode 100644
index 0000000..cbd28c0
--- /dev/null
+++ b/metkit/bamboo/INTEL-env.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# Initialise module environment if it is not
+if [[ ! $(command -v module > /dev/null 2>&1) ]]; then
+  . /usr/local/apps/module/init/bash
+fi
+# Unload modules not available for Intel
+module unload grib_api
+module unload emos
+module unload fftw
+module unload libemos
+module switch gnu intel/16.0.3
diff --git a/metkit/etc/CMakeLists.txt b/metkit/etc/CMakeLists.txt
new file mode 100644
index 0000000..0e9444f
--- /dev/null
+++ b/metkit/etc/CMakeLists.txt
@@ -0,0 +1,3 @@
+configure_file( language.json ${CMAKE_BINARY_DIR}/etc/language.json @ONLY )
+configure_file( param.json ${CMAKE_BINARY_DIR}/etc/param.json @ONLY )
+
diff --git a/metkit/etc/language.json b/metkit/etc/language.json
new file mode 100644
index 0000000..2bb9841
--- /dev/null
+++ b/metkit/etc/language.json
@@ -0,0 +1,547 @@
+{
+    "dissemination": {
+        "area": {
+            "type": "enum-or-more",
+            "values": [
+                [
+                    "off",
+                    "g"
+                ]
+            ],
+            "flatten": false,
+            "more": [{
+                "type": "float"
+            }],
+            "never": [{
+                "repres": "sh"
+            }],
+            "only": [{}]
+        },
+        "class": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                "od"
+            ],
+            "default": "od"
+        },
+        "domain": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                [
+                    "g",
+                    "global"
+                ],
+                "m",
+                "c",
+                "h"
+            ],
+            "default": "g"
+        },
+        "expver": {
+            "type": "expver",
+            "flatten": false,
+            "default": "0001"
+        },
+        "catn": {
+            "type": "integer",
+            "flatten": false
+        },
+         "hdate": {
+            "type": "integer"
+        },
+        "format": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                [
+                    "grib",
+                    "grib1",
+                    "gb"
+                ],
+                [
+                    "grib2"
+                ],
+                [
+                    "bufr",
+                    "bf"
+                ],
+                [
+                    "grid",
+                    "gd"
+                ]
+            ]
+        },
+        "grid": {
+            "type": "enum-or-more",
+            "flatten": false,
+            "values": [
+                "auto",
+                "O1280",
+                "F1280",
+                "N640",
+                "O640",
+                "O320",
+                "O160",
+                "N160",
+                "N320"
+            ],
+            "more": [{
+                "type": "float"
+            }]
+        },
+        "rotation": {
+            "type": "any",
+            "flatten": false
+        },
+        "fcperiod": {
+            "type": "integer"
+        },
+        "levelist": {
+            "type": "to-by-list",
+            "by": 1,
+            "never": [{
+                "levtype": "sfc"
+            }],
+            "default": [
+                "1000",
+                "850",
+                "700",
+                "500",
+                "400",
+                "300"
+            ]
+        },
+        "levtype": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                [
+                    "pl",
+                    "pressure levels"
+                ],
+                [
+                    "ml",
+                    "model levels"
+                ],
+                [
+                    "sfc",
+                    "surface"
+                ],
+                [
+                    "pt",
+                    "potential temperature"
+                ],
+                [
+                    "pv",
+                    "potential vorticity"
+                ]
+            ],
+            "default": "pl",
+            "_comment": "CH/SW6/req/curr/N5 has 2 levtype",
+            "multiple": true
+        },
+        "param": {
+            "type": "param",
+            "default": "129",
+            "multiple": true
+        },
+        "priority": {
+            "type": "integer",
+            "flatten": false
+        },
+        "step": {
+            "type": "range",
+            "by": 12,
+            "default": "0"
+        },
+        "stream": {
+            "type": "enum",
+            "default": "oper",
+            "flatten": false,
+            "values": [
+                [
+                    "oper",
+                    "da"
+                ],
+                [
+                    "enfo",
+                    "ef",
+                    "ensemble forecast"
+                ],
+                [
+                    "wave",
+                    "wv"
+                ],
+                [
+                    "scwv"
+                ],
+                [
+                    "enwh"
+                ],
+                [
+                    "waef",
+                    "we"
+                ],
+                "esmm",
+                "ehmm",
+                "mmsm",
+                "scda",
+                "msmm",
+                "efhs",
+                "efov",
+                "enfh",
+                "mmsf",
+                "mmsa",
+                "scef",
+                "mnfc"
+            ]
+        },
+        "projection": {
+            "type": "any"
+        },
+        "number": {
+            "type": "to-by-list",
+            "by": 1,
+            "aliases": [
+                "xxxxensemble"
+            ],
+            "only": [{
+                "type": [
+                    "pf",
+                    "cr",
+                    "cm"
+                ]
+            }]
+        },
+          "ensemble": {
+            "type": "to-by-list",
+            "by": 1,
+            "aliases": [
+                "xxxxxensemble"
+            ],
+            "only": [{
+                "type": [
+                    "pf"
+                ]
+            }]
+        },
+        "quantile": {
+            "type": "enum",
+            "only": [{
+                "type": [
+                    "pd",
+                    "pb",
+                    "taem",
+                    "sot"
+                ]
+            }],
+            "values": [
+                "1:3",
+                "2:3",
+                "3:3",
+                "1:5",
+                "2:5",
+                "3:5",
+                "4:5",
+                "5:5",
+                "1:10",
+                "2:10",
+                "3:10",
+                "4:10",
+                "5:10",
+                "6:10",
+                "7:10",
+                "8:10",
+                "9:10",
+                "10:10", [
+                    "100",
+                    "10:100"
+                ],
+                [
+                    "900",
+                    "90:100"
+                ]
+            ],
+            "multiple": true
+        },
+        "channel": {
+            "type": "integer",
+            "only": [{
+                "type": "ssd"
+            }]
+        },
+        "ident": {
+            "type": "integer",
+            "only": [{
+                "type": "ssd"
+            }]
+        },
+        "instrument": {
+            "type": "integer",
+            "only": [{
+                "type": "ssd"
+            }]
+        },
+        "target": {
+            "type": "any",
+            "flatten": false
+        },
+        "fcmonth": {
+            "type": "to-by-list",
+            "by": 1
+        },
+        "method": {
+            "type": "integer"
+        },
+        "origin": {
+            "type": "enum",
+            "values": [
+                "ecmf",
+                "ecmwf"
+            ]
+        },
+        "system": {
+            "type": "integer"
+        },
+        "time": {
+            "type": "time",
+            "default": "1200",
+            "multiple": true
+        },
+        "date": {
+            "type": "any"
+        },
+        "type": {
+            "type": "enum",
+            "default": "an",
+            "flatten": false,
+            "values": [
+                [
+                    "an",
+                    "analysis"
+                ],
+                [
+                    "fc",
+                    "forecast"
+                ],
+                [
+                    "cf",
+                    "control forecast"
+                ],
+                [
+                    "pf",
+                    "perturbed forecast"
+                ],
+                [
+                    "em",
+                    "ensemble mean"
+                ],
+                [
+                    "es",
+                    "ensemble standard deviation"
+                ],
+                [
+                    "cm",
+                    "cluster mean"
+                ],
+                [
+                    "cs",
+                    "cluster standard deviation"
+                ],
+                [
+                    "efi",
+                    "extreme forecast index"
+                ],
+                [
+                    "efic",
+                    "extreme forecast index control"
+                ],
+                [
+                    "taem",
+                    "time average ensemble mean"
+                ],
+                [
+                    "taes",
+                    "time average ensemble standard deviation"
+                ],
+                [
+                    "cr",
+                    "cluster representative"
+                ],
+                [
+                    "wp",
+                    "weather parameter"
+                ],
+                "pd", [
+                    "tf",
+                    "trajectory forecast"
+                ],
+                "ssd", [
+                    "sot",
+                    "shift of tails index"
+                ],
+                [
+                    "cv",
+                    "calibration validation"
+                ],
+                "pb", [
+                    "ep",
+                    "event probability"
+                ],
+                "fp",
+                "fcmean",
+                "fcmax",
+                "fcmin",
+                "fcstdev", [
+                    "tu",
+                    "tubes"
+                ]
+            ]
+        },
+        "use": {
+            "type": "enum",
+            "multiple": true,
+            "flatten": false,
+            "values": [
+                "bc",
+                "daily", [
+                    "thursday",
+                    "mt",
+                    "monthly run thursday",
+                    "monthly thursday"
+                ],
+                [
+                    "monday",
+                    "mm",
+                    "monthly run monday",
+                    "monthly monday"
+                ]
+            ]
+        },
+        "option": {
+            "type": "enum",
+            "multiple": true,
+            "flatten": false,
+            "values": [
+                "normal",
+                "delay",
+                "wmoe",
+                "gts",
+                "tcpip",
+                "nobackup",
+                "grib1table1"
+            ],
+            "default": "normal"
+        },
+        "padding": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                "none",
+                "auto"
+            ]
+        },
+        "accuracy": {
+            "type": "any",
+            "flatten": false
+        },
+        "interpolation": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                [
+                    "nearest-lsm",
+                    "nearest lsm"
+                ],
+                [
+                    "off",
+                    "default",
+                    "any"
+                ]
+            ]
+        },
+        "country": {
+            "type": "any"
+        },
+        "file": {
+            "type": "enum",
+            "values": [
+                "bin"
+            ]
+        },
+        "bitmap": {
+            "type": "any",
+            "flatten": false
+        },
+        "direction": {
+            "type": "to-by-list",
+            "by": 1,
+            "only": [{
+                "param": [
+                    "251",
+                    "140251"
+                ]
+            }]
+        },
+        "frequency": {
+            "type": "to-by-list",
+            "by": 1,
+            "only": [{
+                "param": [
+                    "251",
+                    "140251"
+                ]
+            }]
+        },
+        "frame": {
+            "type": "integer"
+        },
+        "packing": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                [
+                    "so",
+                    "second order"
+                ],
+                "av",
+                "co",
+                "simple"
+            ]
+        },
+        "repres": {
+            "type": "enum",
+            "flatten": false,
+            "values": [
+                "gg",
+                "sh",
+                "ll",
+                "np",
+                "rl", [
+                    "0.25",
+                    "fix me HUE:E1 !"
+                ]
+            ],
+            "multiple": true
+        },
+        "resol": {
+            "type": "any"
+        },
+        "gaussian": {
+            "type": "enum",
+            "values": [
+                "reduced",
+                "regular"
+            ]
+        },
+        "reference": {
+            "type": "integer"
+        },
+        "requirements": {
+            "type": "any"
+        }
+    },
+    "end": {}
+}
diff --git a/metkit/etc/param.json b/metkit/etc/param.json
new file mode 100644
index 0000000..43ecdad
--- /dev/null
+++ b/metkit/etc/param.json
@@ -0,0 +1,19553 @@
+[
+    [
+        "1",
+        "strf",
+        "stream function"
+    ],
+    [
+        "2",
+        "vp",
+        "velocity potential",
+        "vpot"
+    ],
+    [
+        "3",
+        "pt",
+        "potential temperature"
+    ],
+    [
+        "4",
+        "eqpt",
+        "equivalent potential temperature"
+    ],
+    [
+        "5",
+        "sept",
+        "saturated equivalent potential temperature"
+    ],
+    [
+        "6",
+        "ssfr",
+        "soil sand fraction"
+    ],
+    [
+        "7",
+        "scfr",
+        "soil clay fraction"
+    ],
+    [
+        "8",
+        "sro",
+        "surface runoff"
+    ],
+    [
+        "9",
+        "ssro",
+        "sub-surface runoff"
+    ],
+    [
+        "10",
+        "ws",
+        "wind speed"
+    ],
+    [
+        "11",
+        "udvw",
+        "u component of divergent wind"
+    ],
+    [
+        "12",
+        "vdvw",
+        "v component of divergent wind"
+    ],
+    [
+        "13",
+        "urtw",
+        "u component of rotational wind"
+    ],
+    [
+        "14",
+        "vrtw",
+        "v component of rotational wind"
+    ],
+    [
+        "15",
+        "aluvp",
+        "uv visible albedo for direct radiation"
+    ],
+    [
+        "16",
+        "aluvd",
+        "uv visible albedo for diffuse radiation"
+    ],
+    [
+        "17",
+        "alnip",
+        "near ir albedo for direct radiation"
+    ],
+    [
+        "18",
+        "alnid",
+        "near ir albedo for diffuse radiation"
+    ],
+    [
+        "19",
+        "uvcs",
+        "clear sky surface uv"
+    ],
+    [
+        "20",
+        "parcs",
+        "clear sky surface photosynthetically active radiation"
+    ],
+    [
+        "21",
+        "uctp",
+        "unbalanced component of temperature"
+    ],
+    [
+        "22",
+        "ucln",
+        "unbalanced component of logarithm of surface pressure"
+    ],
+    [
+        "23",
+        "ucdv",
+        "unbalanced component of divergence"
+    ],
+    [
+        "26",
+        "cl",
+        "lake cover"
+    ],
+    [
+        "27",
+        "cvl",
+        "low vegetation cover"
+    ],
+    [
+        "28",
+        "cvh",
+        "high vegetation cover"
+    ],
+    [
+        "29",
+        "tvl",
+        "type of low vegetation"
+    ],
+    [
+        "30",
+        "tvh",
+        "type of high vegetation"
+    ],
+    [
+        "31",
+        "ci",
+        "sea-ice cover"
+    ],
+    [
+        "32",
+        "asn",
+        "snow albedo"
+    ],
+    [
+        "33",
+        "rsn",
+        "snow density"
+    ],
+    [
+        "34",
+        "sst",
+        "sea surface temperature",
+        "sstk"
+    ],
+    [
+        "35",
+        "istl1",
+        "ice temperature layer 1"
+    ],
+    [
+        "36",
+        "istl2",
+        "ice temperature layer 2"
+    ],
+    [
+        "37",
+        "istl3",
+        "ice temperature layer 3"
+    ],
+    [
+        "38",
+        "istl4",
+        "ice temperature layer 4"
+    ],
+    [
+        "39",
+        "swvl1",
+        "volumetric soil water layer 1",
+        "swv1"
+    ],
+    [
+        "40",
+        "swvl2",
+        "volumetric soil water layer 2",
+        "swv2"
+    ],
+    [
+        "41",
+        "swvl3",
+        "volumetric soil water layer 3",
+        "swv3"
+    ],
+    [
+        "42",
+        "swvl4",
+        "volumetric soil water layer 4",
+        "swv4"
+    ],
+    [
+        "43",
+        "slt",
+        "soil type"
+    ],
+    [
+        "44",
+        "es",
+        "snow evaporation"
+    ],
+    [
+        "45",
+        "smlt",
+        "snowmelt"
+    ],
+    [
+        "46",
+        "sdur",
+        "solar duration"
+    ],
+    [
+        "47",
+        "dsrp",
+        "direct solar radiation"
+    ],
+    [
+        "48",
+        "magss",
+        "magnitude of turbulent surface stress"
+    ],
+    [
+        "49",
+        "10fg",
+        "10 metre wind gust since previous post-processing"
+    ],
+    [
+        "50",
+        "lspf",
+        "large-scale precipitation fraction"
+    ],
+    [
+        "51",
+        "mx2t24",
+        "maximum temperature at 2 metres in the last 24 hours"
+    ],
+    [
+        "52",
+        "mn2t24",
+        "minimum temperature at 2 metres in the last 24 hours"
+    ],
+    [
+        "53",
+        "mont",
+        "montgomery potential"
+    ],
+    [
+        "54",
+        "pres",
+        "pressure"
+    ],
+    [
+        "55",
+        "mean2t24",
+        "mean temperature at 2 metres in the last 24 hours"
+    ],
+    [
+        "56",
+        "mn2d24",
+        "mean 2 metre dewpoint temperature in the last 24 hours"
+    ],
+    [
+        "57",
+        "uvb",
+        "downward uv radiation at the surface"
+    ],
+    [
+        "58",
+        "par",
+        "photosynthetically active radiation at the surface"
+    ],
+    [
+        "59",
+        "cape",
+        "convective available potential energy"
+    ],
+    [
+        "60",
+        "pv",
+        "potential vorticity"
+    ],
+    [
+        "62",
+        "obct",
+        "observation count"
+    ],
+    [
+        "63",
+        "stsktd",
+        "start time for skin temperature difference"
+    ],
+    [
+        "64",
+        "ftsktd",
+        "finish time for skin temperature difference"
+    ],
+    [
+        "65",
+        "sktd",
+        "skin temperature difference"
+    ],
+    [
+        "66",
+        "lai_lv",
+        "leaf area index, low vegetation",
+        "lailv"
+    ],
+    [
+        "67",
+        "lai_hv",
+        "leaf area index, high vegetation",
+        "laihv"
+    ],
+    [
+        "68",
+        "msr_lv",
+        "minimum stomatal resistance, low vegetation"
+    ],
+    [
+        "69",
+        "msr_hv",
+        "minimum stomatal resistance, high vegetation"
+    ],
+    [
+        "70",
+        "bc_lv",
+        "biome cover, low vegetation"
+    ],
+    [
+        "71",
+        "bc_hv",
+        "biome cover, high vegetation"
+    ],
+    [
+        "72",
+        "issrd",
+        "instantaneous surface solar radiation downwards"
+    ],
+    [
+        "73",
+        "istrd",
+        "instantaneous surface thermal radiation downwards"
+    ],
+    [
+        "74",
+        "sdfor",
+        "standard deviation of filtered subgrid orography"
+    ],
+    [
+        "75",
+        "crwc",
+        "specific rain water content"
+    ],
+    [
+        "76",
+        "cswc",
+        "specific snow water content"
+    ],
+    [
+        "77",
+        "etadot",
+        "eta-coordinate vertical velocity",
+        "etad"
+    ],
+    [
+        "78",
+        "tclw",
+        "total column liquid water"
+    ],
+    [
+        "79",
+        "tciw",
+        "total column ice water"
+    ],
+    [
+        "121",
+        "mx2t6",
+        "maximum temperature at 2 metres in the last 6 hours"
+    ],
+    [
+        "122",
+        "mn2t6",
+        "minimum temperature at 2 metres in the last 6 hours"
+    ],
+    [
+        "123",
+        "10fg6",
+        "10 metre wind gust in the last 6 hours"
+    ],
+    [
+        "124",
+        "emis",
+        "surface emissivity"
+    ],
+    [
+        "125",
+        "vite",
+        "vertically integrated total energy"
+    ],
+    [
+        "126",
+        "generic parameter for sensitive area prediction"
+    ],
+    [
+        "127",
+        "at",
+        "atmospheric tide"
+    ],
+    [
+        "128",
+        "bv",
+        "budget values"
+    ],
+    [
+        "129",
+        "z",
+        "geopotential"
+    ],
+    [
+        "130",
+        "t",
+        "temperature"
+    ],
+    [
+        "131",
+        "u",
+        "u component of wind"
+    ],
+    [
+        "132",
+        "v",
+        "v component of wind"
+    ],
+    [
+        "133",
+        "q",
+        "specific humidity"
+    ],
+    [
+        "134",
+        "sp",
+        "surface pressure"
+    ],
+    [
+        "135",
+        "w",
+        "vertical velocity"
+    ],
+    [
+        "136",
+        "tcw",
+        "total column water"
+    ],
+    [
+        "137",
+        "tcwv",
+        "total column water vapour"
+    ],
+    [
+        "138",
+        "vo",
+        "vorticity (relative)"
+    ],
+    [
+        "139",
+        "stl1",
+        "soil temperature level 1"
+    ],
+    [
+        "140",
+        "swl1",
+        "soil wetness level 1"
+    ],
+    [
+        "141",
+        "sd",
+        "snow depth"
+    ],
+    [
+        "142",
+        "lsp",
+        "large-scale precipitation"
+    ],
+    [
+        "143",
+        "cp",
+        "convective precipitation"
+    ],
+    [
+        "144",
+        "sf",
+        "snowfall"
+    ],
+    [
+        "145",
+        "bld",
+        "boundary layer dissipation"
+    ],
+    [
+        "146",
+        "sshf",
+        "surface sensible heat flux"
+    ],
+    [
+        "147",
+        "slhf",
+        "surface latent heat flux"
+    ],
+    [
+        "148",
+        "chnk",
+        "charnock",
+		"ss"
+    ],
+    [
+        "149",
+        "snr",
+        "surface net radiation"
+    ],
+    [
+        "150",
+        "tnr",
+        "top net radiation"
+    ],
+    [
+        "151",
+        "msl",
+        "mean sea level pressure"
+    ],
+    [
+        "152",
+        "lnsp",
+        "logarithm of surface pressure"
+    ],
+    [
+        "153",
+        "swhr",
+        "short-wave heating rate"
+    ],
+    [
+        "154",
+        "lwhr",
+        "long-wave heating rate"
+    ],
+    [
+        "155",
+        "d",
+        "divergence"
+    ],
+    [
+        "156",
+        "gh",
+        "geopotential height"
+    ],
+    [
+        "157",
+        "r",
+        "relative humidity"
+    ],
+    [
+        "158",
+        "tsp",
+        "tendency of surface pressure"
+    ],
+    [
+        "159",
+        "blh",
+        "boundary layer height"
+    ],
+    [
+        "160",
+        "sdor",
+        "standard deviation of orography"
+    ],
+    [
+        "161",
+        "isor",
+        "anisotropy of sub-gridscale orography"
+    ],
+    [
+        "162",
+        "anor",
+        "angle of sub-gridscale orography"
+    ],
+    [
+        "163",
+        "slor",
+        "slope of sub-gridscale orography"
+    ],
+    [
+        "164",
+        "tcc",
+        "total cloud cover"
+    ],
+    [
+        "165",
+        "10u",
+        "10 metre u wind component"
+    ],
+    [
+        "166",
+        "10v",
+        "10 metre v wind component"
+    ],
+    [
+        "167",
+        "2t",
+        "2 metre temperature"
+    ],
+    [
+        "168",
+        "2d",
+        "2 metre dewpoint temperature"
+    ],
+    [
+        "169",
+        "ssrd",
+        "surface solar radiation downwards"
+    ],
+    [
+        "170",
+        "stl2",
+        "soil temperature level 2"
+    ],
+    [
+        "171",
+        "swl2",
+        "soil wetness level 2"
+    ],
+    [
+        "172",
+        "lsm",
+        "land-sea mask"
+    ],
+    [
+        "173",
+        "sr",
+        "surface roughness"
+    ],
+    [
+        "174",
+        "al",
+        "albedo",
+        "174.128"
+    ],
+    [
+        "175",
+        "strd",
+        "surface thermal radiation downwards"
+    ],
+    [
+        "176",
+        "ssr",
+        "surface net solar radiation"
+    ],
+    [
+        "177",
+        "str",
+        "surface net thermal radiation"
+    ],
+    [
+        "178",
+        "tsr",
+        "top net solar radiation"
+    ],
+    [
+        "179",
+        "ttr",
+        "top net thermal radiation"
+    ],
+    [
+        "180",
+        "ewss",
+        "eastward turbulent surface stress"
+    ],
+    [
+        "181",
+        "nsss",
+        "northward turbulent surface stress"
+    ],
+    [
+        "182",
+        "e",
+        "evaporation"
+    ],
+    [
+        "183",
+        "stl3",
+        "soil temperature level 3"
+    ],
+    [
+        "184",
+        "swl3",
+        "soil wetness level 3"
+    ],
+    [
+        "185",
+        "ccc",
+        "convective cloud cover"
+    ],
+    [
+        "186",
+        "lcc",
+        "low cloud cover"
+    ],
+    [
+        "187",
+        "mcc",
+        "medium cloud cover"
+    ],
+    [
+        "188",
+        "hcc",
+        "high cloud cover"
+    ],
+    [
+        "189",
+        "sund",
+        "sunshine duration"
+    ],
+    [
+        "190",
+        "ewov",
+        "east-west component of sub-gridscale orographic variance"
+    ],
+    [
+        "191",
+        "nsov",
+        "north-south component of sub-gridscale orographic variance"
+    ],
+    [
+        "192",
+        "nwov",
+        "north-west/south-east component of sub-gridscale orographic variance"
+    ],
+    [
+        "193",
+        "neov",
+        "north-east/south-west component of sub-gridscale orographic variance"
+    ],
+    [
+        "194",
+        "btmp",
+        "brightness temperature"
+    ],
+    [
+        "195",
+        "lgws",
+        "eastward gravity wave surface stress"
+    ],
+    [
+        "196",
+        "mgws",
+        "northward gravity wave surface stress"
+    ],
+    [
+        "197",
+        "gwd",
+        "gravity wave dissipation"
+    ],
+    [
+        "198",
+        "src",
+        "skin reservoir content"
+    ],
+    [
+        "199",
+        "veg",
+        "vegetation fraction"
+    ],
+    [
+        "200",
+        "vso",
+        "variance of sub-gridscale orography"
+    ],
+    [
+        "201",
+        "mx2t",
+        "maximum temperature at 2 metres since previous post-processing"
+    ],
+    [
+        "202",
+        "mn2t",
+        "minimum temperature at 2 metres since previous post-processing"
+    ],
+    [
+        "203",
+        "o3",
+        "ozone mass mixing ratio"
+    ],
+    [
+        "204",
+        "paw",
+        "precipitation analysis weights"
+    ],
+    [
+        "205",
+        "ro",
+        "runoff"
+    ],
+    [
+        "206",
+        "tco3",
+        "total column ozone"
+    ],
+    [
+        "207",
+        "10si",
+        "10 metre wind speed"
+    ],
+    [
+        "208",
+        "tsrc",
+        "top net solar radiation, clear sky"
+    ],
+    [
+        "209",
+        "ttrc",
+        "top net thermal radiation, clear sky"
+    ],
+    [
+        "210",
+        "ssrc",
+        "surface net solar radiation, clear sky"
+    ],
+    [
+        "211",
+        "strc",
+        "surface net thermal radiation, clear sky"
+    ],
+    [
+        "212",
+        "tisr",
+        "toa incident solar radiation"
+    ],
+    [
+        "213",
+        "vimd",
+        "vertically integrated moisture divergence"
+    ],
+    [
+        "214",
+        "dhr",
+        "diabatic heating by radiation"
+    ],
+    [
+        "215",
+        "dhvd",
+        "diabatic heating by vertical diffusion"
+    ],
+    [
+        "216",
+        "dhcc",
+        "diabatic heating by cumulus convection"
+    ],
+    [
+        "217",
+        "dhlc",
+        "diabatic heating large-scale condensation"
+    ],
+    [
+        "218",
+        "vdzw",
+        "vertical diffusion of zonal wind"
+    ],
+    [
+        "219",
+        "vdmw",
+        "vertical diffusion of meridional wind"
+    ],
+    [
+        "220",
+        "ewgd",
+        "east-west gravity wave drag tendency"
+    ],
+    [
+        "221",
+        "nsgd",
+        "north-south gravity wave drag tendency"
+    ],
+    [
+        "222",
+        "ctzw",
+        "convective tendency of zonal wind"
+    ],
+    [
+        "223",
+        "ctmw",
+        "convective tendency of meridional wind"
+    ],
+    [
+        "224",
+        "vdh",
+        "vertical diffusion of humidity"
+    ],
+    [
+        "225",
+        "htcc",
+        "humidity tendency by cumulus convection"
+    ],
+    [
+        "226",
+        "htlc",
+        "humidity tendency by large-scale condensation"
+    ],
+    [
+        "227",
+        "crnh",
+        "tendency due to removal of negative humidity"
+    ],
+    [
+        "228",
+        "tp",
+        "total precipitation"
+    ],
+    [
+        "229",
+        "iews",
+        "instantaneous eastward turbulent surface stress"
+    ],
+    [
+        "230",
+        "inss",
+        "instantaneous northward turbulent surface stress"
+    ],
+    [
+        "231",
+        "ishf",
+        "instantaneous surface sensible heat flux"
+    ],
+    [
+        "232",
+        "ie",
+        "instantaneous moisture flux"
+    ],
+    [
+        "233",
+        "asq",
+        "apparent surface humidity"
+    ],
+    [
+        "234",
+        "lsrh",
+        "logarithm of surface roughness length for heat"
+    ],
+    [
+        "235",
+        "skt",
+        "skin temperature"
+    ],
+    [
+        "236",
+        "stl4",
+        "soil temperature level 4"
+    ],
+    [
+        "237",
+        "swl4",
+        "soil wetness level 4"
+    ],
+    [
+        "238",
+        "tsn",
+        "temperature of snow layer"
+    ],
+    [
+        "239",
+        "csf",
+        "convective snowfall"
+    ],
+    [
+        "240",
+        "lsf",
+        "large-scale snowfall"
+    ],
+    [
+        "241",
+        "acf",
+        "accumulated cloud fraction tendency"
+    ],
+    [
+        "242",
+        "alw",
+        "accumulated liquid water tendency"
+    ],
+    [
+        "243",
+        "fal",
+        "forecast albedo"
+    ],
+    [
+        "244",
+        "fsr",
+        "forecast surface roughness"
+    ],
+    [
+        "245",
+        "flsr",
+        "forecast logarithm of surface roughness for heat"
+    ],
+    [
+        "246",
+        "clwc",
+        "specific cloud liquid water content"
+    ],
+    [
+        "247",
+        "ciwc",
+        "specific cloud ice water content"
+    ],
+    [
+        "248",
+        "cc",
+        "fraction of cloud cover"
+    ],
+    [
+        "249",
+        "aiw",
+        "accumulated ice water tendency"
+    ],
+    [
+        "250",
+        "ice",
+        "ice age"
+    ],
+    [
+        "251",
+        "atte",
+        "adiabatic tendency of temperature"
+    ],
+    [
+        "252",
+        "athe",
+        "adiabatic tendency of humidity"
+    ],
+    [
+        "253",
+        "atze",
+        "adiabatic tendency of zonal wind"
+    ],
+    [
+        "254",
+        "atmw",
+        "adiabatic tendency of meridional wind"
+    ],
+    [
+        "129001",
+        "strfgrd",
+        "stream function gradient"
+    ],
+    [
+        "129002",
+        "vpotgrd",
+        "velocity potential gradient"
+    ],
+    [
+        "129003",
+        "ptgrd",
+        "potential temperature gradient"
+    ],
+    [
+        "129004",
+        "eqptgrd",
+        "equivalent potential temperature gradient"
+    ],
+    [
+        "129005",
+        "septgrd",
+        "saturated equivalent potential temperature gradient"
+    ],
+    [
+        "129011",
+        "udvwgrd",
+        "u component of divergent wind gradient"
+    ],
+    [
+        "129012",
+        "vdvwgrd",
+        "v component of divergent wind gradient"
+    ],
+    [
+        "129013",
+        "urtwgrd",
+        "u component of rotational wind gradient"
+    ],
+    [
+        "129014",
+        "vrtwgrd",
+        "v component of rotational wind gradient"
+    ],
+    [
+        "129021",
+        "uctpgrd",
+        "unbalanced component of temperature gradient"
+    ],
+    [
+        "129022",
+        "uclngrd",
+        "unbalanced component of logarithm of surface pressure gradient"
+    ],
+    [
+        "129023",
+        "ucdvgrd",
+        "unbalanced component of divergence gradient"
+    ],
+    [
+        "129026",
+        "clgrd",
+        "lake cover gradient"
+    ],
+    [
+        "129027",
+        "cvlgrd",
+        "low vegetation cover gradient"
+    ],
+    [
+        "129028",
+        "cvhgrd",
+        "high vegetation cover gradient"
+    ],
+    [
+        "129029",
+        "tvlgrd",
+        "type of low vegetation gradient"
+    ],
+    [
+        "129030",
+        "tvhgrd",
+        "type of high vegetation gradient"
+    ],
+    [
+        "129031",
+        "sicgrd",
+        "sea-ice cover gradient"
+    ],
+    [
+        "129032",
+        "asngrd",
+        "snow albedo gradient"
+    ],
+    [
+        "129033",
+        "rsngrd",
+        "snow density gradient"
+    ],
+    [
+        "129034",
+        "sstkgrd",
+        "sea surface temperature gradient"
+    ],
+    [
+        "129035",
+        "istl1grd",
+        "ice surface temperature layer 1 gradient"
+    ],
+    [
+        "129036",
+        "istl2grd",
+        "ice surface temperature layer 2 gradient"
+    ],
+    [
+        "129037",
+        "istl3grd",
+        "ice surface temperature layer 3 gradient"
+    ],
+    [
+        "129038",
+        "istl4grd",
+        "ice surface temperature layer 4 gradient"
+    ],
+    [
+        "129039",
+        "swvl1grd",
+        "volumetric soil water layer 1 gradient"
+    ],
+    [
+        "129040",
+        "swvl2grd",
+        "volumetric soil water layer 2 gradient"
+    ],
+    [
+        "129041",
+        "swvl3grd",
+        "volumetric soil water layer 3 gradient"
+    ],
+    [
+        "129042",
+        "swvl4grd",
+        "volumetric soil water layer 4 gradient"
+    ],
+    [
+        "129043",
+        "sltgrd",
+        "soil type gradient"
+    ],
+    [
+        "129044",
+        "esgrd",
+        "snow evaporation gradient"
+    ],
+    [
+        "129045",
+        "smltgrd",
+        "snowmelt gradient"
+    ],
+    [
+        "129046",
+        "sdurgrd",
+        "solar duration gradient"
+    ],
+    [
+        "129047",
+        "dsrpgrd",
+        "direct solar radiation gradient"
+    ],
+    [
+        "129048",
+        "magssgrd",
+        "magnitude of turbulent surface stress gradient"
+    ],
+    [
+        "129049",
+        "10fggrd",
+        "10 metre wind gust gradient"
+    ],
+    [
+        "129050",
+        "lspfgrd",
+        "large-scale precipitation fraction gradient"
+    ],
+    [
+        "129051",
+        "mx2t24grd",
+        "maximum 2 metre temperature gradient"
+    ],
+    [
+        "129052",
+        "mn2t24grd",
+        "minimum 2 metre temperature gradient"
+    ],
+    [
+        "129053",
+        "montgrd",
+        "montgomery potential gradient"
+    ],
+    [
+        "129054",
+        "presgrd",
+        "pressure gradient"
+    ],
+    [
+        "129055",
+        "mean2t24grd",
+        "mean 2 metre temperature in the last 24 hours gradient"
+    ],
+    [
+        "129056",
+        "mn2d24grd",
+        "mean 2 metre dewpoint temperature in the last 24 hours gradient"
+    ],
+    [
+        "129057",
+        "uvbgrd",
+        "downward uv radiation at the surface gradient"
+    ],
+    [
+        "129058",
+        "pargrd",
+        "photosynthetically active radiation at the surface gradient"
+    ],
+    [
+        "129059",
+        "capegrd",
+        "convective available potential energy gradient"
+    ],
+    [
+        "129060",
+        "pvgrd",
+        "potential vorticity gradient"
+    ],
+    [
+        "129061",
+        "tpogrd",
+        "total precipitation from observations gradient"
+    ],
+    [
+        "129062",
+        "obctgrd",
+        "observation count gradient"
+    ],
+    [
+        "129063",
+        "start time for skin temperature difference"
+    ],
+    [
+        "129064",
+        "finish time for skin temperature difference"
+    ],
+    [
+        "129065",
+        "skin temperature difference"
+    ],
+    [
+        "129066",
+        "leaf area index, low vegetation"
+    ],
+    [
+        "129067",
+        "leaf area index, high vegetation"
+    ],
+    [
+        "129068",
+        "minimum stomatal resistance, low vegetation"
+    ],
+    [
+        "129069",
+        "minimum stomatal resistance, high vegetation"
+    ],
+    [
+        "129070",
+        "biome cover, low vegetation"
+    ],
+    [
+        "129071",
+        "biome cover, high vegetation"
+    ],
+    [
+        "129078",
+        "total column liquid water"
+    ],
+    [
+        "129079",
+        "total column ice water"
+    ],
+    [
+        "129121",
+        "mx2t6grd",
+        "maximum temperature at 2 metres gradient"
+    ],
+    [
+        "129122",
+        "mn2t6grd",
+        "minimum temperature at 2 metres gradient"
+    ],
+    [
+        "129123",
+        "10fg6grd",
+        "10 metre wind gust in the last 6 hours gradient"
+    ],
+    [
+        "129125",
+        "vertically integrated total energy"
+    ],
+    [
+        "129126",
+        "generic parameter for sensitive area prediction"
+    ],
+    [
+        "129127",
+        "atgrd",
+        "atmospheric tide gradient"
+    ],
+    [
+        "129128",
+        "bvgrd",
+        "budget values gradient"
+    ],
+    [
+        "129129",
+        "zgrd",
+        "geopotential gradient"
+    ],
+    [
+        "129130",
+        "tgrd",
+        "temperature gradient"
+    ],
+    [
+        "129131",
+        "ugrd",
+        "u component of wind gradient"
+    ],
+    [
+        "129132",
+        "vgrd",
+        "v component of wind gradient"
+    ],
+    [
+        "129133",
+        "qgrd",
+        "specific humidity gradient"
+    ],
+    [
+        "129134",
+        "spgrd",
+        "surface pressure gradient"
+    ],
+    [
+        "129135",
+        "wgrd",
+        "vertical velocity (pressure) gradient"
+    ],
+    [
+        "129136",
+        "tcwgrd",
+        "total column water gradient"
+    ],
+    [
+        "129137",
+        "tcwvgrd",
+        "total column water vapour gradient"
+    ],
+    [
+        "129138",
+        "vogrd",
+        "vorticity (relative) gradient"
+    ],
+    [
+        "129139",
+        "stl1grd",
+        "soil temperature level 1 gradient"
+    ],
+    [
+        "129140",
+        "swl1grd",
+        "soil wetness level 1 gradient"
+    ],
+    [
+        "129141",
+        "sdgrd",
+        "snow depth gradient"
+    ],
+    [
+        "129142",
+        "lspgrd",
+        "stratiform precipitation (large-scale precipitation) gradient"
+    ],
+    [
+        "129143",
+        "cpgrd",
+        "convective precipitation gradient"
+    ],
+    [
+        "129144",
+        "sfgrd",
+        "snowfall (convective + stratiform) gradient"
+    ],
+    [
+        "129145",
+        "bldgrd",
+        "boundary layer dissipation gradient"
+    ],
+    [
+        "129146",
+        "sshfgrd",
+        "surface sensible heat flux gradient"
+    ],
+    [
+        "129147",
+        "slhfgrd",
+        "surface latent heat flux gradient"
+    ],
+    [
+        "129148",
+        "chnkgrd",
+        "charnock gradient"
+    ],
+    [
+        "129149",
+        "snrgrd",
+        "surface net radiation gradient"
+    ],
+    [
+        "129150",
+        "tnrgrd",
+        "top net radiation gradient"
+    ],
+    [
+        "129151",
+        "mslgrd",
+        "mean sea level pressure gradient"
+    ],
+    [
+        "129152",
+        "lnspgrd",
+        "logarithm of surface pressure gradient"
+    ],
+    [
+        "129153",
+        "swhrgrd",
+        "short-wave heating rate gradient"
+    ],
+    [
+        "129154",
+        "lwhrgrd",
+        "long-wave heating rate gradient"
+    ],
+    [
+        "129155",
+        "dgrd",
+        "divergence gradient"
+    ],
+    [
+        "129156",
+        "ghgrd",
+        "height gradient"
+    ],
+    [
+        "129157",
+        "rgrd",
+        "relative humidity gradient"
+    ],
+    [
+        "129158",
+        "tspgrd",
+        "tendency of surface pressure gradient"
+    ],
+    [
+        "129159",
+        "blhgrd",
+        "boundary layer height gradient"
+    ],
+    [
+        "129160",
+        "sdorgrd",
+        "standard deviation of orography gradient"
+    ],
+    [
+        "129161",
+        "isorgrd",
+        "anisotropy of sub-gridscale orography gradient"
+    ],
+    [
+        "129162",
+        "anorgrd",
+        "angle of sub-gridscale orography gradient"
+    ],
+    [
+        "129163",
+        "slorgrd",
+        "slope of sub-gridscale orography gradient"
+    ],
+    [
+        "129164",
+        "tccgrd",
+        "total cloud cover gradient"
+    ],
+    [
+        "129165",
+        "10ugrd",
+        "10 metre u wind component gradient"
+    ],
+    [
+        "129166",
+        "10vgrd",
+        "10 metre v wind component gradient"
+    ],
+    [
+        "129167",
+        "2tgrd",
+        "2 metre temperature gradient"
+    ],
+    [
+        "129168",
+        "2dgrd",
+        "2 metre dewpoint temperature gradient"
+    ],
+    [
+        "129169",
+        "ssrdgrd",
+        "surface solar radiation downwards gradient"
+    ],
+    [
+        "129170",
+        "stl2grd",
+        "soil temperature level 2 gradient"
+    ],
+    [
+        "129171",
+        "swl2grd",
+        "soil wetness level 2 gradient"
+    ],
+    [
+        "129172",
+        "lsmgrd",
+        "land-sea mask gradient"
+    ],
+    [
+        "129173",
+        "srgrd",
+        "surface roughness gradient"
+    ],
+    [
+        "129174",
+        "algrd",
+        "albedo gradient"
+    ],
+    [
+        "129175",
+        "strdgrd",
+        "surface thermal radiation downwards gradient"
+    ],
+    [
+        "129176",
+        "ssrgrd",
+        "surface net solar radiation gradient"
+    ],
+    [
+        "129177",
+        "strgrd",
+        "surface net thermal radiation gradient"
+    ],
+    [
+        "129178",
+        "tsrgrd",
+        "top net solar radiation gradient"
+    ],
+    [
+        "129179",
+        "ttrgrd",
+        "top net thermal radiation gradient"
+    ],
+    [
+        "129180",
+        "ewssgrd",
+        "east-west surface stress gradient"
+    ],
+    [
+        "129181",
+        "nsssgrd",
+        "north-south surface stress gradient"
+    ],
+    [
+        "129182",
+        "egrd",
+        "evaporation gradient"
+    ],
+    [
+        "129183",
+        "stl3grd",
+        "soil temperature level 3 gradient"
+    ],
+    [
+        "129184",
+        "swl3grd",
+        "soil wetness level 3 gradient"
+    ],
+    [
+        "129185",
+        "cccgrd",
+        "convective cloud cover gradient"
+    ],
+    [
+        "129186",
+        "lccgrd",
+        "low cloud cover gradient"
+    ],
+    [
+        "129187",
+        "mccgrd",
+        "medium cloud cover gradient"
+    ],
+    [
+        "129188",
+        "hccgrd",
+        "high cloud cover gradient"
+    ],
+    [
+        "129189",
+        "sundgrd",
+        "sunshine duration gradient"
+    ],
+    [
+        "129190",
+        "ewovgrd",
+        "east-west component of sub-gridscale orographic variance gradient"
+    ],
+    [
+        "129191",
+        "nsovgrd",
+        "north-south component of sub-gridscale orographic variance gradient"
+    ],
+    [
+        "129192",
+        "nwovgrd",
+        "north-west/south-east component of sub-gridscale orographic variance gradient"
+    ],
+    [
+        "129193",
+        "neovgrd",
+        "north-east/south-west component of sub-gridscale orographic variance gradient"
+    ],
+    [
+        "129194",
+        "btmpgrd",
+        "brightness temperature gradient"
+    ],
+    [
+        "129195",
+        "lgwsgrd",
+        "longitudinal component of gravity wave stress gradient"
+    ],
+    [
+        "129196",
+        "mgwsgrd",
+        "meridional component of gravity wave stress gradient"
+    ],
+    [
+        "129197",
+        "gwdgrd",
+        "gravity wave dissipation gradient"
+    ],
+    [
+        "129198",
+        "srcgrd",
+        "skin reservoir content gradient"
+    ],
+    [
+        "129199",
+        "veggrd",
+        "vegetation fraction gradient"
+    ],
+    [
+        "129200",
+        "vsogrd",
+        "variance of sub-gridscale orography gradient"
+    ],
+    [
+        "129201",
+        "mx2tgrd",
+        "maximum temperature at 2 metres since previous post-processing gradient"
+    ],
+    [
+        "129202",
+        "mn2tgrd",
+        "minimum temperature at 2 metres since previous post-processing gradient"
+    ],
+    [
+        "129203",
+        "o3grd",
+        "ozone mass mixing ratio gradient"
+    ],
+    [
+        "129204",
+        "pawgrd",
+        "precipitation analysis weights gradient"
+    ],
+    [
+        "129205",
+        "rogrd",
+        "runoff gradient"
+    ],
+    [
+        "129206",
+        "tco3grd",
+        "total column ozone gradient"
+    ],
+    [
+        "129207",
+        "10sigrd",
+        "10 metre wind speed gradient"
+    ],
+    [
+        "129208",
+        "tsrcgrd",
+        "top net solar radiation, clear sky gradient"
+    ],
+    [
+        "129209",
+        "ttrcgrd",
+        "top net thermal radiation, clear sky gradient"
+    ],
+    [
+        "129210",
+        "ssrcgrd",
+        "surface net solar radiation, clear sky gradient"
+    ],
+    [
+        "129211",
+        "strcgrd",
+        "surface net thermal radiation, clear sky gradient"
+    ],
+    [
+        "129212",
+        "tisrgrd",
+        "toa incident solar radiation gradient"
+    ],
+    [
+        "129214",
+        "dhrgrd",
+        "diabatic heating by radiation gradient"
+    ],
+    [
+        "129215",
+        "dhvdgrd",
+        "diabatic heating by vertical diffusion gradient"
+    ],
+    [
+        "129216",
+        "dhccgrd",
+        "diabatic heating by cumulus convection gradient"
+    ],
+    [
+        "129217",
+        "dhlcgrd",
+        "diabatic heating large-scale condensation gradient"
+    ],
+    [
+        "129218",
+        "vdzwgrd",
+        "vertical diffusion of zonal wind gradient"
+    ],
+    [
+        "129219",
+        "vdmwgrd",
+        "vertical diffusion of meridional wind gradient"
+    ],
+    [
+        "129220",
+        "ewgdgrd",
+        "east-west gravity wave drag tendency gradient"
+    ],
+    [
+        "129221",
+        "nsgdgrd",
+        "north-south gravity wave drag tendency gradient"
+    ],
+    [
+        "129222",
+        "ctzwgrd",
+        "convective tendency of zonal wind gradient"
+    ],
+    [
+        "129223",
+        "ctmwgrd",
+        "convective tendency of meridional wind gradient"
+    ],
+    [
+        "129224",
+        "vdhgrd",
+        "vertical diffusion of humidity gradient"
+    ],
+    [
+        "129225",
+        "htccgrd",
+        "humidity tendency by cumulus convection gradient"
+    ],
+    [
+        "129226",
+        "htlcgrd",
+        "humidity tendency by large-scale condensation gradient"
+    ],
+    [
+        "129227",
+        "crnhgrd",
+        "change from removal of negative humidity gradient"
+    ],
+    [
+        "129228",
+        "tpgrd",
+        "total precipitation gradient"
+    ],
+    [
+        "129229",
+        "iewsgrd",
+        "instantaneous x surface stress gradient"
+    ],
+    [
+        "129230",
+        "inssgrd",
+        "instantaneous y surface stress gradient"
+    ],
+    [
+        "129231",
+        "ishfgrd",
+        "instantaneous surface heat flux gradient"
+    ],
+    [
+        "129232",
+        "iegrd",
+        "instantaneous moisture flux gradient"
+    ],
+    [
+        "129233",
+        "asqgrd",
+        "apparent surface humidity gradient"
+    ],
+    [
+        "129234",
+        "lsrhgrd",
+        "logarithm of surface roughness length for heat gradient"
+    ],
+    [
+        "129235",
+        "sktgrd",
+        "skin temperature gradient"
+    ],
+    [
+        "129236",
+        "stl4grd",
+        "soil temperature level 4 gradient"
+    ],
+    [
+        "129237",
+        "swl4grd",
+        "soil wetness level 4 gradient"
+    ],
+    [
+        "129238",
+        "tsngrd",
+        "temperature of snow layer gradient"
+    ],
+    [
+        "129239",
+        "csfgrd",
+        "convective snowfall gradient"
+    ],
+    [
+        "129240",
+        "lsfgrd",
+        "large scale snowfall gradient"
+    ],
+    [
+        "129241",
+        "acfgrd",
+        "accumulated cloud fraction tendency gradient"
+    ],
+    [
+        "129242",
+        "alwgrd",
+        "accumulated liquid water tendency gradient"
+    ],
+    [
+        "129243",
+        "falgrd",
+        "forecast albedo gradient"
+    ],
+    [
+        "129244",
+        "fsrgrd",
+        "forecast surface roughness gradient"
+    ],
+    [
+        "129245",
+        "flsrgrd",
+        "forecast logarithm of surface roughness for heat gradient"
+    ],
+    [
+        "129246",
+        "clwcgrd",
+        "specific cloud liquid water content gradient"
+    ],
+    [
+        "129247",
+        "ciwcgrd",
+        "specific cloud ice water content gradient"
+    ],
+    [
+        "129248",
+        "ccgrd",
+        "cloud cover gradient"
+    ],
+    [
+        "129249",
+        "aiwgrd",
+        "accumulated ice water tendency gradient"
+    ],
+    [
+        "129250",
+        "icegrd",
+        "ice age gradient"
+    ],
+    [
+        "129251",
+        "attegrd",
+        "adiabatic tendency of temperature gradient"
+    ],
+    [
+        "129252",
+        "athegrd",
+        "adiabatic tendency of humidity gradient"
+    ],
+    [
+        "129253",
+        "atzegrd",
+        "adiabatic tendency of zonal wind gradient"
+    ],
+    [
+        "129254",
+        "atmwgrd",
+        "adiabatic tendency of meridional wind gradient"
+    ],
+    [
+        "130208",
+        "tsru",
+        "top solar radiation upward"
+    ],
+    [
+        "130209",
+        "ttru",
+        "top thermal radiation upward"
+    ],
+    [
+        "130210",
+        "tsuc",
+        "top solar radiation upward, clear sky"
+    ],
+    [
+        "130211",
+        "ttuc",
+        "top thermal radiation upward, clear sky"
+    ],
+    [
+        "130212",
+        "clw",
+        "cloud liquid water"
+    ],
+    [
+        "130213",
+        "cf",
+        "cloud fraction"
+    ],
+    [
+        "130214",
+        "dhr",
+        "diabatic heating by radiation"
+    ],
+    [
+        "130215",
+        "dhvd",
+        "diabatic heating by vertical diffusion"
+    ],
+    [
+        "130216",
+        "dhcc",
+        "diabatic heating by cumulus convection"
+    ],
+    [
+        "130217",
+        "dhlc",
+        "diabatic heating by large-scale condensation"
+    ],
+    [
+        "130218",
+        "vdzw",
+        "vertical diffusion of zonal wind"
+    ],
+    [
+        "130219",
+        "vdmw",
+        "vertical diffusion of meridional wind"
+    ],
+    [
+        "130220",
+        "ewgd",
+        "east-west gravity wave drag"
+    ],
+    [
+        "130221",
+        "nsgd",
+        "north-south gravity wave drag"
+    ],
+    [
+        "130224",
+        "vdh",
+        "vertical diffusion of humidity"
+    ],
+    [
+        "130225",
+        "htcc",
+        "humidity tendency by cumulus convection"
+    ],
+    [
+        "130226",
+        "htlc",
+        "humidity tendency by large-scale condensation"
+    ],
+    [
+        "130228",
+        "att",
+        "adiabatic tendency of temperature"
+    ],
+    [
+        "130229",
+        "ath",
+        "adiabatic tendency of humidity"
+    ],
+    [
+        "130230",
+        "atzw",
+        "adiabatic tendency of zonal wind"
+    ],
+    [
+        "130231",
+        "atmwax",
+        "adiabatic tendency of meridional wind"
+    ],
+    [
+        "130232",
+        "mvv",
+        "mean vertical velocity"
+    ],
+    [
+        "131001",
+        "2tag2",
+        "2m temperature anomaly of at least +2k"
+    ],
+    [
+        "131002",
+        "2tag1",
+        "2m temperature anomaly of at least +1k"
+    ],
+    [
+        "131003",
+        "2tag0",
+        "2m temperature anomaly of at least 0k"
+    ],
+    [
+        "131004",
+        "2talm1",
+        "2m temperature anomaly of at most -1k"
+    ],
+    [
+        "131005",
+        "2talm2",
+        "2m temperature anomaly of at most -2k"
+    ],
+    [
+        "131006",
+        "tpag20",
+        "total precipitation anomaly of at least 20 mm"
+    ],
+    [
+        "131007",
+        "tpag10",
+        "total precipitation anomaly of at least 10 mm"
+    ],
+    [
+        "131008",
+        "tpag0",
+        "total precipitation anomaly of at least 0 mm"
+    ],
+    [
+        "131009",
+        "stag0",
+        "surface temperature anomaly of at least 0k"
+    ],
+    [
+        "131010",
+        "mslag0",
+        "mean sea level pressure anomaly of at least 0 pa"
+    ],
+    [
+        "131015",
+        "h0dip",
+        "height of 0 degree isotherm probability"
+    ],
+    [
+        "131016",
+        "hslp",
+        "height of snowfall limit probability"
+    ],
+    [
+        "131017",
+        "saip",
+        "showalter index probability"
+    ],
+    [
+        "131018",
+        "whip",
+        "whiting index probability"
+    ],
+    [
+        "131020",
+        "talm2",
+        "temperature anomaly less than -2 k",
+        "temperature anomaly of at most -2 k",
+        "talm2k"
+    ],
+    [
+        "131021",
+        "tag2",
+        "temperature anomaly of at least +2 k",
+        "temperature anomaly of at least 2 k",
+        "tag2k"
+    ],
+    [
+        "131022",
+        "talm8",
+        "temperature anomaly less than -8 k",
+        "temperature anomaly of at most -8 k",
+        "talm8k"
+    ],
+    [
+        "131023",
+        "talm4",
+        "temperature anomaly less than -4 k",
+        "temperature anomaly of at most -4 k",
+        "talm4k"
+    ],
+    [
+        "131024",
+        "tag4",
+        "temperature anomaly greater than +4 k",
+        "temperature anomaly of at least 4 k",
+        "tag4k"
+    ],
+    [
+        "131025",
+        "tag8",
+        "temperature anomaly greater than +8 k",
+        "temperature anomaly of at least 8 k",
+        "tag8k"
+    ],
+    [
+        "131049",
+        "10gp",
+        "10 metre wind gust probability"
+    ],
+    [
+        "131059",
+        "capep",
+        "convective available potential energy probability"
+    ],
+    [
+        "131060",
+        "tpg1",
+        "total precipitation of at least 1 mm"
+    ],
+    [
+        "131061",
+        "tpg5",
+        "total precipitation of at least 5 mm"
+    ],
+    [
+        "131062",
+        "tpg10",
+        "total precipitation of at least 10 mm"
+    ],
+    [
+        "131063",
+        "tpg20",
+        "total precipitation of at least 20 mm"
+    ],
+    [
+        "131064",
+        "tpl01",
+        "total precipitation less than 0.1 mm",
+        "tplo1"
+    ],
+    [
+        "131065",
+        "tprl1",
+        "total precipitation rate less than 1 mm/day"
+    ],
+    [
+        "131066",
+        "tprg3",
+        "total precipitation rate of at least 3 mm/day"
+    ],
+    [
+        "131067",
+        "tprg5",
+        "total precipitation rate of at least 5 mm/day"
+    ],
+    [
+        "131068",
+        "10spg10",
+        "10 metre wind speed of at least 10 m/s"
+    ],
+    [
+        "131069",
+        "10spg15",
+        "10 metre wind speed of at least 15 m/s"
+    ],
+    [
+        "131070",
+        "10fgg15",
+        "10 metre wind gust of at least 15 m/s"
+    ],
+    [
+        "131071",
+        "10fgg20",
+        "10 metre wind gust of at least 20 m/s"
+    ],
+    [
+        "131072",
+        "10fgg25",
+        "10 metre wind gust of at least 25 m/s"
+    ],
+    [
+        "131073",
+        "2tl273",
+        "2 metre temperature less than 273.15 k"
+    ],
+    [
+        "131074",
+        "swhg2",
+        "significant wave height of at least 2 m"
+    ],
+    [
+        "131075",
+        "swhg4",
+        "significant wave height of at least 4 m"
+    ],
+    [
+        "131076",
+        "swhg6",
+        "significant wave height of at least 6 m"
+    ],
+    [
+        "131077",
+        "swhg8",
+        "significant wave height of at least 8 m"
+    ],
+    [
+        "131078",
+        "mwpg8",
+        "mean wave period of at least 8 s"
+    ],
+    [
+        "131079",
+        "mwpg10",
+        "mean wave period of at least 10 s"
+    ],
+    [
+        "131080",
+        "mwpg12",
+        "mean wave period of at least 12 s"
+    ],
+    [
+        "131081",
+        "mwpg15",
+        "mean wave period of at least 15 s"
+    ],
+    [
+        "131082",
+        "tpg40",
+        "total precipitation of at least 40 mm"
+    ],
+    [
+        "131083",
+        "tpg60",
+        "total precipitation of at least 60 mm"
+    ],
+    [
+        "131084",
+        "tpg80",
+        "total precipitation of at least 80 mm"
+    ],
+    [
+        "131085",
+        "tpg100",
+        "total precipitation of at least 100 mm"
+    ],
+    [
+        "131086",
+        "tpg150",
+        "total precipitation of at least 150 mm"
+    ],
+    [
+        "131087",
+        "tpg200",
+        "total precipitation of at least 200 mm"
+    ],
+    [
+        "131088",
+        "tpg300",
+        "total precipitation of at least 300 mm"
+    ],
+    [
+        "131129",
+        "zp",
+        "geopotential probability"
+    ],
+    [
+        "131130",
+        "tap",
+        "temperature anomaly probability"
+    ],
+    [
+        "131139",
+        "stl1p",
+        "soil temperature level 1 probability"
+    ],
+    [
+        "131144",
+        "sfp",
+        "snowfall (convective + stratiform) probability"
+    ],
+    [
+        "131151",
+        "mslpp",
+        "mean sea level pressure probability"
+    ],
+    [
+        "131164",
+        "tccp",
+        "total cloud cover probability"
+    ],
+    [
+        "131165",
+        "10sp",
+        "10 metre speed probability"
+    ],
+    [
+        "131167",
+        "2tp",
+        "2 metre temperature probability"
+    ],
+    [
+        "131201",
+        "mx2tp",
+        "maximum 2 metre temperature probability"
+    ],
+    [
+        "131202",
+        "mn2tp",
+        "minimum 2 metre temperature probability"
+    ],
+    [
+        "131228",
+        "tpp",
+        "total precipitation probability"
+    ],
+    [
+        "131229",
+        "swhp",
+        "significant wave height probability"
+    ],
+    [
+        "131232",
+        "mwpp",
+        "mean wave period probability"
+    ],
+    [
+        "132049",
+        "10fgi",
+        "10 metre wind gust index",
+        "49.132",
+        "10gi"
+    ],
+    [
+        "132144",
+        "sfi",
+        "snowfall index",
+        "144.132"
+    ],
+    [
+        "132165",
+        "10wsi",
+        "10 metre speed index",
+        "165.132"
+    ],
+    [
+        "132167",
+        "2ti",
+        "2 metre temperature index",
+        "167.132"
+    ],
+    [
+        "214001",
+        "uvcossza",
+        " cosine of solar zenith angle"
+    ],
+    [
+        "132201",
+        "mx2ti",
+        "maximum temperature at 2 metres index",
+        "201.132"
+    ],
+    [
+        "132202",
+        "mn2ti",
+        "minimum temperature at 2 metres index",
+        "202.132"
+    ],
+    [
+        "132228",
+        "tpi",
+        "total precipitation index"
+    ],
+    [
+        "133001",
+        "2tplm10",
+        "2m temperature probability less than -10 c"
+    ],
+    [
+        "133002",
+        "2tplm5",
+        "2m temperature probability less than -5 c"
+    ],
+    [
+        "133003",
+        "2tpl0",
+        "2m temperature probability less than 0 c"
+    ],
+    [
+        "133004",
+        "2tpl5",
+        "2m temperature probability less than 5 c"
+    ],
+    [
+        "133005",
+        "2tpl10",
+        "2m temperature probability less than 10 c"
+    ],
+    [
+        "133006",
+        "2tpg25",
+        "2m temperature probability greater than 25 c"
+    ],
+    [
+        "133007",
+        "2tpg30",
+        "2m temperature probability greater than 30 c"
+    ],
+    [
+        "133008",
+        "2tpg35",
+        "2m temperature probability greater than 35 c"
+    ],
+    [
+        "133009",
+        "2tpg40",
+        "2m temperature probability greater than 40 c"
+    ],
+    [
+        "133010",
+        "2tpg45",
+        "2m temperature probability greater than 45 c"
+    ],
+    [
+        "133011",
+        "mn2tplm10",
+        "minimum 2 metre temperature probability less than -10 c"
+    ],
+    [
+        "133012",
+        "mn2tplm5",
+        "minimum 2 metre temperature probability less than -5 c"
+    ],
+    [
+        "133013",
+        "mn2tpl0",
+        "minimum 2 metre temperature probability less than 0 c"
+    ],
+    [
+        "133014",
+        "mn2tpl5",
+        "minimum 2 metre temperature probability less than 5 c"
+    ],
+    [
+        "133015",
+        "mn2tpl10",
+        "minimum 2 metre temperature probability less than 10 c"
+    ],
+    [
+        "133016",
+        "mx2tpg25",
+        "maximum 2 metre temperature probability greater than 25 c"
+    ],
+    [
+        "133017",
+        "mx2tpg30",
+        "maximum 2 metre temperature probability greater than 30 c"
+    ],
+    [
+        "133018",
+        "mx2tpg35",
+        "maximum 2 metre temperature probability greater than 35 c"
+    ],
+    [
+        "133019",
+        "mx2tpg40",
+        "maximum 2 metre temperature probability greater than 40 c"
+    ],
+    [
+        "133020",
+        "mx2tpg45",
+        "maximum 2 metre temperature probability greater than 45 c"
+    ],
+    [
+        "133021",
+        "10spg10",
+        "10 metre wind speed probability of at least 10 m/s"
+    ],
+    [
+        "133022",
+        "10spg15",
+        "10 metre wind speed probability of at least 15 m/s"
+    ],
+    [
+        "133023",
+        "10spg20",
+        "10 metre wind speed probability of at least 20 m/s"
+    ],
+    [
+        "133024",
+        "10spg35",
+        "10 metre wind speed probability of at least 35 m/s"
+    ],
+    [
+        "133025",
+        "10spg50",
+        "10 metre wind speed probability of at least 50 m/s"
+    ],
+    [
+        "133026",
+        "10gpg20",
+        "10 metre wind gust probability of at least 20 m/s"
+    ],
+    [
+        "133027",
+        "10gpg35",
+        "10 metre wind gust probability of at least 35 m/s"
+    ],
+    [
+        "133028",
+        "10gpg50",
+        "10 metre wind gust probability of at least 50 m/s"
+    ],
+    [
+        "133029",
+        "10gpg75",
+        "10 metre wind gust probability of at least 75 m/s"
+    ],
+    [
+        "133030",
+        "10gpg100",
+        "10 metre wind gust probability of at least 100 m/s"
+    ],
+    [
+        "133031",
+        "tppg1",
+        "total precipitation probability of at least 1 mm"
+    ],
+    [
+        "133032",
+        "tppg5",
+        "total precipitation probability of at least 5 mm"
+    ],
+    [
+        "133033",
+        "tppg10",
+        "total precipitation probability of at least 10 mm"
+    ],
+    [
+        "133034",
+        "tppg20",
+        "total precipitation probability of at least 20 mm"
+    ],
+    [
+        "133035",
+        "tppg40",
+        "total precipitation probability of at least 40 mm"
+    ],
+    [
+        "133036",
+        "tppg60",
+        "total precipitation probability of at least 60 mm"
+    ],
+    [
+        "133037",
+        "tppg80",
+        "total precipitation probability of at least 80 mm"
+    ],
+    [
+        "133038",
+        "tppg100",
+        "total precipitation probability of at least 100 mm"
+    ],
+    [
+        "133039",
+        "tppg150",
+        "total precipitation probability of at least 150 mm"
+    ],
+    [
+        "133040",
+        "tppg200",
+        "total precipitation probability of at least 200 mm"
+    ],
+    [
+        "133041",
+        "tppg300",
+        "total precipitation probability of at least 300 mm"
+    ],
+    [
+        "133042",
+        "sfpg1",
+        "snowfall probability of at least 1 mm"
+    ],
+    [
+        "133043",
+        "sfpg5",
+        "snowfall probability of at least 5 mm"
+    ],
+    [
+        "133044",
+        "sfpg10",
+        "snowfall probability of at least 10 mm"
+    ],
+    [
+        "133045",
+        "sfpg20",
+        "snowfall probability of at least 20 mm"
+    ],
+    [
+        "133046",
+        "sfpg40",
+        "snowfall probability of at least 40 mm"
+    ],
+    [
+        "133047",
+        "sfpg60",
+        "snowfall probability of at least 60 mm"
+    ],
+    [
+        "133048",
+        "sfpg80",
+        "snowfall probability of at least 80 mm"
+    ],
+    [
+        "133049",
+        "sfpg100",
+        "snowfall probability of at least 100 mm"
+    ],
+    [
+        "133050",
+        "sfpg150",
+        "snowfall probability of at least 150 mm"
+    ],
+    [
+        "133051",
+        "sfpg200",
+        "snowfall probability of at least 200 mm"
+    ],
+    [
+        "133052",
+        "sfpg300",
+        "snowfall probability of at least 300 mm"
+    ],
+    [
+        "133053",
+        "tccpg10",
+        "total cloud cover probability greater than 10%"
+    ],
+    [
+        "133054",
+        "tccpg20",
+        "total cloud cover probability greater than 20%"
+    ],
+    [
+        "133055",
+        "tccpg30",
+        "total cloud cover probability greater than 30%"
+    ],
+    [
+        "133056",
+        "tccpg40",
+        "total cloud cover probability greater than 40%"
+    ],
+    [
+        "133057",
+        "tccpg50",
+        "total cloud cover probability greater than 50%"
+    ],
+    [
+        "133058",
+        "tccpg60",
+        "total cloud cover probability greater than 60%"
+    ],
+    [
+        "133059",
+        "tccpg70",
+        "total cloud cover probability greater than 70%"
+    ],
+    [
+        "133060",
+        "tccpg80",
+        "total cloud cover probability greater than 80%"
+    ],
+    [
+        "133061",
+        "tccpg90",
+        "total cloud cover probability greater than 90%"
+    ],
+    [
+        "133062",
+        "tccpg99",
+        "total cloud cover probability greater than 99%"
+    ],
+    [
+        "133063",
+        "hccpg10",
+        "high cloud cover probability greater than 10%"
+    ],
+    [
+        "133064",
+        "hccpg20",
+        "high cloud cover probability greater than 20%"
+    ],
+    [
+        "133065",
+        "hccpg30",
+        "high cloud cover probability greater than 30%"
+    ],
+    [
+        "133066",
+        "hccpg40",
+        "high cloud cover probability greater than 40%"
+    ],
+    [
+        "133067",
+        "hccpg50",
+        "high cloud cover probability greater than 50%"
+    ],
+    [
+        "133068",
+        "hccpg60",
+        "high cloud cover probability greater than 60%"
+    ],
+    [
+        "133069",
+        "hccpg70",
+        "high cloud cover probability greater than 70%"
+    ],
+    [
+        "133070",
+        "hccpg80",
+        "high cloud cover probability greater than 80%"
+    ],
+    [
+        "133071",
+        "hccpg90",
+        "high cloud cover probability greater than 90%"
+    ],
+    [
+        "133072",
+        "hccpg99",
+        "high cloud cover probability greater than 99%"
+    ],
+    [
+        "133073",
+        "mccpg10",
+        "medium cloud cover probability greater than 10%"
+    ],
+    [
+        "133074",
+        "mccpg20",
+        "medium cloud cover probability greater than 20%"
+    ],
+    [
+        "133075",
+        "mccpg30",
+        "medium cloud cover probability greater than 30%"
+    ],
+    [
+        "133076",
+        "mccpg40",
+        "medium cloud cover probability greater than 40%"
+    ],
+    [
+        "133077",
+        "mccpg50",
+        "medium cloud cover probability greater than 50%"
+    ],
+    [
+        "133078",
+        "mccpg60",
+        "medium cloud cover probability greater than 60%"
+    ],
+    [
+        "133079",
+        "mccpg70",
+        "medium cloud cover probability greater than 70%"
+    ],
+    [
+        "133080",
+        "mccpg80",
+        "medium cloud cover probability greater than 80%"
+    ],
+    [
+        "133081",
+        "mccpg90",
+        "medium cloud cover probability greater than 90%"
+    ],
+    [
+        "133082",
+        "mccpg99",
+        "medium cloud cover probability greater than 99%"
+    ],
+    [
+        "133083",
+        "lccpg10",
+        "low cloud cover probability greater than 10%"
+    ],
+    [
+        "133084",
+        "lccpg20",
+        "low cloud cover probability greater than 20%"
+    ],
+    [
+        "133085",
+        "lccpg30",
+        "low cloud cover probability greater than 30%"
+    ],
+    [
+        "133086",
+        "lccpg40",
+        "low cloud cover probability greater than 40%"
+    ],
+    [
+        "133087",
+        "lccpg50",
+        "low cloud cover probability greater than 50%"
+    ],
+    [
+        "133088",
+        "lccpg60",
+        "low cloud cover probability greater than 60%"
+    ],
+    [
+        "133089",
+        "lccpg70",
+        "low cloud cover probability greater than 70%"
+    ],
+    [
+        "133090",
+        "lccpg80",
+        "low cloud cover probability greater than 80%"
+    ],
+    [
+        "133091",
+        "lccpg90",
+        "low cloud cover probability greater than 90%"
+    ],
+    [
+        "133092",
+        "lccpg99",
+        "low cloud cover probability greater than 99%"
+    ],
+    [
+        "140200",
+        "maxswh",
+        "maximum of significant wave height"
+    ],
+    [
+        "140217",
+        "tmax",
+        "period corresponding to maximum individual wave height"
+    ],
+    [
+        "140218",
+        "hmax",
+        "maximum individual wave height"
+    ],
+    [
+        "140219",
+        "wmb",
+        "model bathymetry"
+    ],
+    [
+        "140220",
+        "mp1",
+        "mean wave period based on first moment"
+    ],
+    [
+        "140221",
+        "mp2",
+        "mean wave period based on second moment"
+    ],
+    [
+        "140222",
+        "wdw",
+        "wave spectral directional width"
+    ],
+    [
+        "140223",
+        "p1ww",
+        "mean wave period based on first moment for wind waves"
+    ],
+    [
+        "140224",
+        "p2ww",
+        "mean wave period based on second moment for wind waves"
+    ],
+    [
+        "140225",
+        "dwww",
+        "wave spectral directional width for wind waves"
+    ],
+    [
+        "140226",
+        "p1ps",
+        "mean wave period based on first moment for swell"
+    ],
+    [
+        "140227",
+        "p2ps",
+        "mean wave period based on second moment for swell"
+    ],
+    [
+        "140228",
+        "dwps",
+        "wave spectral directional width for swell"
+    ],
+    [
+        "140229",
+        "swh",
+        "significant height of combined wind waves and swell"
+    ],
+    [
+        "140230",
+        "mwd",
+        "mean wave direction"
+    ],
+    [
+        "140231",
+        "pp1d",
+        "peak period of 1d spectra"
+    ],
+    [
+        "140232",
+        "mwp",
+        "mean wave period"
+    ],
+    [
+        "140233",
+        "cdww",
+        "coefficient of drag with waves"
+    ],
+    [
+        "140234",
+        "shww",
+        "significant height of wind waves"
+    ],
+    [
+        "140235",
+        "mdww",
+        "mean direction of wind waves"
+    ],
+    [
+        "140236",
+        "mpww",
+        "mean period of wind waves"
+    ],
+    [
+        "140237",
+        "shts",
+        "significant height of total swell"
+    ],
+    [
+        "140238",
+        "mdts",
+        "mean direction of total swell"
+    ],
+    [
+        "140239",
+        "mpts",
+        "mean period of total swell"
+    ],
+    [
+        "140240",
+        "sdhs",
+        "standard deviation wave height"
+    ],
+    [
+        "140241",
+        "mu10",
+        "mean of 10 metre wind speed"
+    ],
+    [
+        "140242",
+        "mdwi",
+        "mean wind direction"
+    ],
+    [
+        "140243",
+        "sdu",
+        "standard deviation of 10 metre wind speed"
+    ],
+    [
+        "140244",
+        "msqs",
+        "mean square slope of waves"
+    ],
+    [
+        "140245",
+        "wind",
+        "10 metre wind speed"
+    ],
+    [
+        "140246",
+        "awh",
+        "altimeter wave height"
+    ],
+    [
+        "140247",
+        "acwh",
+        "altimeter corrected wave height"
+    ],
+    [
+        "140248",
+        "arrc",
+        "altimeter range relative correction"
+    ],
+    [
+        "140249",
+        "dwi",
+        "10 metre wind direction"
+    ],
+    [
+        "140250",
+        "2dsp",
+        "2d wave spectra (multiple)"
+    ],
+    [
+        "140251",
+        "2dfd",
+        "2d wave spectra (single)"
+    ],
+    [
+        "140252",
+        "wsk",
+        "wave spectral kurtosis"
+    ],
+    [
+        "140253",
+        "bfi",
+        "benjamin-feir index"
+    ],
+    [
+        "140254",
+        "wsp",
+        "wave spectral peakedness"
+    ],
+    [
+        "150129",
+        "ocpt",
+        "ocean potential temperature"
+    ],
+    [
+        "150130",
+        "ocs",
+        "ocean salinity"
+    ],
+    [
+        "150131",
+        "ocpd",
+        "ocean potential density"
+    ],
+    [
+        "150133",
+        "ocu",
+        "ocean u wind component"
+    ],
+    [
+        "150134",
+        "ocv",
+        "ocean v wind component"
+    ],
+    [
+        "150135",
+        "ocw",
+        "ocean w wind component"
+    ],
+    [
+        "150137",
+        "rn",
+        "richardson number"
+    ],
+    [
+        "150139",
+        "uv",
+        "u*v product"
+    ],
+    [
+        "150140",
+        "ut",
+        "u*t product"
+    ],
+    [
+        "150141",
+        "vt",
+        "v*t product"
+    ],
+    [
+        "150142",
+        "uu",
+        "u*u product"
+    ],
+    [
+        "150143",
+        "vv",
+        "v*v product"
+    ],
+    [
+        "150144",
+        "uv - u~v~"
+    ],
+    [
+        "150145",
+        "ut - u~t~"
+    ],
+    [
+        "150146",
+        "vt - v~t~"
+    ],
+    [
+        "150147",
+        "uu - u~u~"
+    ],
+    [
+        "150148",
+        "vv - v~v~"
+    ],
+    [
+        "150152",
+        "sl",
+        "sea level"
+    ],
+    [
+        "150153",
+        "barotropic stream function"
+    ],
+    [
+        "150154",
+        "mld",
+        "mixed layer depth"
+    ],
+    [
+        "150155",
+        "depth"
+    ],
+    [
+        "150168",
+        "u stress"
+    ],
+    [
+        "150169",
+        "v stress"
+    ],
+    [
+        "150170",
+        "turbulent kinetic energy input"
+    ],
+    [
+        "150171",
+        "nsf",
+        "net surface heat flux"
+    ],
+    [
+        "150172",
+        "surface solar radiation"
+    ],
+    [
+        "150173",
+        "p-e"
+    ],
+    [
+        "150180",
+        "diagnosed sea surface temperature error"
+    ],
+    [
+        "150181",
+        "heat flux correction"
+    ],
+    [
+        "150182",
+        "observed sea surface temperature"
+    ],
+    [
+        "150183",
+        "observed heat flux"
+    ],
+    [
+        "151128",
+        "in situ temperature"
+    ],
+    [
+        "151129",
+        "ocpt",
+        "ocean potential temperature"
+    ],
+    [
+        "151130",
+        "s",
+        "salinity"
+    ],
+    [
+        "151131",
+        "ocu",
+        "ocean current zonal component"
+    ],
+    [
+        "151132",
+        "ocv",
+        "ocean current meridional component"
+    ],
+    [
+        "151133",
+        "ocw",
+        "ocean current vertical component"
+    ],
+    [
+        "151134",
+        "mst",
+        "modulus of strain rate tensor"
+    ],
+    [
+        "151135",
+        "vvs",
+        "vertical viscosity"
+    ],
+    [
+        "151136",
+        "vdf",
+        "vertical diffusivity"
+    ],
+    [
+        "151137",
+        "dep",
+        "bottom level depth"
+    ],
+    [
+        "151138",
+        "sth",
+        "sigma-theta"
+    ],
+    [
+        "151139",
+        "rn",
+        "richardson number"
+    ],
+    [
+        "151140",
+        "uv",
+        "uv product"
+    ],
+    [
+        "151141",
+        "ut",
+        "ut product"
+    ],
+    [
+        "151142",
+        "vt",
+        "vt product"
+    ],
+    [
+        "151143",
+        "uu",
+        "uu product"
+    ],
+    [
+        "151144",
+        "vv",
+        "vv product"
+    ],
+    [
+        "151145",
+        "sl",
+        "sea level"
+    ],
+    [
+        "151146",
+        "sl_1",
+        "sea level previous timestep"
+    ],
+    [
+        "151147",
+        "bsf",
+        "barotropic stream function"
+    ],
+    [
+        "151148",
+        "mld",
+        "mixed layer depth"
+    ],
+    [
+        "151149",
+        "btp",
+        "bottom pressure (equivalent height)"
+    ],
+    [
+        "151150",
+        "sh",
+        "steric height"
+    ],
+    [
+        "151151",
+        "crl",
+        "curl of wind stress"
+    ],
+    [
+        "151152",
+        "divergence of wind stress"
+    ],
+    [
+        "151153",
+        "tax",
+        "u stress"
+    ],
+    [
+        "151154",
+        "tay",
+        "v stress"
+    ],
+    [
+        "151155",
+        "tki",
+        "turbulent kinetic energy input"
+    ],
+    [
+        "151156",
+        "nsf",
+        "net surface heat flux"
+    ],
+    [
+        "151157",
+        "asr",
+        "absorbed solar radiation"
+    ],
+    [
+        "151158",
+        "pme",
+        "precipitation - evaporation"
+    ],
+    [
+        "151159",
+        "sst",
+        "specified sea surface temperature"
+    ],
+    [
+        "151160",
+        "shf",
+        "specified surface heat flux"
+    ],
+    [
+        "151161",
+        "dte",
+        "diagnosed sea surface temperature error"
+    ],
+    [
+        "151162",
+        "hfc",
+        "heat flux correction"
+    ],
+    [
+        "151163",
+        "20d",
+        "20 degrees isotherm depth"
+    ],
+    [
+        "151164",
+        "tav300",
+        "average potential temperature in the upper 300m"
+    ],
+    [
+        "151165",
+        "uba1",
+        "vertically integrated zonal velocity (previous time step)"
+    ],
+    [
+        "151166",
+        "vba1",
+        "vertically integrated meridional velocity (previous time step)"
+    ],
+    [
+        "151167",
+        "ztr",
+        "vertically integrated zonal volume transport"
+    ],
+    [
+        "151168",
+        "mtr",
+        "vertically integrated meridional volume transport"
+    ],
+    [
+        "151169",
+        "zht",
+        "vertically integrated zonal heat transport"
+    ],
+    [
+        "151170",
+        "mht",
+        "vertically integrated meridional heat transport"
+    ],
+    [
+        "151171",
+        "umax",
+        "u velocity maximum"
+    ],
+    [
+        "151172",
+        "dumax",
+        "depth of the velocity maximum"
+    ],
+    [
+        "151173",
+        "smax",
+        "salinity maximum"
+    ],
+    [
+        "151174",
+        "dsmax",
+        "depth of salinity maximum"
+    ],
+    [
+        "151175",
+        "sav300",
+        "average salinity in the upper 300m"
+    ],
+    [
+        "151176",
+        "ldp",
+        "layer thickness at scalar points"
+    ],
+    [
+        "151177",
+        "ldu",
+        "layer thickness at vector points"
+    ],
+    [
+        "151178",
+        "pti",
+        "potential temperature increment"
+    ],
+    [
+        "151179",
+        "ptae",
+        "potential temperature analysis error"
+    ],
+    [
+        "151180",
+        "bpt",
+        "background potential temperature"
+    ],
+    [
+        "151181",
+        "apt",
+        "analysed potential temperature"
+    ],
+    [
+        "151182",
+        "ptbe",
+        "potential temperature background error"
+    ],
+    [
+        "151183",
+        "as",
+        "analysed salinity"
+    ],
+    [
+        "151184",
+        "sali",
+        "salinity increment"
+    ],
+    [
+        "151185",
+        "ebt",
+        "estimated bias in temperature"
+    ],
+    [
+        "151186",
+        "ebs",
+        "estimated bias in salinity"
+    ],
+    [
+        "151187",
+        "uvi",
+        "zonal velocity increment (from balance operator)"
+    ],
+    [
+        "151188",
+        "vvi",
+        "meridional velocity increment (from balance operator)"
+    ],
+    [
+        "151190",
+        "subi",
+        "salinity increment (from salinity data)"
+    ],
+    [
+        "151191",
+        "sale",
+        "salinity analysis error"
+    ],
+    [
+        "151192",
+        "bsal",
+        "background salinity"
+    ],
+    [
+        "151194",
+        "salbe",
+        "salinity background error"
+    ],
+    [
+        "151199",
+        "ebta",
+        "estimated temperature bias from assimilation"
+    ],
+    [
+        "151200",
+        "ebsa",
+        "estimated salinity bias from assimilation"
+    ],
+    [
+        "151201",
+        "lti",
+        "temperature increment from relaxation term"
+    ],
+    [
+        "151202",
+        "lsi",
+        "salinity increment from relaxation term"
+    ],
+    [
+        "151203",
+        "bzpga",
+        "bias in the zonal pressure gradient (applied)"
+    ],
+    [
+        "151204",
+        "bmpga",
+        "bias in the meridional pressure gradient (applied)"
+    ],
+    [
+        "151205",
+        "ebtl",
+        "estimated temperature bias from relaxation"
+    ],
+    [
+        "151206",
+        "ebsl",
+        "estimated salinity bias from relaxation"
+    ],
+    [
+        "151207",
+        "fgbt",
+        "first guess bias in temperature"
+    ],
+    [
+        "151208",
+        "fgbs",
+        "first guess bias in salinity"
+    ],
+    [
+        "151209",
+        "bpa",
+        "applied bias in pressure"
+    ],
+    [
+        "151210",
+        "fgbp",
+        "fg bias in pressure"
+    ],
+    [
+        "151211",
+        "pta",
+        "bias in temperature(applied)"
+    ],
+    [
+        "151212",
+        "psa",
+        "bias in salinity (applied)"
+    ],
+    [
+        "160049",
+        "10fgrea",
+        "10 metre wind gust during averaging time"
+    ],
+    [
+        "160135",
+        "wrea",
+        "vertical velocity (pressure)"
+    ],
+    [
+        "160137",
+        "pwcrea",
+        "precipitable water content"
+    ],
+    [
+        "160140",
+        "swl1rea",
+        "soil wetness level 1"
+    ],
+    [
+        "160141",
+        "sdrea",
+        "snow depth"
+    ],
+    [
+        "160142",
+        "lsprea",
+        "large-scale precipitation"
+    ],
+    [
+        "160143",
+        "cprea",
+        "convective precipitation"
+    ],
+    [
+        "160144",
+        "sfrea",
+        "snowfall"
+    ],
+    [
+        "160156",
+        "ghrea",
+        "height"
+    ],
+    [
+        "160157",
+        "rrea",
+        "relative humidity"
+    ],
+    [
+        "160171",
+        "swl2rea",
+        "soil wetness level 2"
+    ],
+    [
+        "160180",
+        "ewssrea",
+        "east-west surface stress"
+    ],
+    [
+        "160181",
+        "nsssrea",
+        "north-south surface stress"
+    ],
+    [
+        "160182",
+        "erea",
+        "evaporation"
+    ],
+    [
+        "160184",
+        "swl3rea",
+        "soil wetness level 3"
+    ],
+    [
+        "160198",
+        "srcrea",
+        "skin reservoir content"
+    ],
+    [
+        "160199",
+        "vegrea",
+        "percentage of vegetation"
+    ],
+    [
+        "160201",
+        "mx2trea",
+        "maximum temperature at 2 metres during averaging time"
+    ],
+    [
+        "160202",
+        "mn2trea",
+        "minimum temperature at 2 metres during averaging time"
+    ],
+    [
+        "160205",
+        "rorea",
+        "runoff"
+    ],
+    [
+        "160206",
+        "zzrea",
+        "standard deviation of geopotential"
+    ],
+    [
+        "160207",
+        "tzrea",
+        "covariance of temperature and geopotential"
+    ],
+    [
+        "160208",
+        "ttrea",
+        "standard deviation of temperature"
+    ],
+    [
+        "160209",
+        "qzrea",
+        "covariance of specific humidity and geopotential"
+    ],
+    [
+        "160210",
+        "qtrea",
+        "covariance of specific humidity and temperature"
+    ],
+    [
+        "160211",
+        "qqrea",
+        "standard deviation of specific humidity"
+    ],
+    [
+        "160212",
+        "uzrea",
+        "covariance of u component and geopotential"
+    ],
+    [
+        "160213",
+        "utrea",
+        "covariance of u component and temperature"
+    ],
+    [
+        "160214",
+        "uqrea",
+        "covariance of u component and specific humidity"
+    ],
+    [
+        "160215",
+        "uurea",
+        "standard deviation of u velocity"
+    ],
+    [
+        "160216",
+        "vzrea",
+        "covariance of v component and geopotential"
+    ],
+    [
+        "160217",
+        "vtrea",
+        "covariance of v component and temperature"
+    ],
+    [
+        "160218",
+        "vqrea",
+        "covariance of v component and specific humidity"
+    ],
+    [
+        "160219",
+        "vurea",
+        "covariance of v component and u component"
+    ],
+    [
+        "160220",
+        "vvrea",
+        "standard deviation of v component"
+    ],
+    [
+        "160221",
+        "wzrea",
+        "covariance of w component and geopotential"
+    ],
+    [
+        "160222",
+        "wtrea",
+        "covariance of w component and temperature"
+    ],
+    [
+        "160223",
+        "wqrea",
+        "covariance of w component and specific humidity"
+    ],
+    [
+        "160224",
+        "wurea",
+        "covariance of w component and u component"
+    ],
+    [
+        "160225",
+        "wvrea",
+        "covariance of w component and v component"
+    ],
+    [
+        "160226",
+        "wwrea",
+        "standard deviation of vertical velocity"
+    ],
+    [
+        "160231",
+        "ishfrea",
+        "instantaneous surface heat flux"
+    ],
+    [
+        "160239",
+        "csfrea",
+        "convective snowfall"
+    ],
+    [
+        "160240",
+        "lsfrea",
+        "large scale snowfall"
+    ],
+    [
+        "160241",
+        "clwcerrea",
+        "cloud liquid water content"
+    ],
+    [
+        "160242",
+        "ccrea",
+        "cloud cover"
+    ],
+    [
+        "160243",
+        "falrea",
+        "forecast albedo"
+    ],
+    [
+        "160246",
+        "10wsrea",
+        "10 metre wind speed"
+    ],
+    [
+        "160247",
+        "moflrea",
+        "momentum flux"
+    ],
+    [
+        "160249",
+        "gravity wave dissipation flux"
+    ],
+    [
+        "160254",
+        "hsdrea",
+        "heaviside beta function"
+    ],
+    [
+        "162051",
+        "surface geopotential"
+    ],
+    [
+        "162053",
+        "vima",
+        "vertical integral of mass of atmosphere"
+    ],
+    [
+        "162054",
+        "vit",
+        "vertical integral of temperature"
+    ],
+    [
+        "162055",
+        "viwv",
+        "vertical integral of water vapour"
+    ],
+    [
+        "162056",
+        "vilw",
+        "vertical integral of cloud liquid water"
+    ],
+    [
+        "162057",
+        "viiw",
+        "vertical integral of cloud frozen water"
+    ],
+    [
+        "162058",
+        "vioz",
+        "vertical integral of ozone"
+    ],
+    [
+        "162059",
+        "vike",
+        "vertical integral of kinetic energy"
+    ],
+    [
+        "162060",
+        "vithe",
+        "vertical integral of thermal energy"
+    ],
+    [
+        "162061",
+        "vipie",
+        "vertical integral of potential+internal energy"
+    ],
+    [
+        "162062",
+        "vipile",
+        "vertical integral of potential+internal+latent energy"
+    ],
+    [
+        "162063",
+        "vitoe",
+        "vertical integral of total energy"
+    ],
+    [
+        "162064",
+        "viec",
+        "vertical integral of energy conversion"
+    ],
+    [
+        "162065",
+        "vimae",
+        "vertical integral of eastward mass flux"
+    ],
+    [
+        "162066",
+        "viman",
+        "vertical integral of northward mass flux"
+    ],
+    [
+        "162067",
+        "vikee",
+        "vertical integral of eastward kinetic energy flux"
+    ],
+    [
+        "162068",
+        "viken",
+        "vertical integral of northward kinetic energy flux"
+    ],
+    [
+        "162069",
+        "vithee",
+        "vertical integral of eastward heat flux"
+    ],
+    [
+        "162070",
+        "vithen",
+        "vertical integral of northward heat flux"
+    ],
+    [
+        "162071",
+        "viwve",
+        "vertical integral of eastward water vapour flux"
+    ],
+    [
+        "162072",
+        "viwvn",
+        "vertical integral of northward water vapour flux"
+    ],
+    [
+        "162073",
+        "vige",
+        "vertical integral of eastward geopotential flux"
+    ],
+    [
+        "162074",
+        "vign",
+        "vertical integral of northward geopotential flux"
+    ],
+    [
+        "162075",
+        "vitoee",
+        "vertical integral of eastward total energy flux"
+    ],
+    [
+        "162076",
+        "vitoen",
+        "vertical integral of northward total energy flux"
+    ],
+    [
+        "162077",
+        "vioze",
+        "vertical integral of eastward ozone flux"
+    ],
+    [
+        "162078",
+        "viozn",
+        "vertical integral of northward ozone flux"
+    ],
+    [
+        "162081",
+        "vimad",
+        "vertical integral of divergence of mass flux"
+    ],
+    [
+        "162082",
+        "viked",
+        "vertical integral of divergence of kinetic energy flux"
+    ],
+    [
+        "162083",
+        "vithed",
+        "vertical integral of divergence of thermal energy flux"
+    ],
+    [
+        "162084",
+        "viwvd",
+        "vertical integral of divergence of moisture flux"
+    ],
+    [
+        "162085",
+        "vigd",
+        "vertical integral of divergence of geopotential flux"
+    ],
+    [
+        "162086",
+        "vitoed",
+        "vertical integral of divergence of total energy flux"
+    ],
+    [
+        "162087",
+        "viozd",
+        "vertical integral of divergence of ozone flux"
+    ],
+    [
+        "162100",
+        "srta",
+        "tendency of short wave radiation"
+    ],
+    [
+        "162101",
+        "trta",
+        "tendency of long wave radiation"
+    ],
+    [
+        "162102",
+        "srtca",
+        "tendency of clear sky short wave radiation"
+    ],
+    [
+        "162103",
+        "trtca",
+        "tendency of clear sky long wave radiation"
+    ],
+    [
+        "162104",
+        "umfa",
+        "updraught mass flux"
+    ],
+    [
+        "162105",
+        "dmfa",
+        "downdraught mass flux"
+    ],
+    [
+        "162106",
+        "udra",
+        "updraught detrainment rate"
+    ],
+    [
+        "162107",
+        "ddra",
+        "downdraught detrainment rate"
+    ],
+    [
+        "162108",
+        "tpfa",
+        "total precipitation flux"
+    ],
+    [
+        "162109",
+        "tdcha",
+        "turbulent diffusion coefficient for heat"
+    ],
+    [
+        "162110",
+        "ttpha",
+        "tendency of temperature due to physics"
+    ],
+    [
+        "162111",
+        "qtpha",
+        "tendency of specific humidity due to physics"
+    ],
+    [
+        "162112",
+        "utpha",
+        "tendency of u component due to physics"
+    ],
+    [
+        "162113",
+        "vtpha",
+        "tendency of v component due to physics"
+    ],
+    [
+        "162206",
+        "variance of geopotential"
+    ],
+    [
+        "162207",
+        "covariance of geopotential/temperature"
+    ],
+    [
+        "162208",
+        "variance of temperature"
+    ],
+    [
+        "162209",
+        "covariance of geopotential/specific humidity"
+    ],
+    [
+        "162210",
+        "covariance of temperature/specific humidity"
+    ],
+    [
+        "162211",
+        "variance of specific humidity"
+    ],
+    [
+        "162212",
+        "covariance of u component/geopotential"
+    ],
+    [
+        "162213",
+        "covariance of u component/temperature"
+    ],
+    [
+        "162214",
+        "covariance of u component/specific humidity"
+    ],
+    [
+        "162215",
+        "variance of u component"
+    ],
+    [
+        "162216",
+        "covariance of v component/geopotential"
+    ],
+    [
+        "162217",
+        "covariance of v component/temperature"
+    ],
+    [
+        "162218",
+        "covariance of v component/specific humidity"
+    ],
+    [
+        "162219",
+        "covariance of v component/u component"
+    ],
+    [
+        "162220",
+        "variance of v component"
+    ],
+    [
+        "162221",
+        "covariance of omega/geopotential"
+    ],
+    [
+        "162222",
+        "covariance of omega/temperature"
+    ],
+    [
+        "162223",
+        "covariance of omega/specific humidity"
+    ],
+    [
+        "162224",
+        "covariance of omega/u component"
+    ],
+    [
+        "162225",
+        "covariance of omega/v component"
+    ],
+    [
+        "162226",
+        "variance of omega"
+    ],
+    [
+        "162227",
+        "variance of surface pressure"
+    ],
+    [
+        "162229",
+        "variance of relative humidity"
+    ],
+    [
+        "162230",
+        "covariance of u component/ozone"
+    ],
+    [
+        "162231",
+        "covariance of v component/ozone"
+    ],
+    [
+        "162232",
+        "covariance of omega/ozone"
+    ],
+    [
+        "162233",
+        "variance of ozone"
+    ],
+    [
+        "170149",
+        "tsw",
+        "total soil moisture"
+    ],
+    [
+        "170171",
+        "swl2",
+        "soil wetness level 2"
+    ],
+    [
+        "170179",
+        "ttr",
+        "top net thermal radiation"
+    ],
+    [
+        "171001",
+        "strfa",
+        "stream function anomaly"
+    ],
+    [
+        "171002",
+        "vpota",
+        "velocity potential anomaly"
+    ],
+    [
+        "171003",
+        "pta",
+        "potential temperature anomaly"
+    ],
+    [
+        "171004",
+        "epta",
+        "equivalent potential temperature anomaly"
+    ],
+    [
+        "171005",
+        "septa",
+        "saturated equivalent potential temperature anomaly"
+    ],
+    [
+        "171011",
+        "udwa",
+        "u component of divergent wind anomaly"
+    ],
+    [
+        "171012",
+        "vdwa",
+        "v component of divergent wind anomaly"
+    ],
+    [
+        "171013",
+        "urwa",
+        "u component of rotational wind anomaly"
+    ],
+    [
+        "171014",
+        "vrwa",
+        "v component of rotational wind anomaly"
+    ],
+    [
+        "171021",
+        "uctpa",
+        "unbalanced component of temperature anomaly"
+    ],
+    [
+        "171022",
+        "uclna",
+        "unbalanced component of logarithm of surface pressure anomaly"
+    ],
+    [
+        "171023",
+        "ucdva",
+        "unbalanced component of divergence anomaly"
+    ],
+    [
+        "171026",
+        "cla",
+        "lake cover anomaly"
+    ],
+    [
+        "171027",
+        "cvla",
+        "low vegetation cover anomaly"
+    ],
+    [
+        "171028",
+        "cvha",
+        "high vegetation cover anomaly"
+    ],
+    [
+        "171029",
+        "tvla",
+        "type of low vegetation anomaly"
+    ],
+    [
+        "171030",
+        "tvha",
+        "type of high vegetation anomaly"
+    ],
+    [
+        "171031",
+        "sica",
+        "sea-ice cover anomaly"
+    ],
+    [
+        "171032",
+        "asna",
+        "snow albedo anomaly"
+    ],
+    [
+        "171033",
+        "rsna",
+        "snow density anomaly"
+    ],
+    [
+        "171034",
+        "ssta",
+        "sea surface temperature anomaly",
+        "sstak"
+    ],
+    [
+        "171035",
+        "istal1",
+        "ice surface temperature anomaly layer 1"
+    ],
+    [
+        "171036",
+        "istal2",
+        "ice surface temperature anomaly layer 2"
+    ],
+    [
+        "171037",
+        "istal3",
+        "ice surface temperature anomaly layer 3"
+    ],
+    [
+        "171038",
+        "istal4",
+        "ice surface temperature anomaly layer 4"
+    ],
+    [
+        "171039",
+        "swval1",
+        "volumetric soil water anomaly layer 1"
+    ],
+    [
+        "171040",
+        "swval2",
+        "volumetric soil water anomaly layer 2"
+    ],
+    [
+        "171041",
+        "swval3",
+        "volumetric soil water anomaly layer 3"
+    ],
+    [
+        "171042",
+        "swval4",
+        "volumetric soil water anomaly layer 4"
+    ],
+    [
+        "171043",
+        "slta",
+        "soil type anomaly"
+    ],
+    [
+        "171044",
+        "esa",
+        "snow evaporation anomaly"
+    ],
+    [
+        "171045",
+        "smlta",
+        "snowmelt anomaly"
+    ],
+    [
+        "171046",
+        "sdura",
+        "solar duration anomaly"
+    ],
+    [
+        "171047",
+        "dsrpa",
+        "direct solar radiation anomaly"
+    ],
+    [
+        "171048",
+        "magssa",
+        "magnitude of turbulent surface stress anomaly"
+    ],
+    [
+        "171049",
+        "10fga",
+        "10 metre wind gust anomaly"
+    ],
+    [
+        "171050",
+        "lspfa",
+        "large-scale precipitation fraction anomaly"
+    ],
+    [
+        "171051",
+        "mx2t24a",
+        "maximum 2 metre temperature in the last 24 hours anomaly"
+    ],
+    [
+        "171052",
+        "mn2t24a",
+        "minimum 2 metre temperature in the last 24 hours anomaly"
+    ],
+    [
+        "171053",
+        "monta",
+        "montgomery potential anomaly"
+    ],
+    [
+        "171054",
+        "pa",
+        "pressure anomaly"
+    ],
+    [
+        "171055",
+        "mn2t24a",
+        "mean 2 metre temperature in the last 24 hours anomaly"
+    ],
+    [
+        "171056",
+        "mn2d24a",
+        "mean 2 metre dewpoint temperature in the last 24 hours anomaly"
+    ],
+    [
+        "171057",
+        "uvba",
+        "downward uv radiation at the surface anomaly"
+    ],
+    [
+        "171058",
+        "para",
+        "photosynthetically active radiation at the surface anomaly"
+    ],
+    [
+        "171059",
+        "capea",
+        "convective available potential energy anomaly"
+    ],
+    [
+        "171060",
+        "pva",
+        "potential vorticity anomaly"
+    ],
+    [
+        "171061",
+        "tpoa",
+        "total precipitation from observations anomaly"
+    ],
+    [
+        "171062",
+        "obcta",
+        "observation count anomaly"
+    ],
+    [
+        "171063",
+        "stsktda",
+        "start time for skin temperature difference anomaly"
+    ],
+    [
+        "171064",
+        "ftsktda",
+        "finish time for skin temperature difference anomaly"
+    ],
+    [
+        "171065",
+        "sktda",
+        "skin temperature difference anomaly"
+    ],
+    [
+        "171078",
+        "tclwa",
+        "total column liquid water anomaly"
+    ],
+    [
+        "171079",
+        "tciwa",
+        "total column ice water anomaly"
+    ],
+    [
+        "171125",
+        "vitea",
+        "vertically integrated total energy anomaly"
+    ],
+    [
+        "171126",
+        "generic parameter for sensitive area prediction"
+    ],
+    [
+        "171127",
+        "ata",
+        "atmospheric tide anomaly"
+    ],
+    [
+        "171128",
+        "bva",
+        "budget values anomaly"
+    ],
+    [
+        "171129",
+        "za",
+        "geopotential anomaly"
+    ],
+    [
+        "171130",
+        "ta",
+        "temperature anomaly"
+    ],
+    [
+        "171131",
+        "ua",
+        "u component of wind anomaly"
+    ],
+    [
+        "171132",
+        "va",
+        "v component of wind anomaly"
+    ],
+    [
+        "171133",
+        "qa",
+        "specific humidity anomaly"
+    ],
+    [
+        "171134",
+        "spa",
+        "surface pressure anomaly"
+    ],
+    [
+        "171135",
+        "wa",
+        "vertical velocity (pressure) anomaly"
+    ],
+    [
+        "171136",
+        "tcwa",
+        "total column water anomaly"
+    ],
+    [
+        "171137",
+        "tcwva",
+        "total column water vapour anomaly"
+    ],
+    [
+        "171138",
+        "voa",
+        "relative vorticity anomaly"
+    ],
+    [
+        "171139",
+        "stal1",
+        "soil temperature anomaly level 1"
+    ],
+    [
+        "171140",
+        "swal1",
+        "soil wetness anomaly level 1"
+    ],
+    [
+        "171141",
+        "sda",
+        "snow depth anomaly"
+    ],
+    [
+        "171142",
+        "lspa",
+        "stratiform precipitation (large-scale precipitation) anomaly"
+    ],
+    [
+        "171143",
+        "cpa",
+        "convective precipitation anomaly"
+    ],
+    [
+        "171144",
+        "sfa",
+        "snowfall (convective + stratiform) anomaly"
+    ],
+    [
+        "171145",
+        "blda",
+        "boundary layer dissipation anomaly"
+    ],
+    [
+        "171146",
+        "sshfa",
+        "surface sensible heat flux anomaly"
+    ],
+    [
+        "171147",
+        "slhfa",
+        "surface latent heat flux anomaly"
+    ],
+    [
+        "171148",
+        "chnka",
+        "charnock anomaly"
+    ],
+    [
+        "171149",
+        "snra",
+        "surface net radiation anomaly"
+    ],
+    [
+        "171150",
+        "tnra",
+        "top net radiation anomaly"
+    ],
+    [
+        "171151",
+        "msla",
+        "mean sea level pressure anomaly",
+        "151.171"
+    ],
+    [
+        "171152",
+        "lspa",
+        "logarithm of surface pressure anomaly"
+    ],
+    [
+        "171153",
+        "swhra",
+        "short-wave heating rate anomaly"
+    ],
+    [
+        "171154",
+        "lwhra",
+        "long-wave heating rate anomaly"
+    ],
+    [
+        "171155",
+        "da",
+        "relative divergence anomaly"
+    ],
+    [
+        "171156",
+        "gha",
+        "height anomaly"
+    ],
+    [
+        "171157",
+        "ra",
+        "relative humidity anomaly"
+    ],
+    [
+        "171158",
+        "tspa",
+        "tendency of surface pressure anomaly"
+    ],
+    [
+        "171159",
+        "blha",
+        "boundary layer height anomaly"
+    ],
+    [
+        "171160",
+        "sdora",
+        "standard deviation of orography anomaly"
+    ],
+    [
+        "171161",
+        "isora",
+        "anisotropy of sub-gridscale orography anomaly"
+    ],
+    [
+        "171162",
+        "anora",
+        "angle of sub-gridscale orography anomaly"
+    ],
+    [
+        "171163",
+        "slora",
+        "slope of sub-gridscale orography anomaly"
+    ],
+    [
+        "171164",
+        "tcca",
+        "total cloud cover anomaly"
+    ],
+    [
+        "171165",
+        "10ua",
+        "10 metre u wind component anomaly"
+    ],
+    [
+        "171166",
+        "10va",
+        "10 metre v wind component anomaly"
+    ],
+    [
+        "171167",
+        "2ta",
+        "2 metre temperature anomaly"
+    ],
+    [
+        "171168",
+        "2da",
+        "2 metre dewpoint temperature anomaly"
+    ],
+    [
+        "171169",
+        "ssrda",
+        "surface solar radiation downwards anomaly"
+    ],
+    [
+        "171170",
+        "slal2",
+        "soil temperature anomaly level 2"
+    ],
+    [
+        "171171",
+        "swal2",
+        "soil wetness anomaly level 2"
+    ],
+    [
+        "171173",
+        "sra",
+        "surface roughness anomaly"
+    ],
+    [
+        "171174",
+        "ala",
+        "albedo anomaly"
+    ],
+    [
+        "171175",
+        "strda",
+        "surface thermal radiation downwards anomaly"
+    ],
+    [
+        "171176",
+        "ssra",
+        "surface net solar radiation anomaly"
+    ],
+    [
+        "171177",
+        "stra",
+        "surface net thermal radiation anomaly"
+    ],
+    [
+        "171178",
+        "tsra",
+        "top net solar radiation anomaly"
+    ],
+    [
+        "171179",
+        "ttra",
+        "top net thermal radiation anomaly"
+    ],
+    [
+        "171180",
+        "eqssa",
+        "east-west surface stress anomaly"
+    ],
+    [
+        "171181",
+        "nsssa",
+        "north-south surface stress anomaly"
+    ],
+    [
+        "171182",
+        "ea",
+        "evaporation anomaly"
+    ],
+    [
+        "171183",
+        "stal3",
+        "soil temperature anomaly level 3"
+    ],
+    [
+        "171184",
+        "swal3",
+        "soil wetness anomaly level 3"
+    ],
+    [
+        "171185",
+        "ccca",
+        "convective cloud cover anomaly"
+    ],
+    [
+        "171186",
+        "lcca",
+        "low cloud cover anomaly"
+    ],
+    [
+        "171187",
+        "mcca",
+        "medium cloud cover anomaly"
+    ],
+    [
+        "171188",
+        "hcca",
+        "high cloud cover anomaly"
+    ],
+    [
+        "171189",
+        "sunda",
+        "sunshine duration anomaly"
+    ],
+    [
+        "171190",
+        "ewova",
+        "east-west component of sub-gridscale orographic variance anomaly"
+    ],
+    [
+        "171191",
+        "nsova",
+        "north-south component of sub-gridscale orographic variance anomaly"
+    ],
+    [
+        "171192",
+        "nwova",
+        "north-west/south-east component of sub-gridscale orographic variance anomaly"
+    ],
+    [
+        "171193",
+        "neova",
+        "north-east/south-west component of sub-gridscale orographic variance anomaly"
+    ],
+    [
+        "171194",
+        "btmpa",
+        "brightness temperature anomaly"
+    ],
+    [
+        "171195",
+        "lgwsa",
+        "longitudinal component of gravity wave stress anomaly"
+    ],
+    [
+        "171196",
+        "mgwsa",
+        "meridional component of gravity wave stress anomaly"
+    ],
+    [
+        "171197",
+        "gwda",
+        "gravity wave dissipation anomaly"
+    ],
+    [
+        "171198",
+        "srca",
+        "skin reservoir content anomaly"
+    ],
+    [
+        "171199",
+        "vfa",
+        "vegetation fraction anomaly"
+    ],
+    [
+        "171200",
+        "vsoa",
+        "variance of sub-gridscale orography anomaly"
+    ],
+    [
+        "171201",
+        "mx2ta",
+        "maximum temperature at 2 metres anomaly"
+    ],
+    [
+        "171202",
+        "mn2ta",
+        "minimum temperature at 2 metres anomaly"
+    ],
+    [
+        "171203",
+        "o3a",
+        "ozone mass mixing ratio anomaly"
+    ],
+    [
+        "171204",
+        "pawa",
+        "precipitation analysis weights anomaly"
+    ],
+    [
+        "171205",
+        "roa",
+        "runoff anomaly"
+    ],
+    [
+        "171206",
+        "tco3a",
+        "total column ozone anomaly"
+    ],
+    [
+        "171207",
+        "10ua",
+        "10 metre wind speed anomaly"
+    ],
+    [
+        "171208",
+        "tsrca",
+        "top net solar radiation clear sky anomaly"
+    ],
+    [
+        "171209",
+        "ttrca",
+        "top net thermal radiation clear sky anomaly"
+    ],
+    [
+        "171210",
+        "ssrca",
+        "surface net solar radiation clear sky anomaly"
+    ],
+    [
+        "171211",
+        "strca",
+        "surface net thermal radiation, clear sky anomaly"
+    ],
+    [
+        "171212",
+        "sia",
+        "solar insolation anomaly"
+    ],
+    [
+        "171214",
+        "dhra",
+        "diabatic heating by radiation anomaly"
+    ],
+    [
+        "171215",
+        "dhvda",
+        "diabatic heating by vertical diffusion anomaly"
+    ],
+    [
+        "171216",
+        "dhcca",
+        "diabatic heating by cumulus convection anomaly"
+    ],
+    [
+        "171217",
+        "dhlca",
+        "diabatic heating by large-scale condensation anomaly"
+    ],
+    [
+        "171218",
+        "vdzwa",
+        "vertical diffusion of zonal wind anomaly"
+    ],
+    [
+        "171219",
+        "vdmwa",
+        "vertical diffusion of meridional wind anomaly"
+    ],
+    [
+        "171220",
+        "ewgda",
+        "east-west gravity wave drag tendency anomaly"
+    ],
+    [
+        "171221",
+        "nsgda",
+        "north-south gravity wave drag tendency anomaly"
+    ],
+    [
+        "171222",
+        "ctzwa",
+        "convective tendency of zonal wind anomaly"
+    ],
+    [
+        "171223",
+        "ctmwa",
+        "convective tendency of meridional wind anomaly"
+    ],
+    [
+        "171224",
+        "vdha",
+        "vertical diffusion of humidity anomaly"
+    ],
+    [
+        "171225",
+        "htcca",
+        "humidity tendency by cumulus convection anomaly"
+    ],
+    [
+        "171226",
+        "htlca",
+        "humidity tendency by large-scale condensation anomaly"
+    ],
+    [
+        "171227",
+        "crnha",
+        "change from removal of negative humidity anomaly"
+    ],
+    [
+        "171228",
+        "tpa",
+        "total precipitation anomaly"
+    ],
+    [
+        "171229",
+        "iewsa",
+        "instantaneous x surface stress anomaly"
+    ],
+    [
+        "171230",
+        "inssa",
+        "instantaneous y surface stress anomaly"
+    ],
+    [
+        "171231",
+        "ishfa",
+        "instantaneous surface heat flux anomaly"
+    ],
+    [
+        "171232",
+        "iea",
+        "instantaneous moisture flux anomaly"
+    ],
+    [
+        "171233",
+        "asqa",
+        "apparent surface humidity anomaly"
+    ],
+    [
+        "171234",
+        "lsrha",
+        "logarithm of surface roughness length for heat anomaly"
+    ],
+    [
+        "171235",
+        "skta",
+        "skin temperature anomaly"
+    ],
+    [
+        "171236",
+        "stal4",
+        "soil temperature level 4 anomaly"
+    ],
+    [
+        "171237",
+        "swal4",
+        "soil wetness level 4 anomaly"
+    ],
+    [
+        "171238",
+        "tsna",
+        "temperature of snow layer anomaly"
+    ],
+    [
+        "171239",
+        "csfa",
+        "convective snowfall anomaly"
+    ],
+    [
+        "171240",
+        "lsfa",
+        "large scale snowfall anomaly"
+    ],
+    [
+        "171241",
+        "acfa",
+        "accumulated cloud fraction tendency anomaly"
+    ],
+    [
+        "171242",
+        "alwa",
+        "accumulated liquid water tendency anomaly"
+    ],
+    [
+        "171243",
+        "fala",
+        "forecast albedo anomaly"
+    ],
+    [
+        "171244",
+        "fsra",
+        "forecast surface roughness anomaly"
+    ],
+    [
+        "171245",
+        "flsra",
+        "forecast logarithm of surface roughness for heat anomaly"
+    ],
+    [
+        "171246",
+        "clwca",
+        "cloud liquid water content anomaly"
+    ],
+    [
+        "171247",
+        "ciwca",
+        "cloud ice water content anomaly"
+    ],
+    [
+        "171248",
+        "cca",
+        "cloud cover anomaly"
+    ],
+    [
+        "171249",
+        "aiwa",
+        "accumulated ice water tendency anomaly"
+    ],
+    [
+        "171250",
+        "iaa",
+        "ice age anomaly"
+    ],
+    [
+        "171251",
+        "attea",
+        "adiabatic tendency of temperature anomaly"
+    ],
+    [
+        "171252",
+        "athea",
+        "adiabatic tendency of humidity anomaly"
+    ],
+    [
+        "171253",
+        "atzea",
+        "adiabatic tendency of zonal wind anomaly"
+    ],
+    [
+        "171254",
+        "atmwa",
+        "adiabatic tendency of meridional wind anomaly"
+    ],
+    [
+        "172044",
+        "esrate",
+        "snow evaporation"
+    ],
+    [
+        "172045",
+        "snowmelt"
+    ],
+    [
+        "172048",
+        "magnitude of turbulent surface stress"
+    ],
+    [
+        "172050",
+        "large-scale precipitation fraction"
+    ],
+    [
+        "172142",
+        "stratiform precipitation (large-scale precipitation)"
+    ],
+    [
+        "172143",
+        "cprate",
+        "convective precipitation"
+    ],
+    [
+        "172144",
+        "snowfall (convective + stratiform)"
+    ],
+    [
+        "172145",
+        "bldrate",
+        "boundary layer dissipation"
+    ],
+    [
+        "172146",
+        "surface sensible heat flux"
+    ],
+    [
+        "172147",
+        "surface latent heat flux"
+    ],
+    [
+        "172149",
+        "surface net radiation"
+    ],
+    [
+        "172153",
+        "short-wave heating rate"
+    ],
+    [
+        "172154",
+        "long-wave heating rate"
+    ],
+    [
+        "172169",
+        "surface solar radiation downwards"
+    ],
+    [
+        "172175",
+        "surface thermal radiation downwards"
+    ],
+    [
+        "172176",
+        "surface solar radiation"
+    ],
+    [
+        "172177",
+        "surface thermal radiation"
+    ],
+    [
+        "172178",
+        "top solar radiation"
+    ],
+    [
+        "172179",
+        "top thermal radiation"
+    ],
+    [
+        "172180",
+        "east-west surface stress"
+    ],
+    [
+        "172181",
+        "north-south surface stress"
+    ],
+    [
+        "172182",
+        "erate",
+        "evaporation"
+    ],
+    [
+        "172189",
+        "sunshine duration"
+    ],
+    [
+        "172195",
+        "longitudinal component of gravity wave stress"
+    ],
+    [
+        "172196",
+        "meridional component of gravity wave stress"
+    ],
+    [
+        "172197",
+        "gwdrate",
+        "gravity wave dissipation"
+    ],
+    [
+        "172205",
+        "runoff"
+    ],
+    [
+        "172208",
+        "top net solar radiation, clear sky"
+    ],
+    [
+        "172209",
+        "top net thermal radiation, clear sky"
+    ],
+    [
+        "172210",
+        "surface net solar radiation, clear sky"
+    ],
+    [
+        "172211",
+        "surface net thermal radiation, clear sky"
+    ],
+    [
+        "172212",
+        "solar insolation"
+    ],
+    [
+        "172228",
+        "tprate",
+        "total precipitation"
+    ],
+    [
+        "172239",
+        "convective snowfall"
+    ],
+    [
+        "172240",
+        "large scale snowfall"
+    ],
+    [
+        "173044",
+        "snow evaporation anomaly"
+    ],
+    [
+        "173045",
+        "snowmelt anomaly"
+    ],
+    [
+        "173048",
+        "magnitude of turbulent surface stress anomaly"
+    ],
+    [
+        "173050",
+        "large-scale precipitation fraction anomaly"
+    ],
+    [
+        "173142",
+        "stratiform precipitation (large-scale precipitation) anomaly"
+    ],
+    [
+        "173143",
+        "convective precipitation anomaly"
+    ],
+    [
+        "173144",
+        "sfara",
+        "snowfall (convective + stratiform) anomalous rate of accumulation"
+    ],
+    [
+        "173145",
+        "boundary layer dissipation anomaly"
+    ],
+    [
+        "173146",
+        "surface sensible heat flux anomaly"
+    ],
+    [
+        "173147",
+        "surface latent heat flux anomaly"
+    ],
+    [
+        "173149",
+        "surface net radiation anomaly"
+    ],
+    [
+        "173153",
+        "short-wave heating rate anomaly"
+    ],
+    [
+        "173154",
+        "long-wave heating rate anomaly"
+    ],
+    [
+        "173169",
+        "surface solar radiation downwards anomaly"
+    ],
+    [
+        "173175",
+        "surface thermal radiation downwards anomaly"
+    ],
+    [
+        "173176",
+        "surface solar radiation anomaly"
+    ],
+    [
+        "173177",
+        "surface thermal radiation anomaly"
+    ],
+    [
+        "173178",
+        "top solar radiation anomaly"
+    ],
+    [
+        "173179",
+        "top thermal radiation anomaly"
+    ],
+    [
+        "173180",
+        "east-west surface stress anomaly"
+    ],
+    [
+        "173181",
+        "north-south surface stress anomaly"
+    ],
+    [
+        "173182",
+        "evaporation anomaly"
+    ],
+    [
+        "173189",
+        "sundara",
+        "sunshine duration anomalous rate of accumulation"
+    ],
+    [
+        "173195",
+        "longitudinal component of gravity wave stress anomaly"
+    ],
+    [
+        "173196",
+        "meridional component of gravity wave stress anomaly"
+    ],
+    [
+        "173197",
+        "gravity wave dissipation anomaly"
+    ],
+    [
+        "173205",
+        "runoff anomaly"
+    ],
+    [
+        "173208",
+        "top net solar radiation, clear sky anomaly"
+    ],
+    [
+        "173209",
+        "top net thermal radiation, clear sky anomaly"
+    ],
+    [
+        "173210",
+        "surface net solar radiation, clear sky anomaly"
+    ],
+    [
+        "173211",
+        "surface net thermal radiation, clear sky anomaly"
+    ],
+    [
+        "173212",
+        "solar insolation anomaly"
+    ],
+    [
+        "173228",
+        "tpara",
+        "total precipitation anomalous rate of accumulation",
+        "228.173"
+    ],
+    [
+        "173239",
+        "convective snowfall anomaly"
+    ],
+    [
+        "173240",
+        "large scale snowfall anomaly"
+    ],
+    [
+        "174006",
+        "total soil moisture"
+    ],
+    [
+        "174008",
+        "sro",
+        "surface runoff"
+    ],
+    [
+        "174009",
+        "ssro",
+        "sub-surface runoff"
+    ],
+    [
+        "174031",
+        "fraction of sea-ice in sea"
+    ],
+    [
+        "174034",
+        "open-sea surface temperature"
+    ],
+    [
+        "174039",
+        "volumetric soil water layer 1"
+    ],
+    [
+        "174040",
+        "volumetric soil water layer 2"
+    ],
+    [
+        "174041",
+        "volumetric soil water layer 3"
+    ],
+    [
+        "174042",
+        "volumetric soil water layer 4"
+    ],
+    [
+        "174049",
+        "10 metre wind gust in the last 24 hours"
+    ],
+    [
+        "174055",
+        "1.5m temperature - mean in the last 24 hours"
+    ],
+    [
+        "174083",
+        "net primary productivity"
+    ],
+    [
+        "174085",
+        "10m u wind over land"
+    ],
+    [
+        "174086",
+        "10m v wind over land"
+    ],
+    [
+        "174087",
+        "1.5m temperature over land"
+    ],
+    [
+        "174088",
+        "1.5m dewpoint temperature over land"
+    ],
+    [
+        "174089",
+        "top incoming solar radiation"
+    ],
+    [
+        "174090",
+        "top outgoing solar radiation"
+    ],
+    [
+        "174094",
+        "mean sea surface temperature"
+    ],
+    [
+        "174095",
+        "1.5m specific humidity"
+    ],
+    [
+        "174098",
+        "sit",
+        "sea-ice thickness"
+    ],
+    [
+        "174099",
+        "liquid water potential temperature"
+    ],
+    [
+        "174110",
+        "ocean ice concentration"
+    ],
+    [
+        "174111",
+        "ocean mean ice depth"
+    ],
+    [
+        "174139",
+        "soil temperature layer 1"
+    ],
+    [
+        "174164",
+        "average potential temperature in upper 293.4m"
+    ],
+    [
+        "174167",
+        "1.5m temperature"
+    ],
+    [
+        "174168",
+        "1.5m dewpoint temperature"
+    ],
+    [
+        "174170",
+        "soil temperature layer 2"
+    ],
+    [
+        "174175",
+        "average salinity in upper 293.4m"
+    ],
+    [
+        "174183",
+        "soil temperature layer 3"
+    ],
+    [
+        "174201",
+        "1.5m temperature - maximum in the last 24 hours"
+    ],
+    [
+        "174202",
+        "1.5m temperature - minimum in the last 24 hours"
+    ],
+    [
+        "174236",
+        "soil temperature layer 4"
+    ],
+    [
+        "175006",
+        "total soil moisture"
+    ],
+    [
+        "175031",
+        "fraction of sea-ice in sea"
+    ],
+    [
+        "175034",
+        "open-sea surface temperature"
+    ],
+    [
+        "175039",
+        "volumetric soil water layer 1"
+    ],
+    [
+        "175040",
+        "volumetric soil water layer 2"
+    ],
+    [
+        "175041",
+        "volumetric soil water layer 3"
+    ],
+    [
+        "175042",
+        "volumetric soil water layer 4"
+    ],
+    [
+        "175049",
+        "10m wind gust in the last 24 hours"
+    ],
+    [
+        "175055",
+        "1.5m temperature - mean in the last 24 hours"
+    ],
+    [
+        "175083",
+        "net primary productivity"
+    ],
+    [
+        "175085",
+        "10m u wind over land"
+    ],
+    [
+        "175086",
+        "10m v wind over land"
+    ],
+    [
+        "175087",
+        "1.5m temperature over land"
+    ],
+    [
+        "175088",
+        "1.5m dewpoint temperature over land"
+    ],
+    [
+        "175089",
+        "top incoming solar radiation"
+    ],
+    [
+        "175090",
+        "top outgoing solar radiation"
+    ],
+    [
+        "175110",
+        "ocean ice concentration"
+    ],
+    [
+        "175111",
+        "ocean mean ice depth"
+    ],
+    [
+        "175139",
+        "soil temperature layer 1"
+    ],
+    [
+        "175164",
+        "average potential temperature in upper 293.4m"
+    ],
+    [
+        "175167",
+        "1.5m temperature"
+    ],
+    [
+        "175168",
+        "1.5m dewpoint temperature"
+    ],
+    [
+        "175170",
+        "soil temperature layer 2"
+    ],
+    [
+        "175175",
+        "average salinity in upper 293.4m"
+    ],
+    [
+        "175183",
+        "soil temperature layer 3"
+    ],
+    [
+        "175201",
+        "1.5m temperature - maximum in the last 24 hours"
+    ],
+    [
+        "175202",
+        "1.5m temperature - minimum in the last 24 hours"
+    ],
+    [
+        "175236",
+        "soil temperature layer 4"
+    ],
+    [
+        "180149",
+        "tsw",
+        "total soil wetness"
+    ],
+    [
+        "180176",
+        "ssr",
+        "surface net solar radiation"
+    ],
+    [
+        "180177",
+        "str",
+        "surface net thermal radiation"
+    ],
+    [
+        "180178",
+        "tsr",
+        "top net solar radiation"
+    ],
+    [
+        "180179",
+        "ttr",
+        "top net thermal radiation"
+    ],
+    [
+        "190141",
+        "sdsien",
+        "snow depth"
+    ],
+    [
+        "190170",
+        "cap",
+        "field capacity"
+    ],
+    [
+        "190171",
+        "wiltsien",
+        "wilting point"
+    ],
+    [
+        "190173",
+        "sr",
+        "roughness length"
+    ],
+    [
+        "190229",
+        "tsm",
+        "total soil moisture"
+    ],
+    [
+        "200001",
+        "strfdiff",
+        "stream function difference"
+    ],
+    [
+        "200002",
+        "vpotdiff",
+        "velocity potential difference"
+    ],
+    [
+        "200003",
+        "ptdiff",
+        "potential temperature difference"
+    ],
+    [
+        "200004",
+        "eqptdiff",
+        "equivalent potential temperature difference"
+    ],
+    [
+        "200005",
+        "septdiff",
+        "saturated equivalent potential temperature difference"
+    ],
+    [
+        "200011",
+        "udvwdiff",
+        "u component of divergent wind difference"
+    ],
+    [
+        "200012",
+        "vdvwdiff",
+        "v component of divergent wind difference"
+    ],
+    [
+        "200013",
+        "urtwdiff",
+        "u component of rotational wind difference"
+    ],
+    [
+        "200014",
+        "vrtwdiff",
+        "v component of rotational wind difference"
+    ],
+    [
+        "200021",
+        "uctpdiff",
+        "unbalanced component of temperature difference"
+    ],
+    [
+        "200022",
+        "uclndiff",
+        "unbalanced component of logarithm of surface pressure difference"
+    ],
+    [
+        "200023",
+        "ucdvdiff",
+        "unbalanced component of divergence difference"
+    ],
+    [
+        "200026",
+        "cldiff",
+        "lake cover difference"
+    ],
+    [
+        "200027",
+        "cvldiff",
+        "low vegetation cover difference"
+    ],
+    [
+        "200028",
+        "cvhdiff",
+        "high vegetation cover difference"
+    ],
+    [
+        "200029",
+        "tvldiff",
+        "type of low vegetation difference"
+    ],
+    [
+        "200030",
+        "tvhdiff",
+        "type of high vegetation difference"
+    ],
+    [
+        "200031",
+        "sicdiff",
+        "sea-ice cover difference"
+    ],
+    [
+        "200032",
+        "asndiff",
+        "snow albedo difference"
+    ],
+    [
+        "200033",
+        "rsndiff",
+        "snow density difference"
+    ],
+    [
+        "200034",
+        "sstdiff",
+        "sea surface temperature difference"
+    ],
+    [
+        "200035",
+        "istl1diff",
+        "ice surface temperature layer 1 difference"
+    ],
+    [
+        "200036",
+        "istl2diff",
+        "ice surface temperature layer 2 difference"
+    ],
+    [
+        "200037",
+        "istl3diff",
+        "ice surface temperature layer 3 difference"
+    ],
+    [
+        "200038",
+        "istl4diff",
+        "ice surface temperature layer 4 difference"
+    ],
+    [
+        "200039",
+        "swvl1diff",
+        "volumetric soil water layer 1 difference"
+    ],
+    [
+        "200040",
+        "swvl2diff",
+        "volumetric soil water layer 2 difference"
+    ],
+    [
+        "200041",
+        "swvl3diff",
+        "volumetric soil water layer 3 difference"
+    ],
+    [
+        "200042",
+        "swvl4diff",
+        "volumetric soil water layer 4 difference"
+    ],
+    [
+        "200043",
+        "sltdiff",
+        "soil type difference"
+    ],
+    [
+        "200044",
+        "esdiff",
+        "snow evaporation difference"
+    ],
+    [
+        "200045",
+        "smltdiff",
+        "snowmelt difference"
+    ],
+    [
+        "200046",
+        "sdurdiff",
+        "solar duration difference"
+    ],
+    [
+        "200047",
+        "dsrpdiff",
+        "direct solar radiation difference"
+    ],
+    [
+        "200048",
+        "magssdiff",
+        "magnitude of turbulent surface stress difference"
+    ],
+    [
+        "200049",
+        "10fgdiff",
+        "10 metre wind gust difference"
+    ],
+    [
+        "200050",
+        "lspfdiff",
+        "large-scale precipitation fraction difference"
+    ],
+    [
+        "200051",
+        "mx2t24diff",
+        "maximum 2 metre temperature difference"
+    ],
+    [
+        "200052",
+        "mn2t24diff",
+        "minimum 2 metre temperature difference"
+    ],
+    [
+        "200053",
+        "montdiff",
+        "montgomery potential difference"
+    ],
+    [
+        "200054",
+        "presdiff",
+        "pressure difference"
+    ],
+    [
+        "200055",
+        "mean2t24diff",
+        "mean 2 metre temperature in the last 24 hours difference"
+    ],
+    [
+        "200056",
+        "mn2d24diff",
+        "mean 2 metre dewpoint temperature in the last 24 hours difference"
+    ],
+    [
+        "200057",
+        "uvbdiff",
+        "downward uv radiation at the surface difference"
+    ],
+    [
+        "200058",
+        "pardiff",
+        "photosynthetically active radiation at the surface difference"
+    ],
+    [
+        "200059",
+        "capediff",
+        "convective available potential energy difference"
+    ],
+    [
+        "200060",
+        "pvdiff",
+        "potential vorticity difference"
+    ],
+    [
+        "200061",
+        "tpodiff",
+        "total precipitation from observations difference"
+    ],
+    [
+        "200062",
+        "obctdiff",
+        "observation count difference"
+    ],
+    [
+        "200063",
+        "start time for skin temperature difference"
+    ],
+    [
+        "200064",
+        "finish time for skin temperature difference"
+    ],
+    [
+        "200065",
+        "skin temperature difference"
+    ],
+    [
+        "200066",
+        "leaf area index, low vegetation"
+    ],
+    [
+        "200067",
+        "leaf area index, high vegetation"
+    ],
+    [
+        "200068",
+        "minimum stomatal resistance, low vegetation"
+    ],
+    [
+        "200069",
+        "minimum stomatal resistance, high vegetation"
+    ],
+    [
+        "200070",
+        "biome cover, low vegetation"
+    ],
+    [
+        "200071",
+        "biome cover, high vegetation"
+    ],
+    [
+        "200078",
+        "total column liquid water"
+    ],
+    [
+        "200079",
+        "total column ice water"
+    ],
+    [
+        "200121",
+        "mx2t6diff",
+        "maximum temperature at 2 metres difference"
+    ],
+    [
+        "200122",
+        "mn2t6diff",
+        "minimum temperature at 2 metres difference"
+    ],
+    [
+        "200123",
+        "10fg6diff",
+        "10 metre wind gust in the last 6 hours difference"
+    ],
+    [
+        "200125",
+        "vertically integrated total energy"
+    ],
+    [
+        "200126",
+        "generic parameter for sensitive area prediction"
+    ],
+    [
+        "200127",
+        "atdiff",
+        "atmospheric tide difference"
+    ],
+    [
+        "200128",
+        "bvdiff",
+        "budget values difference"
+    ],
+    [
+        "200129",
+        "zdiff",
+        "geopotential difference"
+    ],
+    [
+        "200130",
+        "tdiff",
+        "temperature difference"
+    ],
+    [
+        "200131",
+        "udiff",
+        "u component of wind difference"
+    ],
+    [
+        "200132",
+        "vdiff",
+        "v component of wind difference"
+    ],
+    [
+        "200133",
+        "qdiff",
+        "specific humidity difference"
+    ],
+    [
+        "200134",
+        "spdiff",
+        "surface pressure difference"
+    ],
+    [
+        "200135",
+        "wdiff",
+        "vertical velocity (pressure) difference"
+    ],
+    [
+        "200136",
+        "tcwdiff",
+        "total column water difference"
+    ],
+    [
+        "200137",
+        "tcwvdiff",
+        "total column water vapour difference"
+    ],
+    [
+        "200138",
+        "vodiff",
+        "vorticity (relative) difference"
+    ],
+    [
+        "200139",
+        "stl1diff",
+        "soil temperature level 1 difference"
+    ],
+    [
+        "200140",
+        "swl1diff",
+        "soil wetness level 1 difference"
+    ],
+    [
+        "200141",
+        "sddiff",
+        "snow depth difference"
+    ],
+    [
+        "200142",
+        "lspdiff",
+        "stratiform precipitation (large-scale precipitation) difference"
+    ],
+    [
+        "200143",
+        "cpdiff",
+        "convective precipitation difference"
+    ],
+    [
+        "200144",
+        "sfdiff",
+        "snowfall (convective + stratiform) difference"
+    ],
+    [
+        "200145",
+        "blddiff",
+        "boundary layer dissipation difference"
+    ],
+    [
+        "200146",
+        "sshfdiff",
+        "surface sensible heat flux difference"
+    ],
+    [
+        "200147",
+        "slhfdiff",
+        "surface latent heat flux difference"
+    ],
+    [
+        "200148",
+        "chnkdiff",
+        "charnock difference"
+    ],
+    [
+        "200149",
+        "snrdiff",
+        "surface net radiation difference"
+    ],
+    [
+        "200150",
+        "tnrdiff",
+        "top net radiation difference"
+    ],
+    [
+        "200151",
+        "msldiff",
+        "mean sea level pressure difference"
+    ],
+    [
+        "200152",
+        "lnspdiff",
+        "logarithm of surface pressure difference"
+    ],
+    [
+        "200153",
+        "swhrdiff",
+        "short-wave heating rate difference"
+    ],
+    [
+        "200154",
+        "lwhrdiff",
+        "long-wave heating rate difference"
+    ],
+    [
+        "200155",
+        "ddiff",
+        "divergence difference"
+    ],
+    [
+        "200156",
+        "ghdiff",
+        "height difference"
+    ],
+    [
+        "200157",
+        "rdiff",
+        "relative humidity difference"
+    ],
+    [
+        "200158",
+        "tspdiff",
+        "tendency of surface pressure difference"
+    ],
+    [
+        "200159",
+        "blhdiff",
+        "boundary layer height difference"
+    ],
+    [
+        "200160",
+        "sdordiff",
+        "standard deviation of orography difference"
+    ],
+    [
+        "200161",
+        "isordiff",
+        "anisotropy of sub-gridscale orography difference"
+    ],
+    [
+        "200162",
+        "anordiff",
+        "angle of sub-gridscale orography difference"
+    ],
+    [
+        "200163",
+        "slordiff",
+        "slope of sub-gridscale orography difference"
+    ],
+    [
+        "200164",
+        "tccdiff",
+        "total cloud cover difference"
+    ],
+    [
+        "200165",
+        "10udiff",
+        "10 metre u wind component difference"
+    ],
+    [
+        "200166",
+        "10vdiff",
+        "10 metre v wind component difference"
+    ],
+    [
+        "200167",
+        "2tdiff",
+        "2 metre temperature difference"
+    ],
+    [
+        "200168",
+        "2ddiff",
+        "2 metre dewpoint temperature difference"
+    ],
+    [
+        "200169",
+        "ssrddiff",
+        "surface solar radiation downwards difference"
+    ],
+    [
+        "200170",
+        "stl2diff",
+        "soil temperature level 2 difference"
+    ],
+    [
+        "200171",
+        "swl2diff",
+        "soil wetness level 2 difference"
+    ],
+    [
+        "200172",
+        "lsmdiff",
+        "land-sea mask difference"
+    ],
+    [
+        "200173",
+        "srdiff",
+        "surface roughness difference"
+    ],
+    [
+        "200174",
+        "aldiff",
+        "albedo difference"
+    ],
+    [
+        "200175",
+        "strddiff",
+        "surface thermal radiation downwards difference"
+    ],
+    [
+        "200176",
+        "ssrdiff",
+        "surface net solar radiation difference"
+    ],
+    [
+        "200177",
+        "strdiff",
+        "surface net thermal radiation difference"
+    ],
+    [
+        "200178",
+        "tsrdiff",
+        "top net solar radiation difference"
+    ],
+    [
+        "200179",
+        "ttrdiff",
+        "top net thermal radiation difference"
+    ],
+    [
+        "200180",
+        "ewssdiff",
+        "east-west surface stress difference"
+    ],
+    [
+        "200181",
+        "nsssdiff",
+        "north-south surface stress difference"
+    ],
+    [
+        "200182",
+        "ediff",
+        "evaporation difference"
+    ],
+    [
+        "200183",
+        "stl3diff",
+        "soil temperature level 3 difference"
+    ],
+    [
+        "200184",
+        "swl3diff",
+        "soil wetness level 3 difference"
+    ],
+    [
+        "200185",
+        "cccdiff",
+        "convective cloud cover difference"
+    ],
+    [
+        "200186",
+        "lccdiff",
+        "low cloud cover difference"
+    ],
+    [
+        "200187",
+        "mccdiff",
+        "medium cloud cover difference"
+    ],
+    [
+        "200188",
+        "hccdiff",
+        "high cloud cover difference"
+    ],
+    [
+        "200189",
+        "sunddiff",
+        "sunshine duration difference"
+    ],
+    [
+        "200190",
+        "ewovdiff",
+        "east-west component of sub-gridscale orographic variance difference"
+    ],
+    [
+        "200191",
+        "nsovdiff",
+        "north-south component of sub-gridscale orographic variance difference"
+    ],
+    [
+        "200192",
+        "nwovdiff",
+        "north-west/south-east component of sub-gridscale orographic variance difference"
+    ],
+    [
+        "200193",
+        "neovdiff",
+        "north-east/south-west component of sub-gridscale orographic variance difference"
+    ],
+    [
+        "200194",
+        "btmpdiff",
+        "brightness temperature difference"
+    ],
+    [
+        "200195",
+        "lgwsdiff",
+        "longitudinal component of gravity wave stress difference"
+    ],
+    [
+        "200196",
+        "mgwsdiff",
+        "meridional component of gravity wave stress difference"
+    ],
+    [
+        "200197",
+        "gwddiff",
+        "gravity wave dissipation difference"
+    ],
+    [
+        "200198",
+        "srcdiff",
+        "skin reservoir content difference"
+    ],
+    [
+        "200199",
+        "vegdiff",
+        "vegetation fraction difference"
+    ],
+    [
+        "200200",
+        "vsodiff",
+        "variance of sub-gridscale orography difference"
+    ],
+    [
+        "200201",
+        "mx2tdiff",
+        "maximum temperature at 2 metres since previous post-processing difference"
+    ],
+    [
+        "200202",
+        "mn2tdiff",
+        "minimum temperature at 2 metres since previous post-processing difference"
+    ],
+    [
+        "200203",
+        "o3diff",
+        "ozone mass mixing ratio difference"
+    ],
+    [
+        "200204",
+        "pawdiff",
+        "precipitation analysis weights difference"
+    ],
+    [
+        "200205",
+        "rodiff",
+        "runoff difference"
+    ],
+    [
+        "200206",
+        "tco3diff",
+        "total column ozone difference"
+    ],
+    [
+        "200207",
+        "10sidiff",
+        "10 metre wind speed difference"
+    ],
+    [
+        "200208",
+        "tsrcdiff",
+        "top net solar radiation, clear sky difference"
+    ],
+    [
+        "200209",
+        "ttrcdiff",
+        "top net thermal radiation, clear sky difference"
+    ],
+    [
+        "200210",
+        "ssrcdiff",
+        "surface net solar radiation, clear sky difference"
+    ],
+    [
+        "200211",
+        "strcdiff",
+        "surface net thermal radiation, clear sky difference"
+    ],
+    [
+        "200212",
+        "tisrdiff",
+        "toa incident solar radiation difference"
+    ],
+    [
+        "200214",
+        "dhrdiff",
+        "diabatic heating by radiation difference"
+    ],
+    [
+        "200215",
+        "dhvddiff",
+        "diabatic heating by vertical diffusion difference"
+    ],
+    [
+        "200216",
+        "dhccdiff",
+        "diabatic heating by cumulus convection difference"
+    ],
+    [
+        "200217",
+        "dhlcdiff",
+        "diabatic heating large-scale condensation difference"
+    ],
+    [
+        "200218",
+        "vdzwdiff",
+        "vertical diffusion of zonal wind difference"
+    ],
+    [
+        "200219",
+        "vdmwdiff",
+        "vertical diffusion of meridional wind difference"
+    ],
+    [
+        "200220",
+        "ewgddiff",
+        "east-west gravity wave drag tendency difference"
+    ],
+    [
+        "200221",
+        "nsgddiff",
+        "north-south gravity wave drag tendency difference"
+    ],
+    [
+        "200222",
+        "ctzwdiff",
+        "convective tendency of zonal wind difference"
+    ],
+    [
+        "200223",
+        "ctmwdiff",
+        "convective tendency of meridional wind difference"
+    ],
+    [
+        "200224",
+        "vdhdiff",
+        "vertical diffusion of humidity difference"
+    ],
+    [
+        "200225",
+        "htccdiff",
+        "humidity tendency by cumulus convection difference"
+    ],
+    [
+        "200226",
+        "htlcdiff",
+        "humidity tendency by large-scale condensation difference"
+    ],
+    [
+        "200227",
+        "crnhdiff",
+        "change from removal of negative humidity difference"
+    ],
+    [
+        "200228",
+        "tpdiff",
+        "total precipitation difference"
+    ],
+    [
+        "200229",
+        "iewsdiff",
+        "instantaneous x surface stress difference"
+    ],
+    [
+        "200230",
+        "inssdiff",
+        "instantaneous y surface stress difference"
+    ],
+    [
+        "200231",
+        "ishfdiff",
+        "instantaneous surface heat flux difference"
+    ],
+    [
+        "200232",
+        "iediff",
+        "instantaneous moisture flux difference"
+    ],
+    [
+        "200233",
+        "asqdiff",
+        "apparent surface humidity difference"
+    ],
+    [
+        "200234",
+        "lsrhdiff",
+        "logarithm of surface roughness length for heat difference"
+    ],
+    [
+        "200235",
+        "sktdiff",
+        "skin temperature difference"
+    ],
+    [
+        "200236",
+        "stl4diff",
+        "soil temperature level 4 difference"
+    ],
+    [
+        "200237",
+        "swl4diff",
+        "soil wetness level 4 difference"
+    ],
+    [
+        "200238",
+        "tsndiff",
+        "temperature of snow layer difference"
+    ],
+    [
+        "200239",
+        "csfdiff",
+        "convective snowfall difference"
+    ],
+    [
+        "200240",
+        "lsfdiff",
+        "large scale snowfall difference"
+    ],
+    [
+        "200241",
+        "acfdiff",
+        "accumulated cloud fraction tendency difference"
+    ],
+    [
+        "200242",
+        "alwdiff",
+        "accumulated liquid water tendency difference"
+    ],
+    [
+        "200243",
+        "faldiff",
+        "forecast albedo difference"
+    ],
+    [
+        "200244",
+        "fsrdiff",
+        "forecast surface roughness difference"
+    ],
+    [
+        "200245",
+        "flsrdiff",
+        "forecast logarithm of surface roughness for heat difference"
+    ],
+    [
+        "200246",
+        "clwcdiff",
+        "specific cloud liquid water content difference"
+    ],
+    [
+        "200247",
+        "ciwcdiff",
+        "specific cloud ice water content difference"
+    ],
+    [
+        "200248",
+        "ccdiff",
+        "cloud cover difference"
+    ],
+    [
+        "200249",
+        "aiwdiff",
+        "accumulated ice water tendency difference"
+    ],
+    [
+        "200250",
+        "icediff",
+        "ice age difference"
+    ],
+    [
+        "200251",
+        "attediff",
+        "adiabatic tendency of temperature difference"
+    ],
+    [
+        "200252",
+        "athediff",
+        "adiabatic tendency of humidity difference"
+    ],
+    [
+        "200253",
+        "atzediff",
+        "adiabatic tendency of zonal wind difference"
+    ],
+    [
+        "200254",
+        "atmwdiff",
+        "adiabatic tendency of meridional wind difference"
+    ],
+    [
+        "201001",
+        "downward shortwave radiant flux density"
+    ],
+    [
+        "201002",
+        "upward shortwave radiant flux density"
+    ],
+    [
+        "201003",
+        "downward longwave radiant flux density"
+    ],
+    [
+        "201004",
+        "upward longwave radiant flux density"
+    ],
+    [
+        "201005",
+        "apab_s",
+        "downwd photosynthetic active radiant flux density"
+    ],
+    [
+        "201006",
+        "net shortwave flux"
+    ],
+    [
+        "201007",
+        "net longwave flux"
+    ],
+    [
+        "201008",
+        "total net radiative flux density"
+    ],
+    [
+        "201009",
+        "downw shortw radiant flux density, cloudfree part"
+    ],
+    [
+        "201010",
+        "upw shortw radiant flux density, cloudy part"
+    ],
+    [
+        "201011",
+        "downw longw radiant flux density, cloudfree part"
+    ],
+    [
+        "201012",
+        "upw longw radiant flux density, cloudy part"
+    ],
+    [
+        "201013",
+        "sohr_rad",
+        "shortwave radiative heating rate"
+    ],
+    [
+        "201014",
+        "thhr_rad",
+        "longwave radiative heating rate"
+    ],
+    [
+        "201015",
+        "total radiative heating rate"
+    ],
+    [
+        "201016",
+        "soil heat flux, surface"
+    ],
+    [
+        "201017",
+        "soil heat flux, bottom of layer"
+    ],
+    [
+        "201029",
+        "clc",
+        "fractional cloud cover"
+    ],
+    [
+        "201030",
+        "cloud cover, grid scale"
+    ],
+    [
+        "201031",
+        "qc",
+        "specific cloud water content"
+    ],
+    [
+        "201032",
+        "cloud water content, grid scale, vert integrated"
+    ],
+    [
+        "201033",
+        "qi",
+        "specific cloud ice content, grid scale"
+    ],
+    [
+        "201034",
+        "cloud ice content, grid scale, vert integrated"
+    ],
+    [
+        "201035",
+        "specific rainwater content, grid scale"
+    ],
+    [
+        "201036",
+        "specific snow content, grid scale"
+    ],
+    [
+        "201037",
+        "specific rainwater content, gs, vert. integrated"
+    ],
+    [
+        "201038",
+        "specific snow content, gs, vert. integrated"
+    ],
+    [
+        "201041",
+        "twater",
+        "total column water"
+    ],
+    [
+        "201042",
+        "vert. integral of divergence of tot. water content"
+    ],
+    [
+        "201050",
+        "ch_cm_cl",
+        "cloud covers ch_cm_cl (000...888)"
+    ],
+    [
+        "201051",
+        "cloud cover ch (0..8)"
+    ],
+    [
+        "201052",
+        "cloud cover cm (0..8)"
+    ],
+    [
+        "201053",
+        "cloud cover cl (0..8)"
+    ],
+    [
+        "201054",
+        "total cloud cover (0..8)"
+    ],
+    [
+        "201055",
+        "fog (0..8)"
+    ],
+    [
+        "201056",
+        "fog"
+    ],
+    [
+        "201060",
+        "cloud cover, convective cirrus"
+    ],
+    [
+        "201061",
+        "specific cloud water content, convective clouds"
+    ],
+    [
+        "201062",
+        "cloud water content, conv clouds, vert integrated"
+    ],
+    [
+        "201063",
+        "specific cloud ice content, convective clouds"
+    ],
+    [
+        "201064",
+        "cloud ice content, conv clouds, vert integrated"
+    ],
+    [
+        "201065",
+        "convective mass flux"
+    ],
+    [
+        "201066",
+        "updraft velocity, convection"
+    ],
+    [
+        "201067",
+        "entrainment parameter, convection"
+    ],
+    [
+        "201068",
+        "hbas_con",
+        "cloud base, convective clouds (above msl)"
+    ],
+    [
+        "201069",
+        "htop_con",
+        "cloud top, convective clouds (above msl)"
+    ],
+    [
+        "201070",
+        "convective layers (00...77)  (bke)"
+    ],
+    [
+        "201071",
+        "ko-index"
+    ],
+    [
+        "201072",
+        "bas_con",
+        "convection base index"
+    ],
+    [
+        "201073",
+        "top_con",
+        "convection top index"
+    ],
+    [
+        "201074",
+        "dt_con",
+        "convective temperature tendency"
+    ],
+    [
+        "201075",
+        "dqv_con",
+        "convective tendency of specific humidity"
+    ],
+    [
+        "201076",
+        "convective tendency of total heat"
+    ],
+    [
+        "201077",
+        "convective tendency of total water"
+    ],
+    [
+        "201078",
+        "du_con",
+        "convective momentum tendency (x-component)"
+    ],
+    [
+        "201079",
+        "dv_con",
+        "convective momentum tendency (y-component)"
+    ],
+    [
+        "201080",
+        "convective vorticity tendency"
+    ],
+    [
+        "201081",
+        "convective divergence tendency"
+    ],
+    [
+        "201082",
+        "htop_dc",
+        "top of dry convection (above msl)"
+    ],
+    [
+        "201083",
+        "dry convection top index"
+    ],
+    [
+        "201084",
+        "hzerocl",
+        "height of 0 degree celsius isotherm above msl"
+    ],
+    [
+        "201085",
+        "snowlmt",
+        "height of snow-fall limit"
+    ],
+    [
+        "201099",
+        "qrs_gsp",
+        "spec. content of precip. particles"
+    ],
+    [
+        "201100",
+        "prr_gsp",
+        "surface precipitation rate, rain, grid scale"
+    ],
+    [
+        "201101",
+        "prs_gsp",
+        "surface precipitation rate, snow, grid scale"
+    ],
+    [
+        "201102",
+        "rain_gsp",
+        "surface precipitation amount, rain, grid scale"
+    ],
+    [
+        "201111",
+        "prr_con",
+        "surface precipitation rate, rain, convective"
+    ],
+    [
+        "201112",
+        "prs_con",
+        "surface precipitation rate, snow, convective"
+    ],
+    [
+        "201113",
+        "rain_con",
+        "surface precipitation amount, rain, convective"
+    ],
+    [
+        "201139",
+        "pp",
+        "deviation of pressure from reference value"
+    ],
+    [
+        "201150",
+        "coefficient of horizontal diffusion"
+    ],
+    [
+        "201187",
+        "vmax_10m",
+        "maximum wind velocity"
+    ],
+    [
+        "201200",
+        "w_i",
+        "water content of interception store"
+    ],
+    [
+        "201203",
+        "t_snow",
+        "snow temperature"
+    ],
+    [
+        "201215",
+        "t_ice",
+        "ice surface temperature"
+    ],
+    [
+        "201241",
+        "cape_con",
+        "convective available potential energy"
+    ],
+    [
+        "210001",
+        "aermr01",
+        "sea salt aerosol (0.03 - 0.5 um) mixing ratio"
+    ],
+    [
+        "210002",
+        "aermr02",
+        "sea salt aerosol (0.5 - 5 um) mixing ratio"
+    ],
+    [
+        "210003",
+        "aermr03",
+        "sea salt aerosol (5 - 20 um) mixing ratio"
+    ],
+    [
+        "210004",
+        "aermr04",
+        "dust aerosol (0.03 - 0.55 um) mixing ratio"
+    ],
+    [
+        "210005",
+        "aermr05",
+        "dust aerosol (0.55 - 0.9 um) mixing ratio"
+    ],
+    [
+        "210006",
+        "aermr06",
+        "dust aerosol (0.9 - 20 um) mixing ratio"
+    ],
+    [
+        "210007",
+        "aermr07",
+        "hydrophobic organic matter aerosol mixing ratio"
+    ],
+    [
+        "210008",
+        "aermr08",
+        "hydrophilic organic matter aerosol mixing ratio"
+    ],
+    [
+        "210009",
+        "aermr09",
+        "hydrophobic black carbon aerosol mixing ratio"
+    ],
+    [
+        "210010",
+        "aermr10",
+        "hydrophilic black carbon aerosol mixing ratio"
+    ],
+    [
+        "210011",
+        "aermr11",
+        "sulphate aerosol mixing ratio"
+    ],
+    [
+        "210012",
+        "aermr12",
+        "so2 precursor mixing ratio"
+    ],
+    [
+        "210016",
+        "aergn01",
+        "aerosol type 1 source/gain accumulated"
+    ],
+    [
+        "210017",
+        "aergn02",
+        "aerosol type 2 source/gain accumulated"
+    ],
+    [
+        "210018",
+        "aergn03",
+        "aerosol type 3 source/gain accumulated"
+    ],
+    [
+        "210019",
+        "aergn04",
+        "aerosol type 4 source/gain accumulated"
+    ],
+    [
+        "210020",
+        "aergn05",
+        "aerosol type 5 source/gain accumulated"
+    ],
+    [
+        "210021",
+        "aergn06",
+        "aerosol type 6 source/gain accumulated"
+    ],
+    [
+        "210022",
+        "aergn07",
+        "aerosol type 7 source/gain accumulated"
+    ],
+    [
+        "210023",
+        "aergn08",
+        "aerosol type 8 source/gain accumulated"
+    ],
+    [
+        "210024",
+        "aergn09",
+        "aerosol type 9 source/gain accumulated"
+    ],
+    [
+        "210025",
+        "aergn10",
+        "aerosol type 10 source/gain accumulated"
+    ],
+    [
+        "210026",
+        "aergn11",
+        "aerosol type 11 source/gain accumulated"
+    ],
+    [
+        "210027",
+        "aergn12",
+        "aerosol type 12 source/gain accumulated"
+    ],
+    [
+        "210031",
+        "aerls01",
+        "aerosol type 1 sink/loss accumulated"
+    ],
+    [
+        "210032",
+        "aerls02",
+        "aerosol type 2 sink/loss accumulated"
+    ],
+    [
+        "210033",
+        "aerls03",
+        "aerosol type 3 sink/loss accumulated"
+    ],
+    [
+        "210034",
+        "aerls04",
+        "aerosol type 4 sink/loss accumulated"
+    ],
+    [
+        "210035",
+        "aerls05",
+        "aerosol type 5 sink/loss accumulated"
+    ],
+    [
+        "210036",
+        "aerls06",
+        "aerosol type 6 sink/loss accumulated"
+    ],
+    [
+        "210037",
+        "aerls07",
+        "aerosol type 7 sink/loss accumulated"
+    ],
+    [
+        "210038",
+        "aerls08",
+        "aerosol type 8 sink/loss accumulated"
+    ],
+    [
+        "210039",
+        "aerls09",
+        "aerosol type 9 sink/loss accumulated"
+    ],
+    [
+        "210040",
+        "aerls10",
+        "aerosol type 10 sink/loss accumulated"
+    ],
+    [
+        "210041",
+        "aerls11",
+        "aerosol type 11 sink/loss accumulated"
+    ],
+    [
+        "210042",
+        "aerls12",
+        "aerosol type 12 sink/loss accumulated"
+    ],
+    [
+        "210046",
+        "aerpr",
+        "aerosol precursor mixing ratio"
+    ],
+    [
+        "210047",
+        "aersm",
+        "aerosol small mode mixing ratio"
+    ],
+    [
+        "210048",
+        "aerlg",
+        "aerosol large mode mixing ratio"
+    ],
+    [
+        "210049",
+        "aodpr",
+        "aerosol precursor optical depth"
+    ],
+    [
+        "210050",
+        "aodsm",
+        "aerosol small mode optical depth"
+    ],
+    [
+        "210051",
+        "aodlg",
+        "aerosol large mode optical depth"
+    ],
+    [
+        "210052",
+        "aerdep",
+        "dust emission potential"
+    ],
+    [
+        "210053",
+        "aerlts",
+        "lifting threshold speed"
+    ],
+    [
+        "210054",
+        "aerscc",
+        "soil clay content"
+    ],
+    [
+        "210061",
+        "co2",
+        "carbon dioxide"
+    ],
+    [
+        "210062",
+        "ch4",
+        "methane"
+    ],
+    [
+        "210063",
+        "n2o",
+        "nitrous oxide"
+    ],
+    [
+        "210064",
+        "tcco2",
+        "total column carbon dioxide"
+    ],
+    [
+        "210065",
+        "tcch4",
+        "total column methane"
+    ],
+    [
+        "210066",
+        "tcn2o",
+        "total column nitrous oxide"
+    ],
+    [
+        "210067",
+        "co2of",
+        "ocean flux of carbon dioxide"
+    ],
+    [
+        "210068",
+        "co2nbf",
+        "natural biosphere flux of carbon dioxide"
+    ],
+    [
+        "210069",
+        "co2apf",
+        "anthropogenic emissions of carbon dioxide"
+    ],
+    [
+        "210070",
+        "ch4f",
+        "methane surface fluxes"
+    ],
+    [
+        "210071",
+        "kch4",
+        "methane loss rate due to radical hydroxyl (oh)"
+    ],
+    [
+        "210080",
+        "co2fire",
+        "wildfire flux of carbon dioxide"
+    ],
+    [
+        "210081",
+        "cofire",
+        "wildfire flux of carbon monoxide"
+    ],
+    [
+        "210082",
+        "ch4fire",
+        "wildfire flux of methane"
+    ],
+    [
+        "210083",
+        "nmhcfire",
+        "wildfire flux of non-methane hydro-carbons"
+    ],
+    [
+        "210084",
+        "h2fire",
+        "wildfire flux of hydrogen"
+    ],
+    [
+        "210085",
+        "noxfire",
+        "wildfire flux of nitrogen oxides nox"
+    ],
+    [
+        "210086",
+        "n2ofire",
+        "wildfire flux of nitrous oxide"
+    ],
+    [
+        "210087",
+        "pm2p5fire",
+        "wildfire flux of particulate matter pm2.5"
+    ],
+    [
+        "210088",
+        "tpmfire",
+        "wildfire flux of total particulate matter"
+    ],
+    [
+        "210089",
+        "tcfire",
+        "wildfire flux of total carbon in aerosols"
+    ],
+    [
+        "210090",
+        "ocfire",
+        "wildfire flux of organic carbon"
+    ],
+    [
+        "210091",
+        "bcfire",
+        "wildfire flux of black carbon"
+    ],
+    [
+        "210092",
+        "cfire",
+        "wildfire overall flux of burnt carbon"
+    ],
+    [
+        "210093",
+        "c4ffire",
+        "wildfire fraction of c4 plants"
+    ],
+    [
+        "210094",
+        "vegfire",
+        "wildfire vegetation map index"
+    ],
+    [
+        "210095",
+        "ccfire",
+        "wildfire combustion completeness"
+    ],
+    [
+        "210096",
+        "flfire",
+        "wildfire fuel load: carbon per unit area"
+    ],
+    [
+        "210097",
+        "offire",
+        "wildfire fraction of area observed"
+    ],
+    [
+        "210098",
+        "nofrp",
+        "number of positive frp pixels per grid cell"
+    ],
+    [
+        "210099",
+        "frpfire",
+        "wildfire radiative power"
+    ],
+    [
+        "210100",
+        "crfire",
+        "wildfire combustion rate"
+    ],
+    [
+        "210121",
+        "no2",
+        "nitrogen dioxide"
+    ],
+    [
+        "210122",
+        "so2",
+        "sulphur dioxide"
+    ],
+    [
+        "210123",
+        "co",
+        "carbon monoxide"
+    ],
+    [
+        "210124",
+        "hcho",
+        "formaldehyde"
+    ],
+    [
+        "210125",
+        "tcno2",
+        "total column nitrogen dioxide"
+    ],
+    [
+        "210126",
+        "tcso2",
+        "total column sulphur dioxide"
+    ],
+    [
+        "210127",
+        "tcco",
+        "total column carbon monoxide"
+    ],
+    [
+        "210128",
+        "tchcho",
+        "total column formaldehyde"
+    ],
+    [
+        "210129",
+        "nox",
+        "nitrogen oxides"
+    ],
+    [
+        "210130",
+        "tcnox",
+        "total column nitrogen oxides"
+    ],
+    [
+        "210131",
+        "grg1",
+        "reactive tracer 1 mass mixing ratio"
+    ],
+    [
+        "210132",
+        "tcgrg1",
+        "total column grg tracer 1"
+    ],
+    [
+        "210133",
+        "grg2",
+        "reactive tracer 2 mass mixing ratio"
+    ],
+    [
+        "210134",
+        "tcgrg2",
+        "total column grg tracer 2"
+    ],
+    [
+        "210135",
+        "grg3",
+        "reactive tracer 3 mass mixing ratio"
+    ],
+    [
+        "210136",
+        "tcgrg3",
+        "total column grg tracer 3"
+    ],
+    [
+        "210137",
+        "grg4",
+        "reactive tracer 4 mass mixing ratio"
+    ],
+    [
+        "210138",
+        "tcgrg4",
+        "total column grg tracer 4"
+    ],
+    [
+        "210139",
+        "grg5",
+        "reactive tracer 5 mass mixing ratio"
+    ],
+    [
+        "210140",
+        "tcgrg5",
+        "total column grg tracer 5"
+    ],
+    [
+        "210141",
+        "grg6",
+        "reactive tracer 6 mass mixing ratio"
+    ],
+    [
+        "210142",
+        "tcgrg6",
+        "total column grg tracer 6"
+    ],
+    [
+        "210143",
+        "grg7",
+        "reactive tracer 7 mass mixing ratio"
+    ],
+    [
+        "210144",
+        "tcgrg7",
+        "total column grg tracer 7"
+    ],
+    [
+        "210145",
+        "grg8",
+        "reactive tracer 8 mass mixing ratio"
+    ],
+    [
+        "210146",
+        "tcgrg8",
+        "total column grg tracer 8"
+    ],
+    [
+        "210147",
+        "grg9",
+        "reactive tracer 9 mass mixing ratio"
+    ],
+    [
+        "210148",
+        "tcgrg9",
+        "total column grg tracer 9"
+    ],
+    [
+        "210149",
+        "grg10",
+        "reactive tracer 10 mass mixing ratio"
+    ],
+    [
+        "210150",
+        "tcgrg10",
+        "total column grg tracer 10"
+    ],
+    [
+        "210151",
+        "sfnox",
+        "surface flux nitrogen oxides"
+    ],
+    [
+        "210152",
+        "sfno2",
+        "surface flux nitrogen dioxide"
+    ],
+    [
+        "210153",
+        "sfso2",
+        "surface flux sulphur dioxide"
+    ],
+    [
+        "210154",
+        "sfco2",
+        "surface flux carbon monoxide"
+    ],
+    [
+        "210155",
+        "sfhcho",
+        "surface flux formaldehyde"
+    ],
+    [
+        "210156",
+        "sfgo3",
+        "surface flux gems ozone"
+    ],
+    [
+        "210157",
+        "sfgr1",
+        "surface flux reactive tracer 1"
+    ],
+    [
+        "210158",
+        "sfgr2",
+        "surface flux reactive tracer 2"
+    ],
+    [
+        "210159",
+        "sfgr3",
+        "surface flux reactive tracer 3"
+    ],
+    [
+        "210160",
+        "sfgr4",
+        "surface flux reactive tracer 4"
+    ],
+    [
+        "210161",
+        "sfgr5",
+        "surface flux reactive tracer 5"
+    ],
+    [
+        "210162",
+        "sfgr6",
+        "surface flux reactive tracer 6"
+    ],
+    [
+        "210163",
+        "sfgr7",
+        "surface flux reactive tracer 7"
+    ],
+    [
+        "210164",
+        "sfgr8",
+        "surface flux reactive tracer 8"
+    ],
+    [
+        "210165",
+        "sfgr9",
+        "surface flux reactive tracer 9"
+    ],
+    [
+        "210166",
+        "sfgr10",
+        "surface flux reactive tracer 10"
+    ],
+    [
+        "210181",
+        "ra",
+        "radon"
+    ],
+    [
+        "210182",
+        "sf6",
+        "sulphur hexafluoride"
+    ],
+    [
+        "210183",
+        "tcra",
+        "total column radon"
+    ],
+    [
+        "210184",
+        "tcsf6",
+        "total column sulphur hexafluoride"
+    ],
+    [
+        "210185",
+        "sf6apf",
+        "anthropogenic emissions of sulphur hexafluoride"
+    ],
+    [
+        "210203",
+        "go3",
+        "gems ozone"
+    ],
+    [
+        "210206",
+        "gtco3",
+        "gems total column ozone"
+    ],
+    [
+        "210207",
+        "aod550",
+        "total aerosol optical depth at 550nm"
+    ],
+    [
+        "210208",
+        "ssaod550",
+        "sea salt aerosol optical depth at 550nm"
+    ],
+    [
+        "210209",
+        "duaod550",
+        "dust aerosol optical depth at 550nm"
+    ],
+    [
+        "210210",
+        "omaod550",
+        "organic matter aerosol optical depth at 550nm"
+    ],
+    [
+        "210211",
+        "bcaod550",
+        "black carbon aerosol optical depth at 550nm"
+    ],
+    [
+        "210212",
+        "suaod550",
+        "sulphate aerosol optical depth at 550nm"
+    ],
+    [
+        "210213",
+        "aod469",
+        "total aerosol optical depth at 469nm"
+    ],
+    [
+        "210214",
+        "aod670",
+        "total aerosol optical depth at 670nm"
+    ],
+    [
+        "210215",
+        "aod865",
+        "total aerosol optical depth at 865nm"
+    ],
+    [
+        "210216",
+        "aod1240",
+        "total aerosol optical depth at 1240nm"
+    ],
+    [
+        "211046",
+        "aerprdiff",
+        "aerosol precursor mixing ratio"
+    ],
+    [
+        "211047",
+        "aersmdiff",
+        "aerosol small mode mixing ratio"
+    ],
+    [
+        "211048",
+        "aerlgdiff",
+        "aerosol large mode mixing ratio"
+    ],
+    [
+        "211049",
+        "aodprdiff",
+        "aerosol precursor optical depth"
+    ],
+    [
+        "211050",
+        "aodsmdiff",
+        "aerosol small mode optical depth"
+    ],
+    [
+        "211051",
+        "aodlgdiff",
+        "aerosol large mode optical depth"
+    ],
+    [
+        "211061",
+        "co2diff",
+        "carbon dioxide"
+    ],
+    [
+        "211062",
+        "ch4diff",
+        "methane"
+    ],
+    [
+        "211063",
+        "n2odiff",
+        "nitrous oxide"
+    ],
+    [
+        "211121",
+        "no2diff",
+        "nitrogen dioxide"
+    ],
+    [
+        "211122",
+        "so2diff",
+        "sulphur dioxide"
+    ],
+    [
+        "211123",
+        "codiff",
+        "carbon monoxide"
+    ],
+    [
+        "211124",
+        "hchodiff",
+        "formaldehyde"
+    ],
+    [
+        "211203",
+        "go3diff",
+        "gems ozone"
+    ],
+    [
+        "220228",
+        "tpoc",
+        "total precipitation observation count"
+    ],
+    [
+        "228001",
+        "cin",
+        "convective inhibition"
+    ],
+    [
+        "228002",
+        "orog",
+        "orography"
+    ],
+    [
+        "228003",
+        "zust",
+        "friction velocity"
+    ],
+    [
+        "228004",
+        "mean2t",
+        "mean temperature at 2 metres"
+    ],
+    [
+        "228005",
+        "mean10ws",
+        "mean of 10 metre wind speed"
+    ],
+    [
+        "228006",
+        "meantcc",
+        "mean total cloud cover"
+    ],
+    [
+        "228007",
+        "dl",
+        "lake depth"
+    ],
+    [
+        "228008",
+        "lmlt",
+        "lake mix-layer temperature"
+    ],
+    [
+        "228009",
+        "lmld",
+        "lake mix-layer depth"
+    ],
+    [
+        "228010",
+        "lblt",
+        "lake bottom temperature"
+    ],
+    [
+        "228011",
+        "ltlt",
+        "lake total layer temperature"
+    ],
+    [
+        "228012",
+        "lshf",
+        "lake shape factor"
+    ],
+    [
+        "228013",
+        "lict",
+        "lake ice temperature"
+    ],
+    [
+        "228014",
+        "licd",
+        "lake ice depth"
+    ],
+    [
+        "228015",
+        "dndzn",
+        "minimum vertical gradient of refractivity inside trapping layer"
+    ],
+    [
+        "228016",
+        "dndza",
+        "mean vertical gradient of refractivity inside trapping layer"
+    ],
+    [
+        "228017",
+        "dctb",
+        "duct base height"
+    ],
+    [
+        "228018",
+        "tplb",
+        "trapping layer base height"
+    ],
+    [
+        "228019",
+        "tplt",
+        "trapping layer top height"
+    ],
+    [
+        "228039",
+        "sm",
+        "soil moisture"
+    ],
+    [
+        "228131",
+        "u10n",
+        "neutral wind at 10 m u-component"
+    ],
+    [
+        "228132",
+        "v10n",
+        "neutral wind at 10 m v-component"
+    ],
+    [
+        "228139",
+        "st",
+        "soil temperature"
+    ],
+    [
+        "228141",
+        "sd",
+        "snow depth water equivalent"
+    ],
+    [
+        "228144",
+        "sf",
+        "snow fall water equivalent"
+    ],
+    [
+        "228164",
+        "tcc",
+        "total cloud cover"
+    ],
+    [
+        "228170",
+        "cap",
+        "field capacity"
+    ],
+    [
+        "228171",
+        "wilt",
+        "wilting point"
+    ],
+    [
+        "228228",
+        "tp",
+        "total precipitation"
+    ],
+    [
+        "230044",
+        "esvar",
+        "snow evaporation (variable resolution)"
+    ],
+    [
+        "230045",
+        "smltvar",
+        "snowmelt (variable resolution)"
+    ],
+    [
+        "230046",
+        "sdurvar",
+        "solar duration (variable resolution)"
+    ],
+    [
+        "230057",
+        "uvbvar",
+        "downward uv radiation at the surface (variable resolution)"
+    ],
+    [
+        "230058",
+        "parvar",
+        "photosynthetically active radiation at the surface (variable resolution)"
+    ],
+    [
+        "230142",
+        "lspvar",
+        "stratiform precipitation (large-scale precipitation) (variable resolution)"
+    ],
+    [
+        "230143",
+        "cpvar",
+        "convective precipitation (variable resolution)"
+    ],
+    [
+        "230144",
+        "sfvar",
+        "snowfall (convective + stratiform) (variable resolution)"
+    ],
+    [
+        "230145",
+        "bldvar",
+        "boundary layer dissipation (variable resolution)"
+    ],
+    [
+        "230146",
+        "sshfvar",
+        "surface sensible heat flux (variable resolution)"
+    ],
+    [
+        "230147",
+        "slhfvar",
+        "surface latent heat flux (variable resolution)"
+    ],
+    [
+        "230169",
+        "ssrdvar",
+        "surface solar radiation downwards (variable resolution)"
+    ],
+    [
+        "230175",
+        "strdvar",
+        "surface thermal radiation downwards (variable resolution)"
+    ],
+    [
+        "230176",
+        "ssrvar",
+        "surface net solar radiation (variable resolution)"
+    ],
+    [
+        "230177",
+        "strvar",
+        "surface net thermal radiation (variable resolution)"
+    ],
+    [
+        "230178",
+        "tsrvar",
+        "top net solar radiation (variable resolution)"
+    ],
+    [
+        "230179",
+        "ttrvar",
+        "top net thermal radiation (variable resolution)"
+    ],
+    [
+        "230180",
+        "ewssvar",
+        "east-west surface stress (variable resolution)"
+    ],
+    [
+        "230181",
+        "nsssvar",
+        "north-south surface stress (variable resolution)"
+    ],
+    [
+        "230182",
+        "evar",
+        "evaporation (variable resolution)"
+    ],
+    [
+        "230189",
+        "sundvar",
+        "sunshine duration (variable resolution)"
+    ],
+    [
+        "230195",
+        "lgwsvar",
+        "longitudinal component of gravity wave stress (variable resolution)"
+    ],
+    [
+        "230196",
+        "mgwsvar",
+        "meridional component of gravity wave stress (variable resolution)"
+    ],
+    [
+        "230197",
+        "gwdvar",
+        "gravity wave dissipation (variable resolution)"
+    ],
+    [
+        "230198",
+        "srcvar",
+        "skin reservoir content (variable resolution)"
+    ],
+    [
+        "230205",
+        "rovar",
+        "runoff (variable resolution)"
+    ],
+    [
+        "230208",
+        "tsrcvar",
+        "top net solar radiation, clear sky (variable resolution)"
+    ],
+    [
+        "230209",
+        "ttrcvar",
+        "top net thermal radiation, clear sky (variable resolution)"
+    ],
+    [
+        "230210",
+        "ssrcvar",
+        "surface net solar radiation, clear sky (variable resolution)"
+    ],
+    [
+        "230211",
+        "strcvar",
+        "surface net thermal radiation, clear sky (variable resolution)"
+    ],
+    [
+        "230212",
+        "tisrvar",
+        "toa incident solar radiation (variable resolution)"
+    ],
+    [
+        "234139",
+        "sts",
+        "surface temperature significance"
+    ],
+    [
+        "234151",
+        "msls",
+        "mean sea level pressure significance"
+    ],
+    [
+        "234167",
+        "2ts",
+        "2 metre temperature significance"
+    ],
+    [
+        "234228",
+        "tps",
+        "total precipitation significance"
+    ],
+    [
+        "3042",
+        "absd",
+        "absolute divergence"
+    ],
+    [
+        "3064",
+        "srweq",
+        "snow fall rate water equivalent"
+    ],
+    [
+        "3105",
+        "swell",
+        "significant height of swell waves"
+    ],
+    [
+        "3027",
+        "gpa",
+        "geopotential height anomaly"
+    ],
+    [
+        "3062",
+        "lsp",
+        "large scale precipitation"
+    ],
+    [
+        "3025",
+        "ta",
+        "temperature anomaly"
+    ],
+    [
+        "3055",
+        "vp",
+        "vapour pressure"
+    ],
+    [
+        "3047",
+        "dirc",
+        "direction of current"
+    ],
+    [
+        "3049",
+        "ucurr",
+        "u-component of current "
+    ],
+    [
+        "3119",
+        "lwrad",
+        "radiance (with respect to wave number)"
+    ],
+    [
+        "3028",
+        "wvsp1",
+        "wave spectra (1)"
+    ],
+    [
+        "3104",
+        "swdir",
+        "direction of swell waves"
+    ],
+    [
+        "3093",
+        "diced",
+        "direction of ice drift"
+    ],
+    [
+        "3056",
+        "satd",
+        "saturation deficit"
+    ],
+    [
+        "3114",
+        "nlwrt",
+        "net long-wave radiation flux(atmosph.top)"
+    ],
+    [
+        "3103",
+        "mpww",
+        "mean period of wind waves"
+    ],
+    [
+        "3108",
+        "mpps",
+        "primary wave mean period"
+    ],
+    [
+        "3088",
+        "s",
+        "salinity"
+    ],
+    [
+        "3024",
+        "pli",
+        "parcel lifted index (to 500 hpa)"
+    ],
+    [
+        "3069",
+        "mthd",
+        "main thermocline depth"
+    ],
+    [
+        "3125",
+        "vflx",
+        "momentum flux, v-component"
+    ],
+    [
+        "3095",
+        "uice",
+        "u-component of ice drift"
+    ],
+    [
+        "3018",
+        "depr",
+        "dew point depression (or deficit)"
+    ],
+    [
+        "3082",
+        "dslm",
+        "deviation of sea-level from mean"
+    ],
+    [
+        "3008",
+        "h",
+        "geometrical height"
+    ],
+    [
+        "3046",
+        "vvcsh",
+        "vertical v-component shear"
+    ],
+    [
+        "3048",
+        "spc",
+        "speed of current"
+    ],
+    [
+        "3097",
+        "iceg",
+        "ice growth rate"
+    ],
+    [
+        "3016",
+        "tmin",
+        "minimum temperature"
+    ],
+    [
+        "3029",
+        "wvsp2",
+        "wave spectra (2)"
+    ],
+    [
+        "3017",
+        "dpt",
+        "dew point temperature"
+    ],
+    [
+        "3115",
+        "lwavr",
+        "long wave radiation flux"
+    ],
+    [
+        "3031",
+        "wdir",
+        "wind direction"
+    ],
+    [
+        "3021",
+        "rdsp1",
+        "radar spectra (1)"
+    ],
+    [
+        "3080",
+        "wtmp",
+        "water temperature"
+    ],
+    [
+        "3112",
+        "nlwrs",
+        "net long-wave radiation flux (surface)"
+    ],
+    [
+        "3124",
+        "uflx",
+        "momentum flux, u-component"
+    ],
+    [
+        "3101",
+        "mdww",
+        "mean direction of wind waves"
+    ],
+    [
+        "3127",
+        "imgd",
+        "image data"
+    ],
+    [
+        "3116",
+        "swavr",
+        "short wave radiation flux"
+    ],
+    [
+        "3096",
+        "vice",
+        "v-component of ice drift"
+    ],
+    [
+        "3086",
+        "ssw",
+        "soil moisture content"
+    ],
+    [
+        "3037",
+        "mntsf",
+        "montgomery stream function"
+    ],
+    [
+        "3094",
+        "siced",
+        "speed of ice drift"
+    ],
+    [
+        "3012",
+        "vptmp",
+        "virtual potential temperature"
+    ],
+    [
+        "3009",
+        "hstdv",
+        "standard deviation of height"
+    ],
+    [
+        "3050",
+        "vcurr",
+        "v-component of current "
+    ],
+    [
+        "3067",
+        "mld",
+        "mixed layer depth"
+    ],
+    [
+        "3111",
+        "nswrs",
+        "net short-wave radiation flux (surface)"
+    ],
+    [
+        "3068",
+        "tthdp",
+        "transient thermocline depth"
+    ],
+    [
+        "3110",
+        "swp",
+        "secondary wave mean period"
+    ],
+    [
+        "3102",
+        "shww",
+        "significant height of wind waves"
+    ],
+    [
+        "3126",
+        "wmixe",
+        "wind mixing energy"
+    ],
+    [
+        "3070",
+        "mtha",
+        "main thermocline anomaly"
+    ],
+    [
+        "3060",
+        "tstm",
+        "thunderstorm probability"
+    ],
+    [
+        "3003",
+        "ptend",
+        "pressure tendency"
+    ],
+    [
+        "3015",
+        "tmax",
+        "maximum temperature"
+    ],
+    [
+        "3098",
+        "iced",
+        "ice divergence"
+    ],
+    [
+        "3100",
+        "swh",
+        "signific.height,combined wind waves+swell"
+    ],
+    [
+        "3117",
+        "grad",
+        "global radiation flux"
+    ],
+    [
+        "3092",
+        "icetk",
+        "ice thickness"
+    ],
+    [
+        "3091",
+        "icec",
+        "ice cover (1=ice, 0=no ice)"
+    ],
+    [
+        "3109",
+        "dirsw",
+        "secondary wave direction"
+    ],
+    [
+        "3030",
+        "wvsp3",
+        "wave spectra (3)"
+    ],
+    [
+        "3026",
+        "presa",
+        "pressure anomaly"
+    ],
+    [
+        "3107",
+        "mdps",
+        "primary wave direction"
+    ],
+    [
+        "3014",
+        "papt",
+        "pseudo-adiabatic potential temperature"
+    ],
+    [
+        "3022",
+        "rdsp2",
+        "radar spectra (2)"
+    ],
+    [
+        "3019",
+        "lapr",
+        "lapse rate"
+    ],
+    [
+        "3077",
+        "bli",
+        "best lifted index (to 500 hpa)"
+    ],
+    [
+        "3038",
+        "sgcvv",
+        "sigma coordinate vertical velocity"
+    ],
+    [
+        "3089",
+        "den",
+        "density"
+    ],
+    [
+        "3059",
+        "prate",
+        "precipitation rate"
+    ],
+    [
+        "3054",
+        "pwat",
+        "precipitable water"
+    ],
+    [
+        "3020",
+        "vis",
+        "visibility"
+    ],
+    [
+        "3005",
+        "icaht",
+        "icao standard atmosphere reference height"
+    ],
+    [
+        "3106",
+        "swper",
+        "mean period of swell waves"
+    ],
+    [
+        "3063",
+        "acpcp",
+        "convective precipitation (water)"
+    ],
+    [
+        "3041",
+        "absv",
+        "absolute vorticity"
+    ],
+    [
+        "3113",
+        "nswrt",
+        "net short-wave radiation flux(atmosph.top)"
+    ],
+    [
+        "3120",
+        "swrad",
+        "radiance (with respect to wave length)"
+    ],
+    [
+        "3099",
+        "snom",
+        "snow melt"
+    ],
+    [
+        "3053",
+        "mixr",
+        "humidity mixing ratio"
+    ],
+    [
+        "3023",
+        "rdsp3",
+        "radar spectra (3)"
+    ],
+    [
+        "3045",
+        "vucsh",
+        "vertical u-component shear"
+    ],
+    [
+        "228080",
+        "aco2nee",
+        "accumulated carbon dioxide net ecosystem exchange"
+    ],
+    [
+        "260002",
+        "lhtfl",
+        "latent heat net flux"
+    ],
+    [
+        "260003",
+        "shtfl",
+        "sensible heat net flux"
+    ],
+    [
+        "260004",
+        "heatx",
+        "heat index"
+    ],
+    [
+        "260005",
+        "wcf",
+        "wind chill factor"
+    ],
+    [
+        "260006",
+        "mindpd",
+        "minimum dew point depression"
+    ],
+    [
+        "260007",
+        "snohf",
+        "snow phase change heat flux"
+    ],
+    [
+        "260008",
+        "vapp",
+        "vapor pressure"
+    ],
+    [
+        "260009",
+        "ncpcp",
+        "large scale precipitation (non-convective)"
+    ],
+    [
+        "260010",
+        "srweq",
+        "snowfall rate water equivalent"
+    ],
+    [
+        "260011",
+        "snoc",
+        "convective snow"
+    ],
+    [
+        "260012",
+        "snol",
+        "large scale snow"
+    ],
+    [
+        "260013",
+        "snoag",
+        "snow age"
+    ],
+    [
+        "260014",
+        "absh",
+        "absolute humidity"
+    ],
+    [
+        "260015",
+        "ptype",
+        "precipitation type"
+    ],
+    [
+        "260016",
+        "iliqw",
+        "integrated liquid water"
+    ],
+    [
+        "260017",
+        "tcond",
+        "condensate"
+    ],
+    [
+        "260018",
+        "clwmr",
+        "cloud mixing ratio"
+    ],
+    [
+        "260019",
+        "icmr",
+        "ice water mixing ratio"
+    ],
+    [
+        "260020",
+        "rwmr",
+        "rain mixing ratio"
+    ],
+    [
+        "260021",
+        "snmr",
+        "snow mixing ratio"
+    ],
+    [
+        "260022",
+        "mconv",
+        "horizontal moisture convergence"
+    ],
+    [
+        "260023",
+        "maxrh",
+        "maximum relative humidity"
+    ],
+    [
+        "260024",
+        "maxah",
+        "maximum absolute humidity"
+    ],
+    [
+        "260025",
+        "asnow",
+        "total snowfall"
+    ],
+    [
+        "260026",
+        "pwcat",
+        "precipitable water category"
+    ],
+    [
+        "260027",
+        "hail",
+        "hail"
+    ],
+    [
+        "260028",
+        "grle",
+        "graupel (snow pellets)"
+    ],
+    [
+        "260029",
+        "crain",
+        "categorical rain"
+    ],
+    [
+        "260030",
+        "cfrzr",
+        "categorical freezing rain"
+    ],
+    [
+        "260031",
+        "cicep",
+        "categorical ice pellets"
+    ],
+    [
+        "260032",
+        "csnow",
+        "categorical snow"
+    ],
+    [
+        "260033",
+        "cprat",
+        "convective precipitation rate"
+    ],
+    [
+        "260034",
+        "mdiv",
+        "horizontal moisture divergence"
+    ],
+    [
+        "260035",
+        "cpofp",
+        "percent frozen precipitation"
+    ],
+    [
+        "260036",
+        "pevap",
+        "potential evaporation"
+    ],
+    [
+        "260037",
+        "pevpr",
+        "potential evaporation rate"
+    ],
+    [
+        "260038",
+        "snowc",
+        "snow cover"
+    ],
+    [
+        "260039",
+        "frain",
+        "rain fraction of total cloud water"
+    ],
+    [
+        "260040",
+        "rime",
+        "rime factor"
+    ],
+    [
+        "260041",
+        "tcolr",
+        "total column integrated rain"
+    ],
+    [
+        "260042",
+        "tcols",
+        "total column integrated snow"
+    ],
+    [
+        "260043",
+        "lswp",
+        "large scale water precipitation (non-convective)"
+    ],
+    [
+        "260044",
+        "cwp",
+        "convective water precipitation"
+    ],
+    [
+        "260045",
+        "twatp",
+        "total water precipitation"
+    ],
+    [
+        "260046",
+        "tsnowp",
+        "total snow precipitation"
+    ],
+    [
+        "260047",
+        "tcwat",
+        "total column water (vertically integrated total water (vapour + cloud water/ice))"
+    ],
+    [
+        "260048",
+        "tprate",
+        "total precipitation rate"
+    ],
+    [
+        "260049",
+        "tsrwe",
+        "total snowfall rate water equivalent"
+    ],
+    [
+        "260050",
+        "lsprate",
+        "large scale precipitation rate"
+    ],
+    [
+        "260051",
+        "csrwe",
+        "convective snowfall rate water equivalent"
+    ],
+    [
+        "260052",
+        "lssrwe",
+        "large scale snowfall rate water equivalent"
+    ],
+    [
+        "260053",
+        "tsrate",
+        "total snowfall rate"
+    ],
+    [
+        "260054",
+        "csrate",
+        "convective snowfall rate"
+    ],
+    [
+        "260055",
+        "lssrate",
+        "large scale snowfall rate"
+    ],
+    [
+        "260056",
+        "sdwe",
+        "water equivalent of accumulated snow depth"
+    ],
+    [
+        "260057",
+        "tciwv",
+        "total column integrated water vapour"
+    ],
+    [
+        "260058",
+        "rprate",
+        "rain precipitation rate"
+    ],
+    [
+        "260059",
+        "sprate",
+        "snow precipitation rate"
+    ],
+    [
+        "260060",
+        "fprate",
+        "freezing rain precipitation rate"
+    ],
+    [
+        "260061",
+        "iprate",
+        "ice pellets precipitation rate"
+    ],
+    [
+        "260062",
+        "uflx",
+        "momentum flux, u component"
+    ],
+    [
+        "260063",
+        "vflx",
+        "momentum flux, v component"
+    ],
+    [
+        "260064",
+        "maxgust",
+        "maximum wind speed"
+    ],
+    [
+        "260065",
+        "gust",
+        "wind speed (gust)"
+    ],
+    [
+        "260066",
+        "ugust",
+        "u-component of wind (gust)"
+    ],
+    [
+        "260067",
+        "vgust",
+        "v-component of wind (gust)"
+    ],
+    [
+        "260068",
+        "vwsh",
+        "vertical speed shear"
+    ],
+    [
+        "260069",
+        "mflx",
+        "horizontal momentum flux"
+    ],
+    [
+        "260070",
+        "ustm",
+        "u-component storm motion"
+    ],
+    [
+        "260071",
+        "vstm",
+        "v-component storm motion"
+    ],
+    [
+        "260072",
+        "cd",
+        "drag coefficient"
+    ],
+    [
+        "260073",
+        "fricv",
+        "frictional velocity"
+    ],
+    [
+        "260074",
+        "prmsl",
+        "pressure reduced to msl"
+    ],
+    [
+        "260075",
+        "dist",
+        "geometric height"
+    ],
+    [
+        "260076",
+        "alts",
+        "altimeter setting"
+    ],
+    [
+        "260077",
+        "thick",
+        "thickness"
+    ],
+    [
+        "260078",
+        "presalt",
+        "pressure altitude"
+    ],
+    [
+        "260079",
+        "denalt",
+        "density altitude"
+    ],
+    [
+        "260080",
+        "5wavh",
+        "5-wave geopotential height"
+    ],
+    [
+        "260081",
+        "u-gwd",
+        "zonal flux of gravity wave stress"
+    ],
+    [
+        "260082",
+        "v-gwd",
+        "meridional flux of gravity wave stress"
+    ],
+    [
+        "260083",
+        "hpbl",
+        "planetary boundary layer height"
+    ],
+    [
+        "260084",
+        "5wava",
+        "5-wave geopotential height anomaly"
+    ],
+    [
+        "260085",
+        "sdsgso",
+        "standard deviation of sub-grid scale orography"
+    ],
+    [
+        "260086",
+        "nswrt",
+        "net short-wave radiation flux (top of atmosphere)"
+    ],
+    [
+        "260087",
+        "dswrf",
+        "downward short-wave radiation flux"
+    ],
+    [
+        "260088",
+        "uswrf",
+        "upward short-wave radiation flux"
+    ],
+    [
+        "260089",
+        "nswrf",
+        "net short wave radiation flux"
+    ],
+    [
+        "260090",
+        "photar",
+        "photosynthetically active radiation"
+    ],
+    [
+        "260091",
+        "nswrfcs",
+        "net short-wave radiation flux, clear sky"
+    ],
+    [
+        "260092",
+        "dwuvr",
+        "downward uv radiation"
+    ],
+    [
+        "260093",
+        "uviucs",
+        "uv index (under clear sky)"
+    ],
+    [
+        "260094",
+        "uvi",
+        "uv index "
+    ],
+    [
+        "260095",
+        "nlwrs",
+        "net long wave radiation flux (surface)"
+    ],
+    [
+        "260096",
+        "nlwrt",
+        "net long wave radiation flux (top of atmosphere)"
+    ],
+    [
+        "260097",
+        "dlwrf",
+        "downward long-wave radiation flux"
+    ],
+    [
+        "260098",
+        "ulwrf",
+        "upward long-wave radiation flux"
+    ],
+    [
+        "260099",
+        "nlwrf",
+        "net long wave radiation flux"
+    ],
+    [
+        "260100",
+        "nlwrcs",
+        "net long-wave radiation flux, clear sky"
+    ],
+    [
+        "260101",
+        "cice",
+        "cloud ice"
+    ],
+    [
+        "260102",
+        "cwat",
+        "cloud water"
+    ],
+    [
+        "260103",
+        "cdca",
+        "cloud amount"
+    ],
+    [
+        "260104",
+        "cdct",
+        "cloud type"
+    ],
+    [
+        "260105",
+        "tmaxt",
+        "thunderstorm maximum tops"
+    ],
+    [
+        "260106",
+        "thunc",
+        "thunderstorm coverage"
+    ],
+    [
+        "260107",
+        "cdcb",
+        "cloud base"
+    ],
+    [
+        "260108",
+        "cdct",
+        "cloud top"
+    ],
+    [
+        "260109",
+        "ceil",
+        "ceiling"
+    ],
+    [
+        "260110",
+        "cdlyr",
+        "non-convective cloud cover"
+    ],
+    [
+        "260111",
+        "cwork",
+        "cloud work function"
+    ],
+    [
+        "260112",
+        "cuefi",
+        "convective cloud efficiency"
+    ],
+    [
+        "260113",
+        "tcond",
+        "total condensate"
+    ],
+    [
+        "260114",
+        "tcolw",
+        "total column-integrated cloud water"
+    ],
+    [
+        "260115",
+        "tcoli",
+        "total column-integrated cloud ice"
+    ],
+    [
+        "260116",
+        "tcolc",
+        "total column-integrated condensate"
+    ],
+    [
+        "260117",
+        "fice",
+        "ice fraction of total condensate"
+    ],
+    [
+        "260118",
+        "cdcimr",
+        "cloud ice mixing ratio"
+    ],
+    [
+        "260119",
+        "suns",
+        "sunshine"
+    ],
+    [
+        "260120",
+        "horizontal extent of cumulonimbus (cb)"
+    ],
+    [
+        "260121",
+        "kx",
+        "k index"
+    ],
+    [
+        "260122",
+        "kox",
+        "ko index"
+    ],
+    [
+        "260123",
+        "totalx",
+        "total totals index"
+    ],
+    [
+        "260124",
+        "sx",
+        "sweat index"
+    ],
+    [
+        "260125",
+        "hlcy",
+        "storm relative helicity"
+    ],
+    [
+        "260126",
+        "ehlx",
+        "energy helicity index"
+    ],
+    [
+        "260127",
+        "lftx",
+        "surface lifted index"
+    ],
+    [
+        "260128",
+        "4lftx",
+        "best (4-layer) lifted index"
+    ],
+    [
+        "260129",
+        "aerot",
+        "aerosol type"
+    ],
+    [
+        "260130",
+        "tozne",
+        "total ozone"
+    ],
+    [
+        "260131",
+        "o3mr",
+        "ozone mixing ratio"
+    ],
+    [
+        "260132",
+        "tcioz",
+        "total column integrated ozone"
+    ],
+    [
+        "260133",
+        "bswid",
+        "base spectrum width"
+    ],
+    [
+        "260134",
+        "bref",
+        "base reflectivity"
+    ],
+    [
+        "260135",
+        "brvel",
+        "base radial velocity"
+    ],
+    [
+        "260136",
+        "veril",
+        "vertically-integrated liquid"
+    ],
+    [
+        "260137",
+        "lmaxbr",
+        "layer-maximum base reflectivity"
+    ],
+    [
+        "260138",
+        "prec",
+        "precipitation"
+    ],
+    [
+        "260139",
+        "acces",
+        "air concentration of caesium 137"
+    ],
+    [
+        "260140",
+        "aciod",
+        "air concentration of iodine 131"
+    ],
+    [
+        "260141",
+        "acradp",
+        "air concentration of radioactive pollutant"
+    ],
+    [
+        "260142",
+        "gdces",
+        "ground deposition of caesium 137"
+    ],
+    [
+        "260143",
+        "gdiod",
+        "ground deposition of iodine 131"
+    ],
+    [
+        "260144",
+        "gdradp",
+        "ground deposition of radioactive pollutant"
+    ],
+    [
+        "260145",
+        "tiaccp",
+        "time-integrated air concentration of caesium pollutant"
+    ],
+    [
+        "260146",
+        "tiacip",
+        "time-integrated air concentration of iodine pollutant"
+    ],
+    [
+        "260147",
+        "tiacrp",
+        "time-integrated air concentration of radioactive pollutant"
+    ],
+    [
+        "260148",
+        "volash",
+        "volcanic ash"
+    ],
+    [
+        "260149",
+        "icit",
+        "icing top"
+    ],
+    [
+        "260150",
+        "icib",
+        "icing base"
+    ],
+    [
+        "260151",
+        "ici",
+        "icing"
+    ],
+    [
+        "260152",
+        "turbt",
+        "turbulence top"
+    ],
+    [
+        "260153",
+        "turbb",
+        "turbulence base"
+    ],
+    [
+        "260154",
+        "turb",
+        "turbulence"
+    ],
+    [
+        "260155",
+        "tke",
+        "turbulent kinetic energy"
+    ],
+    [
+        "260156",
+        "pblreg",
+        "planetary boundary layer regime"
+    ],
+    [
+        "260157",
+        "conti",
+        "contrail intensity"
+    ],
+    [
+        "260158",
+        "contet",
+        "contrail engine type"
+    ],
+    [
+        "260159",
+        "contt",
+        "contrail top"
+    ],
+    [
+        "260160",
+        "contb",
+        "contrail base"
+    ],
+    [
+        "260161",
+        "mxsalb",
+        "maximum snow albedo"
+    ],
+    [
+        "260162",
+        "snfalb",
+        "snow free albedo"
+    ],
+    [
+        "260163",
+        "icing"
+    ],
+    [
+        "260164",
+        "in-cloud turbulence"
+    ],
+    [
+        "260165",
+        "cat",
+        "clear air turbulence (cat)"
+    ],
+    [
+        "260166",
+        "supercooled large droplet probability (see note 4)"
+    ],
+    [
+        "260167",
+        "var190m0",
+        "arbitrary text string"
+    ],
+    [
+        "260168",
+        "tsec",
+        "seconds prior to initial reference time (defined in section 1)"
+    ],
+    [
+        "260169",
+        "ffldg",
+        "flash flood guidance (encoded as an accumulation over a floating subinterval of time between the ref"
+    ],
+    [
+        "260170",
+        "ffldro",
+        "flash flood runoff (encoded as an accumulation over a floating subinterval of time)"
+    ],
+    [
+        "260171",
+        "rssc",
+        "remotely sensed snow cover"
+    ],
+    [
+        "260172",
+        "esct",
+        "elevation of snow covered terrain"
+    ],
+    [
+        "260173",
+        "swepon",
+        "snow water equivalent percent of normal"
+    ],
+    [
+        "260174",
+        "bgrun",
+        "baseflow-groundwater runoff"
+    ],
+    [
+        "260175",
+        "ssrun",
+        "storm surface runoff"
+    ],
+    [
+        "260176",
+        "cppop",
+        "conditional percent precipitation amount fractile for an overall period (encoded as an accumulation)"
+    ],
+    [
+        "260177",
+        "pposp",
+        "percent precipitation in a sub-period of an overall period (encoded as per cent accumulation over th"
+    ],
+    [
+        "260178",
+        "pop",
+        "probability of 0.01 inch of precipitation (pop)"
+    ],
+    [
+        "260179",
+        "land",
+        "land cover (1=land, 0=sea)"
+    ],
+    [
+        "260180",
+        "veg",
+        "vegetation"
+    ],
+    [
+        "260181",
+        "watr",
+        "water runoff"
+    ],
+    [
+        "260182",
+        "evapt",
+        "evapotranspiration"
+    ],
+    [
+        "260183",
+        "mterh",
+        "model terrain height"
+    ],
+    [
+        "260184",
+        "landu",
+        "land use"
+    ],
+    [
+        "260185",
+        "soilw",
+        "volumetric soil moisture content"
+    ],
+    [
+        "260186",
+        "gflux",
+        "ground heat flux"
+    ],
+    [
+        "260187",
+        "mstav",
+        "moisture availability"
+    ],
+    [
+        "260188",
+        "sfexc",
+        "exchange coefficient"
+    ],
+    [
+        "260189",
+        "cnwat",
+        "plant canopy surface water"
+    ],
+    [
+        "260190",
+        "bmixl",
+        "blackadar mixing length scale"
+    ],
+    [
+        "260191",
+        "ccond",
+        "canopy conductance"
+    ],
+    [
+        "260192",
+        "rsmin",
+        "minimal stomatal resistance"
+    ],
+    [
+        "260193",
+        "rcs",
+        "solar parameter in canopy conductance"
+    ],
+    [
+        "260194",
+        "rct",
+        "temperature parameter in canopy conductance"
+    ],
+    [
+        "260195",
+        "rcsol",
+        "soil moisture parameter in canopy conductance"
+    ],
+    [
+        "260196",
+        "rcq",
+        "humidity parameter in canopy conductance"
+    ],
+    [
+        "260197",
+        "cisoilw",
+        "column-integrated soil water"
+    ],
+    [
+        "260198",
+        "hflux",
+        "heat flux"
+    ],
+    [
+        "260199",
+        "vsw",
+        "volumetric soil moisture"
+    ],
+    [
+        "260200",
+        "vwiltm",
+        "volumetric wilting point"
+    ],
+    [
+        "260201",
+        "uplst",
+        "upper layer soil temperature"
+    ],
+    [
+        "260202",
+        "uplsm",
+        "upper layer soil moisture"
+    ],
+    [
+        "260203",
+        "lowlsm",
+        "lower layer soil moisture"
+    ],
+    [
+        "260204",
+        "botlst",
+        "bottom layer soil temperature"
+    ],
+    [
+        "260205",
+        "soill",
+        "liquid volumetric soil moisture (non-frozen)"
+    ],
+    [
+        "260206",
+        "rlyrs",
+        "number of soil layers in root zone"
+    ],
+    [
+        "260207",
+        "smref",
+        "transpiration stress-onset (soil moisture)"
+    ],
+    [
+        "260208",
+        "smdry",
+        "direct evaporation cease (soil moisture)"
+    ],
+    [
+        "260209",
+        "poros",
+        "soil porosity"
+    ],
+    [
+        "260210",
+        "liqvsm",
+        "liquid volumetric soil moisture (non-frozen)"
+    ],
+    [
+        "260211",
+        "voltso",
+        "volumetric transpiration stress-onset (soil moisture)"
+    ],
+    [
+        "260212",
+        "transo",
+        "transpiration stress-onset (soil moisture)"
+    ],
+    [
+        "260213",
+        "voldec",
+        "volumetric direct evaporation cease (soil moisture)"
+    ],
+    [
+        "260214",
+        "direc",
+        "direct evaporation cease (soil moisture)"
+    ],
+    [
+        "260215",
+        "soilp",
+        "soil porosity"
+    ],
+    [
+        "260216",
+        "vsosm",
+        "volumetric saturation of soil moisture"
+    ],
+    [
+        "260217",
+        "satosm",
+        "saturation of soil moisture"
+    ],
+    [
+        "260218",
+        "estp",
+        "estimated precipitation"
+    ],
+    [
+        "260219",
+        "irrate",
+        "instantaneous rain rate"
+    ],
+    [
+        "260220",
+        "ctoph",
+        "cloud top height"
+    ],
+    [
+        "260221",
+        "ctophqi",
+        "cloud top height quality indicator"
+    ],
+    [
+        "260222",
+        "estu",
+        "estimated u component of wind "
+    ],
+    [
+        "260223",
+        "estv",
+        "estimated v component of wind"
+    ],
+    [
+        "260224",
+        "npixu",
+        "number of pixels used"
+    ],
+    [
+        "260225",
+        "solza",
+        "solar zenith angle"
+    ],
+    [
+        "260226",
+        "raza",
+        "relative azimuth angle"
+    ],
+    [
+        "260227",
+        "rfl06",
+        "reflectance in 0.6 micron channel"
+    ],
+    [
+        "260228",
+        "rfl08",
+        "reflectance in 0.8 micron channel"
+    ],
+    [
+        "260229",
+        "rfl16",
+        "reflectance in 1.6 micron channel"
+    ],
+    [
+        "260230",
+        "rfl39",
+        "reflectance in 3.9 micron channel"
+    ],
+    [
+        "260231",
+        "atmdiv",
+        "atmospheric divergence"
+    ],
+    [
+        "260232",
+        "wvdir",
+        "direction of wind waves"
+    ],
+    [
+        "260233",
+        "dirpw",
+        "primary wave direction"
+    ],
+    [
+        "260234",
+        "perpw",
+        "primary wave mean period"
+    ],
+    [
+        "260235",
+        "persw",
+        "secondary wave mean period"
+    ],
+    [
+        "260236",
+        "dirc",
+        "current direction"
+    ],
+    [
+        "260237",
+        "spc",
+        "current speed"
+    ],
+    [
+        "260239",
+        "ist",
+        "ice temperature"
+    ],
+    [
+        "260240",
+        "dslm",
+        "deviation of sea level from mean"
+    ],
+    [
+        "260241",
+        "tsec",
+        "seconds prior to initial reference time (defined in section 1)"
+    ],
+    [
+        "260243",
+        "ttrad",
+        "temperature tendency by all radiation"
+    ],
+    [
+        "260244",
+        "rev",
+        "relative error variance"
+    ],
+    [
+        "260245",
+        "lrghr",
+        "large scale condensate heating rate"
+    ],
+    [
+        "260246",
+        "cnvhr",
+        "deep convective heating rate"
+    ],
+    [
+        "260247",
+        "thflx",
+        "total downward heat flux at surface"
+    ],
+    [
+        "260248",
+        "ttdia",
+        "temperature tendency by all physics"
+    ],
+    [
+        "260249",
+        "ttphy",
+        "temperature tendency by non-radiation physics"
+    ],
+    [
+        "260250",
+        "tsd1d",
+        "standard dev. of ir temp. over 1x1 deg. area"
+    ],
+    [
+        "260251",
+        "shahr",
+        "shallow convective heating rate"
+    ],
+    [
+        "260252",
+        "vdfhr",
+        "vertical diffusion heating rate"
+    ],
+    [
+        "260253",
+        "thz0",
+        "potential temperature at top of viscous sublayer"
+    ],
+    [
+        "260254",
+        "tchp",
+        "tropical cyclone heat potential"
+    ],
+    [
+        "260261",
+        "minrh",
+        "minimum relative humidity"
+    ],
+    [
+        "260269",
+        "tipd",
+        "total icing potential diagnostic"
+    ],
+    [
+        "260270",
+        "ncip",
+        "number concentration for ice particles"
+    ],
+    [
+        "260271",
+        "snot",
+        "snow temperature"
+    ],
+    [
+        "260272",
+        "tclsw",
+        "total column-integrated supercooled liquid water"
+    ],
+    [
+        "260273",
+        "tcolm",
+        "total column-integrated melting ice"
+    ],
+    [
+        "260274",
+        "emnp",
+        "evaporation - precipitation"
+    ],
+    [
+        "260275",
+        "sbsno",
+        "sublimation (evaporation from snow)"
+    ],
+    [
+        "260276",
+        "cnvmr",
+        "deep convective moistening rate"
+    ],
+    [
+        "260277",
+        "shamr",
+        "shallow convective moistening rate"
+    ],
+    [
+        "260278",
+        "vdfmr",
+        "vertical diffusion moistening rate"
+    ],
+    [
+        "260279",
+        "condp",
+        "condensation pressure of parcali lifted from indicate surface"
+    ],
+    [
+        "260280",
+        "lrgmr",
+        "large scale moistening rate"
+    ],
+    [
+        "260281",
+        "qz0",
+        "specific humidity at top of viscous sublayer"
+    ],
+    [
+        "260282",
+        "qmax",
+        "maximum specific humidity at 2m"
+    ],
+    [
+        "260283",
+        "qmin",
+        "minimum specific humidity at 2m"
+    ],
+    [
+        "260284",
+        "arain",
+        "liquid precipitation (rainfall)"
+    ],
+    [
+        "260285",
+        "snowt",
+        "snow temperature, depth-avg"
+    ],
+    [
+        "260286",
+        "apcpn",
+        "total precipitation (nearest grid point)"
+    ],
+    [
+        "260287",
+        "acpcpn",
+        "convective precipitation (nearest grid point)"
+    ],
+    [
+        "260288",
+        "frzr",
+        "freezing rain"
+    ],
+    [
+        "260295",
+        "lauv",
+        "latitude of u wind component of velocity"
+    ],
+    [
+        "260296",
+        "louv",
+        "longitude of u wind component of velocity"
+    ],
+    [
+        "260297",
+        "lavv",
+        "latitude of v wind component of velocity"
+    ],
+    [
+        "260298",
+        "lovv",
+        "longitude of v wind component of velocity"
+    ],
+    [
+        "260299",
+        "lapp",
+        "latitude of presure point"
+    ],
+    [
+        "260300",
+        "lopp",
+        "longitude of presure point"
+    ],
+    [
+        "260301",
+        "vedh",
+        "vertical eddy diffusivity heat exchange"
+    ],
+    [
+        "260302",
+        "covmz",
+        "covariance between meridional and zonal components of the wind."
+    ],
+    [
+        "260303",
+        "covtz",
+        "covariance between temperature and zonal components of the wind."
+    ],
+    [
+        "260304",
+        "covtm",
+        "covariance between temperature and meridional components of the wind."
+    ],
+    [
+        "260305",
+        "vdfua",
+        "vertical diffusion zonal acceleration"
+    ],
+    [
+        "260306",
+        "vdfva",
+        "vertical diffusion meridional acceleration"
+    ],
+    [
+        "260307",
+        "gwdu",
+        "gravity wave drag zonal acceleration"
+    ],
+    [
+        "260308",
+        "gwdv",
+        "gravity wave drag meridional acceleration"
+    ],
+    [
+        "260309",
+        "cnvu",
+        "convective zonal momentum mixing acceleration"
+    ],
+    [
+        "260310",
+        "cnvv",
+        "convective meridional momentum mixing acceleration"
+    ],
+    [
+        "260311",
+        "wtend",
+        "tendency of vertical velocity"
+    ],
+    [
+        "260312",
+        "omgalf",
+        "omega (dp/dt) divide by density"
+    ],
+    [
+        "260313",
+        "cngwdu",
+        "convective gravity wave drag zonal acceleration"
+    ],
+    [
+        "260314",
+        "cngwdv",
+        "convective gravity wave drag meridional acceleration"
+    ],
+    [
+        "260315",
+        "lmv",
+        "velocity point model surface"
+    ],
+    [
+        "260316",
+        "pvmww",
+        "potential vorticity (mass-weighted)"
+    ],
+    [
+        "260317",
+        "mslet",
+        "mslp (eta model reduction)"
+    ],
+    [
+        "260323",
+        "mslma",
+        "mslp (maps system reduction)"
+    ],
+    [
+        "260324",
+        "tslsa",
+        "3-hr pressure tendency (std. atmos. reduction)"
+    ],
+    [
+        "260325",
+        "plpl",
+        "pressure of level from which parcel was lifted"
+    ],
+    [
+        "260326",
+        "lpsx",
+        "x-gradient of log pressure"
+    ],
+    [
+        "260327",
+        "lpsy",
+        "y-gradient of log pressure"
+    ],
+    [
+        "260328",
+        "hgtx",
+        "x-gradient of height"
+    ],
+    [
+        "260329",
+        "hgty",
+        "y-gradient of height"
+    ],
+    [
+        "260330",
+        "layth",
+        "layer thickness"
+    ],
+    [
+        "260331",
+        "nlgsp",
+        "natural log of surface pressure"
+    ],
+    [
+        "260332",
+        "cnvumf",
+        "convective updraft mass flux"
+    ],
+    [
+        "260333",
+        "cnvdmf",
+        "convective downdraft mass flux"
+    ],
+    [
+        "260334",
+        "cnvdemf",
+        "convective detrainment mass flux"
+    ],
+    [
+        "260335",
+        "lmh",
+        "mass point model surface"
+    ],
+    [
+        "260336",
+        "hgtn",
+        "geopotential height (nearest grid point)"
+    ],
+    [
+        "260337",
+        "presn",
+        "pressure (nearest grid point)"
+    ],
+    [
+        "260340",
+        "duvb",
+        "uv-b downward solar flux"
+    ],
+    [
+        "260341",
+        "cduvb",
+        "clear sky uv-b downward solar flux"
+    ],
+    [
+        "260342",
+        "csdsf",
+        "clear sky downward solar flux"
+    ],
+    [
+        "260343",
+        "swhr",
+        "solar radiative heating rate"
+    ],
+    [
+        "260344",
+        "csusf",
+        "clear sky upward solar flux"
+    ],
+    [
+        "260345",
+        "cfnsf",
+        "cloud forcing net solar flux"
+    ],
+    [
+        "260346",
+        "vbdsf",
+        "visible beam downward solar flux"
+    ],
+    [
+        "260347",
+        "vddsf",
+        "visible diffuse downward solar flux"
+    ],
+    [
+        "260348",
+        "nbdsf",
+        "near ir beam downward solar flux"
+    ],
+    [
+        "260349",
+        "nddsf",
+        "near ir diffuse downward solar flux"
+    ],
+    [
+        "260350",
+        "dtrf",
+        "downward total radiation flux"
+    ],
+    [
+        "260351",
+        "utrf",
+        "upward total radiation flux"
+    ],
+    [
+        "260354",
+        "lwhr",
+        "long-wave radiative heating rate"
+    ],
+    [
+        "260355",
+        "csulf",
+        "clear sky upward long wave flux"
+    ],
+    [
+        "260356",
+        "csdlf",
+        "clear sky downward long wave flux"
+    ],
+    [
+        "260357",
+        "cfnlf",
+        "cloud forcing net long wave flux"
+    ],
+    [
+        "260366",
+        "mflux",
+        "convective cloud mass flux"
+    ],
+    [
+        "260369",
+        "ri",
+        "richardson number"
+    ],
+    [
+        "260370",
+        "cwdi",
+        "convective weather detection index"
+    ],
+    [
+        "260372",
+        "uphl",
+        "updraft helicity"
+    ],
+    [
+        "260373",
+        "lai",
+        "leaf area index"
+    ],
+    [
+        "260374",
+        "pmtc",
+        "particulate matter (coarse)"
+    ],
+    [
+        "260375",
+        "pmtf",
+        "particulate matter (fine)"
+    ],
+    [
+        "260376",
+        "lpmtf",
+        "particulate matter (fine)"
+    ],
+    [
+        "260377",
+        "lipmf",
+        "integrated column particulate matter (fine)"
+    ],
+    [
+        "260379",
+        "ozcon",
+        "ozone concentration (ppb)"
+    ],
+    [
+        "260380",
+        "ozcat",
+        "categorical ozone concentration"
+    ],
+    [
+        "260381",
+        "vdfoz",
+        "ozone vertical diffusion"
+    ],
+    [
+        "260382",
+        "poz",
+        "ozone production"
+    ],
+    [
+        "260383",
+        "toz",
+        "ozone tendency"
+    ],
+    [
+        "260384",
+        "pozt",
+        "ozone production from temperature term"
+    ],
+    [
+        "260385",
+        "pozo",
+        "ozone production from col ozone term"
+    ],
+    [
+        "260386",
+        "refzr",
+        "derived radar reflectivity backscatter from rain"
+    ],
+    [
+        "260387",
+        "refzi",
+        "derived radar reflectivity backscatter from ice"
+    ],
+    [
+        "260388",
+        "refzc",
+        "derived radar reflectivity backscatter from parameterized convection"
+    ],
+    [
+        "260389",
+        "refd",
+        "derived radar reflectivity"
+    ],
+    [
+        "260390",
+        "refc",
+        "maximum/composite radar reflectivity"
+    ],
+    [
+        "260391",
+        "ltng",
+        "lightning"
+    ],
+    [
+        "260394",
+        "srcono",
+        "slight risk convective outlook"
+    ],
+    [
+        "260395",
+        "mrcono",
+        "moderate risk convective outlook"
+    ],
+    [
+        "260396",
+        "hrcono",
+        "high risk convective outlook"
+    ],
+    [
+        "260397",
+        "torprob",
+        "tornado probability"
+    ],
+    [
+        "260398",
+        "hailprob",
+        "hail probability"
+    ],
+    [
+        "260399",
+        "windprob",
+        "wind probability"
+    ],
+    [
+        "260400",
+        "storprob",
+        "significant tornado probability"
+    ],
+    [
+        "260401",
+        "shailpro",
+        "significant hail probability"
+    ],
+    [
+        "260402",
+        "swindpro",
+        "significant wind probability"
+    ],
+    [
+        "260403",
+        "tstmc",
+        "categorical thunderstorm (1-yes, 0-no)"
+    ],
+    [
+        "260404",
+        "mixly",
+        "number of mixed layers next to surface"
+    ],
+    [
+        "260405",
+        "flght",
+        "flight category"
+    ],
+    [
+        "260406",
+        "cicel",
+        "confidence - ceiling"
+    ],
+    [
+        "260407",
+        "civis",
+        "confidence - visibility"
+    ],
+    [
+        "260408",
+        "ciflt",
+        "confidence - flight category"
+    ],
+    [
+        "260409",
+        "lavni",
+        "low-level aviation interest"
+    ],
+    [
+        "260410",
+        "havni",
+        "high-level aviation interest"
+    ],
+    [
+        "260411",
+        "sbsalb",
+        "visible, black sky albedo"
+    ],
+    [
+        "260412",
+        "swsalb",
+        "visible, white sky albedo"
+    ],
+    [
+        "260413",
+        "nbsalb",
+        "near ir, black sky albedo"
+    ],
+    [
+        "260414",
+        "nwsalb",
+        "near ir, white sky albedo"
+    ],
+    [
+        "260415",
+        "prsvr",
+        "total probability of severe thunderstorms (days 2,3)"
+    ],
+    [
+        "260416",
+        "prsigsvr",
+        "total probability of extreme severe thunderstorms (days 2,3)"
+    ],
+    [
+        "260417",
+        "sipd",
+        "supercooled large droplet (sld) potential"
+    ],
+    [
+        "260418",
+        "epsr",
+        "radiative emissivity"
+    ],
+    [
+        "260419",
+        "tpfi",
+        "turbulence potential forecast index"
+    ],
+    [
+        "260420",
+        "vaftd",
+        "volcanic ash forecast transport and dispersion"
+    ],
+    [
+        "260421",
+        "nlat",
+        "latitude (-90 to +90)"
+    ],
+    [
+        "260422",
+        "elon",
+        "east longitude (0 - 360)"
+    ],
+    [
+        "260424",
+        "mlyno",
+        "model layer number (from bottom up)"
+    ],
+    [
+        "260425",
+        "nlatn",
+        "latitude (nearest neighbor) (-90 to +90)"
+    ],
+    [
+        "260426",
+        "elonn",
+        "east longitude (nearest neighbor) (0 - 360)"
+    ],
+    [
+        "260429",
+        "cpozp",
+        "probability of freezing precipitation"
+    ],
+    [
+        "260431",
+        "ppffg",
+        "probability of precipitation exceeding flash flood guidance values"
+    ],
+    [
+        "260432",
+        "cwr",
+        "probability of wetting rain, exceeding in 0.10 in a given time period"
+    ],
+    [
+        "260439",
+        "vgtyp",
+        "vegetation type"
+    ],
+    [
+        "260442",
+        "wilt",
+        "wilting point"
+    ],
+    [
+        "260447",
+        "rdrip",
+        "rate of water dropping from canopy to ground"
+    ],
+    [
+        "260448",
+        "icwat",
+        "ice-free water surface"
+    ],
+    [
+        "260449",
+        "akhs",
+        "surface exchange coefficients for t and q divided by delta z"
+    ],
+    [
+        "260450",
+        "akms",
+        "surface exchange coefficients for u and v divided by delta z"
+    ],
+    [
+        "260451",
+        "vegt",
+        "vegetation canopy temperature"
+    ],
+    [
+        "260452",
+        "sstor",
+        "surface water storage"
+    ],
+    [
+        "260453",
+        "lsoil",
+        "liquid soil moisture content (non-frozen)"
+    ],
+    [
+        "260454",
+        "ewatr",
+        "open water evaporation (standing water)"
+    ],
+    [
+        "260455",
+        "gwrec",
+        "groundwater recharge"
+    ],
+    [
+        "260456",
+        "qrec",
+        "flood plain recharge"
+    ],
+    [
+        "260457",
+        "sfcrh",
+        "roughness length for heat"
+    ],
+    [
+        "260458",
+        "ndvi",
+        "normalized difference vegetation index"
+    ],
+    [
+        "260459",
+        "landn",
+        "land-sea coverage (nearest neighbor) [land=1,sea=0]"
+    ],
+    [
+        "260460",
+        "amixl",
+        "asymptotic mixing length scale"
+    ],
+    [
+        "260461",
+        "wvinc",
+        "water vapor added by precip assimilation"
+    ],
+    [
+        "260462",
+        "wcinc",
+        "water condensate added by precip assimilation"
+    ],
+    [
+        "260463",
+        "wvconv",
+        "water vapor flux convergance (vertical int)"
+    ],
+    [
+        "260464",
+        "wcconv",
+        "water condensate flux convergance (vertical int)"
+    ],
+    [
+        "260465",
+        "wvuflx",
+        "water vapor zonal flux (vertical int)"
+    ],
+    [
+        "260466",
+        "wvvflx",
+        "water vapor meridional flux (vertical int)"
+    ],
+    [
+        "260467",
+        "wcuflx",
+        "water condensate zonal flux (vertical int)"
+    ],
+    [
+        "260468",
+        "wcvflx",
+        "water condensate meridional flux (vertical int)"
+    ],
+    [
+        "260469",
+        "acond",
+        "aerodynamic conductance"
+    ],
+    [
+        "260470",
+        "evcw",
+        "canopy water evaporation"
+    ],
+    [
+        "260471",
+        "trans",
+        "transpiration"
+    ],
+    [
+        "260474",
+        "sltyp",
+        "surface slope type"
+    ],
+    [
+        "260478",
+        "evbs",
+        "direct evaporation from bare soil"
+    ],
+    [
+        "260479",
+        "lspa",
+        "land surface precipitation accumulation"
+    ],
+    [
+        "260480",
+        "baret",
+        "bare soil surface skin temperature"
+    ],
+    [
+        "260481",
+        "avsft",
+        "average surface skin temperature"
+    ],
+    [
+        "260482",
+        "radt",
+        "effective radiative skin temperature"
+    ],
+    [
+        "260483",
+        "fldcp",
+        "field capacity"
+    ],
+    [
+        "260484",
+        "usct",
+        "scatterometer estimated u wind component"
+    ],
+    [
+        "260485",
+        "vsct",
+        "scatterometer estimated v wind component"
+    ],
+    [
+        "260486",
+        "wstp",
+        "wave steepness"
+    ],
+    [
+        "260487",
+        "omlu",
+        "ocean mixed layer u velocity"
+    ],
+    [
+        "260488",
+        "omlv",
+        "ocean mixed layer v velocity"
+    ],
+    [
+        "260489",
+        "ubaro",
+        "barotropic u velocity"
+    ],
+    [
+        "260490",
+        "vbaro",
+        "barotropic v velocity"
+    ],
+    [
+        "260491",
+        "surge",
+        "storm surge"
+    ],
+    [
+        "260492",
+        "etsrg",
+        "extra tropical storm surge"
+    ],
+    [
+        "260493",
+        "elevhtml",
+        "ocean surface elevation relative to geoid"
+    ],
+    [
+        "260494",
+        "sshg",
+        "sea surface height relative to geoid"
+    ],
+    [
+        "260495",
+        "p2omlt",
+        "ocean mixed layer potential density (reference 2000m)<br"
+    ],
+    [
+        "260496",
+        "aohflx",
+        "net air-ocean heat flux"
+    ],
+    [
+        "260497",
+        "ashfl",
+        "assimilative heat flux"
+    ],
+    [
+        "260498",
+        "sstt",
+        "surface temperature trend"
+    ],
+    [
+        "260499",
+        "ssst",
+        "surface salinity trend"
+    ],
+    [
+        "260500",
+        "keng",
+        "kinetic energy"
+    ],
+    [
+        "260501",
+        "sltfl",
+        "salt flux"
+    ],
+    [
+        "260502",
+        "wtmpc",
+        "3-d temperature"
+    ],
+    [
+        "260503",
+        "salin",
+        "3-d salinity"
+    ],
+    [
+        "260504",
+        "bkeng",
+        "barotropic kinectic energy"
+    ],
+    [
+        "260505",
+        "dbss",
+        "geometric depth below sea surface"
+    ],
+    [
+        "260506",
+        "intfd",
+        "interface depths"
+    ],
+    [
+        "260507",
+        "ohc",
+        "ocean heat content"
+    ],
+    [
+        "260508",
+        "imgd",
+        "image data"
+    ],
+    [
+        "260509",
+        "al",
+        "albedo"
+    ],
+    [
+        "132216",
+        "maxswhi",
+        "maximum of significant wave height index",
+        "216.132"
+    ],
+    [
+        "500008",
+        "hhl",
+        "geometric height of the layer limits above sea level(nn)"
+    ],
+    [
+        "140216",
+        "vst",
+        "v-component stokes drift"
+    ],
+    [
+        "210101",
+        "maxfrpfire",
+        "wildfire radiative power maximum"
+    ],
+    [
+        "210102",
+        "so2fire",
+        "wildfire flux of sulfur dioxide"
+    ],
+    [
+        "228134",
+        "vtnowd",
+        "v-tendency from non-orographic wave drag"
+    ],
+    [
+        "228136",
+        "utnowd",
+        "u-tendency from non-orographic wave drag"
+    ],
+    [
+        "228246",
+        "100u",
+        "100 metre u wind component"
+    ],
+    [
+        "228247",
+        "100v",
+        "100 metre v wind component"
+    ],
+    [
+        "228253",
+        "ascat_sm_cdfa",
+        "ascat first soil moisture cdf matching parameter"
+    ],
+    [
+        "228254",
+        "ascat_sm_cdfb",
+        "ascat second soil moisture cdf matching parameter"
+    ],
+    [
+        "140215",
+        "ust",
+        "u-component stokes drift"
+    ],
+    [
+        "500050",
+        "clch",
+        "cloud cover (0 - 400 hpa)"
+    ],
+    [
+        "500049",
+        "clcm",
+        "cloud cover (400 - 800 hpa)"
+    ],
+    [
+        "500048",
+        "clcl",
+        "cloud cover (800 hpa - soil)"
+    ],
+    [
+        "500047",
+        "clc_con",
+        "convective cloud cover"
+    ],
+    [
+        "500046",
+        "clct",
+        "total cloud cover"
+    ],
+    [
+        "500045",
+        "h_snow",
+        "snow depth"
+    ],
+    [
+        "500044",
+        "w_snow",
+        "snow depth water equivalent"
+    ],
+    [
+        "500043",
+        "prec_con",
+        "convective precipitation rate"
+    ],
+    [
+        "500042",
+        "prec_gsp",
+        "large-scale precipitation rate"
+    ],
+    [
+        "500041",
+        "tot_prec",
+        "total precipitation rate (s)"
+    ],
+    [
+        "500040",
+        "tqi",
+        "total column-integrated cloud ice"
+    ],
+    [
+        "500039",
+        "aevap_s",
+        "evaporation (s)"
+    ],
+    [
+        "500038",
+        "tqv",
+        "total column integrated water vapour"
+    ],
+    [
+        "500037",
+        "relhum",
+        "relative humidity"
+    ],
+    [
+        "500036",
+        "relhum_2m",
+        "2m relative humidity"
+    ],
+    [
+        "500035",
+        "qv",
+        "specific humidity"
+    ],
+    [
+        "500034",
+        "qv_2m",
+        "specific humidity (2m)"
+    ],
+    [
+        "500033",
+        "qv_s",
+        "specific humidity (s)"
+    ],
+    [
+        "500032",
+        "w",
+        "vertical velocity (geometric) (w)"
+    ],
+    [
+        "500031",
+        "omega",
+        "vertical velocity (pressure) ( omega=dp/dt )"
+    ],
+    [
+        "500030",
+        "v",
+        "v component of wind"
+    ],
+    [
+        "500029",
+        "v_10m",
+        "v component of wind"
+    ],
+    [
+        "500028",
+        "u",
+        "u component of wind"
+    ],
+    [
+        "500027",
+        "u_10m",
+        "u component of wind"
+    ],
+    [
+        "500026",
+        "sp",
+        "wind speed (sp)"
+    ],
+    [
+        "500025",
+        "sp_10m",
+        "wind speed (sp_10m)"
+    ],
+    [
+        "500024",
+        "dd",
+        "wind direction (dd)"
+    ],
+    [
+        "500023",
+        "dd_10m",
+        "wind direction (dd_10m)"
+    ],
+    [
+        "500022",
+        "wvsp3",
+        "wave spectra (3)"
+    ],
+    [
+        "500021",
+        "wvsp2",
+        "wave spectra (2)"
+    ],
+    [
+        "500020",
+        "wvsp1",
+        "wave spectra (1)"
+    ],
+    [
+        "500019",
+        "dbz_max",
+        "radar spectra (1)"
+    ],
+    [
+        "500018",
+        "td_2m_av",
+        "2m dew point temperature (av)"
+    ],
+    [
+        "500017",
+        "td_2m",
+        "2m dew point temperature"
+    ],
+    [
+        "500016",
+        "tmin_2m",
+        "min 2m temperature (i)"
+    ],
+    [
+        "500015",
+        "tmax_2m",
+        "max 2m temperature (i)"
+    ],
+    [
+        "500013",
+        "t_2m_cl",
+        "climat. temperature, 2m temperature"
+    ],
+    [
+        "500012",
+        "t_2m_av",
+        "2m temperature (av)"
+    ],
+    [
+        "500011",
+        "t_2m",
+        "2m temperature"
+    ],
+    [
+        "500010",
+        "t_g",
+        "temperature (g)"
+    ],
+    [
+        "500009",
+        "to3",
+        "total column integrated ozone"
+    ],
+    [
+        "500000",
+        "ps",
+        "pressure (s) (not reduced)"
+    ],
+    [
+        "500001",
+        "p",
+        "pressure"
+    ],
+    [
+        "500002",
+        "pmsl",
+        "pressure reduced to msl"
+    ],
+    [
+        "500003",
+        "dpsdt",
+        "pressure tendency (s)"
+    ],
+    [
+        "500004",
+        "fis",
+        "geopotential (s)"
+    ],
+    [
+        "500005",
+        "fif",
+        "geopotential (full lev)"
+    ],
+    [
+        "500006",
+        "fi",
+        "geopotential"
+    ],
+    [
+        "500007",
+        "hsurf",
+        "geometric height of the earths surface above sea level"
+    ],
+    [
+        "500051",
+        "tqc",
+        "total column-integrated cloud water"
+    ],
+    [
+        "500052",
+        "snow_con",
+        "convective snowfall rate water equivalent (s)"
+    ],
+    [
+        "500053",
+        "snow_gsp",
+        "large-scale snowfall rate water equivalent (s)"
+    ],
+    [
+        "500054",
+        "fr_land",
+        "land cover (1=land, 0=sea)"
+    ],
+    [
+        "500055",
+        "z0",
+        "surface roughness length surface roughness"
+    ],
+    [
+        "500057",
+        "albedo_b",
+        "albedo (in short-wave)"
+    ],
+    [
+        "500056",
+        "alb_rad",
+        "albedo (in short-wave)"
+    ],
+    [
+        "500058",
+        "t_cl",
+        "soil temperature  ( 36 cm depth, vv=0h)"
+    ],
+    [
+        "500060",
+        "t_m",
+        "soil temperature"
+    ],
+    [
+        "500061",
+        "t_s",
+        "soil temperature"
+    ],
+    [
+        "500059",
+        "t_cl_lm",
+        "soil temperature (41 cm depth)"
+    ],
+    [
+        "500062",
+        "w_cl",
+        "column-integrated soil moisture"
+    ],
+    [
+        "500063",
+        "w_g1",
+        "column-integrated soil moisture (1) 0 -10 cm"
+    ],
+    [
+        "500064",
+        "w_g2",
+        "column-integrated soil moisture (2) 10-100cm"
+    ],
+    [
+        "500065",
+        "plcov",
+        "plant cover"
+    ],
+    [
+        "500066",
+        "runoff_g",
+        "water runoff (10-100)"
+    ],
+    [
+        "500068",
+        "runoff_s",
+        "water runoff (s)"
+    ],
+    [
+        "500067",
+        "runoff_g_lm",
+        "water runoff (10-190)"
+    ],
+    [
+        "500069",
+        "fr_ice",
+        "sea ice cover ( 0= free, 1=cover)"
+    ],
+    [
+        "500070",
+        "h_ice",
+        "sea ice thickness"
+    ],
+    [
+        "500071",
+        "swh",
+        "significant height of combined wind waves and swell"
+    ],
+    [
+        "500072",
+        "mdww",
+        "direction of wind waves"
+    ],
+    [
+        "500073",
+        "shww",
+        "significant height of wind waves"
+    ],
+    [
+        "500074",
+        "mpww",
+        "mean period of wind waves"
+    ],
+    [
+        "500075",
+        "mdps",
+        "direction of swell waves"
+    ],
+    [
+        "500076",
+        "shps",
+        "significant height of swell waves"
+    ],
+    [
+        "500077",
+        "mpps",
+        "mean period of swell waves"
+    ],
+    [
+        "500078",
+        "asob_s",
+        "net short wave radiation flux (m) (at the surface)"
+    ],
+    [
+        "500079",
+        "sobs_rad",
+        "net short wave radiation flux"
+    ],
+    [
+        "500080",
+        "athb_s",
+        "net long wave radiation flux (m) (at the surface)"
+    ],
+    [
+        "500081",
+        "thbs_rad",
+        "net long wave radiation flux"
+    ],
+    [
+        "500082",
+        "asob_t",
+        "net short wave radiation flux (m) (on the model top)"
+    ],
+    [
+        "500083",
+        "sobt_rad",
+        "net short wave radiation flux"
+    ],
+    [
+        "500084",
+        "athb_t",
+        "net long wave radiation flux (m) (on the model top)"
+    ],
+    [
+        "500085",
+        "thbt_rad",
+        "net long wave radiation flux"
+    ],
+    [
+        "500086",
+        "alhfl_s",
+        "latent heat net flux (m)"
+    ],
+    [
+        "500087",
+        "ashfl_s",
+        "sensible heat net flux (m)"
+    ],
+    [
+        "500088",
+        "aumfl_s",
+        "momentum flux, u-component (m)"
+    ],
+    [
+        "500089",
+        "avmfl_s",
+        "momentum flux, v-component (m)"
+    ],
+    [
+        "500090",
+        "apab_s",
+        "photosynthetically active radiation (m) (at the surface)"
+    ],
+    [
+        "500091",
+        "pabs_rad",
+        "photosynthetically active radiation"
+    ],
+    [
+        "500092",
+        "sohr_rad",
+        "solar radiation heating rate"
+    ],
+    [
+        "500093",
+        "thhr_rad",
+        "thermal radiation heating rate"
+    ],
+    [
+        "500094",
+        "alhfl_bs",
+        "latent heat flux from bare soil"
+    ],
+    [
+        "500095",
+        "alhfl_pl",
+        "latent heat flux from plants"
+    ],
+    [
+        "500096",
+        "dursun",
+        "sunshine"
+    ],
+    [
+        "500097",
+        "rstom",
+        "stomatal resistance"
+    ],
+    [
+        "500098",
+        "clc",
+        "cloud cover"
+    ],
+    [
+        "500099",
+        "clc_sgs",
+        "non-convective cloud cover, grid scale"
+    ],
+    [
+        "500100",
+        "qc",
+        "cloud mixing ratio"
+    ],
+    [
+        "500101",
+        "qi",
+        "cloud ice mixing ratio"
+    ],
+    [
+        "500102",
+        "qr",
+        "rain mixing ratio"
+    ],
+    [
+        "500103",
+        "qs",
+        "snow mixing ratio"
+    ],
+    [
+        "500104",
+        "tqr",
+        "total column integrated rain"
+    ],
+    [
+        "500105",
+        "tqs",
+        "total column integrated snow"
+    ],
+    [
+        "500106",
+        "qg",
+        "grauple"
+    ],
+    [
+        "500107",
+        "tqg",
+        "total column integrated grauple"
+    ],
+    [
+        "500108",
+        "twater",
+        "total column integrated water (all components incl. precipitation)"
+    ],
+    [
+        "500109",
+        "tdiv_hum",
+        "vertical integral of divergence of total water content (s)"
+    ],
+    [
+        "500110",
+        "qc_rad",
+        "subgrid scale cloud water"
+    ],
+    [
+        "500111",
+        "qi_rad",
+        "subgridscale cloud ice"
+    ],
+    [
+        "500112",
+        "clch_8",
+        "cloud cover ch (0..8)"
+    ],
+    [
+        "500113",
+        "clcm_8",
+        "cloud cover cm (0..8)"
+    ],
+    [
+        "500114",
+        "clcl_8",
+        "cloud cover cl (0..8)"
+    ],
+    [
+        "500115",
+        "hbas_sc",
+        "cloud base above msl, shallow convection"
+    ],
+    [
+        "500116",
+        "htop_sc",
+        "cloud top above msl, shallow convection"
+    ],
+    [
+        "500117",
+        "clw_con",
+        "specific cloud water content, convective cloud"
+    ],
+    [
+        "500118",
+        "hbas_con",
+        "height of convective cloud base (i)"
+    ],
+    [
+        "500119",
+        "htop_con",
+        "height of convective cloud top (i)"
+    ],
+    [
+        "500120",
+        "bas_con",
+        "base index (vertical level) of main convective cloud (i)"
+    ],
+    [
+        "500121",
+        "top_con",
+        "top index (vertical level) of main convective cloud (i)"
+    ],
+    [
+        "500122",
+        "dt_con",
+        "temperature tendency due to convection"
+    ],
+    [
+        "500123",
+        "dqv_con",
+        "specific humitiy tendency due to convection"
+    ],
+    [
+        "500124",
+        "du_con",
+        "zonal wind tendency due to convection"
+    ],
+    [
+        "500125",
+        "dv_con",
+        "meridional wind tendency due to convection"
+    ],
+    [
+        "500126",
+        "htop_dc",
+        "height of top of dry convection"
+    ],
+    [
+        "500127",
+        "hzerocl",
+        "height of 0 degree celsius level code 0,3,6 ?"
+    ],
+    [
+        "500128",
+        "snowlmt",
+        "height of snow fall limit"
+    ],
+    [
+        "500129",
+        "dqc_con",
+        "tendency of specific cloud liquid water content due to conversion"
+    ],
+    [
+        "500130",
+        "dqi_con",
+        "tendency of  specific cloud ice content due to convection"
+    ],
+    [
+        "500131",
+        "q_sedim",
+        "specific content of precipitation particles (needed for water loadin)g"
+    ],
+    [
+        "500132",
+        "prr_gsp",
+        "large scale rain rate"
+    ],
+    [
+        "500133",
+        "prs_gsp",
+        "large scale snowfall rate water equivalent"
+    ],
+    [
+        "500134",
+        "rain_gsp",
+        "large scale rain rate (s)"
+    ],
+    [
+        "500135",
+        "prr_con",
+        "convective rain rate"
+    ],
+    [
+        "500136",
+        "prs_con",
+        "convective snowfall rate water equivalent"
+    ],
+    [
+        "500137",
+        "rain_con",
+        "convective rain rate (s)"
+    ],
+    [
+        "500138",
+        "rr_f",
+        "rain amount, grid-scale plus convective"
+    ],
+    [
+        "500139",
+        "rr_c",
+        "snow amount, grid-scale plus convective"
+    ],
+    [
+        "500140",
+        "dt_gsp",
+        "temperature tendency due to grid scale precipation"
+    ],
+    [
+        "500141",
+        "dqv_gsp",
+        "specific humitiy tendency due to grid scale precipitation"
+    ],
+    [
+        "500142",
+        "dqc_gsp",
+        "tendency of specific cloud liquid water content due to grid scale precipitation"
+    ],
+    [
+        "500143",
+        "freshsnw",
+        "fresh snow factor (weighting function for albedo indicating freshness of snow)"
+    ],
+    [
+        "500144",
+        "dqi_gsp",
+        "tendency of specific cloud ice content due to grid scale precipitation"
+    ],
+    [
+        "500145",
+        "prg_gsp",
+        "graupel (snow pellets) precipitation rate"
+    ],
+    [
+        "500146",
+        "grau_gsp",
+        "graupel (snow pellets) precipitation rate"
+    ],
+    [
+        "500147",
+        "rho_snow",
+        "snow density"
+    ],
+    [
+        "500148",
+        "pp",
+        "pressure perturbation"
+    ],
+    [
+        "500149",
+        "sdi_1",
+        "supercell detection index 1 (rot. up+down drafts)"
+    ],
+    [
+        "500150",
+        "sdi_2",
+        "supercell detection index 2 (only rot. up drafts)"
+    ],
+    [
+        "500151",
+        "cape_mu",
+        "convective available potential energy, most unstable"
+    ],
+    [
+        "500152",
+        "cin_mu",
+        "convective inhibition, most unstable"
+    ],
+    [
+        "500153",
+        "cape_ml",
+        "convective available potential energy, mean layer"
+    ],
+    [
+        "500154",
+        "cin_ml",
+        "convective inhibition, mean layer"
+    ],
+    [
+        "500155",
+        "tke_con",
+        "convective turbulent kinetic enery"
+    ],
+    [
+        "500156",
+        "tketens",
+        "tendency of turbulent kinetic energy"
+    ],
+    [
+        "500157",
+        "ke",
+        "kinetic energy"
+    ],
+    [
+        "500158",
+        "tke",
+        "turbulent kinetic energy"
+    ],
+    [
+        "500159",
+        "tkvm",
+        "turbulent diffusioncoefficient for momentum"
+    ],
+    [
+        "500160",
+        "tkvh",
+        "turbulent diffusion coefficient for heat (and moisture)"
+    ],
+    [
+        "500161",
+        "tcm",
+        "turbulent transfer coefficient for impulse"
+    ],
+    [
+        "500162",
+        "tch",
+        "turbulent transfer coefficient for heat (and moisture)"
+    ],
+    [
+        "500163",
+        "mh",
+        "mixed layer depth"
+    ],
+    [
+        "500164",
+        "vmax_10m",
+        "maximum wind 10m"
+    ],
+    [
+        "500165",
+        "ru-103",
+        "air concentration of ruthenium 103"
+    ],
+    [
+        "500166",
+        "t_so",
+        "soil temperature (multilayers)"
+    ],
+    [
+        "500167",
+        "w_so",
+        "column-integrated soil moisture (multilayers)"
+    ],
+    [
+        "500168",
+        "w_so_ice",
+        "soil ice content (multilayers)"
+    ],
+    [
+        "500169",
+        "w_i",
+        "plant canopy surface water"
+    ],
+    [
+        "500170",
+        "t_snow",
+        "snow temperature (top of snow)"
+    ],
+    [
+        "500171",
+        "prs_min",
+        "minimal stomatal resistance"
+    ],
+    [
+        "500172",
+        "t_ice",
+        "sea ice temperature"
+    ],
+    [
+        "500173",
+        "dbz_850",
+        "base reflectivity"
+    ],
+    [
+        "500174",
+        "dbz",
+        "base reflectivity"
+    ],
+    [
+        "500175",
+        "dbz_cmax",
+        "base reflectivity (cmax)"
+    ],
+    [
+        "500176",
+        "dttdiv",
+        "unknown"
+    ],
+    [
+        "500177",
+        "sotr_rad",
+        "effective transmissivity of solar radiation"
+    ],
+    [
+        "500178",
+        "evatra_sum",
+        "sum of contributions to evaporation"
+    ],
+    [
+        "500179",
+        "tra_sum",
+        "total transpiration from all soil layers"
+    ],
+    [
+        "500180",
+        "totforce_s",
+        "total forcing at soil surface"
+    ],
+    [
+        "500181",
+        "resid_wso",
+        "residuum of soil moisture"
+    ],
+    [
+        "500182",
+        "mflx_con",
+        "massflux at convective cloud base"
+    ],
+    [
+        "500183",
+        "cape_con",
+        "convective available potential energy"
+    ],
+    [
+        "500184",
+        "qcvg_con",
+        "moisture convergence for kuo-type closure"
+    ],
+    [
+        "500185",
+        "mwd",
+        "total wave direction"
+    ],
+    [
+        "500186",
+        "mwp_x",
+        "wind sea mean period"
+    ],
+    [
+        "500187",
+        "ppww",
+        "wind sea peak period"
+    ],
+    [
+        "500188",
+        "mpp_s",
+        "swell mean period"
+    ],
+    [
+        "500189",
+        "ppps",
+        "swell peak period"
+    ],
+    [
+        "500190",
+        "pp1d",
+        "total wave peak period"
+    ],
+    [
+        "500191",
+        "tm10",
+        "total wave mean period"
+    ],
+    [
+        "500192",
+        "tm01",
+        "total tm1 period"
+    ],
+    [
+        "500193",
+        "tm02",
+        "total tm2 period"
+    ],
+    [
+        "500194",
+        "sprd",
+        "total directional spread"
+    ],
+    [
+        "500195",
+        "ana_err_fi",
+        "analysis error(standard deviation), geopotential(gpm)"
+    ],
+    [
+        "500196",
+        "ana_err_u",
+        "analysis error(standard deviation), u-comp. of wind"
+    ],
+    [
+        "500197",
+        "ana_err_v",
+        "analysis error(standard deviation), v-comp. of wind"
+    ],
+    [
+        "500198",
+        "du_sso",
+        "zonal wind tendency due to subgrid scale oro."
+    ],
+    [
+        "500199",
+        "dv_sso",
+        "meridional wind tendency due to subgrid scale oro."
+    ],
+    [
+        "500200",
+        "sso_stdh",
+        "standard deviation of sub-grid scale orography"
+    ],
+    [
+        "500201",
+        "sso_gamma",
+        "anisotropy of sub-gridscale orography"
+    ],
+    [
+        "500202",
+        "sso_theta",
+        "angle of sub-gridscale orography"
+    ],
+    [
+        "500203",
+        "sso_sigma",
+        "slope of sub-gridscale orography"
+    ],
+    [
+        "500204",
+        "emis_rad",
+        "surface emissivity"
+    ],
+    [
+        "500205",
+        "soiltyp",
+        "soil type"
+    ],
+    [
+        "500206",
+        "lai",
+        "leaf area index"
+    ],
+    [
+        "500207",
+        "rootdp",
+        "root depth of vegetation"
+    ],
+    [
+        "500208",
+        "hmo3",
+        "height of ozone maximum (climatological)"
+    ],
+    [
+        "500209",
+        "vio3",
+        "vertically integrated ozone content (climatological)"
+    ],
+    [
+        "500210",
+        "plcov_mx",
+        "plant covering degree in the vegetation phase"
+    ],
+    [
+        "500211",
+        "plcov_mn",
+        "plant covering degree in the quiescent phas"
+    ],
+    [
+        "500212",
+        "lai_mx",
+        "max leaf area index"
+    ],
+    [
+        "500213",
+        "lai_mn",
+        "min leaf area index"
+    ],
+    [
+        "500214",
+        "oro_mod",
+        "orographie + land-meer-verteilung"
+    ],
+    [
+        "500215",
+        "wvar1",
+        "variance of soil moisture content (0-10)"
+    ],
+    [
+        "500216",
+        "wvar2",
+        "variance of soil moisture content (10-100)"
+    ],
+    [
+        "500217",
+        "for_e",
+        "evergreen forest"
+    ],
+    [
+        "500218",
+        "for_d",
+        "deciduous forest"
+    ],
+    [
+        "500219",
+        "ndvi",
+        "normalized differential vegetation index"
+    ],
+    [
+        "500220",
+        "ndvi_max",
+        "normalized differential vegetation index (ndvi)"
+    ],
+    [
+        "500221",
+        "ndvi_mrat",
+        "ratio of monthly mean ndvi (normalized differential vegetation index) to annual maximum"
+    ],
+    [
+        "500222",
+        "ndviratio",
+        "ratio of monthly mean ndvi (normalized differential vegetation index) to annual maximum"
+    ],
+    [
+        "500223",
+        "aer_so4",
+        "total sulfate aerosol"
+    ],
+    [
+        "500224",
+        "aer_so412",
+        "total sulfate aerosol (12m)"
+    ],
+    [
+        "500225",
+        "aer_dust",
+        "total soil dust aerosol"
+    ],
+    [
+        "500226",
+        "aer_dust12",
+        "total soil dust aerosol (12m)"
+    ],
+    [
+        "500228",
+        "aer_org12",
+        "organic aerosol (12m)"
+    ],
+    [
+        "500227",
+        "aer_org",
+        "organic aerosol"
+    ],
+    [
+        "500230",
+        "aer_bc12",
+        "black carbon aerosol (12m)"
+    ],
+    [
+        "500229",
+        "aer_bc",
+        "black carbon aerosol"
+    ],
+    [
+        "500232",
+        "aer_ss12",
+        "sea salt aerosol (12m)"
+    ],
+    [
+        "500231",
+        "aer_ss",
+        "sea salt aerosol"
+    ],
+    [
+        "500233",
+        "dqvdt",
+        "tendency of specific humidity"
+    ],
+    [
+        "500234",
+        "qvsflx",
+        "water vapor flux"
+    ],
+    [
+        "500235",
+        "fc",
+        "coriolis parameter"
+    ],
+    [
+        "500236",
+        "rlat",
+        "geographical latitude"
+    ],
+    [
+        "500237",
+        "rlon",
+        "geographical longitude"
+    ],
+    [
+        "500238",
+        "ustr",
+        "friction velocity"
+    ],
+    [
+        "500239",
+        "ztd",
+        "delay of the gps signal trough the (total) atm."
+    ],
+    [
+        "500240",
+        "zwd",
+        "delay of the gps signal trough wet atmos."
+    ],
+    [
+        "500241",
+        "zhd",
+        "delay of the gps signal trough dry atmos."
+    ],
+    [
+        "500242",
+        "o3",
+        "ozone mixing ratio"
+    ],
+    [
+        "500243",
+        "ru-103",
+        "air concentration of ruthenium 103 (ru103- concentration)"
+    ],
+    [
+        "500244",
+        "ru-103d",
+        "ru103-dry deposition"
+    ],
+    [
+        "500245",
+        "ru-103w",
+        "ru103-wet deposition"
+    ],
+    [
+        "500246",
+        "sr-90",
+        "air concentration of strontium 90"
+    ],
+    [
+        "500247",
+        "sr-90d",
+        "sr90-dry deposition"
+    ],
+    [
+        "500248",
+        "sr-90w",
+        "sr90-wet deposition"
+    ],
+    [
+        "500249",
+        "i-131a",
+        "i131-concentration"
+    ],
+    [
+        "500250",
+        "i-131ad",
+        "i131-dry deposition"
+    ],
+    [
+        "500251",
+        "i-131aw",
+        "i131-wet deposition"
+    ],
+    [
+        "500252",
+        "cs-137",
+        "cs137-concentration"
+    ],
+    [
+        "500253",
+        "cs-137d",
+        "cs137-dry deposition"
+    ],
+    [
+        "500254",
+        "cs-137w",
+        "cs137-wet deposition"
+    ],
+    [
+        "500255",
+        "te-132",
+        "air concentration of tellurium 132 (te132-concentration)"
+    ],
+    [
+        "500256",
+        "te-132d",
+        "te132-dry deposition"
+    ],
+    [
+        "500257",
+        "te-132w",
+        "te132-wet deposition"
+    ],
+    [
+        "500258",
+        "zr-95",
+        "air concentration of zirconium 95 (zr95-concentration)"
+    ],
+    [
+        "500259",
+        "zr-95d",
+        "zr95-dry deposition"
+    ],
+    [
+        "500260",
+        "zr-95w",
+        "zr95-wet deposition"
+    ],
+    [
+        "500261",
+        "kr-85",
+        "air concentration of krypton 85  (kr85-concentration)"
+    ],
+    [
+        "500262",
+        "kr-85d",
+        "kr85-dry deposition"
+    ],
+    [
+        "500263",
+        "kr-85w",
+        "kr85-wet deposition"
+    ],
+    [
+        "500264",
+        "tr-2",
+        "tracer - concentration"
+    ],
+    [
+        "500265",
+        "tr-2d",
+        "tracer - dry deposition"
+    ],
+    [
+        "500266",
+        "tr-2w",
+        "tracer - wet deposition"
+    ],
+    [
+        "500267",
+        "xe-133",
+        "air concentration of xenon 133 (xe133  - concentration)"
+    ],
+    [
+        "500268",
+        "xe-133d",
+        "xe133  - dry deposition"
+    ],
+    [
+        "500269",
+        "xe-133w",
+        "xe133  - wet deposition"
+    ],
+    [
+        "500270",
+        "i-131g",
+        "i131g - concentration"
+    ],
+    [
+        "500271",
+        "i-131gd",
+        "xe133  - wet deposition"
+    ],
+    [
+        "500272",
+        "i-131gw",
+        "i131g  - wet deposition"
+    ],
+    [
+        "500273",
+        "i-131o",
+        "i131o  - concentration"
+    ],
+    [
+        "500274",
+        "i-131od",
+        "i131o  - dry deposition"
+    ],
+    [
+        "500275",
+        "i-131ow",
+        "i131o  - wet deposition"
+    ],
+    [
+        "500276",
+        "ba-140",
+        "air concentration of barium 40"
+    ],
+    [
+        "500277",
+        "ba-140d",
+        "ba140 - dry deposition"
+    ],
+    [
+        "500278",
+        "ba-140w",
+        "ba140  - wet deposition"
+    ],
+    [
+        "500279",
+        "austr_sso",
+        "u-momentum flux due to sso-effects"
+    ],
+    [
+        "500280",
+        "ustr_sso",
+        "u-momentum flux due to sso-effects"
+    ],
+    [
+        "500282",
+        "vstr_sso",
+        "v-momentum flux due to sso-effects"
+    ],
+    [
+        "500281",
+        "avstr_sso",
+        "v-momentum flux due to sso-effects"
+    ],
+    [
+        "500283",
+        "avdis_sso",
+        "gravity wave dissipation (vertical integral)"
+    ],
+    [
+        "500284",
+        "vdis_sso",
+        "gravity wave dissipation (vertical integral)"
+    ],
+    [
+        "500285",
+        "uv_max",
+        "uv_index_maximum_w  uv_index clouded (w), daily maximum"
+    ],
+    [
+        "500286",
+        "w_shaer",
+        "wind shear"
+    ],
+    [
+        "500287",
+        "srh",
+        "storm relative helicity"
+    ],
+    [
+        "500288",
+        "vabs",
+        "absolute vorticity advection"
+    ],
+    [
+        "500289",
+        "cl_typ",
+        "niederschlagbew.-artkombination niederschl.-bew.-blautherm. (283..407)"
+    ],
+    [
+        "500290",
+        "ccl_gnd",
+        "konvektions-u-grenzehoehe der konvektionsuntergrenze ueber grund"
+    ],
+    [
+        "500291",
+        "ccl_nn",
+        "konv.-u-grenze-nn   hoehe der konvektionsuntergrenze ueber nn"
+    ],
+    [
+        "500292",
+        "ww",
+        "weather interpretation (wmo)"
+    ],
+    [
+        "500293",
+        "advorg",
+        "geostrophische  vorticityadvektion"
+    ],
+    [
+        "500294",
+        "advor",
+        "geo temperatur adv  geostrophische schichtdickenadvektion"
+    ],
+    [
+        "500295",
+        "adrtg",
+        "schichtdicken-advektion"
+    ],
+    [
+        "500296",
+        "wdiv",
+        "winddivergenz"
+    ],
+    [
+        "500297",
+        "fqn",
+        "qn-vektor q isother-senkr-kompqn ,komp. q-vektor senkrecht zu den isothermen"
+    ],
+    [
+        "500298",
+        "ipv",
+        "isentrope potentielle vorticity"
+    ],
+    [
+        "500299",
+        "up",
+        "xipv wind x-komp     wind x-komponente auf isentropen flaechen"
+    ],
+    [
+        "500300",
+        "vp",
+        "yipv wind y-komp     wind y-komponente auf isentropen flaechen"
+    ],
+    [
+        "500301",
+        "ptheta",
+        "druck einer isentropen flaeche"
+    ],
+    [
+        "500302",
+        "ko",
+        "ko index"
+    ],
+    [
+        "500303",
+        "thetae",
+        "aequivalentpotentielle temperatur"
+    ],
+    [
+        "500304",
+        "ceiling",
+        "ceiling"
+    ],
+    [
+        "500305",
+        "ice_grd",
+        "icing grade (1=lgt,2=mod,3=sev)"
+    ],
+    [
+        "500306",
+        "cldepth",
+        "modified cloud depth for media"
+    ],
+    [
+        "500307",
+        "clct_mod",
+        "modified cloud cover for media"
+    ],
+    [
+        "500308",
+        "efa-ps",
+        "monthly mean of rms of difference fg-an of pressure reduced to msl"
+    ],
+    [
+        "500309",
+        "eia-ps",
+        "monthly mean of rms of difference ia-an of pressure reduced to msl"
+    ],
+    [
+        "500310",
+        "efa-u",
+        "monthly mean of rms of difference fg-an of u-component of wind"
+    ],
+    [
+        "500311",
+        "eia-u",
+        "monthly mean of rms of difference ia-an of u-component of wind"
+    ],
+    [
+        "500312",
+        "efa-v",
+        "monthly mean of rms of difference fg-an of v-component of wind"
+    ],
+    [
+        "500313",
+        "eia-v",
+        "monthly mean of rms of difference ia-an of v-component of wind"
+    ],
+    [
+        "500314",
+        "efa-fi",
+        "monthly mean of rms of difference fg-an of geopotential"
+    ],
+    [
+        "500315",
+        "eia-fi",
+        "monthly mean of rms of difference ia-an of geopotential"
+    ],
+    [
+        "500316",
+        "efa-rh",
+        "monthly mean of rms of difference fg-an of relative humidity"
+    ],
+    [
+        "500317",
+        "eia-rh",
+        "monthly mean of rms of difference ia-an of relative humidity"
+    ],
+    [
+        "500318",
+        "efa-t",
+        "monthly mean of rms of difference fg-an of temperature"
+    ],
+    [
+        "500319",
+        "eia-t",
+        "monthly mean of rms of difference ia-an of temperature"
+    ],
+    [
+        "500320",
+        "efa-om",
+        "monthly mean of rms of difference fg-an of vert.velocity (pressure)"
+    ],
+    [
+        "500321",
+        "eia-om",
+        "monthly mean of rms of difference ia-an of vert.velocity (pressure)"
+    ],
+    [
+        "500322",
+        "efa-ke",
+        "monthly mean of rms of difference fg-an of kinetic energy"
+    ],
+    [
+        "500323",
+        "eia-ke",
+        "monthly mean of rms of difference ia-an of kinetic energy"
+    ],
+    [
+        "500324",
+        "synme5_bt_cl",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500325",
+        "synme5_bt_cs",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500327",
+        "synme5_rad_cs",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500326",
+        "synme5_rad_cl",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500328",
+        "synme6_bt_cl",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500330",
+        "synme6_rad_cl",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500331",
+        "synme6_rad_cs",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500329",
+        "synme6_bt_cs",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500332",
+        "synme7_bt_cl_ir11.5",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500334",
+        "synme7_bt_cs_ir11.5",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500335",
+        "synme7_bt_cs_wv6.4",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500336",
+        "synme7_rad_cl_ir11.5",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500338",
+        "synme7_rad_cs_ir11.5",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500339",
+        "synme7_rad_cs_wv6.4",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500333",
+        "synme7_bt_cl_wv6.4",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500337",
+        "synme7_rad_cl_wv6.4",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500366",
+        "synmsg_rad_cs_ir13.4",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500367",
+        "synmsg_rad_cs_ir3.9",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500368",
+        "synmsg_rad_cs_ir8.7",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500369",
+        "synmsg_rad_cs_ir9.7",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500370",
+        "synmsg_rad_cs_wv6.2",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500371",
+        "synmsg_rad_cs_wv7.3",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500340",
+        "synmsg_bt_cl_ir10.8",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500342",
+        "synmsg_bt_cl_ir13.4",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500343",
+        "synmsg_bt_cl_ir3.9",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500344",
+        "synmsg_bt_cl_ir8.7",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500345",
+        "synmsg_bt_cl_ir9.7",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500347",
+        "synmsg_bt_cl_wv7.3",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500348",
+        "synmsg_bt_cs_ir8.7",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500349",
+        "synmsg_bt_cs_ir10.8",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500350",
+        "synmsg_bt_cs_ir12.1",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500352",
+        "synmsg_bt_cs_ir3.9",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500353",
+        "synmsg_bt_cs_ir9.7",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500355",
+        "synmsg_bt_cs_wv7.3",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500356",
+        "synmsg_rad_cl_ir10.8",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500357",
+        "synmsg_rad_cl_ir12.1",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500359",
+        "synmsg_rad_cl_ir3.9",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500360",
+        "synmsg_rad_cl_ir8.7",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500361",
+        "synmsg_rad_cl_ir9.7",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500363",
+        "synmsg_rad_cl_wv7.3",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500364",
+        "synmsg_rad_cs_ir10.8",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500365",
+        "synmsg_rad_cs_ir12.1",
+        "synth. sat. radiance clear sky"
+    ],
+    [
+        "500341",
+        "synmsg_bt_cl_ir12.1",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500346",
+        "synmsg_bt_cl_wv6.2",
+        "synth. sat. brightness temperature cloudy"
+    ],
+    [
+        "500351",
+        "synmsg_bt_cs_ir13.4",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500354",
+        "synmsg_bt_cs_wv6.2",
+        "synth. sat. brightness temperature clear sky"
+    ],
+    [
+        "500358",
+        "synmsg_rad_cl_ir13.4",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500362",
+        "synmsg_rad_cl_wv6.2",
+        "synth. sat. radiance cloudy"
+    ],
+    [
+        "500372",
+        "t_2m_s",
+        "smoothed forecast, temperature"
+    ],
+    [
+        "500373",
+        "tmax_2m_s",
+        "smoothed forecast, maximum temp."
+    ],
+    [
+        "500374",
+        "tmin_2m_s",
+        "smoothed forecast, minimum temp."
+    ],
+    [
+        "500375",
+        "td_2m_s",
+        "smoothed forecast, dew point temp."
+    ],
+    [
+        "500376",
+        "u_10m_s",
+        "smoothed forecast, u comp. of wind"
+    ],
+    [
+        "500377",
+        "v_10m_s",
+        "smoothed forecast, v comp. of wind"
+    ],
+    [
+        "500378",
+        "tot_prec_s",
+        "smoothed forecast, total precipitation rate"
+    ],
+    [
+        "500379",
+        "clct_s",
+        "smoothed forecast, total cloud cover"
+    ],
+    [
+        "500380",
+        "clcl_s",
+        "smoothed forecast, cloud cover low"
+    ],
+    [
+        "500381",
+        "clcm_s",
+        "smoothed forecast, cloud cover medium"
+    ],
+    [
+        "500382",
+        "clch_s",
+        "smoothed forecast, cloud cover high"
+    ],
+    [
+        "500383",
+        "snow_gsp_s",
+        "smoothed forecast, large-scale snowfall rate w.e."
+    ],
+    [
+        "500384",
+        "t_s_s",
+        "smoothed forecast, soil temperature"
+    ],
+    [
+        "500385",
+        "vmax_10m_s",
+        "smoothed forecast, wind speed (gust)"
+    ],
+    [
+        "500386",
+        "tot_prec_c",
+        "calibrated forecast, total precipitation rate"
+    ],
+    [
+        "500387",
+        "snow_gsp_c",
+        "calibrated forecast, large-scale snowfall rate w.e."
+    ],
+    [
+        "500388",
+        "vmax_10m_c",
+        "calibrated forecast, wind speed (gust)"
+    ],
+    [
+        "500389",
+        "obsmsg_alb_hrv",
+        "obser. sat. meteosat sec. generation albedo (scaled)"
+    ],
+    [
+        "500390",
+        "obsmsg_alb_nir1.6",
+        "obser. sat. meteosat sec. generation albedo (scaled)"
+    ],
+    [
+        "500391",
+        "obsmsg_alb_vis0.6",
+        "obser. sat. meteosat sec. generation albedo (scaled)"
+    ],
+    [
+        "500392",
+        "obsmsg_alb_vis0.8",
+        "obser. sat. meteosat sec. generation albedo (scaled)"
+    ],
+    [
+        "500393",
+        "obsmsg_bt_ir10.8",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500394",
+        "obsmsg_bt_ir12.0",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500395",
+        "obsmsg_bt_ir13.4",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500396",
+        "obsmsg_bt_ir3.9",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500397",
+        "obsmsg_bt_ir8.7",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500398",
+        "obsmsg_bt_ir9.7",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500399",
+        "obsmsg_bt_wv6.2",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "500400",
+        "obsmsg_bt_wv7.3",
+        "obser. sat. meteosat sec. generation brightness temperature"
+    ],
+    [
+        "228022",
+        "cdir",
+        "clear-sky direct solar radiation at surface"
+    ],
+    [
+        "228021",
+        "fdir",
+        "total sky direct solar radiation at surface"
+    ],
+    [
+        "210112",
+        "hialkanesfire",
+        "wildfire flux of higher alkanes (cnh2n+2, c>=4)"
+    ],
+    [
+        "210111",
+        "hialkenesfire",
+        "wildfire flux of higher alkenes (cnh2n, c>=4)"
+    ],
+    [
+        "210110",
+        "toluenefire",
+        "wildfire flux of toluene_lump (c7h8+ c6h6 + c8h10)"
+    ],
+    [
+        "210117",
+        "c2h6sfire",
+        "wildfire flux of dimethyl sulfide (dms) (c2h6s)"
+    ],
+    [
+        "210116",
+        "nh3fire",
+        "wildfire flux of ammonia (nh3)"
+    ],
+    [
+        "210115",
+        "c3h6ofire",
+        "wildfire flux of acetone (c3h6o)"
+    ],
+    [
+        "210114",
+        "c2h4ofire",
+        "wildfire flux of acetaldehyde (c2h4o)"
+    ],
+    [
+        "210113",
+        "ch2ofire",
+        "wildfire flux of formaldehyde (ch2o)"
+    ],
+    [
+        "210109",
+        "terpenesfire",
+        "wildfire flux of terpenes (c5h8)n"
+    ],
+    [
+        "210108",
+        "c5h8fire",
+        "wildfire flux of isoprene (c5h8)"
+    ],
+    [
+        "210107",
+        "c3h6fire",
+        "wildfire flux of propene (c3h6)"
+    ],
+    [
+        "210106",
+        "c2h4fire",
+        "wildfire flux of ethene (c2h4)"
+    ],
+    [
+        "210105",
+        "c3h8fire",
+        "wildfire flux of propane (c3h8)"
+    ],
+    [
+        "210104",
+        "c2h5ohfire",
+        "wildfire flux of ethanol (c2h5oh)"
+    ],
+    [
+        "210103",
+        "ch3ohfire",
+        "wildfire flux of methanol (ch3oh)"
+    ],
+    [
+        "210013",
+        "aermr13",
+        "volcanic ash aerosol mixing ratio"
+    ],
+    [
+        "210118",
+        "c2h6fire",
+        "wildfire flux of ethane (c2h6)"
+    ],
+    [
+        "210119",
+        "mami",
+        "mean altitude of maximum injection"
+    ],
+    [
+        "210120",
+        "apt",
+        "altitude of plume top"
+    ],
+    [
+        "171121",
+        "mx2t6a",
+        "maximum temperature at 2 metres in the last 6 hours anomaly"
+    ],
+    [
+        "171122",
+        "mn2t6a",
+        "minimum temperature at 2 metres in the last 6 hours anomaly"
+    ],
+    [
+        "131089",
+        "pts",
+        "probability of a tropical storm",
+        "89.131"
+    ],
+    [
+        "131090",
+        "ph",
+        "probability of a hurricane",
+        "90.131"
+    ],
+    [
+        "131091",
+        "ptd",
+        "probability of a tropical depression",
+        "91.131"
+    ],
+    [
+        "131092",
+        "cpts",
+        "climatological probability of a tropical storm"
+    ],
+    [
+        "131093",
+        "cph",
+        "climatological probability of a hurricane"
+    ],
+    [
+        "131094",
+        "cptd",
+        "climatological probability of a tropical depression"
+    ],
+    [
+        "131095",
+        "pats",
+        "probability anomaly of a tropical storm"
+    ],
+    [
+        "131096",
+        "pah",
+        "probability anomaly of a hurricane"
+    ],
+    [
+        "131097",
+        "patd",
+        "probability anomaly of a tropical depression"
+    ],
+    [
+        "210014",
+        "aermr14",
+        "volcanic sulphate aerosol mixing ratio"
+    ],
+    [
+        "210015",
+        "aermr15",
+        "volcanic so2 precursor mixing ratio"
+    ],
+    [
+        "210028",
+        "aerpr03",
+        "so4 aerosol precursor mass mixing ratio"
+    ],
+    [
+        "210029",
+        "aerwv01",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 1"
+    ],
+    [
+        "210030",
+        "aerwv02",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 2"
+    ],
+    [
+        "210043",
+        "emdms",
+        "dms surface emission"
+    ],
+    [
+        "210044",
+        "aerwv03",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 3"
+    ],
+    [
+        "210045",
+        "aerwv04",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 4"
+    ],
+    [
+        "228023",
+        "cbh",
+        "cloud base height"
+    ],
+    [
+        "228024",
+        "deg0l",
+        "zero degree level",
+        "24.228",
+        "degol",
+        "deg0"
+    ],
+    [
+        "228025",
+        "hvis",
+        "horizontal visibility"
+    ],
+    [
+        "228089",
+        "tcrw",
+        "total column rain water"
+    ],
+    [
+        "228090",
+        "tcsw",
+        "total column snow water"
+    ],
+    [
+        "162114",
+        "utendd",
+        "u-tendency from dynamics"
+    ],
+    [
+        "162115",
+        "vtendd",
+        "v-tendency from dynamics"
+    ],
+    [
+        "162116",
+        "ttendd",
+        "t-tendency from dynamics"
+    ],
+    [
+        "162117",
+        "qtendd",
+        "q-tendency from dynamics"
+    ],
+    [
+        "162118",
+        "ttendr",
+        "t-tendency from radiation"
+    ],
+    [
+        "162119",
+        "utendts",
+        "u-tendency from turbulent diffusion + subgrid orography"
+    ],
+    [
+        "162120",
+        "vtendts",
+        "v-tendency from turbulent diffusion + subgrid orography"
+    ],
+    [
+        "162121",
+        "ttendts",
+        "t-tendency from turbulent diffusion + subgrid orography"
+    ],
+    [
+        "162122",
+        "qtendt",
+        "q-tendency from turbulent diffusion"
+    ],
+    [
+        "162123",
+        "utends",
+        "u-tendency from subgrid orography"
+    ],
+    [
+        "162124",
+        "vtends",
+        "v-tendency from subgrid orography"
+    ],
+    [
+        "162125",
+        "ttends",
+        "t-tendency from subgrid orography"
+    ],
+    [
+        "162126",
+        "utendcds",
+        "u-tendency from convection (deep+shallow)"
+    ],
+    [
+        "162127",
+        "vtendcds",
+        "v-tendency from convection (deep+shallow)"
+    ],
+    [
+        "162128",
+        "ttendcds",
+        "t-tendency from convection (deep+shallow)"
+    ],
+    [
+        "162129",
+        "qtendcds",
+        "q-tendency from convection (deep+shallow)"
+    ],
+    [
+        "162130",
+        "lpc",
+        "liquid precipitation flux from convection"
+    ],
+    [
+        "162131",
+        "ipc",
+        "ice precipitation flux from convection"
+    ],
+    [
+        "162132",
+        "ttendcs",
+        "t-tendency from cloud scheme"
+    ],
+    [
+        "162133",
+        "qtendcs",
+        "q-tendency from cloud scheme"
+    ],
+    [
+        "162134",
+        "qltendcs",
+        "ql-tendency from cloud scheme"
+    ],
+    [
+        "162135",
+        "qitendcs",
+        "qi-tendency from cloud scheme"
+    ],
+    [
+        "162136",
+        "lpcs",
+        "liquid precip flux from cloud scheme (stratiform)"
+    ],
+    [
+        "162137",
+        "ipcs",
+        "ice precip flux from cloud scheme (stratiform)"
+    ],
+    [
+        "162138",
+        "utendcs",
+        "u-tendency from shallow convection"
+    ],
+    [
+        "162139",
+        "vtendcs",
+        "v-tendency from shallow convection"
+    ],
+    [
+        "162140",
+        "ttendsc",
+        "t-tendency from shallow convection"
+    ],
+    [
+        "162141",
+        "qtendsc",
+        "q-tendency from shallow convection"
+    ],
+    [
+        "213001",
+        "sppt1",
+        "random pattern 1 for sppt"
+    ],
+    [
+        "213002",
+        "sppt2",
+        "random pattern 2 for sppt"
+    ],
+    [
+        "213003",
+        "sppt3",
+        "random pattern 3 for sppt"
+    ],
+    [
+        "213004",
+        "sppt4",
+        "random pattern 4 for sppt"
+    ],
+    [
+        "213005",
+        "sppt5",
+        "random pattern 5 for sppt"
+    ],
+    [
+        "228249",
+        "100si",
+        "100 metre wind speed"
+    ],
+    [
+        "228250",
+        "irrfr",
+        "irrigation fraction"
+    ],
+    [
+        "228251",
+        "pev",
+        "potential evaporation"
+    ],
+    [
+        "228252",
+        "irr",
+        "irrigation"
+    ],
+    [
+        "162079",
+        "vilwd",
+        "vertical integral of divergence of cloud liquid water flux"
+    ],
+    [
+        "162080",
+        "viiwd",
+        "vertical integral of divergence of cloud frozen water flux"
+    ],
+    [
+        "162088",
+        "vilwe",
+        "vertical integral of eastward cloud liquid water flux"
+    ],
+    [
+        "162089",
+        "vilwn",
+        "vertical integral of northward cloud liquid water flux"
+    ],
+    [
+        "162090",
+        "viiwe",
+        "vertical integral of eastward cloud frozen water flux"
+    ],
+    [
+        "162091",
+        "viiwn",
+        "vertical integral of northward cloud frozen water flux "
+    ],
+    [
+        "162092",
+        "vimat",
+        "vertical integral of mass tendency"
+    ],
+    [
+        "171006",
+        "100ua",
+        "100 metre u wind component anomaly"
+    ],
+    [
+        "171007",
+        "100va",
+        "100 metre v wind component anomaly"
+    ],
+    [
+        "228091",
+        "ccf",
+        "canopy cover fraction"
+    ],
+    [
+        "228092",
+        "stf",
+        "soil texture fraction"
+    ],
+    [
+        "228093",
+        "swv",
+        "volumetric soil moisture"
+    ],
+    [
+        "228094",
+        "ist",
+        "ice temperature"
+    ],
+    [
+        "214002",
+        "uvbed",
+        " uv biologically effective dose"
+    ],
+    [
+        "214003",
+        "uvbedcs",
+        " uv biologically effective dose clear-sky"
+    ],
+    [
+        "214004",
+        "uvsflxt280285",
+        " total surface uv spectral flux (280-285 nm)"
+    ],
+    [
+        "214005",
+        "uvsflxt285290",
+        " total surface uv spectral flux (285-290 nm)"
+    ],
+    [
+        "214006",
+        "uvsflxt290295",
+        " total surface uv spectral flux (290-295 nm)"
+    ],
+    [
+        "214007",
+        "uvsflxt295300",
+        " total surface uv spectral flux (295-300 nm)"
+    ],
+    [
+        "214008",
+        "uvsflxt300305",
+        " total surface uv spectral flux (300-305 nm)"
+    ],
+    [
+        "214009",
+        "uvsflxt305310",
+        " total surface uv spectral flux (305-310 nm)"
+    ],
+    [
+        "214010",
+        "uvsflxt310315",
+        " total surface uv spectral flux (310-315 nm)"
+    ],
+    [
+        "214011",
+        "uvsflxt315320",
+        " total surface uv spectral flux (315-320 nm)"
+    ],
+    [
+        "214012",
+        "uvsflxt320325",
+        " total surface uv spectral flux (320-325 nm)"
+    ],
+    [
+        "214013",
+        "uvsflxt325330",
+        " total surface uv spectral flux (325-330 nm)"
+    ],
+    [
+        "214014",
+        "uvsflxt330335",
+        " total surface uv spectral flux (330-335 nm)"
+    ],
+    [
+        "214015",
+        "uvsflxt335340",
+        " total surface uv spectral flux (335-340 nm)"
+    ],
+    [
+        "214016",
+        "uvsflxt340345",
+        " total surface uv spectral flux (340-345 nm)"
+    ],
+    [
+        "214017",
+        "uvsflxt345350",
+        " total surface uv spectral flux (345-350 nm)"
+    ],
+    [
+        "214018",
+        "uvsflxt350355",
+        " total surface uv spectral flux (350-355 nm)"
+    ],
+    [
+        "214019",
+        "uvsflxt355360",
+        " total surface uv spectral flux (355-360 nm)"
+    ],
+    [
+        "214020",
+        "uvsflxt360365",
+        " total surface uv spectral flux (360-365 nm)"
+    ],
+    [
+        "214021",
+        "uvsflxt365370",
+        " total surface uv spectral flux (365-370 nm)"
+    ],
+    [
+        "214022",
+        "uvsflxt370375",
+        " total surface uv spectral flux (370-375 nm)"
+    ],
+    [
+        "214023",
+        "uvsflxt375380",
+        " total surface uv spectral flux (375-380 nm)"
+    ],
+    [
+        "214024",
+        "uvsflxt380385",
+        " total surface uv spectral flux (380-385 nm)"
+    ],
+    [
+        "214025",
+        "uvsflxt385390",
+        " total surface uv spectral flux (385-390 nm)"
+    ],
+    [
+        "214026",
+        "uvsflxt390395",
+        " total surface uv spectral flux (390-395 nm)"
+    ],
+    [
+        "214027",
+        "uvsflxt395400",
+        " total surface uv spectral flux (395-400 nm)"
+    ],
+    [
+        "214028",
+        "uvsflxcs280285",
+        " clear-sky surface uv spectral flux (280-285 nm)"
+    ],
+    [
+        "214029",
+        "uvsflxcs285290",
+        " clear-sky surface uv spectral flux (285-290 nm)"
+    ],
+    [
+        "214030",
+        "uvsflxcs290295",
+        " clear-sky surface uv spectral flux (290-295 nm)"
+    ],
+    [
+        "214031",
+        "uvsflxcs295300",
+        " clear-sky surface uv spectral flux (295-300 nm)"
+    ],
+    [
+        "214032",
+        "uvsflxcs300305",
+        " clear-sky surface uv spectral flux (300-305 nm)"
+    ],
+    [
+        "214033",
+        "uvsflxcs305310",
+        " clear-sky surface uv spectral flux (305-310 nm)"
+    ],
+    [
+        "214034",
+        "uvsflxcs310315",
+        " clear-sky surface uv spectral flux (310-315 nm)"
+    ],
+    [
+        "214035",
+        "uvsflxcs315320",
+        " clear-sky surface uv spectral flux (315-320 nm)"
+    ],
+    [
+        "214036",
+        "uvsflxcs320325",
+        " clear-sky surface uv spectral flux (320-325 nm)"
+    ],
+    [
+        "214037",
+        "uvsflxcs325330",
+        " clear-sky surface uv spectral flux (325-330 nm)"
+    ],
+    [
+        "214038",
+        "uvsflxcs330335",
+        " clear-sky surface uv spectral flux (330-335 nm)"
+    ],
+    [
+        "214039",
+        "uvsflxcs335340",
+        " clear-sky surface uv spectral flux (335-340 nm)"
+    ],
+    [
+        "214040",
+        "uvsflxcs340345",
+        " clear-sky surface uv spectral flux (340-345 nm)"
+    ],
+    [
+        "214041",
+        "uvsflxcs345350",
+        " clear-sky surface uv spectral flux (345-350 nm)"
+    ],
+    [
+        "214042",
+        "uvsflxcs350355",
+        " clear-sky surface uv spectral flux (350-355 nm)"
+    ],
+    [
+        "214043",
+        "uvsflxcs355360",
+        " clear-sky surface uv spectral flux (355-360 nm)"
+    ],
+    [
+        "214044",
+        "uvsflxcs360365",
+        " clear-sky surface uv spectral flux (360-365 nm)"
+    ],
+    [
+        "214045",
+        "uvsflxcs365370",
+        " clear-sky surface uv spectral flux (365-370 nm)"
+    ],
+    [
+        "214046",
+        "uvsflxcs370375",
+        " clear-sky surface uv spectral flux (370-375 nm)"
+    ],
+    [
+        "214047",
+        "uvsflxcs375380",
+        " clear-sky surface uv spectral flux (375-380 nm)"
+    ],
+    [
+        "214048",
+        "uvsflxcs380385",
+        " clear-sky surface uv spectral flux (380-385 nm)"
+    ],
+    [
+        "214049",
+        "uvsflxcs385390",
+        " clear-sky surface uv spectral flux (385-390 nm)"
+    ],
+    [
+        "214050",
+        "uvsflxcs390395",
+        " clear-sky surface uv spectral flux (390-395 nm)"
+    ],
+    [
+        "214051",
+        "uvsflxcs395400",
+        " clear-sky surface uv spectral flux (395-400 nm)"
+    ],
+    [
+        "214052",
+        "aot340",
+        " profile of optical thickness at 340 nm"
+    ],
+    [
+        "215001",
+        "aersrcsss",
+        " source/gain of sea salt aerosol (0.03 - 0.5 um)"
+    ],
+    [
+        "215002",
+        "aersrcssm",
+        " source/gain of sea salt aerosol (0.5 - 5 um)"
+    ],
+    [
+        "215003",
+        "aersrcssl",
+        " source/gain of sea salt aerosol (5 - 20 um)"
+    ],
+    [
+        "215004",
+        "aerddpsss",
+        " dry deposition of sea salt aerosol (0.03 - 0.5 um)"
+    ],
+    [
+        "215005",
+        "aerddpssm",
+        " dry deposition of sea salt aerosol (0.5 - 5 um)"
+    ],
+    [
+        "215006",
+        "aerddpssl",
+        " dry deposition of sea salt aerosol (5 - 20 um)"
+    ],
+    [
+        "215007",
+        "aersdmsss",
+        " sedimentation of sea salt aerosol (0.03 - 0.5 um)"
+    ],
+    [
+        "215008",
+        "aersdmssm",
+        " sedimentation of sea salt aerosol (0.5 - 5 um)"
+    ],
+    [
+        "215009",
+        "aersdmssl",
+        " sedimentation of sea salt aerosol (5 - 20 um)"
+    ],
+    [
+        "215010",
+        "aerwdlssss",
+        " wet deposition of sea salt aerosol (0.03 - 0.5 um) by large-scale precipitation"
+    ],
+    [
+        "215011",
+        "aerwdlsssm",
+        " wet deposition of sea salt aerosol (0.5 - 5 um) by large-scale precipitation"
+    ],
+    [
+        "215012",
+        "aerwdlsssl",
+        " wet deposition of sea salt aerosol (5 - 20 um) by large-scale precipitation"
+    ],
+    [
+        "215013",
+        "aerwdccsss",
+        " wet deposition of sea salt aerosol (0.03 - 0.5 um) by convective precipitation"
+    ],
+    [
+        "215014",
+        "aerwdccssm",
+        " wet deposition of sea salt aerosol (0.5 - 5 um) by convective precipitation"
+    ],
+    [
+        "215015",
+        "aerwdccssl",
+        " wet deposition of sea salt aerosol (5 - 20 um) by convective precipitation"
+    ],
+    [
+        "215016",
+        "aerngtsss",
+        " negative fixer of sea salt aerosol (0.03 - 0.5 um)"
+    ],
+    [
+        "215017",
+        "aerngtssm",
+        " negative fixer of sea salt aerosol (0.5 - 5 um)"
+    ],
+    [
+        "215018",
+        "aerngtssl",
+        " negative fixer of sea salt aerosol (5 - 20 um)"
+    ],
+    [
+        "215019",
+        "aermsssss",
+        " vertically integrated mass of sea salt aerosol (0.03 - 0.5 um)"
+    ],
+    [
+        "215020",
+        "aermssssm",
+        " vertically integrated mass of sea salt aerosol (0.5 - 5 um)"
+    ],
+    [
+        "215021",
+        "aermssssl",
+        " vertically integrated mass of sea salt aerosol (5 - 20 um)"
+    ],
+    [
+        "215022",
+        "aerodsss",
+        " sea salt aerosol (0.03 - 0.5 um) optical depth"
+    ],
+    [
+        "215023",
+        "aerodssm",
+        " sea salt aerosol (0.5 - 5 um) optical depth"
+    ],
+    [
+        "215024",
+        "aerodssl",
+        " sea salt aerosol (5 - 20 um) optical depth"
+    ],
+    [
+        "215025",
+        "aersrcdus",
+        " source/gain of dust aerosol (0.03 - 0.55 um)"
+    ],
+    [
+        "215026",
+        "aersrcdum",
+        " source/gain of dust aerosol (0.55 - 9 um)"
+    ],
+    [
+        "215027",
+        "aersrcdul",
+        " source/gain of dust aerosol (9 - 20 um)"
+    ],
+    [
+        "215028",
+        "aerddpdus",
+        " dry deposition of dust aerosol (0.03 - 0.55 um)"
+    ],
+    [
+        "215029",
+        "aerddpdum",
+        " dry deposition of dust aerosol (0.55 - 9 um)"
+    ],
+    [
+        "215030",
+        "aerddpdul",
+        " dry deposition of dust aerosol (9 - 20 um)"
+    ],
+    [
+        "215031",
+        "aersdmdus",
+        " sedimentation of dust aerosol (0.03 - 0.55 um)"
+    ],
+    [
+        "215032",
+        "aersdmdum",
+        " sedimentation of dust aerosol (0.55 - 9 um)"
+    ],
+    [
+        "215033",
+        "aersdmdul",
+        " sedimentation of dust aerosol (9 - 20 um)"
+    ],
+    [
+        "215034",
+        "aerwdlsdus",
+        " wet deposition of dust aerosol (0.03 - 0.55 um) by large-scale precipitation"
+    ],
+    [
+        "215035",
+        "aerwdlsdum",
+        " wet deposition of dust aerosol (0.55 - 9 um) by large-scale precipitation"
+    ],
+    [
+        "215036",
+        "aerwdlsdul",
+        " wet deposition of dust aerosol (9 - 20 um) by large-scale precipitation"
+    ],
+    [
+        "215037",
+        "aerwdccdus",
+        " wet deposition of dust aerosol (0.03 - 0.55 um) by convective precipitation"
+    ],
+    [
+        "215038",
+        "aerwdccdum",
+        " wet deposition of dust aerosol (0.55 - 9 um) by convective precipitation"
+    ],
+    [
+        "215039",
+        "aerwdccdul",
+        " wet deposition of dust aerosol (9 - 20 um) by convective precipitation"
+    ],
+    [
+        "215040",
+        "aerngtdus",
+        " negative fixer of dust aerosol (0.03 - 0.55 um)"
+    ],
+    [
+        "215041",
+        "aerngtdum",
+        " negative fixer of dust aerosol (0.55 - 9 um)"
+    ],
+    [
+        "215042",
+        "aerngtdul",
+        " negative fixer of dust aerosol (9 - 20 um)"
+    ],
+    [
+        "215043",
+        "aermssdus",
+        " vertically integrated mass of dust aerosol (0.03 - 0.55 um)"
+    ],
+    [
+        "215044",
+        "aermssdum",
+        " vertically integrated mass of dust aerosol (0.55 - 9 um)"
+    ],
+    [
+        "215045",
+        "aermssdul",
+        " vertically integrated mass of dust aerosol (9 - 20 um)"
+    ],
+    [
+        "215046",
+        "aeroddus",
+        " dust aerosol (0.03 - 0.55 um) optical depth"
+    ],
+    [
+        "215047",
+        "aeroddum",
+        " dust aerosol (0.55 - 9 um) optical depth"
+    ],
+    [
+        "215048",
+        "aeroddul",
+        " dust aerosol (9 - 20 um) optical depth"
+    ],
+    [
+        "215049",
+        "aersrcomhphob",
+        " source/gain of hydrophobic organic matter aerosol"
+    ],
+    [
+        "215050",
+        "aersrcomhphil",
+        " source/gain of hydrophilic organic matter aerosol"
+    ],
+    [
+        "215051",
+        "aerddpomhphob",
+        " dry deposition of hydrophobic organic matter aerosol"
+    ],
+    [
+        "215052",
+        "aerddpomhphil",
+        " dry deposition of hydrophilic organic matter aerosol"
+    ],
+    [
+        "215053",
+        "aersdmomhphob",
+        " sedimentation of hydrophobic organic matter aerosol"
+    ],
+    [
+        "215054",
+        "aersdmomhphil",
+        " sedimentation of hydrophilic organic matter aerosol"
+    ],
+    [
+        "215055",
+        "aerwdlsomhphob",
+        " wet deposition of hydrophobic organic matter aerosol by large-scale precipitation"
+    ],
+    [
+        "215056",
+        "aerwdlsomhphil",
+        " wet deposition of hydrophilic organic matter aerosol by large-scale precipitation"
+    ],
+    [
+        "215057",
+        "aerwdccomhphob",
+        " wet deposition of hydrophobic organic matter aerosol by convective precipitation"
+    ],
+    [
+        "215058",
+        "aerwdccomhphil",
+        " wet deposition of hydrophilic organic matter aerosol by convective precipitation"
+    ],
+    [
+        "215059",
+        "aerngtomhphob",
+        " negative fixer of hydrophobic organic matter aerosol"
+    ],
+    [
+        "215060",
+        "aerngtomhphil",
+        " negative fixer of hydrophilic organic matter aerosol"
+    ],
+    [
+        "215061",
+        "aermssomhphob",
+        " vertically integrated mass of hydrophobic organic matter aerosol"
+    ],
+    [
+        "215062",
+        "aermssomhphil",
+        " vertically integrated mass of hydrophilic organic matter aerosol"
+    ],
+    [
+        "215063",
+        "aerodomhphob",
+        " hydrophobic organic matter aerosol optical depth"
+    ],
+    [
+        "215064",
+        "aerodomhphil",
+        " hydrophilic organic matter aerosol optical depth"
+    ],
+    [
+        "215065",
+        "aersrcbchphob",
+        " source/gain of hydrophobic black carbon aerosol"
+    ],
+    [
+        "215066",
+        "aersrcbchphil",
+        " source/gain of hydrophilic black carbon aerosol"
+    ],
+    [
+        "215067",
+        "aerddpbchphob",
+        " dry deposition of hydrophobic black carbon aerosol"
+    ],
+    [
+        "215068",
+        "aerddpbchphil",
+        " dry deposition of hydrophilic black carbon aerosol"
+    ],
+    [
+        "215069",
+        "aersdmbchphob",
+        " sedimentation of hydrophobic black carbon aerosol"
+    ],
+    [
+        "215070",
+        "aersdmbchphil",
+        " sedimentation of hydrophilic black carbon aerosol"
+    ],
+    [
+        "215071",
+        "aerwdlsbchphob",
+        " wet deposition of hydrophobic black carbon aerosol by large-scale precipitation"
+    ],
+    [
+        "215072",
+        "aerwdlsbchphil",
+        " wet deposition of hydrophilic black carbon aerosol by large-scale precipitation"
+    ],
+    [
+        "215073",
+        "aerwdccbchphob",
+        " wet deposition of hydrophobic black carbon aerosol by convective precipitation"
+    ],
+    [
+        "215074",
+        "aerwdccbchphil",
+        " wet deposition of hydrophilic black carbon aerosol by convective precipitation"
+    ],
+    [
+        "215075",
+        "aerngtbchphob",
+        " negative fixer of hydrophobic black carbon aerosol"
+    ],
+    [
+        "215076",
+        "aerngtbchphil",
+        " negative fixer of hydrophilic black carbon aerosol"
+    ],
+    [
+        "215077",
+        "aermssbchphob",
+        " vertically integrated mass of hydrophobic black carbon aerosol"
+    ],
+    [
+        "215078",
+        "aermssbchphil",
+        " vertically integrated mass of hydrophilic black carbon aerosol"
+    ],
+    [
+        "215079",
+        "aerodbchphob",
+        " hydrophobic black carbon aerosol optical depth"
+    ],
+    [
+        "215080",
+        "aerodbchphil",
+        " hydrophilic black carbon aerosol optical depth"
+    ],
+    [
+        "215081",
+        "aersrcsu",
+        " source/gain of sulphate aerosol"
+    ],
+    [
+        "215082",
+        "aerddpsu",
+        " dry deposition of sulphate aerosol"
+    ],
+    [
+        "215083",
+        "aersdmsu",
+        " sedimentation of sulphate aerosol"
+    ],
+    [
+        "215084",
+        "aerwdlssu",
+        " wet deposition of sulphate aerosol by large-scale precipitation"
+    ],
+    [
+        "215085",
+        "aerwdccsu",
+        " wet deposition of sulphate aerosol by convective precipitation"
+    ],
+    [
+        "215086",
+        "aerngtsu",
+        " negative fixer of sulphate aerosol"
+    ],
+    [
+        "215087",
+        "aermsssu",
+        " vertically integrated mass of sulphate aerosol"
+    ],
+    [
+        "215088",
+        "aerodsu",
+        " sulphate aerosol optical depth"
+    ],
+    [
+        "211001",
+        "aermr01diff",
+        "sea salt aerosol (0.03 - 0.5 um) mixing ratio"
+    ],
+    [
+        "211002",
+        "aermr02diff",
+        "sea salt aerosol (0.5 - 5 um) mixing ratio"
+    ],
+    [
+        "211003",
+        "aermr03diff",
+        "sea salt aerosol (5 - 20 um) mixing ratio"
+    ],
+    [
+        "211004",
+        "aermr04diff",
+        "dust aerosol (0.03 - 0.55 um) mixing ratio"
+    ],
+    [
+        "211005",
+        "aermr05diff",
+        "dust aerosol (0.55 - 0.9 um) mixing ratio"
+    ],
+    [
+        "211006",
+        "aermr06diff",
+        "dust aerosol (0.9 - 20 um) mixing ratio"
+    ],
+    [
+        "211007",
+        "aermr07diff",
+        "hydrophobic organic matter aerosol mixing ratio"
+    ],
+    [
+        "211008",
+        "aermr08diff",
+        "hydrophilic organic matter aerosol mixing ratio"
+    ],
+    [
+        "211009",
+        "aermr09diff",
+        "hydrophobic black carbon aerosol mixing ratio"
+    ],
+    [
+        "211010",
+        "aermr10diff",
+        "hydrophilic black carbon aerosol mixing ratio"
+    ],
+    [
+        "211011",
+        "aermr11diff",
+        "sulphate aerosol mixing ratio"
+    ],
+    [
+        "211012",
+        "aermr12diff",
+        "aerosol type 12 mixing ratio"
+    ],
+    [
+        "211016",
+        "aergn01diff",
+        "aerosol type 1 source/gain accumulated"
+    ],
+    [
+        "211017",
+        "aergn02diff",
+        "aerosol type 2 source/gain accumulated"
+    ],
+    [
+        "211018",
+        "aergn03diff",
+        "aerosol type 3 source/gain accumulated"
+    ],
+    [
+        "211019",
+        "aergn04diff",
+        "aerosol type 4 source/gain accumulated"
+    ],
+    [
+        "211020",
+        "aergn05diff",
+        "aerosol type 5 source/gain accumulated"
+    ],
+    [
+        "211021",
+        "aergn06diff",
+        "aerosol type 6 source/gain accumulated"
+    ],
+    [
+        "211022",
+        "aergn07diff",
+        "aerosol type 7 source/gain accumulated"
+    ],
+    [
+        "211023",
+        "aergn08diff",
+        "aerosol type 8 source/gain accumulated"
+    ],
+    [
+        "211024",
+        "aergn09diff",
+        "aerosol type 9 source/gain accumulated"
+    ],
+    [
+        "211025",
+        "aergn10diff",
+        "aerosol type 10 source/gain accumulated"
+    ],
+    [
+        "211026",
+        "aergn11diff",
+        "aerosol type 11 source/gain accumulated"
+    ],
+    [
+        "211027",
+        "aergn12diff",
+        "aerosol type 12 source/gain accumulated"
+    ],
+    [
+        "211031",
+        "aerls01diff",
+        "aerosol type 1 sink/loss accumulated"
+    ],
+    [
+        "211032",
+        "aerls02diff",
+        "aerosol type 2 sink/loss accumulated"
+    ],
+    [
+        "211033",
+        "aerls03diff",
+        "aerosol type 3 sink/loss accumulated"
+    ],
+    [
+        "211034",
+        "aerls04diff",
+        "aerosol type 4 sink/loss accumulated"
+    ],
+    [
+        "211035",
+        "aerls05diff",
+        "aerosol type 5 sink/loss accumulated"
+    ],
+    [
+        "211036",
+        "aerls06diff",
+        "aerosol type 6 sink/loss accumulated"
+    ],
+    [
+        "211037",
+        "aerls07diff",
+        "aerosol type 7 sink/loss accumulated"
+    ],
+    [
+        "211038",
+        "aerls08diff",
+        "aerosol type 8 sink/loss accumulated"
+    ],
+    [
+        "211039",
+        "aerls09diff",
+        "aerosol type 9 sink/loss accumulated"
+    ],
+    [
+        "211040",
+        "aerls10diff",
+        "aerosol type 10 sink/loss accumulated"
+    ],
+    [
+        "211041",
+        "aerls11diff",
+        "aerosol type 11 sink/loss accumulated"
+    ],
+    [
+        "211042",
+        "aerls12diff",
+        "aerosol type 12 sink/loss accumulated"
+    ],
+    [
+        "211052",
+        "aerdepdiff",
+        "dust emission potential"
+    ],
+    [
+        "211053",
+        "aerltsdiff",
+        "lifting threshold speed"
+    ],
+    [
+        "211054",
+        "aersccdiff",
+        "soil clay content"
+    ],
+    [
+        "211064",
+        "tcco2diff",
+        "total column carbon dioxide"
+    ],
+    [
+        "211065",
+        "tcch4diff",
+        "total column methane"
+    ],
+    [
+        "211066",
+        "tcn2odiff",
+        "total column nitrous oxide"
+    ],
+    [
+        "211067",
+        "co2ofdiff",
+        "ocean flux of carbon dioxide"
+    ],
+    [
+        "211068",
+        "co2nbfdiff",
+        "natural biosphere flux of carbon dioxide"
+    ],
+    [
+        "211069",
+        "co2apfdiff",
+        "anthropogenic emissions of carbon dioxide"
+    ],
+    [
+        "211070",
+        "ch4fdiff",
+        "methane surface fluxes"
+    ],
+    [
+        "211071",
+        "kch4diff",
+        "methane loss rate due to radical hydroxyl (oh)"
+    ],
+    [
+        "211080",
+        "co2firediff",
+        "wildfire flux of carbon dioxide"
+    ],
+    [
+        "211081",
+        "cofirediff",
+        "wildfire flux of carbon monoxide"
+    ],
+    [
+        "211082",
+        "ch4firediff",
+        "wildfire flux of methane"
+    ],
+    [
+        "211083",
+        "nmhcfirediff",
+        "wildfire flux of non-methane hydro-carbons"
+    ],
+    [
+        "211084",
+        "h2firediff",
+        "wildfire flux of hydrogen"
+    ],
+    [
+        "211085",
+        "noxfirediff",
+        "wildfire flux of nitrogen oxides nox"
+    ],
+    [
+        "211086",
+        "n2ofirediff",
+        "wildfire flux of nitrous oxide"
+    ],
+    [
+        "211087",
+        "pm2p5firediff",
+        "wildfire flux of particulate matter pm2.5"
+    ],
+    [
+        "211088",
+        "tpmfirediff",
+        "wildfire flux of total particulate matter"
+    ],
+    [
+        "211089",
+        "tcfirediff",
+        "wildfire flux of total carbon in aerosols"
+    ],
+    [
+        "211090",
+        "ocfirediff",
+        "wildfire flux of organic carbon"
+    ],
+    [
+        "211091",
+        "bcfirediff",
+        "wildfire flux of black carbon"
+    ],
+    [
+        "211092",
+        "cfirediff",
+        "wildfire overall flux of burnt carbon"
+    ],
+    [
+        "211093",
+        "c4ffirediff",
+        "wildfire fraction of c4 plants"
+    ],
+    [
+        "211094",
+        "vegfirediff",
+        "wildfire vegetation map index"
+    ],
+    [
+        "211095",
+        "ccfirediff",
+        "wildfire combustion completeness"
+    ],
+    [
+        "211096",
+        "flfirediff",
+        "wildfire fuel load: carbon per unit area"
+    ],
+    [
+        "211097",
+        "offirediff",
+        "wildfire fraction of area observed"
+    ],
+    [
+        "211098",
+        "oafirediff",
+        "wildfire observed area"
+    ],
+    [
+        "211099",
+        "frpfirediff",
+        "wildfire radiative power"
+    ],
+    [
+        "211100",
+        "crfirediff",
+        "wildfire combustion rate"
+    ],
+    [
+        "211125",
+        "tcno2diff",
+        "total column nitrogen dioxide"
+    ],
+    [
+        "211126",
+        "tcso2diff",
+        "total column sulphur dioxide"
+    ],
+    [
+        "211127",
+        "tccodiff",
+        "total column carbon monoxide"
+    ],
+    [
+        "211128",
+        "tchchodiff",
+        "total column formaldehyde"
+    ],
+    [
+        "211129",
+        "noxdiff",
+        "nitrogen oxides"
+    ],
+    [
+        "211130",
+        "tcnoxdiff",
+        "total column nitrogen oxides"
+    ],
+    [
+        "211131",
+        "grg1diff",
+        "reactive tracer 1 mass mixing ratio"
+    ],
+    [
+        "211132",
+        "tcgrg1diff",
+        "total column grg tracer 1"
+    ],
+    [
+        "211133",
+        "grg2diff",
+        "reactive tracer 2 mass mixing ratio"
+    ],
+    [
+        "211134",
+        "tcgrg2diff",
+        "total column grg tracer 2"
+    ],
+    [
+        "211135",
+        "grg3diff",
+        "reactive tracer 3 mass mixing ratio"
+    ],
+    [
+        "211136",
+        "tcgrg3diff",
+        "total column grg tracer 3"
+    ],
+    [
+        "211137",
+        "grg4diff",
+        "reactive tracer 4 mass mixing ratio"
+    ],
+    [
+        "211138",
+        "tcgrg4diff",
+        "total column grg tracer 4"
+    ],
+    [
+        "211139",
+        "grg5diff",
+        "reactive tracer 5 mass mixing ratio"
+    ],
+    [
+        "211140",
+        "tcgrg5diff",
+        "total column grg tracer 5"
+    ],
+    [
+        "211141",
+        "grg6diff",
+        "reactive tracer 6 mass mixing ratio"
+    ],
+    [
+        "211142",
+        "tcgrg6diff",
+        "total column grg tracer 6"
+    ],
+    [
+        "211143",
+        "grg7diff",
+        "reactive tracer 7 mass mixing ratio"
+    ],
+    [
+        "211144",
+        "tcgrg7diff",
+        "total column grg tracer 7"
+    ],
+    [
+        "211145",
+        "grg8diff",
+        "reactive tracer 8 mass mixing ratio"
+    ],
+    [
+        "211146",
+        "tcgrg8diff",
+        "total column grg tracer 8"
+    ],
+    [
+        "211147",
+        "grg9diff",
+        "reactive tracer 9 mass mixing ratio"
+    ],
+    [
+        "211148",
+        "tcgrg9diff",
+        "total column grg tracer 9"
+    ],
+    [
+        "211149",
+        "grg10diff",
+        "reactive tracer 10 mass mixing ratio"
+    ],
+    [
+        "211150",
+        "tcgrg10diff",
+        "total column grg tracer 10"
+    ],
+    [
+        "211151",
+        "sfnoxdiff",
+        "surface flux nitrogen oxides"
+    ],
+    [
+        "211152",
+        "sfno2diff",
+        "surface flux nitrogen dioxide"
+    ],
+    [
+        "211153",
+        "sfso2diff",
+        "surface flux sulphur dioxide"
+    ],
+    [
+        "211154",
+        "sfco2diff",
+        "surface flux carbon monoxide"
+    ],
+    [
+        "211155",
+        "sfhchodiff",
+        "surface flux formaldehyde"
+    ],
+    [
+        "211156",
+        "sfgo3diff",
+        "surface flux gems ozone"
+    ],
+    [
+        "211157",
+        "sfgr1diff",
+        "surface flux reactive tracer 1"
+    ],
+    [
+        "211158",
+        "sfgr2diff",
+        "surface flux reactive tracer 2"
+    ],
+    [
+        "211159",
+        "sfgr3diff",
+        "surface flux reactive tracer 3"
+    ],
+    [
+        "211160",
+        "sfgr4diff",
+        "surface flux reactive tracer 4"
+    ],
+    [
+        "211161",
+        "sfgr5diff",
+        "surface flux reactive tracer 5"
+    ],
+    [
+        "211162",
+        "sfgr6diff",
+        "surface flux reactive tracer 6"
+    ],
+    [
+        "211163",
+        "sfgr7diff",
+        "surface flux reactive tracer 7"
+    ],
+    [
+        "211164",
+        "sfgr8diff",
+        "surface flux reactive tracer 8"
+    ],
+    [
+        "211165",
+        "sfgr9diff",
+        "surface flux reactive tracer 9"
+    ],
+    [
+        "211166",
+        "sfgr10diff",
+        "surface flux reactive tracer 10"
+    ],
+    [
+        "211181",
+        "radiff",
+        "radon"
+    ],
+    [
+        "211182",
+        "sf6diff",
+        "sulphur hexafluoride"
+    ],
+    [
+        "211183",
+        "tcradiff",
+        "total column radon"
+    ],
+    [
+        "211184",
+        "tcsf6diff",
+        "total column sulphur hexafluoride"
+    ],
+    [
+        "211185",
+        "sf6apfdiff",
+        "anthropogenic emissions of sulphur hexafluoride"
+    ],
+    [
+        "211206",
+        "gtco3diff",
+        "gems total column ozone"
+    ],
+    [
+        "211207",
+        "aod550diff",
+        "total aerosol optical depth at 550nm"
+    ],
+    [
+        "211208",
+        "ssaod550diff",
+        "sea salt aerosol optical depth at 550nm"
+    ],
+    [
+        "211209",
+        "duaod550diff",
+        "dust aerosol optical depth at 550nm"
+    ],
+    [
+        "211210",
+        "omaod550diff",
+        "organic matter aerosol optical depth at 550nm"
+    ],
+    [
+        "211211",
+        "bcaod550diff",
+        "black carbon aerosol optical depth at 550nm"
+    ],
+    [
+        "211212",
+        "suaod550diff",
+        "sulphate aerosol optical depth at 550nm"
+    ],
+    [
+        "211213",
+        "aod469diff",
+        "total aerosol optical depth at 469nm"
+    ],
+    [
+        "211214",
+        "aod670diff",
+        "total aerosol optical depth at 670nm"
+    ],
+    [
+        "211215",
+        "aod865diff",
+        "total aerosol optical depth at 865nm"
+    ],
+    [
+        "211216",
+        "aod1240diff",
+        "total aerosol optical depth at 1240nm"
+    ],
+    [
+        "211101",
+        "maxfrpfirediff",
+        "wildfire radiative power maximum"
+    ],
+    [
+        "211102",
+        "so2firediff",
+        "wildfire flux of sulfur dioxide"
+    ],
+    [
+        "211112",
+        "hialkanesfirediff",
+        "wildfire flux of higher alkanes (cnh2n+2, c>=4)"
+    ],
+    [
+        "211111",
+        "hialkenesfirediff",
+        "wildfire flux of higher alkenes (cnh2n, c>=4)"
+    ],
+    [
+        "211110",
+        "toluenefirediff",
+        "wildfire flux of toluene_lump (c7h8+ c6h6 + c8h10)"
+    ],
+    [
+        "211117",
+        "c2h6sfirediff",
+        "wildfire flux of dimethyl sulfide (dms) (c2h6s)"
+    ],
+    [
+        "211116",
+        "nh3firediff",
+        "wildfire flux of ammonia (nh3)"
+    ],
+    [
+        "211115",
+        "c3h6ofirediff",
+        "wildfire flux of acetone (c3h6o)"
+    ],
+    [
+        "211114",
+        "c2h4ofirediff",
+        "wildfire flux of acetaldehyde (c2h4o)"
+    ],
+    [
+        "211113",
+        "ch2ofirediff",
+        "wildfire flux of formaldehyde (ch2o)"
+    ],
+    [
+        "211109",
+        "terpenesfirediff",
+        "wildfire flux of terpenes (c5h8)n"
+    ],
+    [
+        "211108",
+        "c5h8firediff",
+        "wildfire flux of isoprene (c5h8)"
+    ],
+    [
+        "211107",
+        "c3h6firediff",
+        "wildfire flux of propene (c3h6)"
+    ],
+    [
+        "211106",
+        "c2h4firediff",
+        "wildfire flux of ethene (c2h4)"
+    ],
+    [
+        "211105",
+        "c3h8firediff",
+        "wildfire flux of propane (c3h8)"
+    ],
+    [
+        "211104",
+        "c2h5ohfirediff",
+        "wildfire flux of ethanol (c2h5oh)"
+    ],
+    [
+        "211103",
+        "ch3ohfirediff",
+        "wildfire flux of methanol (ch3oh)"
+    ],
+    [
+        "211013",
+        "aermr13diff",
+        "aerosol type 13 mass mixing ratio"
+    ],
+    [
+        "211118",
+        "c2h6firediff",
+        "wildfire flux of ethane (c2h6)"
+    ],
+    [
+        "211119",
+        "alediff",
+        "altitude of emitter"
+    ],
+    [
+        "211120",
+        "aptdiff",
+        "altitude of plume top"
+    ],
+    [
+        "211014",
+        "aermr14diff",
+        "aerosol type 14 mass mixing ratio"
+    ],
+    [
+        "211015",
+        "aermr15diff",
+        "aerosol type 15 mass mixing ratio"
+    ],
+    [
+        "211028",
+        "aerpr03diff",
+        "so4 aerosol precursor mass mixing ratio"
+    ],
+    [
+        "211029",
+        "aerwv01diff",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 1"
+    ],
+    [
+        "211030",
+        "aerwv02diff",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 2"
+    ],
+    [
+        "211043",
+        "emdmsdiff",
+        "dms surface emission"
+    ],
+    [
+        "211044",
+        "aerwv03diff",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 3"
+    ],
+    [
+        "211045",
+        "aerwv04diff",
+        "water vapour mixing ratio for hydrophilic aerosols in mode 4"
+    ],
+    [
+        "210217",
+        "aod340",
+        "total aerosol optical depth at 340 nm"
+    ],
+    [
+        "210218",
+        "aod355",
+        "total aerosol optical depth at 355 nm"
+    ],
+    [
+        "210219",
+        "aod380",
+        "total aerosol optical depth at 380 nm"
+    ],
+    [
+        "210220",
+        "aod400",
+        "total aerosol optical depth at 400 nm"
+    ],
+    [
+        "210221",
+        "aod440",
+        "total aerosol optical depth at 440 nm"
+    ],
+    [
+        "210222",
+        "aod500",
+        "total aerosol optical depth at 500 nm"
+    ],
+    [
+        "210223",
+        "aod532",
+        "total aerosol optical depth at 532 nm"
+    ],
+    [
+        "210224",
+        "aod645",
+        "total aerosol optical depth at 645 nm"
+    ],
+    [
+        "210225",
+        "aod800",
+        "total aerosol optical depth at 800 nm"
+    ],
+    [
+        "210226",
+        "aod858",
+        "total aerosol optical depth at 858 nm"
+    ],
+    [
+        "210227",
+        "aod1020",
+        "total aerosol optical depth at 1020 nm"
+    ],
+    [
+        "210228",
+        "aod1064",
+        "total aerosol optical depth at 1064 nm"
+    ],
+    [
+        "210229",
+        "aod1640",
+        "total aerosol optical depth at 1640 nm"
+    ],
+    [
+        "215089",
+        "accaod550",
+        "accumulated total aerosol optical depth at 550 nm"
+    ],
+    [
+        "215090",
+        "aluvpsn",
+        "effective (snow effect included) uv visible albedo for direct radiation"
+    ],
+    [
+        "215091",
+        "aerdep10si",
+        "10 metre wind speed dust emission potential"
+    ],
+    [
+        "215092",
+        "aerdep10fg",
+        "10 metre wind gustiness dust emission potential"
+    ],
+    [
+        "215093",
+        "aot532",
+        "total aerosol optical thickness at 532 nm"
+    ],
+    [
+        "215094",
+        "naot532",
+        "natural (sea-salt and dust) aerosol optical thickness at 532 nm"
+    ],
+    [
+        "215095",
+        "aaot532",
+        "antropogenic (black carbon, organic matter, sulphate) aerosol optical thickness at 532 nm"
+    ],
+    [
+        "215096",
+        "aodabs340",
+        "total absorption aerosol optical depth at 340 nm"
+    ],
+    [
+        "215097",
+        "aodabs355",
+        "total absorption aerosol optical depth at 355 nm"
+    ],
+    [
+        "215098",
+        "aodabs380",
+        "total absorption aerosol optical depth at 380 nm"
+    ],
+    [
+        "215099",
+        "aodabs400",
+        "total absorption aerosol optical depth at 400 nm"
+    ],
+    [
+        "215100",
+        "aodabs440",
+        "total absorption aerosol optical depth at 440 nm"
+    ],
+    [
+        "215101",
+        "aodabs469",
+        "total absorption aerosol optical depth at 469 nm"
+    ],
+    [
+        "215102",
+        "aodabs500",
+        "total absorption aerosol optical depth at 500 nm"
+    ],
+    [
+        "215103",
+        "aodabs532",
+        "total absorption aerosol optical depth at 532 nm"
+    ],
+    [
+        "215104",
+        "aodabs550",
+        "total absorption aerosol optical depth at 550 nm"
+    ],
+    [
+        "215105",
+        "aodabs645",
+        "total absorption aerosol optical depth at 645 nm"
+    ],
+    [
+        "215106",
+        "aodabs670",
+        "total absorption aerosol optical depth at 670 nm"
+    ],
+    [
+        "215107",
+        "aodabs800",
+        "total absorption aerosol optical depth at 800 nm"
+    ],
+    [
+        "215108",
+        "aodabs858",
+        "total absorption aerosol optical depth at 858 nm"
+    ],
+    [
+        "215109",
+        "aodabs865",
+        "total absorption aerosol optical depth at 865 nm"
+    ],
+    [
+        "215110",
+        "aodabs1020",
+        "total absorption aerosol optical depth at 1020 nm"
+    ],
+    [
+        "215111",
+        "aodabs1064",
+        "total absorption aerosol optical depth at 1064 nm"
+    ],
+    [
+        "215112",
+        "aodabs1240",
+        "total absorption aerosol optical depth at 1240 nm"
+    ],
+    [
+        "215113",
+        "aodabs1640",
+        "total absorption aerosol optical depth at 1640 nm"
+    ],
+    [
+        "215114",
+        "aodfm340",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 340 nm"
+    ],
+    [
+        "215115",
+        "aodfm355",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 355 nm"
+    ],
+    [
+        "215116",
+        "aodfm380",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 380 nm"
+    ],
+    [
+        "215117",
+        "aodfm400",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 400 nm"
+    ],
+    [
+        "215118",
+        "aodfm440",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 440 nm"
+    ],
+    [
+        "215119",
+        "aodfm469",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 469 nm"
+    ],
+    [
+        "215120",
+        "aodfm500",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 500 nm"
+    ],
+    [
+        "215121",
+        "aodfm532",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 532 nm"
+    ],
+    [
+        "215122",
+        "aodfm550",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 550 nm"
+    ],
+    [
+        "215123",
+        "aodfm645",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 645 nm"
+    ],
+    [
+        "215124",
+        "aodfm670",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 670 nm"
+    ],
+    [
+        "215125",
+        "aodfm800",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 800 nm"
+    ],
+    [
+        "215126",
+        "aodfm858",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 858 nm"
+    ],
+    [
+        "215127",
+        "aodfm865",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 865 nm"
+    ],
+    [
+        "215128",
+        "aodfm1020",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 1020 nm"
+    ],
+    [
+        "215129",
+        "aodfm1064",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 1064 nm"
+    ],
+    [
+        "215130",
+        "aodfm1240",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 1240 nm"
+    ],
+    [
+        "215131",
+        "aodfm1640",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 1640 nm"
+    ],
+    [
+        "215132",
+        "ssa340",
+        "single scattering albedo at 340 nm"
+    ],
+    [
+        "215133",
+        "ssa355",
+        "single scattering albedo at 355 nm"
+    ],
+    [
+        "215134",
+        "ssa380",
+        "single scattering albedo at 380 nm"
+    ],
+    [
+        "215135",
+        "ssa400",
+        "single scattering albedo at 400 nm"
+    ],
+    [
+        "215136",
+        "ssa440",
+        "single scattering albedo at 440 nm"
+    ],
+    [
+        "215137",
+        "ssa469",
+        "single scattering albedo at 469 nm"
+    ],
+    [
+        "215138",
+        "ssa500",
+        "single scattering albedo at 500 nm"
+    ],
+    [
+        "215139",
+        "ssa532",
+        "single scattering albedo at 532 nm"
+    ],
+    [
+        "215140",
+        "ssa550",
+        "single scattering albedo at 550 nm"
+    ],
+    [
+        "215141",
+        "ssa645",
+        "single scattering albedo at 645 nm"
+    ],
+    [
+        "215142",
+        "ssa670",
+        "single scattering albedo at 670 nm"
+    ],
+    [
+        "215143",
+        "ssa800",
+        "single scattering albedo at 800 nm"
+    ],
+    [
+        "215144",
+        "ssa858",
+        "single scattering albedo at 858 nm"
+    ],
+    [
+        "215145",
+        "ssa865",
+        "single scattering albedo at 865 nm"
+    ],
+    [
+        "215146",
+        "ssa1020",
+        "single scattering albedo at 1020 nm"
+    ],
+    [
+        "215147",
+        "ssa1064",
+        "single scattering albedo at 1064 nm"
+    ],
+    [
+        "215148",
+        "ssa1240",
+        "single scattering albedo at 1240 nm"
+    ],
+    [
+        "215149",
+        "ssa1640",
+        "single scattering albedo at 1640 nm"
+    ],
+    [
+        "215150",
+        "assimetry340",
+        "assimetry factor at 340 nm"
+    ],
+    [
+        "215151",
+        "assimetry355",
+        "assimetry factor at 355 nm"
+    ],
+    [
+        "215152",
+        "assimetry380",
+        "assimetry factor at 380 nm"
+    ],
+    [
+        "215153",
+        "assimetry400",
+        "assimetry factor at 400 nm"
+    ],
+    [
+        "215154",
+        "assimetry440",
+        "assimetry factor at 440 nm"
+    ],
+    [
+        "215155",
+        "assimetry469",
+        "assimetry factor at 469 nm"
+    ],
+    [
+        "215156",
+        "assimetry500",
+        "assimetry factor at 500 nm"
+    ],
+    [
+        "215157",
+        "assimetry532",
+        "assimetry factor at 532 nm"
+    ],
+    [
+        "215158",
+        "assimetry550",
+        "assimetry factor at 550 nm"
+    ],
+    [
+        "215159",
+        "assimetry645",
+        "assimetry factor at 645 nm"
+    ],
+    [
+        "215160",
+        "assimetry670",
+        "assimetry factor at 670 nm"
+    ],
+    [
+        "215161",
+        "assimetry800",
+        "assimetry factor at 800 nm"
+    ],
+    [
+        "215162",
+        "assimetry858",
+        "assimetry factor at 858 nm"
+    ],
+    [
+        "215163",
+        "assimetry865",
+        "assimetry factor at 865 nm"
+    ],
+    [
+        "215164",
+        "assimetry1020",
+        "assimetry factor at 1020 nm"
+    ],
+    [
+        "215165",
+        "assimetry1064",
+        "assimetry factor at 1064 nm"
+    ],
+    [
+        "215166",
+        "assimetry1240",
+        "assimetry factor at 1240 nm"
+    ],
+    [
+        "215167",
+        "assimetry1640",
+        "assimetry factor at 1640 nm"
+    ],
+    [
+        "215168",
+        "aersrcso2",
+        "source/gain of sulphur dioxide"
+    ],
+    [
+        "215169",
+        "aerddpso2",
+        "dry deposition of sulphur dioxide"
+    ],
+    [
+        "215170",
+        "aersdmso2",
+        "sedimentation of sulphur dioxide"
+    ],
+    [
+        "215171",
+        "aerwdlsso2",
+        "wet deposition of sulphur dioxide by large-scale precipitation"
+    ],
+    [
+        "215172",
+        "aerwdccso2",
+        "wet deposition of sulphur dioxide by convective precipitation"
+    ],
+    [
+        "215173",
+        "aerngtso2",
+        "negative fixer of sulphur dioxide"
+    ],
+    [
+        "215174",
+        "aermssso2",
+        "vertically integrated mass of sulphur dioxide"
+    ],
+    [
+        "215175",
+        "aerodso2",
+        "sulphur dioxide optical depth"
+    ],
+    [
+        "215176",
+        "aodabs2130",
+        "total absorption aerosol optical depth at 2130 nm"
+    ],
+    [
+        "215177",
+        "aodfm2130",
+        "total fine mode (r < 0.5 um) aerosol optical depth at 2130 nm"
+    ],
+    [
+        "215178",
+        "ssa2130",
+        "single scattering albedo at 2130 nm"
+    ],
+    [
+        "215179",
+        "assimetry2130",
+        "assimetry factor at 2130 nm"
+    ],
+    [
+        "210230",
+        "aod2130",
+        "total aerosol optical depth at 2130 nm"
+    ],
+    [
+        "210057",
+        "ocnuc",
+        "mixing ration of organic carbon aerosol, nucleation mode"
+    ],
+    [
+        "210058",
+        "monot",
+        "monoterpene precursor mixing ratio"
+    ],
+    [
+        "210059",
+        "soapr",
+        "secondary organic precursor mixing ratio"
+    ],
+    [
+        "228129",
+        "ssrdc",
+        "surface solar radiation downward clear-sky"
+    ],
+    [
+        "228130",
+        "strdc",
+        "surface thermal radiation downward clear-sky"
+    ],
+    [
+        "228026",
+        "mx2t3",
+        "maximum temperature at 2 metres in the last 3 hours"
+    ],
+    [
+        "228027",
+        "mn2t3",
+        "minimum temperature at 2 metres in the last 3 hours"
+    ],
+    [
+        "228028",
+        "10fg3",
+        "10 metre wind gust in the last 3 hours",
+        "I10FG3"
+    ],
+    [
+        "300001",
+        "pres",
+        "pressure"
+    ],
+    [
+        "300002",
+        "psnm",
+        "pressure reduced to msl"
+    ],
+    [
+        "300003",
+        "tsps",
+        "pressure tendency"
+    ],
+    [
+        "300006",
+        "geop",
+        "geopotential"
+    ],
+    [
+        "300007",
+        "zgeo",
+        "geopotential height"
+    ],
+    [
+        "300008",
+        "gzge",
+        "geometric height"
+    ],
+    [
+        "300011",
+        "temp",
+        "absolute temperature"
+    ],
+    [
+        "300012",
+        "vtmp",
+        "virtual temperature"
+    ],
+    [
+        "300013",
+        "ptmp",
+        "potential temperature"
+    ],
+    [
+        "300014",
+        "psat",
+        "pseudo-adiabatic potential temperature"
+    ],
+    [
+        "300015",
+        "mxtp",
+        "maximum temperature"
+    ],
+    [
+        "300016",
+        "mntp",
+        "minimum temperature"
+    ],
+    [
+        "300017",
+        "tpor",
+        "dew point temperature"
+    ],
+    [
+        "300018",
+        "dptd",
+        "dew point depression"
+    ],
+    [
+        "300019",
+        "lpsr",
+        "lapse rate"
+    ],
+    [
+        "300021",
+        "rds1",
+        "radar spectra(1)"
+    ],
+    [
+        "300022",
+        "rds2",
+        "radar spectra(2)"
+    ],
+    [
+        "300023",
+        "rds3",
+        "radar spectra(3)"
+    ],
+    [
+        "300025",
+        "tpan",
+        "temperature anomaly"
+    ],
+    [
+        "300026",
+        "psan",
+        "pressure anomaly"
+    ],
+    [
+        "300027",
+        "zgan",
+        "geopot height anomaly"
+    ],
+    [
+        "300028",
+        "wvs1",
+        "wave spectra(1)"
+    ],
+    [
+        "300029",
+        "wvs2",
+        "wave spectra(2)"
+    ],
+    [
+        "300030",
+        "wvs3",
+        "wave spectra(3)"
+    ],
+    [
+        "300031",
+        "wind",
+        "wind direction"
+    ],
+    [
+        "300032",
+        "wins",
+        "wind speed"
+    ],
+    [
+        "300033",
+        "uvel",
+        "zonal wind (u)"
+    ],
+    [
+        "300034",
+        "vvel",
+        "meridional wind (v)"
+    ],
+    [
+        "300035",
+        "fcor",
+        "stream function"
+    ],
+    [
+        "300036",
+        "potv",
+        "velocity potential"
+    ],
+    [
+        "300038",
+        "sgvv",
+        "sigma coord vert vel"
+    ],
+    [
+        "300039",
+        "omeg",
+        "omega"
+    ],
+    [
+        "300040",
+        "omg2",
+        "vertical velocity"
+    ],
+    [
+        "300041",
+        "abvo",
+        "absolute vorticity"
+    ],
+    [
+        "300042",
+        "abdv",
+        "absolute divergence"
+    ],
+    [
+        "300043",
+        "vort",
+        "vorticity"
+    ],
+    [
+        "300044",
+        "divg",
+        "divergence"
+    ],
+    [
+        "300045",
+        "vucs",
+        "vertical u-comp shear"
+    ],
+    [
+        "300046",
+        "vvcs",
+        "vert v-comp shear"
+    ],
+    [
+        "300047",
+        "dirc",
+        "direction of current"
+    ],
+    [
+        "300048",
+        "spdc",
+        "speed of current"
+    ],
+    [
+        "300049",
+        "ucpc",
+        "u-component of current"
+    ],
+    [
+        "300050",
+        "vcpc",
+        "v-component of current"
+    ],
+    [
+        "300051",
+        "umes",
+        "specific humidity"
+    ],
+    [
+        "300052",
+        "umrl",
+        "relative humidity"
+    ],
+    [
+        "300053",
+        "hmxr",
+        "humidity mixing ratio"
+    ],
+    [
+        "300054",
+        "agpl",
+        "inst. precipitable water"
+    ],
+    [
+        "300055",
+        "vapp",
+        "vapour pressure"
+    ],
+    [
+        "300056",
+        "sadf",
+        "saturation deficit"
+    ],
+    [
+        "300057",
+        "evap",
+        "evaporation"
+    ],
+    [
+        "300059",
+        "prcr",
+        "precipitation rate"
+    ],
+    [
+        "300060",
+        "thpb",
+        "thunder probability"
+    ],
+    [
+        "300061",
+        "prec",
+        "total precipitation"
+    ],
+    [
+        "300062",
+        "prge",
+        "large scale precipitation"
+    ],
+    [
+        "300063",
+        "prcv",
+        "convective precipitation"
+    ],
+    [
+        "300064",
+        "neve",
+        "snowfall"
+    ],
+    [
+        "300065",
+        "wenv",
+        "wat equiv acc snow depth"
+    ],
+    [
+        "300066",
+        "nvde",
+        "snow depth"
+    ],
+    [
+        "300067",
+        "mxld",
+        "mixed layer depth"
+    ],
+    [
+        "300068",
+        "tthd",
+        "trans thermocline depth"
+    ],
+    [
+        "300069",
+        "mthd",
+        "main thermocline depth"
+    ],
+    [
+        "300070",
+        "mtha",
+        "main thermocline anom"
+    ],
+    [
+        "300071",
+        "cbnv",
+        "cloud cover"
+    ],
+    [
+        "300072",
+        "cvnv",
+        "convective cloud cover"
+    ],
+    [
+        "300073",
+        "lwnv",
+        "low cloud cover"
+    ],
+    [
+        "300074",
+        "mdnv",
+        "medium cloud cover"
+    ],
+    [
+        "300075",
+        "hinv",
+        "high cloud cover"
+    ],
+    [
+        "300076",
+        "wtnv",
+        "cloud water"
+    ],
+    [
+        "300077",
+        "bli",
+        "best lifted index (to 500 hpa)"
+    ],
+    [
+        "300081",
+        "lsmk",
+        "land sea mask"
+    ],
+    [
+        "300082",
+        "dslm",
+        "dev sea_lev from mean"
+    ],
+    [
+        "300083",
+        "zorl",
+        "roughness length"
+    ],
+    [
+        "300084",
+        "albe",
+        "albedo"
+    ],
+    [
+        "300085",
+        "dstp",
+        "deep soil temperature"
+    ],
+    [
+        "300086",
+        "soic",
+        "soil moisture content"
+    ],
+    [
+        "300087",
+        "vege",
+        "vegetation"
+    ],
+    [
+        "300089",
+        "dens",
+        "density"
+    ],
+    [
+        "300091",
+        "icec",
+        "ice concentration"
+    ],
+    [
+        "300092",
+        "icet",
+        "ice thickness"
+    ],
+    [
+        "300093",
+        "iced",
+        "direction of ice drift"
+    ],
+    [
+        "300094",
+        "ices",
+        "speed of ice drift"
+    ],
+    [
+        "300095",
+        "iceu",
+        "u-comp of ice drift"
+    ],
+    [
+        "300096",
+        "icev",
+        "v-comp of ice drift"
+    ],
+    [
+        "300097",
+        "iceg",
+        "ice growth"
+    ],
+    [
+        "300098",
+        "icdv",
+        "ice divergence"
+    ],
+    [
+        "300100",
+        "shcw",
+        "sig hgt com wave/swell"
+    ],
+    [
+        "300101",
+        "wwdi",
+        "direction of wind wave"
+    ],
+    [
+        "300102",
+        "wwsh",
+        "sig hght of wind waves"
+    ],
+    [
+        "300103",
+        "wwmp",
+        "mean period wind waves"
+    ],
+    [
+        "300104",
+        "swdi",
+        "direction of swell wave"
+    ],
+    [
+        "300105",
+        "swsh",
+        "sig height swell waves"
+    ],
+    [
+        "300106",
+        "swmp",
+        "mean period swell waves"
+    ],
+    [
+        "300107",
+        "prwd",
+        "primary wave direction"
+    ],
+    [
+        "300108",
+        "prmp",
+        "prim wave mean period"
+    ],
+    [
+        "300109",
+        "swdi",
+        "second wave direction"
+    ],
+    [
+        "300110",
+        "swmp",
+        "second wave mean period"
+    ],
+    [
+        "300111",
+        "ocas",
+        "short wave absorbed at ground"
+    ],
+    [
+        "300112",
+        "slds",
+        "net long wave at bottom"
+    ],
+    [
+        "300113",
+        "nswr",
+        "net short-wav rad(top)"
+    ],
+    [
+        "300114",
+        "role",
+        "outgoing long wave at top"
+    ],
+    [
+        "300115",
+        "lwrd",
+        "long-wav rad"
+    ],
+    [
+        "300116",
+        "swea",
+        "short wave absorbed by earth/atmosphere"
+    ],
+    [
+        "300117",
+        "glbr",
+        "global radiation"
+    ],
+    [
+        "300121",
+        "clsf",
+        "latent heat flux from surface"
+    ],
+    [
+        "300122",
+        "cssf",
+        "sensible heat flux from surface"
+    ],
+    [
+        "300123",
+        "blds",
+        "bound layer dissipation"
+    ],
+    [
+        "300127",
+        "imag",
+        "image"
+    ],
+    [
+        "300128",
+        "tp2m",
+        "2 metre temperature"
+    ],
+    [
+        "300129",
+        "dp2m",
+        "2 metre dewpoint temperature"
+    ],
+    [
+        "300130",
+        "u10m",
+        "10 metre u-wind component"
+    ],
+    [
+        "300131",
+        "v10m",
+        "10 metre v-wind component"
+    ],
+    [
+        "300132",
+        "topo",
+        "topography"
+    ],
+    [
+        "300133",
+        "gsfp",
+        "geometric mean surface pressure"
+    ],
+    [
+        "300134",
+        "lnsp",
+        "ln surface pressure"
+    ],
+    [
+        "300135",
+        "pslc",
+        "surface pressure"
+    ],
+    [
+        "300136",
+        "pslm",
+        "m s l pressure (mesinger method)"
+    ],
+    [
+        "300137",
+        "mask",
+        "mask"
+    ],
+    [
+        "300138",
+        "mxwu",
+        "maximum u-wind"
+    ],
+    [
+        "300139",
+        "mxwv",
+        "maximum v-wind"
+    ],
+    [
+        "300140",
+        "cape",
+        "convective avail. pot.energy"
+    ],
+    [
+        "300141",
+        "cine",
+        "convective inhib. energy"
+    ],
+    [
+        "300142",
+        "lhcv",
+        "convective latent heating"
+    ],
+    [
+        "300143",
+        "mscv",
+        "convective moisture source"
+    ],
+    [
+        "300144",
+        "scvm",
+        "shallow conv. moisture source"
+    ],
+    [
+        "300145",
+        "scvh",
+        "shallow convective heating"
+    ],
+    [
+        "300146",
+        "mxwp",
+        "maximum wind press. lvl"
+    ],
+    [
+        "300147",
+        "ustr",
+        "storm motion u-component"
+    ],
+    [
+        "300148",
+        "vstr",
+        "storm motion v-component"
+    ],
+    [
+        "300149",
+        "cbnt",
+        "mean cloud cover"
+    ],
+    [
+        "300150",
+        "pcbs",
+        "pressure at cloud base"
+    ],
+    [
+        "300151",
+        "pctp",
+        "pressure at cloud top"
+    ],
+    [
+        "300152",
+        "fzht",
+        "freezing level height"
+    ],
+    [
+        "300153",
+        "fzrh",
+        "freezing level relative humidity"
+    ],
+    [
+        "300154",
+        "fdlt",
+        "flight levels temperature"
+    ],
+    [
+        "300155",
+        "fdlu",
+        "flight levels u-wind"
+    ],
+    [
+        "300156",
+        "fdlv",
+        "flight levels v-wind"
+    ],
+    [
+        "300157",
+        "tppp",
+        "tropopause pressure"
+    ],
+    [
+        "300158",
+        "tppt",
+        "tropopause temperature"
+    ],
+    [
+        "300159",
+        "tppu",
+        "tropopause u-wind component"
+    ],
+    [
+        "300160",
+        "tppv",
+        "tropopause v-wind component"
+    ],
+    [
+        "300162",
+        "gvdu",
+        "gravity wave drag du/dt"
+    ],
+    [
+        "300163",
+        "gvdv",
+        "gravity wave drag dv/dt"
+    ],
+    [
+        "300164",
+        "gvus",
+        "gravity wave drag sfc zonal stress"
+    ],
+    [
+        "300165",
+        "gvvs",
+        "gravity wave drag sfc meridional stress"
+    ],
+    [
+        "300167",
+        "dvsh",
+        "divergence of specific humidity"
+    ],
+    [
+        "300168",
+        "hmfc",
+        "horiz. moisture flux conv."
+    ],
+    [
+        "300169",
+        "vmfl",
+        "vert. integrated moisture flux conv."
+    ],
+    [
+        "300170",
+        "vadv",
+        "vertical moisture advection"
+    ],
+    [
+        "300171",
+        "nhcm",
+        "neg. hum. corr. moisture source"
+    ],
+    [
+        "300172",
+        "lglh",
+        "large scale latent heating"
+    ],
+    [
+        "300173",
+        "lgms",
+        "large scale moisture source"
+    ],
+    [
+        "300174",
+        "smav",
+        "soil moisture availability"
+    ],
+    [
+        "300175",
+        "tgrz",
+        "soil temperature of root zone"
+    ],
+    [
+        "300176",
+        "bslh",
+        "bare soil latent heat"
+    ],
+    [
+        "300177",
+        "evpp",
+        "potential sfc evaporation"
+    ],
+    [
+        "300178",
+        "rnof",
+        "runoff"
+    ],
+    [
+        "300179",
+        "pitp",
+        "interception loss"
+    ],
+    [
+        "300180",
+        "vpca",
+        "vapor pressure of canopy air space"
+    ],
+    [
+        "300181",
+        "qsfc",
+        "surface spec humidity"
+    ],
+    [
+        "300182",
+        "ussl",
+        "soil wetness of surface"
+    ],
+    [
+        "300183",
+        "uzrs",
+        "soil wetness of root zone"
+    ],
+    [
+        "300184",
+        "uzds",
+        "soil wetness of drainage zone"
+    ],
+    [
+        "300185",
+        "amdl",
+        "storage on canopy"
+    ],
+    [
+        "300186",
+        "amsl",
+        "storage on ground"
+    ],
+    [
+        "300187",
+        "tsfc",
+        "surface temperature"
+    ],
+    [
+        "300188",
+        "tems",
+        "surface absolute temperature"
+    ],
+    [
+        "300189",
+        "tcas",
+        "temperature of canopy air space"
+    ],
+    [
+        "300190",
+        "ctmp",
+        "temperature at canopy"
+    ],
+    [
+        "300191",
+        "tgsc",
+        "ground/surface cover temperature"
+    ],
+    [
+        "300192",
+        "uves",
+        "surface zonal wind (u)"
+    ],
+    [
+        "300193",
+        "usst",
+        "surface zonal wind stress"
+    ],
+    [
+        "300194",
+        "vves",
+        "surface meridional wind (v)"
+    ],
+    [
+        "300195",
+        "vsst",
+        "surface meridional wind stress"
+    ],
+    [
+        "300196",
+        "suvf",
+        "surface momentum flux"
+    ],
+    [
+        "300197",
+        "iswf",
+        "incident short wave flux"
+    ],
+    [
+        "300198",
+        "ghfl",
+        "time ave ground ht flx"
+    ],
+    [
+        "300200",
+        "lwbc",
+        "net long wave at bottom (clear)"
+    ],
+    [
+        "300201",
+        "lwtc",
+        "outgoing long wave at top (clear)"
+    ],
+    [
+        "300202",
+        "swec",
+        "short wv absrbd by earth/atmos (clear)"
+    ],
+    [
+        "300203",
+        "ocac",
+        "short wave absorbed at ground (clear)"
+    ],
+    [
+        "300205",
+        "lwrh",
+        "long wave radiative heating"
+    ],
+    [
+        "300206",
+        "swrh",
+        "short wave radiative heating"
+    ],
+    [
+        "300207",
+        "olis",
+        "downward long wave at bottom"
+    ],
+    [
+        "300208",
+        "olic",
+        "downward long wave at bottom (clear)"
+    ],
+    [
+        "300209",
+        "ocis",
+        "downward short wave at ground"
+    ],
+    [
+        "300210",
+        "ocic",
+        "downward short wave at ground (clear)"
+    ],
+    [
+        "300211",
+        "oles",
+        "upward long wave at bottom"
+    ],
+    [
+        "300212",
+        "oces",
+        "upward short wave at ground"
+    ],
+    [
+        "300213",
+        "swgc",
+        "upward short wave at ground (clear)"
+    ],
+    [
+        "300214",
+        "roce",
+        "upward short wave at top"
+    ],
+    [
+        "300215",
+        "swtc",
+        "upward short wave at top (clear)"
+    ],
+    [
+        "300218",
+        "hhdf",
+        "horizontal heating diffusion"
+    ],
+    [
+        "300219",
+        "hmdf",
+        "horizontal moisture diffusion"
+    ],
+    [
+        "300220",
+        "hddf",
+        "horizontal divergence diffusion"
+    ],
+    [
+        "300221",
+        "hvdf",
+        "horizontal vorticity diffusion"
+    ],
+    [
+        "300222",
+        "vdms",
+        "vertical diff. moisture source"
+    ],
+    [
+        "300223",
+        "vdfu",
+        "vertical diffusion du/dt"
+    ],
+    [
+        "300224",
+        "vdfv",
+        "vertical diffusion dv/dt"
+    ],
+    [
+        "300225",
+        "vdfh",
+        "vertical diffusion heating"
+    ],
+    [
+        "300226",
+        "umrs",
+        "surface relative humidity"
+    ],
+    [
+        "300227",
+        "vdcc",
+        "vertical dist total cloud cover"
+    ],
+    [
+        "300230",
+        "usmt",
+        "time mean surface zonal wind (u)"
+    ],
+    [
+        "300231",
+        "vsmt",
+        "time mean surface meridional wind (v)"
+    ],
+    [
+        "300232",
+        "tsmt",
+        "time mean surface absolute temperature"
+    ],
+    [
+        "300233",
+        "rsmt",
+        "time mean surface relative humidity"
+    ],
+    [
+        "300234",
+        "atmt",
+        "time mean absolute temperature"
+    ],
+    [
+        "300235",
+        "stmt",
+        "time mean deep soil temperature"
+    ],
+    [
+        "300236",
+        "ommt",
+        "time mean derived omega"
+    ],
+    [
+        "300237",
+        "dvmt",
+        "time mean divergence"
+    ],
+    [
+        "300238",
+        "zhmt",
+        "time mean geopotential height"
+    ],
+    [
+        "300239",
+        "lnmt",
+        "time mean log surface pressure"
+    ],
+    [
+        "300240",
+        "mkmt",
+        "time mean mask"
+    ],
+    [
+        "300241",
+        "vvmt",
+        "time mean meridional wind (v)"
+    ],
+    [
+        "300242",
+        "omtm",
+        "time mean omega"
+    ],
+    [
+        "300243",
+        "ptmt",
+        "time mean potential temperature"
+    ],
+    [
+        "300244",
+        "pcmt",
+        "time mean precip. water"
+    ],
+    [
+        "300245",
+        "rhmt",
+        "time mean relative humidity"
+    ],
+    [
+        "300246",
+        "mpmt",
+        "time mean sea level pressure"
+    ],
+    [
+        "300247",
+        "simt",
+        "time mean sigmadot"
+    ],
+    [
+        "300248",
+        "uemt",
+        "time mean specific humidity"
+    ],
+    [
+        "300249",
+        "fcmt",
+        "time mean stream function"
+    ],
+    [
+        "300250",
+        "psmt",
+        "time mean surface pressure"
+    ],
+    [
+        "300251",
+        "tmmt",
+        "time mean surface temperature"
+    ],
+    [
+        "300252",
+        "pvmt",
+        "time mean velocity potential"
+    ],
+    [
+        "300253",
+        "tvmt",
+        "time mean virtual temperature"
+    ],
+    [
+        "300254",
+        "vtmt",
+        "time mean vorticity"
+    ],
+    [
+        "300255",
+        "uvmt",
+        "time mean zonal wind (u)"
+    ],
+    [
+        "85001156",
+        "prec_convec",
+        "total convective precipitation"
+    ],
+    [
+        "85001157",
+        "prec_gde_ech",
+        "total large scale precipitation"
+    ],
+    [
+        "85001160",
+        "cape_ins",
+        "convective available potential energy instantaneous"
+    ],
+    [
+        "140211",
+        "phiaw",
+        "normalized energy flux into waves"
+    ],
+    [
+        "140212",
+        "phioc",
+        "normalized energy flux into ocean"
+    ],
+    [
+        "140213",
+        "tla",
+        "turbulent langmuir number"
+    ],
+    [
+        "140214",
+        "tauoc",
+        "normalized stress into ocean"
+    ],
+    [
+        "228081",
+        "aco2gpp",
+        "accumulated carbon dioxide gross primary production"
+    ],
+    [
+        "228082",
+        "aco2rec",
+        "accumulated carbon dioxide ecosystem respiration"
+    ],
+    [
+        "228083",
+        "fco2nee",
+        "flux of carbon dioxide net ecosystem exchange"
+    ],
+    [
+        "228084",
+        "fco2gpp",
+        "flux of carbon dioxide gross primary production"
+    ],
+    [
+        "228085",
+        "fco2rec",
+        "flux of carbon dioxide ecosystem respiration"
+    ],
+    [
+        "210231",
+        "c7h8fire",
+        "wildfire flux of toluene (c7h8)"
+    ],
+    [
+        "210232",
+        "c6h6fire",
+        "wildfire flux of benzene (c6h6)"
+    ],
+    [
+        "210233",
+        "c8h10fire",
+        "wildfire flux of xylene (c8h10)"
+    ],
+    [
+        "210234",
+        "c4h8fire",
+        "wildfire flux of butenes (c4h8)"
+    ],
+    [
+        "210235",
+        "c5h10fire",
+        "wildfire flux of pentenes (c5h10)"
+    ],
+    [
+        "210236",
+        "c6h12fire",
+        "wildfire flux of hexene (c6h12)"
+    ],
+    [
+        "210237",
+        "c8h16fire",
+        "wildfire flux of octene (c8h16)"
+    ],
+    [
+        "210238",
+        "c4h10fire",
+        "wildfire flux of butanes (c4h10)"
+    ],
+    [
+        "210239",
+        "c5h12fire",
+        "wildfire flux of pentanes (c5h12)"
+    ],
+    [
+        "210240",
+        "c6h14fire",
+        "wildfire flux of hexanes (c6h14)"
+    ],
+    [
+        "210241",
+        "c7h16fire",
+        "wildfire flux of heptane (c7h16)"
+    ],
+    [
+        "228242",
+        "fdif",
+        "surface solar radiation diffuse total sky"
+    ],
+    [
+        "228243",
+        "cdif",
+        "surface solar radiation diffuse clear-sky"
+    ],
+    [
+        "228244",
+        "aldr",
+        "surface albedo of direct radiation"
+    ],
+    [
+        "228245",
+        "aldf",
+        "surface albedo of diffuse radiation"
+    ],
+    [
+        "230008",
+        "srovar",
+        "surface runoff (variable resolution)"
+    ],
+    [
+        "230009",
+        "ssrovar",
+        "sub-surface runoff (variable resolution)"
+    ],
+    [
+        "230174",
+        "alvar",
+        "albedo (variable resolution)"
+    ],
+    [
+        "230228",
+        "tpvar",
+        "total precipitation (variable resolution)"
+    ],
+    [
+        "210072",
+        "pm1",
+        "particulate matter d < 1 um"
+    ],
+    [
+        "210073",
+        "pm2p5",
+        "particulate matter d < 2.5 um"
+    ],
+    [
+        "210074",
+        "pm10",
+        "particulate matter d < 10 um"
+    ],
+    [
+        "210186",
+        "aluvpi",
+        "uv visible albedo for direct radiation, isotropic component "
+    ],
+    [
+        "210187",
+        "aluvpv",
+        "uv visible albedo for direct radiation, volumetric component "
+    ],
+    [
+        "210188",
+        "aluvpg",
+        "uv visible albedo for direct radiation, geometric component "
+    ],
+    [
+        "210189",
+        "alnipi",
+        "near ir albedo for direct radiation, isotropic component "
+    ],
+    [
+        "210190",
+        "alnipv",
+        "near ir albedo for direct radiation, volumetric component"
+    ],
+    [
+        "210191",
+        "alnipg",
+        "near ir albedo for direct radiation, geometric component "
+    ],
+    [
+        "210192",
+        "aluvdi",
+        "uv visible albedo for diffuse radiation, isotropic component "
+    ],
+    [
+        "210193",
+        "aluvdv",
+        "uv visible albedo for diffuse radiation, volumetric component "
+    ],
+    [
+        "210194",
+        "aluvdg",
+        "uv visible albedo for diffuse radiation, geometric component "
+    ],
+    [
+        "210195",
+        "alnidi",
+        "near ir albedo for diffuse radiation, isotropic component "
+    ],
+    [
+        "210196",
+        "alnidv",
+        "near ir albedo for diffuse radiation, volumetric component "
+    ],
+    [
+        "210197",
+        "alnidg",
+        "near ir albedo for diffuse radiation, geometric component "
+    ],
+    [
+        "228040",
+        "swi1",
+        "soil wetness index in layer 1"
+    ],
+    [
+        "228041",
+        "swi2",
+        "soil wetness index in layer 2"
+    ],
+    [
+        "228042",
+        "swi3",
+        "soil wetness index in layer 3"
+    ],
+    [
+        "228043",
+        "swi4",
+        "soil wetness index in layer 4"
+    ],
+    [
+        "228255",
+        "tcclw",
+        "surface long wave-effective total cloudiness"
+    ],
+    [
+        "228248",
+        "tccsw",
+        "surface short wave-effective total cloudiness"
+    ],
+    [
+        "260510",
+        "clbt",
+        "cloudy brightness temperature"
+    ],
+    [
+        "260511",
+        "csbt",
+        "clear-sky brightness temperature"
+    ],
+    [
+        "260257",
+        "ccl",
+        "cloud cover"
+    ],
+    [
+        "240011",
+        "chcross",
+        "cross sectional area of flow in channel"
+    ],
+    [
+        "240012",
+        "chside",
+        "side flow into river channel"
+    ],
+    [
+        "240013",
+        "dis",
+        "discharge from rivers or streams"
+    ],
+    [
+        "240014",
+        "rivsto",
+        "river storage of water"
+    ],
+    [
+        "240015",
+        "fldsto",
+        "floodplain storage of water"
+    ],
+    [
+        "240016",
+        "fldfrc",
+        "water fraction"
+    ],
+    [
+        "240017",
+        "dslr",
+        "days since last observation"
+    ],
+    [
+        "240018",
+        "frost",
+        "frost index"
+    ],
+    [
+        "240020",
+        "woss",
+        "depth of water on soil surface"
+    ],
+    [
+        "240021",
+        "tpups",
+        "upstream accumulated precipitation"
+    ],
+    [
+        "240022",
+        "smups",
+        "upstream accumulated snow melt"
+    ],
+    [
+        "260258",
+        "evarate",
+        "evaporation rate"
+    ],
+    [
+        "240026",
+        "sd_elev",
+        "snow depth at elevation bands"
+    ],
+    [
+        "260242",
+        "2r",
+        "surface air relative humidity"
+    ],
+    [
+        "230021",
+        "fdirvar",
+        "total sky direct solar radiation at surface (variable resolution)"
+    ],
+    [
+        "230022",
+        "cdirvar",
+        "clear-sky direct solar radiation at surface (variable resolution)"
+    ],
+    [
+        "210079",
+        "vafire",
+        "wildfire viewing angle of observation"
+    ],
+    [
+        "228229",
+        "smos_tb_cdfa",
+        "smos first brightness temperature bias correction parameter"
+    ],
+    [
+        "228230",
+        "smos_tb_cdfb",
+        "smos second brightness temperature bias correction parameter"
+    ],
+    [
+        "174186",
+        "vlca",
+        "very low cloud amount"
+    ],
+    [
+        "174142",
+        "lsrrate",
+        "large scale rainfall rate"
+    ],
+    [
+        "174240",
+        "lsfrate",
+        "large scale snowfall rate"
+    ],
+    [
+        "174143",
+        "crfrate",
+        "convective rainfall rate"
+    ],
+    [
+        "174239",
+        "csfrate",
+        "convective snowfall rate"
+    ],
+    [
+        "174052",
+        "rhum",
+        "relative humidity at 1.5m"
+    ],
+    [
+        "174025",
+        "vis15",
+        "visibility at 1.5m"
+    ],
+    [
+        "174248",
+        "tccro",
+        "total cloud amount - random overlap"
+    ],
+    [
+        "174010",
+        "sswcsdown",
+        "clear-sky (ii) down surface sw flux"
+    ],
+    [
+        "174013",
+        "sswcsup",
+        "clear-sky (ii) up surface sw flux"
+    ],
+    [
+        "174249",
+        "tcclwr",
+        "total cloud amount in lw radiation"
+    ],
+    [
+        "174051",
+        "mx15t",
+        "maximum temperature at 1.5m since previous post-processing"
+    ],
+    [
+        "174050",
+        "mn15t",
+        "minimum temperature at 1.5m since previous post-processing"
+    ],
+    [
+        "174116",
+        "swrsurf",
+        "short wave radiation flux at surface"
+    ],
+    [
+        "174117",
+        "swrtop",
+        "short wave radiation flux at top of atmosphere"
+    ],
+    [
+        "174137",
+        "tcwvap",
+        "total column water vapour"
+    ],
+    [
+        "140080",
+        "wx1",
+        "wave experimental parameter 1"
+    ],
+    [
+        "140081",
+        "wx2",
+        "wave experimental parameter 2"
+    ],
+    [
+        "140082",
+        "wx3",
+        "wave experimental parameter 3"
+    ],
+    [
+        "140083",
+        "wx4",
+        "wave experimental parameter 4"
+    ],
+    [
+        "140084",
+        "wx5",
+        "wave experimental parameter 5"
+    ],
+    [
+        "140121",
+        "swh1",
+        "significant wave height of first swell partition"
+    ],
+    [
+        "140122",
+        "mwd1",
+        "mean wave direction of first swell partition"
+    ],
+    [
+        "140123",
+        "mwp1",
+        "mean wave period of first swell partition"
+    ],
+    [
+        "140124",
+        "swh2",
+        "significant wave height of second swell partition"
+    ],
+    [
+        "260256",
+        "hindex",
+        "haines index"
+    ],
+    [
+        "140125",
+        "mwd2",
+        "mean wave direction of second swell partition"
+    ],
+    [
+        "140126",
+        "mwp2",
+        "mean wave period of second swell partition"
+    ],
+    [
+        "140127",
+        "swh3",
+        "significant wave height of third swell partition"
+    ],
+    [
+        "260255",
+        "aptmp",
+        "apparent temperature"
+    ],
+    [
+        "140128",
+        "mwd3",
+        "mean wave direction of third swell partition"
+    ],
+    [
+        "140129",
+        "mwp3",
+        "mean wave period of third swell partition"
+    ],
+    [
+        "140208",
+        "wstar",
+        "free convective velocity over the oceans"
+    ],
+    [
+        "140209",
+        "rhoao",
+        "air density over the oceans"
+    ],
+    [
+        "140210",
+        "mswsi",
+        "mean square wave strain in sea ice"
+    ],
+    [
+        "260530",
+        "scaled radiance"
+    ],
+    [
+        "260531",
+        "scaled albedo"
+    ],
+    [
+        "260532",
+        "scaled brightness temperature"
+    ],
+    [
+        "260533",
+        "scaled precipitable water"
+    ],
+    [
+        "260534",
+        "scaled lifted index"
+    ],
+    [
+        "260535",
+        "scaled cloud top pressure"
+    ],
+    [
+        "260536",
+        "scaled skin temperature"
+    ],
+    [
+        "260537",
+        "cloud mask"
+    ],
+    [
+        "260538",
+        "pixel scene type"
+    ],
+    [
+        "260539",
+        "fire detection indicator"
+    ],
+    [
+        "260550",
+        "cloudy radiance (with respect to wave number)"
+    ],
+    [
+        "260551",
+        "clear-sky radiance (with respect to wave number)"
+    ],
+    [
+        "260552",
+        "wind speed"
+    ],
+    [
+        "260553",
+        "aerosol optical thickness at 0.635 um"
+    ],
+    [
+        "260554",
+        "aerosol optical thickness at 0.810 um"
+    ],
+    [
+        "260555",
+        "aerosol optical thickness at 1.640 um"
+    ],
+    [
+        "260556",
+        "angstrom coefficient"
+    ],
+    [
+        "228032",
+        "asn",
+        "snow albedo"
+    ],
+    [
+        "228205",
+        "ro",
+        "water runoff and drainage"
+    ],
+    [
+        "228086",
+        "sm20",
+        "soil moisture top 20 cm"
+    ],
+    [
+        "228087",
+        "sm100",
+        "soil moisture top 100 cm"
+    ],
+    [
+        "228095",
+        "st20",
+        "soil temperature top 20 cm"
+    ],
+    [
+        "228096",
+        "st100",
+        "soil temperature top 100 cm"
+    ],
+    [
+        "217003",
+        "h2o2",
+        "hydrogen peroxide"
+    ],
+    [
+        "217004",
+        "ch4",
+        "methane"
+    ],
+    [
+        "217006",
+        "hno3",
+        "nitric acid"
+    ],
+    [
+        "217007",
+        "ch3ooh",
+        "methyl peroxide"
+    ],
+    [
+        "217009",
+        "par",
+        "paraffins"
+    ],
+    [
+        "217010",
+        "c2h4",
+        "ethene"
+    ],
+    [
+        "217011",
+        "ole",
+        "olefins"
+    ],
+    [
+        "217012",
+        "ald2",
+        "aldehydes"
+    ],
+    [
+        "217013",
+        "pan",
+        "peroxyacetyl nitrate"
+    ],
+    [
+        "217014",
+        "rooh",
+        "peroxides"
+    ],
+    [
+        "217015",
+        "onit",
+        "organic nitrates"
+    ],
+    [
+        "217016",
+        "c5h8",
+        "isoprene"
+    ],
+    [
+        "217018",
+        "dms",
+        "dimethyl sulfide"
+    ],
+    [
+        "217019",
+        "nh3",
+        "ammonia"
+    ],
+    [
+        "217020",
+        "so4",
+        "sulfate"
+    ],
+    [
+        "217021",
+        "nh4",
+        "ammonium"
+    ],
+    [
+        "217022",
+        "msa",
+        "methane sulfonic acid"
+    ],
+    [
+        "217023",
+        "ch3cocho",
+        "methyl glyoxal"
+    ],
+    [
+        "217024",
+        "o3s",
+        "stratospheric ozone"
+    ],
+    [
+        "217026",
+        "pb",
+        "lead"
+    ],
+    [
+        "217027",
+        "no",
+        "nitrogen monoxide"
+    ],
+    [
+        "217028",
+        "ho2",
+        "hydroperoxy radical"
+    ],
+    [
+        "217029",
+        "ch3o2",
+        "methylperoxy radical"
+    ],
+    [
+        "217030",
+        "oh",
+        "hydroxyl radical"
+    ],
+    [
+        "217032",
+        "no3",
+        "nitrate radical"
+    ],
+    [
+        "217033",
+        "n2o5",
+        "dinitrogen pentoxide"
+    ],
+    [
+        "217034",
+        "ho2no2",
+        "pernitric acid"
+    ],
+    [
+        "217035",
+        "c2o3",
+        "peroxy acetyl radical"
+    ],
+    [
+        "217036",
+        "ror",
+        "organic ethers"
+    ],
+    [
+        "217037",
+        "rxpar",
+        "par budget corrector"
+    ],
+    [
+        "217038",
+        "xo2",
+        "no to no2 operator"
+    ],
+    [
+        "217039",
+        "xo2n",
+        "no to alkyl nitrate operator"
+    ],
+    [
+        "217040",
+        "nh2",
+        "amine"
+    ],
+    [
+        "217041",
+        "psc",
+        "polar stratospheric cloud"
+    ],
+    [
+        "217042",
+        "ch3oh",
+        "methanol"
+    ],
+    [
+        "217043",
+        "hcooh",
+        "formic acid"
+    ],
+    [
+        "217044",
+        "mcooh",
+        "methacrylic acid"
+    ],
+    [
+        "217045",
+        "c2h6",
+        "ethane"
+    ],
+    [
+        "217046",
+        "c2h5oh",
+        "ethanol"
+    ],
+    [
+        "217047",
+        "c3h8",
+        "propane"
+    ],
+    [
+        "217048",
+        "c3h6",
+        "propene"
+    ],
+    [
+        "217049",
+        "c10h16",
+        "terpenes"
+    ],
+    [
+        "217050",
+        "ispd",
+        "methacrolein mvk"
+    ],
+    [
+        "217051",
+        "no3_a",
+        "nitrate"
+    ],
+    [
+        "217052",
+        "ch3coch3",
+        "acetone"
+    ],
+    [
+        "217053",
+        "aco2",
+        "acetone product"
+    ],
+    [
+        "217054",
+        "ic3h7o2",
+        "ic3h7o2"
+    ],
+    [
+        "217055",
+        "hypropo2",
+        "hypropo2"
+    ],
+    [
+        "217056",
+        "noxa",
+        "nitrogen oxides transp"
+    ],
+    [
+        "218003",
+        "tc_h2o2",
+        "total column hydrogen peroxide"
+    ],
+    [
+        "218004",
+        "tc_ch4",
+        "total column methane"
+    ],
+    [
+        "218006",
+        "tc_hno3",
+        "total column nitric acid"
+    ],
+    [
+        "218007",
+        "tc_ch3ooh",
+        "total column methyl peroxide"
+    ],
+    [
+        "218009",
+        "tc_par",
+        "total column paraffins"
+    ],
+    [
+        "218010",
+        "tc_c2h4",
+        "total column ethene"
+    ],
+    [
+        "218011",
+        "tc_ole",
+        "total column olefins"
+    ],
+    [
+        "218012",
+        "tc_ald2",
+        "total column aldehydes"
+    ],
+    [
+        "218013",
+        "tc_pan",
+        "total column  peroxyacetyl nitrate"
+    ],
+    [
+        "218014",
+        "tc_rooh",
+        "total column peroxides"
+    ],
+    [
+        "218015",
+        "tc_onit",
+        "total column organic nitrates"
+    ],
+    [
+        "218016",
+        "tc_c5h8",
+        "total column  isoprene"
+    ],
+    [
+        "218018",
+        "tc_dms",
+        "total column dimethyl sulfide"
+    ],
+    [
+        "218019",
+        "tc_nh3",
+        "total column ammonia"
+    ],
+    [
+        "218020",
+        "tc_so4",
+        "total column  sulfate"
+    ],
+    [
+        "218021",
+        "tc_nh4",
+        "total column ammonium"
+    ],
+    [
+        "218022",
+        "tc_msa",
+        "total column  methane sulfonic acid"
+    ],
+    [
+        "218023",
+        "tc_ch3cocho",
+        "total column methyl glyoxal"
+    ],
+    [
+        "218024",
+        "tc_o3s",
+        "total column stratospheric ozone"
+    ],
+    [
+        "218026",
+        "tc_pb",
+        "total column  lead"
+    ],
+    [
+        "218027",
+        "tc_no",
+        "total column nitrogen monoxide"
+    ],
+    [
+        "218028",
+        "tc_ho2",
+        "total column hydroperoxy radical"
+    ],
+    [
+        "218029",
+        "tc_ch3o2",
+        "total column methylperoxy radical"
+    ],
+    [
+        "218030",
+        "tc_oh",
+        "total column hydroxyl radical"
+    ],
+    [
+        "218032",
+        "tc_no3",
+        "total column nitrate radical"
+    ],
+    [
+        "218033",
+        "tc_n2o5",
+        "total column dinitrogen pentoxide"
+    ],
+    [
+        "218034",
+        "tc_ho2no2",
+        "total column pernitric acid"
+    ],
+    [
+        "218035",
+        "tc_c2o3",
+        "total column peroxy acetyl radical"
+    ],
+    [
+        "218036",
+        "tc_ror",
+        "total column  organic ethers"
+    ],
+    [
+        "218037",
+        "tc_rxpar",
+        "total column par budget corrector"
+    ],
+    [
+        "218038",
+        "tc_xo2",
+        "total column no to no2 operator"
+    ],
+    [
+        "218039",
+        "tc_xo2n",
+        "total column no to alkyl nitrate operator"
+    ],
+    [
+        "218040",
+        "tc_nh2",
+        "total column amine"
+    ],
+    [
+        "218041",
+        "tc_psc",
+        "total column  polar stratospheric cloud"
+    ],
+    [
+        "218042",
+        "tc_ch3oh",
+        "total column methanol"
+    ],
+    [
+        "218043",
+        "tc_hcooh",
+        "total column formic acid"
+    ],
+    [
+        "218044",
+        "tc_mcooh",
+        "total column  methacrylic acid"
+    ],
+    [
+        "218045",
+        "tc_c2h6",
+        "total column  ethane"
+    ],
+    [
+        "218046",
+        "tc_c2h5oh",
+        "total column ethanol"
+    ],
+    [
+        "218047",
+        "tc_c3h8",
+        "total column propane"
+    ],
+    [
+        "218048",
+        "tc_c3h6",
+        "total column propene"
+    ],
+    [
+        "218049",
+        "tc_c10h16",
+        "total column terpenes"
+    ],
+    [
+        "218050",
+        "tc_ispd",
+        "total column methacrolein mvk"
+    ],
+    [
+        "218051",
+        "tc_no3_a",
+        "total column nitrate"
+    ],
+    [
+        "218052",
+        "tc_ch3coch3",
+        "total column acetone"
+    ],
+    [
+        "218053",
+        "tc_aco2",
+        "total column acetone product"
+    ],
+    [
+        "218054",
+        "tc_ic3h7o2",
+        "total column ic3h7o2"
+    ],
+    [
+        "218055",
+        "tc_hypropo2",
+        "total column hypropo2"
+    ],
+    [
+        "218056",
+        "tc_noxa",
+        "total column nitrogen oxides transp"
+    ],
+    [
+        "219001",
+        "e_go3",
+        "ozone emissions"
+    ],
+    [
+        "219002",
+        "e_nox",
+        "nitrogen oxides emissions"
+    ],
+    [
+        "219003",
+        "e_h2o2",
+        "hydrogen peroxide emissions"
+    ],
+    [
+        "219004",
+        "e_ch4",
+        "methane emissions"
+    ],
+    [
+        "219005",
+        "e_co",
+        "carbon monoxide emissions"
+    ],
+    [
+        "219006",
+        "e_hno3",
+        "nitric acid emissions"
+    ],
+    [
+        "219007",
+        "e_ch3ooh",
+        "methyl peroxide emissions"
+    ],
+    [
+        "219008",
+        "e_hcho",
+        "formaldehyde emissions"
+    ],
+    [
+        "219009",
+        "e_par",
+        "paraffins emissions"
+    ],
+    [
+        "219010",
+        "e_c2h4",
+        "ethene emissions"
+    ],
+    [
+        "219011",
+        "e_ole",
+        "olefins emissions"
+    ],
+    [
+        "219012",
+        "e_ald2",
+        "aldehydes emissions"
+    ],
+    [
+        "219013",
+        "e_pan",
+        "peroxyacetyl nitrate emissions"
+    ],
+    [
+        "219014",
+        "e_rooh",
+        "peroxides emissions"
+    ],
+    [
+        "219015",
+        "e_onit",
+        "organic nitrates emissions"
+    ],
+    [
+        "219016",
+        "e_c5h8",
+        "isoprene emissions"
+    ],
+    [
+        "219017",
+        "e_so2",
+        "sulfur dioxide emissions"
+    ],
+    [
+        "219018",
+        "e_dms",
+        "dimethyl sulfide emissions"
+    ],
+    [
+        "219019",
+        "e_nh3",
+        "ammonia emissions"
+    ],
+    [
+        "219020",
+        "e_so4",
+        "sulfate emissions"
+    ],
+    [
+        "219021",
+        "e_nh4",
+        "ammonium emissions"
+    ],
+    [
+        "219022",
+        "e_msa",
+        "methane sulfonic acid emissions"
+    ],
+    [
+        "219023",
+        "e_ch3cocho",
+        "methyl glyoxal emissions"
+    ],
+    [
+        "219024",
+        "e_o3s",
+        "stratospheric ozone emissions"
+    ],
+    [
+        "219025",
+        "e_ra",
+        "radon emissions"
+    ],
+    [
+        "219026",
+        "e_pb",
+        "lead emissions"
+    ],
+    [
+        "219027",
+        "e_no",
+        "nitrogen monoxide emissions"
+    ],
+    [
+        "219028",
+        "e_ho2",
+        "hydroperoxy radical emissions"
+    ],
+    [
+        "219029",
+        "e_ch3o2",
+        "methylperoxy radical emissions"
+    ],
+    [
+        "219030",
+        "e_oh",
+        "hydroxyl radical emissions"
+    ],
+    [
+        "219031",
+        "e_no2",
+        "nitrogen dioxide emissions"
+    ],
+    [
+        "219032",
+        "e_no3",
+        "nitrate radical emissions"
+    ],
+    [
+        "219033",
+        "e_n2o5",
+        "dinitrogen pentoxide emissions"
+    ],
+    [
+        "219034",
+        "e_ho2no2",
+        "pernitric acid emissions"
+    ],
+    [
+        "219035",
+        "e_c2o3",
+        "peroxy acetyl radical emissions"
+    ],
+    [
+        "219036",
+        "e_ror",
+        "organic ethers emissions"
+    ],
+    [
+        "219037",
+        "e_rxpar",
+        "par budget corrector emissions"
+    ],
+    [
+        "219038",
+        "e_xo2",
+        "no to no2 operator emissions"
+    ],
+    [
+        "219039",
+        "e_xo2n",
+        "no to alkyl nitrate operator emissions"
+    ],
+    [
+        "219040",
+        "e_nh2",
+        "amine emissions"
+    ],
+    [
+        "219041",
+        "e_psc",
+        "polar stratospheric cloud emissions"
+    ],
+    [
+        "219042",
+        "e_ch3oh",
+        "methanol emissions"
+    ],
+    [
+        "219043",
+        "e_hcooh",
+        "formic acid emissions"
+    ],
+    [
+        "219044",
+        "e_mcooh",
+        "methacrylic acid emissions"
+    ],
+    [
+        "219045",
+        "e_c2h6",
+        "ethane emissions"
+    ],
+    [
+        "219046",
+        "e_c2h5oh",
+        "ethanol emissions"
+    ],
+    [
+        "219047",
+        "e_c3h8",
+        "propane emissions"
+    ],
+    [
+        "219048",
+        "e_c3h6",
+        "propene emissions"
+    ],
+    [
+        "219049",
+        "e_c10h16",
+        "terpenes emissions"
+    ],
+    [
+        "219050",
+        "e_ispd",
+        "methacrolein mvk  emissions"
+    ],
+    [
+        "219051",
+        "e_no3_a",
+        "nitrate emissions"
+    ],
+    [
+        "219052",
+        "e_ch3coch3",
+        "acetone emissions"
+    ],
+    [
+        "219053",
+        "e_aco2",
+        "acetone product emissions"
+    ],
+    [
+        "219054",
+        "e_ic3h7o2",
+        "ic3h7o2 emissions"
+    ],
+    [
+        "219055",
+        "e_hypropo2",
+        "hypropo2 emissions"
+    ],
+    [
+        "219056",
+        "e_noxa",
+        "nitrogen oxides transp emissions"
+    ],
+    [
+        "221001",
+        "dv_go3",
+        "ozone deposition velocity"
+    ],
+    [
+        "221002",
+        "dv_nox",
+        "nitrogen oxides deposition velocity"
+    ],
+    [
+        "221003",
+        "dv_h2o2",
+        "hydrogen peroxide deposition velocity"
+    ],
+    [
+        "221004",
+        "dv_ch4",
+        "methane deposition velocity"
+    ],
+    [
+        "221005",
+        "dv_co",
+        "carbon monoxide deposition velocity"
+    ],
+    [
+        "221006",
+        "dv_hno3",
+        "nitric acid deposition velocity"
+    ],
+    [
+        "221007",
+        "dv_ch3ooh",
+        "methyl peroxide deposition velocity"
+    ],
+    [
+        "221008",
+        "dv_hcho",
+        "formaldehyde deposition velocity"
+    ],
+    [
+        "221009",
+        "dv_par",
+        "paraffins deposition velocity"
+    ],
+    [
+        "221010",
+        "dv_c2h4",
+        "ethene deposition velocity"
+    ],
+    [
+        "221011",
+        "dv_ole",
+        "olefins deposition velocity"
+    ],
+    [
+        "221012",
+        "dv_ald2",
+        "aldehydes deposition velocity"
+    ],
+    [
+        "221013",
+        "dv_pan",
+        "peroxyacetyl nitrate deposition velocity"
+    ],
+    [
+        "221014",
+        "dv_rooh",
+        "peroxides deposition velocity"
+    ],
+    [
+        "221015",
+        "dv_onit",
+        "organic nitrates deposition velocity"
+    ],
+    [
+        "221016",
+        "dv_c5h8",
+        "isoprene deposition velocity"
+    ],
+    [
+        "221017",
+        "dv_so2",
+        "sulfur dioxide deposition velocity"
+    ],
+    [
+        "221018",
+        "dv_dms",
+        "dimethyl sulfide deposition velocity"
+    ],
+    [
+        "221019",
+        "dv_nh3",
+        "ammonia deposition velocity"
+    ],
+    [
+        "221020",
+        "dv_so4",
+        "sulfate deposition velocity"
+    ],
+    [
+        "221021",
+        "dv_nh4",
+        "ammonium deposition velocity"
+    ],
+    [
+        "221022",
+        "dv_msa",
+        "methane sulfonic acid deposition velocity"
+    ],
+    [
+        "221023",
+        "dv_ch3cocho",
+        "methyl glyoxal deposition velocity"
+    ],
+    [
+        "221024",
+        "dv_o3s",
+        "stratospheric ozone deposition velocity"
+    ],
+    [
+        "221025",
+        "dv_ra",
+        "radon deposition velocity"
+    ],
+    [
+        "221026",
+        "dv_pb",
+        "lead deposition velocity"
+    ],
+    [
+        "221027",
+        "dv_no",
+        "nitrogen monoxide deposition velocity"
+    ],
+    [
+        "221028",
+        "dv_ho2",
+        "hydroperoxy radical deposition velocity"
+    ],
+    [
+        "221029",
+        "dv_ch3o2",
+        "methylperoxy radical deposition velocity"
+    ],
+    [
+        "221030",
+        "dv_oh",
+        "hydroxyl radical deposition velocity"
+    ],
+    [
+        "221031",
+        "dv_no2",
+        "nitrogen dioxide deposition velocity"
+    ],
+    [
+        "221032",
+        "dv_no3",
+        "nitrate radical deposition velocity"
+    ],
+    [
+        "221033",
+        "dv_n2o5",
+        "dinitrogen pentoxide deposition velocity"
+    ],
+    [
+        "221034",
+        "dv_ho2no2",
+        "pernitric acid deposition velocity"
+    ],
+    [
+        "221035",
+        "dv_c2o3",
+        "peroxy acetyl radical deposition velocity"
+    ],
+    [
+        "221036",
+        "dv_ror",
+        "organic ethers deposition velocity"
+    ],
+    [
+        "221037",
+        "dv_rxpar",
+        "par budget corrector deposition velocity"
+    ],
+    [
+        "221038",
+        "dv_xo2",
+        "no to no2 operator deposition velocity"
+    ],
+    [
+        "221039",
+        "dv_xo2n",
+        "no to alkyl nitrate operator deposition velocity"
+    ],
+    [
+        "221040",
+        "dv_nh2",
+        "amine deposition velocity"
+    ],
+    [
+        "221041",
+        "dv_psc",
+        "polar stratospheric cloud deposition velocity"
+    ],
+    [
+        "221042",
+        "dv_ch3oh",
+        "methanol deposition velocity"
+    ],
+    [
+        "221043",
+        "dv_hcooh",
+        "formic acid deposition velocity"
+    ],
+    [
+        "221044",
+        "dv_mcooh",
+        "methacrylic acid deposition velocity"
+    ],
+    [
+        "221045",
+        "dv_c2h6",
+        "ethane deposition velocity"
+    ],
+    [
+        "221046",
+        "dv_c2h5oh",
+        "ethanol deposition velocity"
+    ],
+    [
+        "221047",
+        "dv_c3h8",
+        "propane deposition velocity"
+    ],
+    [
+        "221048",
+        "dv_c3h6",
+        "propene deposition velocity"
+    ],
+    [
+        "221049",
+        "dv_c10h16",
+        "terpenes deposition velocity"
+    ],
+    [
+        "221050",
+        "dv_ispd",
+        "methacrolein mvk  deposition velocity"
+    ],
+    [
+        "221051",
+        "dv_no3_a",
+        "nitrate deposition velocity"
+    ],
+    [
+        "221052",
+        "dv_ch3coch3",
+        "acetone deposition velocity"
+    ],
+    [
+        "221053",
+        "dv_aco2",
+        "acetone product deposition velocity"
+    ],
+    [
+        "221054",
+        "dv_ic3h7o2",
+        "ic3h7o2 deposition velocity"
+    ],
+    [
+        "221055",
+        "dv_hypropo2",
+        "hypropo2 deposition velocity"
+    ],
+    [
+        "221056",
+        "dv_noxa",
+        "nitrogen oxides transp deposition velocity"
+    ],
+    [
+        "210243",
+        "vsuaod550",
+        "volcanic sulphate aerosol optical depth at 550 nm"
+    ],
+    [
+        "210244",
+        "vashaod550",
+        "volcanic ash optical depth at 550 nm"
+    ],
+    [
+        "210245",
+        "taedec550",
+        "profile of total aerosol dry extinction coefficient"
+    ],
+    [
+        "210246",
+        "taedab550",
+        "profile of total aerosol dry absorption coefficient"
+    ],
+    [
+        "210242",
+        "apb",
+        "altitude of plume bottom"
+    ],
+    [
+        "228143",
+        "cp",
+        "convective precipitation"
+    ],
+    [
+        "3066",
+        "sde",
+        "snow depth"
+    ],
+    [
+        "3072",
+        "ccc",
+        "convective cloud cover"
+    ],
+    [
+        "3073",
+        "lcc",
+        "low cloud cover"
+    ],
+    [
+        "3074",
+        "mcc",
+        "medium cloud cover"
+    ],
+    [
+        "3075",
+        "hcc",
+        "high cloud cover"
+    ],
+    [
+        "3079",
+        "lssf",
+        "large scale snow"
+    ],
+    [
+        "260600",
+        "evpsfc",
+        "evaporation"
+    ],
+    [
+        "260601",
+        "tpratsfc",
+        "total precipitation"
+    ],
+    [
+        "260602",
+        "lpratsfc",
+        "large scale precipitation"
+    ],
+    [
+        "260603",
+        "cpratsfc",
+        "convective precipitation"
+    ],
+    [
+        "260604",
+        "srweqsfc",
+        "snowfall rate water equivalent"
+    ],
+    [
+        "260605",
+        "rofsfc",
+        "water run-off"
+    ],
+    [
+        "260606",
+        "bvf2tht",
+        "square of brunt-vaisala frequency"
+    ],
+    [
+        "260607",
+        "aduahbl",
+        "adiabatic zonal acceleration"
+    ],
+    [
+        "260608",
+        "vwvclm",
+        "meridional water vapour flux"
+    ],
+    [
+        "260609",
+        "advaprs",
+        "adiabatic meridional acceleration"
+    ],
+    [
+        "260610",
+        "frcvsfc",
+        "frequency of deep convection"
+    ],
+    [
+        "260611",
+        "frcvssfc",
+        "frequency of shallow convection"
+    ],
+    [
+        "260612",
+        "frscsfc",
+        "frequency of stratocumulus parameterisation"
+    ],
+    [
+        "260613",
+        "gwduahbl",
+        "gravity wave zonal acceleration"
+    ],
+    [
+        "260614",
+        "gwdvahbl",
+        "gravity wave meridional acceleration"
+    ],
+    [
+        "260615",
+        "ltrssfc",
+        "evapotranspiration"
+    ],
+    [
+        "260616",
+        "adhrhbl",
+        "adiabatic heating rate"
+    ],
+    [
+        "260617",
+        "mscsfc",
+        "moisture storage on canopy"
+    ],
+    [
+        "260618",
+        "msgsfc",
+        "moisture storage on ground or cover"
+    ],
+    [
+        "260619",
+        "smcugl",
+        "mass concentration of condensed water in soil"
+    ],
+    [
+        "260620",
+        "cwclm",
+        "cloud liquid water"
+    ],
+    [
+        "260621",
+        "mflxbhbl",
+        "upward mass flux at cloud base"
+    ],
+    [
+        "260622",
+        "mfluxhbl",
+        "upward mass flux"
+    ],
+    [
+        "260623",
+        "admrhbl",
+        "adiabatic moistening rate"
+    ],
+    [
+        "260624",
+        "ozonehbl",
+        "ozone mixing ratio"
+    ],
+    [
+        "260625",
+        "cnvuahbl",
+        "convective zonal acceleration"
+    ],
+    [
+        "3121",
+        "lhf",
+        "latent heat flux"
+    ],
+    [
+        "3122",
+        "shf",
+        "sensible heat flux"
+    ],
+    [
+        "3123",
+        "bld",
+        "boundary layer dissipation"
+    ],
+    [
+        "260626",
+        "fglusfc",
+        "zonal momentum flux by long gravity wave"
+    ],
+    [
+        "260627",
+        "fglvsfc",
+        "meridional momentum flux by long gravity wave"
+    ],
+    [
+        "260628",
+        "fgsvsfc",
+        "meridional momentum flux by short gravity wave"
+    ],
+    [
+        "260629",
+        "fgsusfc",
+        "zonal momentum flux by short gravity wave"
+    ],
+    [
+        "260630",
+        "utheclm",
+        "zonal thermal energy flux"
+    ],
+    [
+        "260631",
+        "vtheclm",
+        "meridional thermal energy flux"
+    ],
+    [
+        "260632",
+        "cnvvahbl",
+        "convective meridional acceleration"
+    ],
+    [
+        "260633",
+        "lrghrhbl",
+        "large scale condensation heating rate"
+    ],
+    [
+        "260634",
+        "cnvhrhbl",
+        "convective heating rate"
+    ],
+    [
+        "260635",
+        "cnvmrhbl",
+        "convective moistening rate"
+    ],
+    [
+        "260636",
+        "vdfhrhbl",
+        "vertical diffusion heating rate"
+    ],
+    [
+        "260637",
+        "vdfuahbl",
+        "vertical diffusion zonal acceleration"
+    ],
+    [
+        "260638",
+        "vdfvahbl",
+        "vertical diffusion meridional acceleration"
+    ],
+    [
+        "260639",
+        "vdfmrhbl",
+        "vertical diffusion moistening rate"
+    ],
+    [
+        "260640",
+        "swhrhbl",
+        "solar radiative heating rate"
+    ],
+    [
+        "260641",
+        "lwhrhbl",
+        "long wave radiative heating rate"
+    ],
+    [
+        "260642",
+        "lrgmrhbl",
+        "large scale moistening rate"
+    ],
+    [
+        "260643",
+        "tovg",
+        "type of vegetation"
+    ],
+    [
+        "228029",
+        "i10fg",
+        "instantaneous 10 metre wind gust"
+    ],
+    [
+        "228222",
+        "mxtpr3",
+        "maximum total precipitation rate in the last 3 hours"
+    ],
+    [
+        "228223",
+        "mntpr3",
+        "minimum total precipitation rate in the last 3 hours"
+    ],
+    [
+        "228224",
+        "mxtpr6",
+        "maximum total precipitation rate in the last 6 hours"
+    ],
+    [
+        "228225",
+        "mntpr6",
+        "minimum total precipitation rate in the last 6 hours"
+    ],
+    [
+        "228226",
+        "mxtpr",
+        "maximum total precipitation rate since previous post-processing"
+    ],
+    [
+        "228227",
+        "mntpr",
+        "minimum total precipitation rate since previous post-processing"
+    ],
+    [
+        "228218",
+        "crr",
+        "convective rain rate"
+    ],
+    [
+        "228219",
+        "lsrr",
+        "large scale rain rate"
+    ],
+    [
+        "228220",
+        "csfr",
+        "convective snowfall rate water equivalent"
+    ],
+    [
+        "228221",
+        "lssfr",
+        "large scale snowfall rate water equivalent"
+    ],
+    [
+        "228217",
+        "ilspf",
+        "instantaneous large-scale surface precipitation fraction"
+    ],
+    [
+        "228088",
+        "tcslw",
+        "total column supercooled liquid water"
+    ],
+    [
+        "140207",
+        "wss",
+        "wave spectral skewness"
+    ],
+    [
+        "228030",
+        "rhw",
+        "relative humidity with respect to water"
+    ],
+    [
+        "228031",
+        "rhi",
+        "relative humidity with respect to ice"
+    ],
+    [
+        "228033",
+        "fspc",
+        "fraction of stratiform precipitation cover"
+    ],
+    [
+        "228034",
+        "fcpc",
+        "fraction of convective precipitation cover"
+    ],
+    [
+        "230020",
+        "parcsvar",
+        "clear sky surface photosynthetically active radiation (variable resolution)"
+    ],
+    [
+        "230050",
+        "lspfvar",
+        "large-scale precipitation fraction (variable resolution)"
+    ],
+    [
+        "230213",
+        "vimdvar",
+        "vertically integrated moisture divergence (variable resolution)"
+    ],
+    [
+        "230239",
+        "csfvar",
+        "convective snowfall (variable resolution)"
+    ],
+    [
+        "230240",
+        "lsfvar",
+        "large-scale snowfall (variable resolution)"
+    ],
+    [
+        "230080",
+        "aco2neevar",
+        "accumulated carbon dioxide net ecosystem exchange (variable resolution)"
+    ],
+    [
+        "230081",
+        "aco2gppvar",
+        "accumulated carbon dioxide gross primary production (variable resolution)"
+    ],
+    [
+        "230082",
+        "aco2recvar",
+        "accumulated carbon dioxide ecosystem respiration (variable resolution)"
+    ],
+    [
+        "230129",
+        "ssrdcvar",
+        "surface solar radiation downward clear-sky (variable resolution)"
+    ],
+    [
+        "230130",
+        "strdcvar",
+        "surface thermal radiation downward clear-sky (variable resolution)"
+    ],
+    [
+        "230251",
+        "pevvar",
+        "potential evaporation (variable resolution)"
+    ],
+    [
+        "228216",
+        "fzra",
+        "accumulated freezing rain"
+    ],
+    [
+        "174097",
+        "sist",
+        "sea-ice snow thickness"
+    ],
+    [
+        "228078",
+        "gppbfas",
+        "gpp coefficient from biogenic flux adjustment system"
+    ],
+    [
+        "228079",
+        "recbfas",
+        "rec coefficient from biogenic flux adjustment system"
+    ],
+    [
+        "215180",
+        "aerext355",
+        "aerosol extinction coefficient at 355 nm"
+    ],
+    [
+        "215181",
+        "aerext532",
+        "aerosol extinction coefficient at 532 nm"
+    ],
+    [
+        "215182",
+        "aerext1064",
+        "aerosol extinction coefficient at 1064 nm"
+    ],
+    [
+        "215183",
+        "aerbackscattoa355",
+        "aerosol backscatter coefficient at 355 nm (from top of atmosphere)"
+    ],
+    [
+        "215184",
+        "aerbackscattoa532",
+        "aerosol backscatter coefficient at 532 nm (from top of atmosphere)"
+    ],
+    [
+        "215185",
+        "aerbackscattoa1064",
+        "aerosol backscatter coefficient at 1064 nm (from top of atmosphere)"
+    ],
+    [
+        "215186",
+        "aerbackscatgnd355",
+        "aerosol backscatter coefficient at 355 nm (from ground)"
+    ],
+    [
+        "215187",
+        "aerbackscatgnd532",
+        "aerosol backscatter coefficient at 532 nm (from ground)"
+    ],
+    [
+        "215188",
+        "aerbackscatgnd1064",
+        "aerosol backscatter coefficient at 1064 nm (from ground)"
+    ],
+    [
+        "230216",
+        "fzravar",
+        "accumulated freezing rain (variable resolution)"
+    ],
+    [
+        "260238",
+        "wz",
+        "geometric vertical velocity"
+    ],
+    [
+        "235001",
+        "mttswr",
+        "mean temperature tendency due to short-wave radiation"
+    ],
+    [
+        "235002",
+        "mttlwr",
+        "mean temperature tendency due to long-wave radiation"
+    ],
+    [
+        "235003",
+        "mttswrcs",
+        "mean temperature tendency due to short-wave radiation, clear sky"
+    ],
+    [
+        "235004",
+        "mttlwrcs",
+        "mean temperature tendency due to long-wave radiation, clear sky"
+    ],
+    [
+        "235005",
+        "mttpm",
+        "mean temperature tendency due to parametrisations"
+    ],
+    [
+        "235006",
+        "mqtpm",
+        "mean specific humidity tendency due to parametrisations"
+    ],
+    [
+        "235007",
+        "mutpm",
+        "mean eastward wind tendency due to parametrisations"
+    ],
+    [
+        "235008",
+        "mvtpm",
+        "mean northward wind tendency due to parametrisations"
+    ],
+    [
+        "235009",
+        "mumf",
+        "mean updraught mass flux"
+    ],
+    [
+        "235010",
+        "mdmf",
+        "mean downdraught mass flux"
+    ],
+    [
+        "235011",
+        "mudr",
+        "mean updraught detrainment rate"
+    ],
+    [
+        "235012",
+        "mddr",
+        "mean downdraught detrainment rate"
+    ],
+    [
+        "235013",
+        "mtpf",
+        "mean total precipitation flux"
+    ],
+    [
+        "235014",
+        "mtdch",
+        "mean turbulent diffusion coefficient for heat"
+    ],
+    [
+        "235020",
+        "msror",
+        "mean surface runoff rate"
+    ],
+    [
+        "235021",
+        "mssror",
+        "mean sub-surface runoff rate"
+    ],
+    [
+        "235022",
+        "msparfcs",
+        "mean surface photosynthetically active radiation flux, clear sky"
+    ],
+    [
+        "235023",
+        "mser",
+        "mean snow evaporation rate"
+    ],
+    [
+        "235024",
+        "msmr",
+        "mean snowmelt rate"
+    ],
+    [
+        "235025",
+        "mmtss",
+        "mean magnitude of turbulent surface stress"
+    ],
+    [
+        "235026",
+        "mlspf",
+        "mean large-scale precipitation fraction"
+    ],
+    [
+        "235027",
+        "msdwuvrf",
+        "mean surface downward uv radiation flux"
+    ],
+    [
+        "235028",
+        "msparf",
+        "mean surface photosynthetically active radiation flux"
+    ],
+    [
+        "235029",
+        "mlspr",
+        "mean large-scale precipitation rate"
+    ],
+    [
+        "235030",
+        "mcpr",
+        "mean convective precipitation rate"
+    ],
+    [
+        "235031",
+        "msr",
+        "mean snowfall rate"
+    ],
+    [
+        "235032",
+        "mbld",
+        "mean boundary layer dissipation"
+    ],
+    [
+        "235033",
+        "msshf",
+        "mean surface sensible heat flux"
+    ],
+    [
+        "235034",
+        "mslhf",
+        "mean surface latent heat flux"
+    ],
+    [
+        "235035",
+        "msdwswrf",
+        "mean surface downward short-wave radiation flux"
+    ],
+    [
+        "235036",
+        "msdwlwrf",
+        "mean surface downward long-wave radiation flux"
+    ],
+    [
+        "235037",
+        "msnswrf",
+        "mean surface net short-wave radiation flux"
+    ],
+    [
+        "235038",
+        "msnlwrf",
+        "mean surface net long-wave radiation flux"
+    ],
+    [
+        "235039",
+        "mtnswrf",
+        "mean top net short-wave radiation flux"
+    ],
+    [
+        "235040",
+        "mtnlwrf",
+        "mean top net long-wave radiation flux"
+    ],
+    [
+        "235041",
+        "metss",
+        "mean eastward turbulent surface stress"
+    ],
+    [
+        "235042",
+        "mntss",
+        "mean northward turbulent surface stress"
+    ],
+    [
+        "235043",
+        "mer",
+        "mean evaporation rate"
+    ],
+    [
+        "235044",
+        "sdf",
+        "sunshine duration fraction"
+    ],
+    [
+        "235045",
+        "megwss",
+        "mean eastward gravity wave surface stress"
+    ],
+    [
+        "235046",
+        "mngwss",
+        "mean northward gravity wave surface stress"
+    ],
+    [
+        "235047",
+        "mgwd",
+        "mean gravity wave dissipation"
+    ],
+    [
+        "235048",
+        "mror",
+        "mean runoff rate"
+    ],
+    [
+        "235049",
+        "mtnswrfcs",
+        "mean top net short-wave radiation flux, clear sky"
+    ],
+    [
+        "235050",
+        "mtnlwrfcs",
+        "mean top net long-wave radiation flux, clear sky"
+    ],
+    [
+        "235051",
+        "msnswrfcs",
+        "mean surface net short-wave radiation flux, clear sky"
+    ],
+    [
+        "235052",
+        "msnlwrfcs",
+        "mean surface net long-wave radiation flux, clear sky"
+    ],
+    [
+        "235053",
+        "mtdwswrf",
+        "mean top downward short-wave radiation flux"
+    ],
+    [
+        "235054",
+        "mvimd",
+        "mean vertically integrated moisture divergence"
+    ],
+    [
+        "235055",
+        "mtpr",
+        "mean total precipitation rate"
+    ],
+    [
+        "235056",
+        "mcsr",
+        "mean convective snowfall rate"
+    ],
+    [
+        "235057",
+        "mlssr",
+        "mean large-scale snowfall rate"
+    ],
+    [
+        "235058",
+        "msdrswrf",
+        "mean surface direct short-wave radiation flux"
+    ],
+    [
+        "235059",
+        "msdrswrfcs",
+        "mean surface direct short-wave radiation flux, clear sky"
+    ],
+    [
+        "235060",
+        "msdfswrf",
+        "mean surface diffuse short-wave radiation flux"
+    ],
+    [
+        "235061",
+        "msdfswrfcs",
+        "mean surface diffuse short-wave radiation flux, clear sky"
+    ],
+    [
+        "235062",
+        "mcdneef",
+        "mean carbon dioxide net ecosystem exchange flux"
+    ],
+    [
+        "235063",
+        "mcdgppf",
+        "mean carbon dioxide gross primary production flux"
+    ],
+    [
+        "235064",
+        "mcderf",
+        "mean carbon dioxide ecosystem respiration flux"
+    ],
+    [
+        "235065",
+        "mrr",
+        "mean rain rate"
+    ],
+    [
+        "235066",
+        "mcrr",
+        "mean convective rain rate"
+    ],
+    [
+        "235067",
+        "mlsrr",
+        "mean large-scale rain rate"
+    ],
+    [
+        "228044",
+        "capes",
+        "convective available potential energy shear"
+    ],
+    [
+        "132059",
+        "capei",
+        "convective available potential energy index"
+    ],
+    [
+        "132044",
+        "capesi",
+        "convective available potential energy shear index"
+    ],
+    [
+        "140120",
+        "sh10",
+        "significant wave height of all waves with period larger than 10s"
+    ],
+    [
+        "228046",
+        "hcct",
+        "height of convective cloud top"
+    ],
+    [
+        "228047",
+        "hwbt0",
+        "height of zero deg wet bulb temperature"
+    ],
+    [
+        "228048",
+        "hwbt1",
+        "height of one deg wet bulb temperature"
+    ],
+    [
+        "228050",
+        "litoti",
+        "instantaneous total lightning density"
+    ],
+    [
+        "228051",
+        "litota",
+        "averaged total lightning density"
+    ],
+    [
+        "228052",
+        "licgi",
+        "instantaneous cloud-to-ground lightning density"
+    ],
+    [
+        "228053",
+        "licga",
+        "averaged cloud-to-ground lightning density"
+    ],
+    [
+        "235068",
+        "msdwswrfcs",
+        "mean surface downward short-wave radiation flux, clear sky"
+    ],
+    [
+        "235069",
+        "msdwlwrfcs",
+        "mean surface downward long-wave radiation flux, clear sky"
+    ],
+    [
+        "235070",
+        "mper",
+        "mean potential evaporation rate"
+    ],
+    [
+        "7001292",
+        "sunsd",
+        "sunshine duration"
+    ],
+    [
+        "7001353",
+        "vrate",
+        "ventilation rate"
+    ],
+    [
+        "240028",
+        "gwus",
+        "groundwater upper storage"
+    ],
+    [
+        "240029",
+        "gwls",
+        "groundwater lower storage"
+    ],
+    [
+        "228054",
+        "ucq",
+        "unbalanced component of specific humidity"
+    ],
+    [
+        "228055",
+        "ucclwc",
+        "unbalanced component of specific cloud liquid water content"
+    ],
+    [
+        "228056",
+        "ucciwc",
+        "unbalanced component of specific cloud ice water content"
+    ],
+    [
+        "230047",
+        "dsrpvar",
+        "direct solar radiation (variable resolution)"
+    ],
+    [
+        "260259",
+        "eva",
+        "evaporation"
+    ],
+    [
+        "260260",
+        "10wdir",
+        "10 metre wind direction"
+    ],
+    [
+        "260360",
+        "sot",
+        "soil temperature"
+    ],
+    [
+        "7001293",
+        "icsev",
+        "icing severity"
+    ],
+    [
+        "140113",
+        "wefxd",
+        "wave energy flux mean direction"
+    ],
+    [
+        "140112",
+        "wefxm",
+        "wave energy flux magnitude"
+    ],
+    [
+        "260262",
+        "dirswrf",
+        "direct short wave radiation flux"
+    ],
+    [
+        "260263",
+        "difswrf",
+        "diffuse short wave radiation flux"
+    ],
+    [
+        "260264",
+        "tidirswrf",
+        "time-integrated surface direct short wave radiation flux"
+    ],
+    [
+        "260361",
+        "dswrf_cs",
+        "downward short-wave radiation flux, clear sky"
+    ],
+    [
+        "260362",
+        "uswrf_cs",
+        "upward short-wave radiation flux, clear sky"
+    ],
+    [
+        "260363",
+        "dlwrf_cs",
+        "downward long-wave radiation flux, clear sky"
+    ],
+    [
+        "260364",
+        "sohf",
+        "soil heat flux"
+    ],
+    [
+        "260365",
+        "percr",
+        "percolation rate"
+    ],
+    [
+        "260367",
+        "sod",
+        "soil depth"
+    ],
+    [
+        "260423",
+        "adswrf_cs",
+        "accumulated surface downward short-wave radiation flux, clear sky"
+    ],
+    [
+        "260427",
+        "auswrf_cs",
+        "accumulated surface upward short-wave radiation flux, clear sky"
+    ],
+    [
+        "260428",
+        "adlwrf_cs",
+        "accumulated surface downward long-wave radiation flux, clear sky"
+    ],
+    [
+        "260430",
+        "perc",
+        "percolation"
+    ],
+    [
+        "174096",
+        "2m specific humidity"
+    ],
+    [
+        "140114",
+        "h1012",
+        "significant wave height of all waves with periods within the inclusive range from 10 to 12 seconds"
+    ],
+    [
+        "140115",
+        "h1214",
+        "significant wave height of all waves with periods within the inclusive range from 12 to 14 seconds"
+    ],
+    [
+        "140116",
+        "h1417",
+        "significant wave height of all waves with periods within the inclusive range from 14 to 17 seconds"
+    ],
+    [
+        "140117",
+        "h1721",
+        "significant wave height of all waves with periods within the inclusive range from 17 to 21 seconds"
+    ],
+    [
+        "140118",
+        "h2125",
+        "significant wave height of all waves with periods within the inclusive range from 21 to 25 seconds"
+    ],
+    [
+        "140119",
+        "h2530",
+        "significant wave height of all waves with periods within the inclusive range from 25 to 30 seconds"
+    ],
+    [
+        "213101",
+        "spp1",
+        "random pattern 1 for spp scheme"
+    ],
+    [
+        "213102",
+        "spp2",
+        "random pattern 2 for spp scheme"
+    ],
+    [
+        "213103",
+        "spp3",
+        "random pattern 3 for spp scheme"
+    ],
+    [
+        "213104",
+        "spp4",
+        "random pattern 4 for spp scheme"
+    ],
+    [
+        "213105",
+        "spp5",
+        "random pattern 5 for spp scheme"
+    ],
+    [
+        "213106",
+        "spp6",
+        "random pattern 6 for spp scheme"
+    ],
+    [
+        "213107",
+        "spp7",
+        "random pattern 7 for spp scheme"
+    ],
+    [
+        "213108",
+        "spp8",
+        "random pattern 8 for spp scheme"
+    ],
+    [
+        "213109",
+        "spp9",
+        "random pattern 9 for spp scheme"
+    ],
+    [
+        "213110",
+        "spp10",
+        "random pattern 10 for spp scheme"
+    ],
+    [
+        "213111",
+        "spp11",
+        "random pattern 11 for spp scheme"
+    ],
+    [
+        "213112",
+        "spp12",
+        "random pattern 12 for spp scheme"
+    ],
+    [
+        "213113",
+        "spp13",
+        "random pattern 13 for spp scheme"
+    ],
+    [
+        "213114",
+        "spp14",
+        "random pattern 14 for spp scheme"
+    ],
+    [
+        "213115",
+        "spp15",
+        "random pattern 15 for spp scheme"
+    ],
+    [
+        "213116",
+        "spp16",
+        "random pattern 16 for spp scheme"
+    ],
+    [
+        "213117",
+        "spp17",
+        "random pattern 17 for spp scheme"
+    ],
+    [
+        "213118",
+        "spp18",
+        "random pattern 18 for spp scheme"
+    ],
+    [
+        "213119",
+        "spp19",
+        "random pattern 19 for spp scheme"
+    ],
+    [
+        "213120",
+        "spp20",
+        "random pattern 20 for spp scheme"
+    ],
+    [
+        "213121",
+        "spp21",
+        "random pattern 21 for spp scheme"
+    ],
+    [
+        "213122",
+        "spp22",
+        "random pattern 22 for spp scheme"
+    ],
+    [
+        "213123",
+        "spp23",
+        "random pattern 23 for spp scheme"
+    ],
+    [
+        "213124",
+        "spp24",
+        "random pattern 24 for spp scheme"
+    ],
+    [
+        "213125",
+        "spp25",
+        "random pattern 25 for spp scheme"
+    ],
+    [
+        "213126",
+        "spp26",
+        "random pattern 26 for spp scheme"
+    ],
+    [
+        "213127",
+        "spp27",
+        "random pattern 27 for spp scheme"
+    ],
+    [
+        "213128",
+        "spp28",
+        "random pattern 28 for spp scheme"
+    ],
+    [
+        "213129",
+        "spp29",
+        "random pattern 29 for spp scheme"
+    ],
+    [
+        "213130",
+        "spp30",
+        "random pattern 30 for spp scheme"
+    ],
+    [
+        "213131",
+        "spp31",
+        "random pattern 31 for spp scheme"
+    ],
+    [
+        "213132",
+        "spp32",
+        "random pattern 32 for spp scheme"
+    ],
+    [
+        "213133",
+        "spp33",
+        "random pattern 33 for spp scheme"
+    ],
+    [
+        "213134",
+        "spp34",
+        "random pattern 34 for spp scheme"
+    ],
+    [
+        "213135",
+        "spp35",
+        "random pattern 35 for spp scheme"
+    ],
+    [
+        "213136",
+        "spp36",
+        "random pattern 36 for spp scheme"
+    ],
+    [
+        "213137",
+        "spp37",
+        "random pattern 37 for spp scheme"
+    ],
+    [
+        "213138",
+        "spp38",
+        "random pattern 38 for spp scheme"
+    ],
+    [
+        "213139",
+        "spp39",
+        "random pattern 39 for spp scheme"
+    ],
+    [
+        "213140",
+        "spp40",
+        "random pattern 40 for spp scheme"
+    ],
+    [
+        "213141",
+        "spp41",
+        "random pattern 41 for spp scheme"
+    ],
+    [
+        "213142",
+        "spp42",
+        "random pattern 42 for spp scheme"
+    ],
+    [
+        "213143",
+        "spp43",
+        "random pattern 43 for spp scheme"
+    ],
+    [
+        "213144",
+        "spp44",
+        "random pattern 44 for spp scheme"
+    ],
+    [
+        "213145",
+        "spp45",
+        "random pattern 45 for spp scheme"
+    ],
+    [
+        "213146",
+        "spp46",
+        "random pattern 46 for spp scheme"
+    ],
+    [
+        "213147",
+        "spp47",
+        "random pattern 47 for spp scheme"
+    ],
+    [
+        "213148",
+        "spp48",
+        "random pattern 48 for spp scheme"
+    ],
+    [
+        "213149",
+        "spp49",
+        "random pattern 49 for spp scheme"
+    ],
+    [
+        "213150",
+        "spp50",
+        "random pattern 50 for spp scheme"
+    ],
+    [
+        "999",
+        "tc",
+        "tropical cyclone"
+    ],
+    [
+        "928",
+        "thk",
+        "thicknes"
+    ],
+    [
+        "956",
+        "thka",
+        "thicknes anomaly"
+    ]
+]
diff --git a/metkit/metkit.sublime-project b/metkit/metkit.sublime-project
new file mode 100644
index 0000000..977875e
--- /dev/null
+++ b/metkit/metkit.sublime-project
@@ -0,0 +1,28 @@
+{
+    "folders": [
+        {
+            "file_exclude_patterns": [".tags", ".tags_sorted_by_file", ".gemtags","CMakeLists.txt.user*"],
+            "path": ".",
+            "follow_symlinks": true
+        }
+    ],
+    "build_systems": [
+        {
+            "working_dir": "${project_path}/../../build/metkit",
+            "cmd": [
+                "make"
+            ],
+            "file_regex": "([/\\w\\-\\.]+):(\\d+):(\\d+:)?",
+            "name": "ecbuild"
+        }
+    ],
+    "SublimeLinter":
+    {
+        "linters":
+        {
+            "cpplint": {
+                "filter": "-whitespace/line_length,-whitespace/blank_line,-runtime/references"
+            },
+        }
+    }
+}
diff --git a/metkit/src/CMakeLists.txt b/metkit/src/CMakeLists.txt
new file mode 100644
index 0000000..72a004a
--- /dev/null
+++ b/metkit/src/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory( metkit )
+add_subdirectory( tools )
+add_subdirectory( tests )
diff --git a/metkit/src/metkit/BaseProtocol.cc b/metkit/src/metkit/BaseProtocol.cc
new file mode 100644
index 0000000..a97ec83
--- /dev/null
+++ b/metkit/src/metkit/BaseProtocol.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BaseProtocol.cc
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+
+#include "metkit/BaseProtocol.h"
+
+namespace metkit {
+
+BaseProtocol::BaseProtocol()
+{
+}
+
+BaseProtocol::~BaseProtocol()
+{
+}
+
+
+void BaseProtocol::print(std::ostream&) const
+{
+}
+
+}
diff --git a/metkit/src/metkit/BaseProtocol.h b/metkit/src/metkit/BaseProtocol.h
new file mode 100644
index 0000000..bbff028
--- /dev/null
+++ b/metkit/src/metkit/BaseProtocol.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BaseProtocol.h
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef BaseProtocol_H
+#define BaseProtocol_H
+
+#include "eckit/eckit.h"
+
+#include "eckit/io/Length.h"
+
+namespace metkit {
+
+class MarsRequest;
+
+class BaseProtocol {
+public:
+	BaseProtocol();
+	virtual ~BaseProtocol();
+
+    virtual eckit::Length retrieve(const MarsRequest&) = 0;
+    virtual void archive(const MarsRequest&, const eckit::Length&) = 0;
+
+    virtual long read(void* buffer, long len) = 0;
+    virtual long write(const void* buffer, long len) = 0;
+    virtual void cleanup() = 0;
+
+protected:
+	virtual void print(std::ostream&) const = 0;
+
+private:
+// No copy allowed
+	BaseProtocol(const BaseProtocol&);
+	BaseProtocol& operator=(const BaseProtocol&);
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const BaseProtocol& p)
+		{ p.print(s); return s; }
+
+};
+
+}
+
+#endif
diff --git a/metkit/src/metkit/CMakeLists.txt b/metkit/src/metkit/CMakeLists.txt
new file mode 100644
index 0000000..ab7529a
--- /dev/null
+++ b/metkit/src/metkit/CMakeLists.txt
@@ -0,0 +1,121 @@
+### config headers
+
+ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/metkit )
+
+configure_file( metkit_config.h.in    metkit_config.h )
+configure_file( metkit_version.h.in   metkit_version.h )
+configure_file( metkit_version.cc.in  metkit_version.cc )
+
+install(FILES
+            ${CMAKE_CURRENT_BINARY_DIR}/metkit_config.h
+            ${CMAKE_CURRENT_BINARY_DIR}/metkit_version.h
+        DESTINATION
+            ${INSTALL_INCLUDE_DIR}/metkit )
+
+### metkit sources
+
+list( APPEND metkit_srcs
+metkit_version.cc
+BaseProtocol.cc
+BaseProtocol.h
+ClientTask.cc
+ClientTask.h
+DHSProtocol.cc
+DHSProtocol.h
+MarsHandle.cc
+MarsHandle.h
+MarsLocation.cc
+MarsLocation.h
+MarsRequest.cc
+MarsRequest.h
+MarsExpension.cc
+MarsExpension.h
+MarsLanguage.cc
+MarsLanguage.h
+MarsParser.cc
+MarsParser.h
+MarsRequestHandle.cc
+MarsRequestHandle.h
+RequestEnvironment.cc
+RequestEnvironment.h
+config/LibMetkit.cc
+config/LibMetkit.h
+types/Type.cc
+types/Type.h
+types/TypeDate.cc
+types/TypeDate.h
+types/TypeAny.cc
+types/TypeAny.h
+types/TypeExpver.cc
+types/TypeExpver.h
+types/TypeToByList.cc
+types/TypeToByList.h
+types/TypeEnum.cc
+types/TypeEnum.h
+types/TypeParam.cc
+types/TypeParam.h
+types/TypeInteger.cc
+types/TypeInteger.h
+types/TypeRange.cc
+types/TypeRange.h
+types/TypeFloat.cc
+types/TypeFloat.h
+types/TypeTime.cc
+types/TypeTime.h
+types/TypeMixed.cc
+types/TypeMixed.h
+types/TypesFactory.cc
+types/TypesFactory.h
+)
+
+if ( HAVE_GRIB )
+
+    list( APPEND metkit_srcs
+        grib/GribToRequest.cc
+        grib/GribToRequest.h
+        grib/EmosFile.h
+        grib/EmosFile.cc
+        grib/GribFile.h
+        grib/GribFile.cc
+        grib/GribAccessor.h
+        grib/GribAccessor.cc
+        grib/GribMutator.h
+        grib/GribMutator.cc
+        grib/GribIndex.h
+        grib/GribIndex.cc
+        grib/GribHandle.h
+        grib/GribHandle.cc
+        grib/GribDataBlob.h
+        grib/GribDataBlob.cc
+        grib/GribMetaData.h
+        grib/GribMetaData.cc
+        )
+
+        set( grib_handling_pkg grib_api )
+        if( HAVE_ECCODES )
+          set( grib_handling_pkg eccodes )
+        endif()
+
+endif ()
+
+ecbuild_add_library(
+
+    TARGET metkit
+
+    INSTALL_HEADERS LISTED
+
+    HEADER_DESTINATION
+        ${INSTALL_INCLUDE_DIR}/metkit
+
+    GENERATED
+        metkit_version.cc
+
+    SOURCES
+        ${metkit_srcs}
+
+    PRIVATE_INCLUDES
+        ${ECKIT_INCLUDE_DIRS}
+        ${GRIB_API_INCLUDE_DIRS}
+
+    LIBS
+        eckit ${grib_handling_pkg} )
diff --git a/metkit/src/metkit/ClientTask.cc b/metkit/src/metkit/ClientTask.cc
new file mode 100644
index 0000000..5283607
--- /dev/null
+++ b/metkit/src/metkit/ClientTask.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include "metkit/ClientTask.h"
+#include "metkit/MarsHandle.h"
+
+namespace metkit {
+
+// Call by the clien code
+ClientTask::ClientTask(const MarsRequest &r, const MarsRequest &e, const std::string &host, int port)
+    : request_(r),
+      environ_(e),
+      port_(port),
+      host_(host),
+      handle_(0) {
+    // Try something unique (per machine)
+    typedef unsigned long long ull;
+    metkitID_ = (ull(::getpid()) << 32 )
+               | (ull(::pthread_self()) << 16)
+               | (ull(::time(0)) & ull(0xffff));
+
+    handle_   = std::auto_ptr<eckit::DataHandle>(new MarsHandle(host_, port_, metkitID_));
+}
+
+
+ClientTask::~ClientTask() {}
+
+void ClientTask::send(eckit::Stream &s) const
+{
+    unsigned long long dummy = 0;
+    s.startObject();
+    s << "MarsTask";
+
+    /* send mars request id */
+    s << dummy;
+
+    /* Send requests */
+    s << request_;
+    s << environ_;
+
+    /* Send cb info */
+    s << host_;
+    s << port_;
+    s << metkitID_;
+
+    /* Send datahandle */
+
+    s << *handle_;
+
+    s.endObject();
+}
+
+
+char ClientTask::receive(eckit::Stream &s) const
+{
+    unsigned long long id;
+    char mode;
+
+    s >> id; ASSERT(id == metkitID_);
+    s >> mode;
+
+    return mode;
+}
+
+}
diff --git a/metkit/src/metkit/ClientTask.h b/metkit/src/metkit/ClientTask.h
new file mode 100644
index 0000000..10b5796
--- /dev/null
+++ b/metkit/src/metkit/ClientTask.h
@@ -0,0 +1,202 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ClientTask.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef ClientTask_H
+#define ClientTask_H
+
+// #include "eckit/bases/Watcher.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/DataHandle.h"
+// #include "eckit/log/UserChannel.h"
+#include "eckit/transaction/TxnEvent.h"
+
+#include "metkit/MarsRequest.h"
+
+namespace metkit {
+
+class ClientTask {
+public:
+
+
+// -- Contructors
+
+	ClientTask(const MarsRequest&,const MarsRequest&, const std::string& name, int port);
+	// ClientTask(eckit::Stream&);
+
+// -- Destructor
+
+	~ClientTask();
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	// void dump(std::ostream&) const;
+	// void json(eckit::JSON&) const;
+
+	// MarsInfo info() const;
+	// const MarsID&  id() const { return transactionID();  }
+	// const MarsRequest& request()   const { return request_; }
+	// const MarsRequest& environ()   const { return environ_; }
+
+	// eckit::Length transferData(const eckit::PathName&);
+	// eckit::PathName transferData();
+
+ //    /// @returns eckit::DataHandle with the data that will be received, but does not give ownership of the eckit::DataHandle
+ //    eckit::DataHandle& getDataHandle();
+
+	// void sendData(eckit::DataHandle&);
+	// void sendHandle(eckit::DataHandle&);
+
+	// void acknowledge(eckit::Stream& s)     { send(s,'a'); }
+	// void patch();
+ //    bool authenticated() const;
+
+	// eckit::DataHandle& dataHandle() { return *handle_; }
+
+	// // Report
+
+	// void success();
+	// void failure(const std::string&);
+	// void retry(const std::string&);
+
+	// void infoMsg(const std::string&);
+	// void warningMsg(const std::string&);
+	// void errorMsg(const std::string&);
+	// void notifyClient(const std::string&);
+	// void notifyStart();
+
+	// void   queueTime();
+
+	// void ping();
+	// void sendCost();
+
+	// void sendChecksum(const std::string&);
+
+	// // Queuing
+
+	// double startingPriority()  const  { return startingPriority_;}
+	// void   startingPriority(double p) { startingPriority_ = p;}
+
+	// // Cost
+
+	// Cost& cost()             { return cost_; }
+	// const Cost& cost() const { return cost_; }
+	// void  costChanged();
+
+
+ //    // Called by PipeProcess
+
+ //    void           send(eckit::Stream&);
+ //    void           reply(eckit::Stream&);
+	// bool           error(std::exception&,int);
+	// void           done();
+
+	// Logging
+
+
+	// Mars tree
+
+	// void push(const std::string&,const std::string&);
+	// void pop();
+	// void reset();
+
+	// const eckit::StringList& treeNames() const { return treeNames_; }
+	// const eckit::StringList& treeValues() const { return treeValues_; }
+
+ //    // For the metkit
+    void send(eckit::Stream&) const;
+	char receive(eckit::Stream&) const;
+
+
+// -- Overridden methods
+
+    // From Streamble
+
+    // virtual void encode(eckit::Stream&) const;
+    // virtual const eckit::ReanimatorBase& reanimator() const { return reanimator_; }
+
+	// From watcher
+
+	// virtual void watch();
+
+// -- Class methods
+
+	// static const eckit::ClassSpec&  classSpec()        { return classSpec_;}
+	// static void  recover(TxnRecoverer<ClientTask>&);
+	// static void  find(TxnFinder<ClientTask>&);
+	// static std::string commandName();
+
+	// None
+
+protected:
+
+// -- Members
+
+	MarsRequest          request_;
+	MarsRequest          environ_;
+
+private:
+
+// -- Members
+
+    unsigned long long   txnID_;
+	unsigned long long   metkitID_;
+	int                  port_;
+	std::string               host_;
+	std::auto_ptr<eckit::DataHandle> handle_;
+	std::string               checksum_;
+
+	// Not sent over streams
+
+	// double               startingPriority_;
+	// Cost                 cost_;
+	// time_t               lastPing_;
+	// time_t               queueTime_;
+
+	// eckit::StringList			treeValues_;
+	// eckit::StringList			treeNames_;
+
+ //    bool                authenticated_;
+
+// -- Methods
+
+	// void print(std::ostream&) const;
+	// void send(eckit::Stream&,char) const;
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+
+    // static eckit::ClassSpec               classSpec_;
+    // static eckit::Reanimator<ClientTask>    reanimator_;
+
+// -- Class methods
+	// None
+
+    // friend std::ostream& operator<<(std::ostream& s, const ClientTask& r)
+    //     { r.print(s); return s; }
+
+    // friend eckit::JSON& operator<<(eckit::JSON& s, const ClientTask& r)
+    //     { r.json(s); return s; }
+
+};
+
+}
+
+#endif
diff --git a/metkit/src/metkit/DHSProtocol.cc b/metkit/src/metkit/DHSProtocol.cc
new file mode 100644
index 0000000..9f18307
--- /dev/null
+++ b/metkit/src/metkit/DHSProtocol.cc
@@ -0,0 +1,274 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DHSProtocol.cc
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#include "metkit/DHSProtocol.h"
+#include "metkit/RequestEnvironment.h"
+
+#include "eckit/net/TCPClient.h"
+#include "eckit/net/TCPStream.h"
+#include "metkit/ClientTask.h"
+
+namespace metkit {
+
+DHSProtocol::DHSProtocol(const std::string& name,const std::string& host, int port, bool forwardMessages )
+: name_(name),
+  host_(host),
+  port_(port),
+  done_(false),
+  error_(false),
+  sending_(false),
+  foreward_(forwardMessages)
+{}
+
+DHSProtocol::~DHSProtocol()
+{
+    done_ = true;
+    cleanup();
+}
+
+eckit::Length DHSProtocol::retrieve(const MarsRequest& request)
+{
+    std::string host = callback_.localHost();
+    int    port = callback_.localPort();
+
+    eckit::Log::info() << "DHSProtocol: call back on " << host << ":" << port << std::endl;
+
+    task_ = std::auto_ptr<ClientTask>(new ClientTask(request, RequestEnvironment::instance().request(), host, port));
+
+    eckit::TCPStream s(eckit::TCPClient().connect(host_, port_));
+
+    task_->send(s);
+
+    ASSERT(task_->receive(s) == 'a'); // Acknoledgement
+
+    eckit::Length result = 0;
+    while(wait(result)) {
+        ;
+    }
+
+    eckit::Log::info() << "DHSProtocol::retrieve " << result << std::endl;
+    return result;
+}
+
+void DHSProtocol::archive(const MarsRequest& request, const eckit::Length& size)
+{
+    std::string host = callback_.localHost();
+    int    port = callback_.localPort();
+
+    eckit::Log::info() << "DHSProtocol::archive " << size << std::endl;
+    eckit::Log::info() << "DHSProtocol: call back on " << host << ":" << port << std::endl;
+
+    task_ = std::auto_ptr<ClientTask>(new ClientTask(request, RequestEnvironment::instance().request(), host, port));
+
+    eckit::TCPStream s(eckit::TCPClient().connect(host_, port_));
+
+    task_->send(s);
+
+    eckit::Log::info() << "DHSProtocol: task sent." << std::endl;
+
+    ASSERT(task_->receive(s) == 'a'); // Acknoledgement
+
+    eckit::Length result = size;
+    while(wait(result)) {
+        ;
+    }
+    eckit::Log::info() << "DHSProtocol: archive completed." << std::endl;
+}
+
+void DHSProtocol::cleanup()
+{
+    if(socket_.isConnected())
+    {
+        if( sending_ )
+        {
+            unsigned long version = 1;
+            unsigned long long crc = 0;
+
+            try {
+                eckit::InstantTCPStream s(socket_);
+                s << version;
+                s << crc;
+            }
+            catch (std::exception& e)
+            {
+                eckit::Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl;
+                eckit::Log::error() << "** Exception is ignored" << std::endl;
+            }
+        }
+        socket_.close();
+    }
+
+    sending_ = false;
+
+    if(!done_) {
+        eckit::Length result = 0;
+            while(wait(result)) {
+            ;
+        }
+    }
+
+    if(error_)
+    {
+        error_ = false;
+        throw eckit::UserError(std::string("Error from [") + name_ + "]: "  + msg_);
+    }
+}
+
+void DHSProtocol::print(std::ostream& s) const
+{
+    s << "DHSProtocol[" << name_ << "]";
+}
+
+long DHSProtocol::read(void* buffer, long len)
+{
+    return socket_.read(buffer,len);
+}
+
+long DHSProtocol::write(const void* buffer, long len)
+{
+    return socket_.write(buffer,len);
+}
+
+bool DHSProtocol::wait(eckit::Length& size)
+{
+    for(;;) {
+
+        socket_ = callback_.accept();
+
+        eckit::InstantTCPStream s(socket_);
+
+        char code = task_->receive(s);
+
+        eckit::Log::debug() << "DHSProtocol: code [" << code << "]" << std::endl;
+
+        std::string msg;
+        long long bytes;
+
+        switch(code)
+        {
+            /* OK */
+            case 'o':
+                done_ = true;
+                return false;
+                break;
+
+                /* read source */
+            case 'r':
+                bytes = size;
+                eckit::Log::debug() << "DHSProtocol:r [" << bytes << "]" << std::endl;
+                s << bytes;
+                sending_ = true;
+                return false;
+                break;
+
+                /* get */
+            case 'h':
+                NOTIMP;
+                break;
+
+            case 'w':
+                s >> bytes;
+                eckit::Log::debug() << "DHSProtocol:w " << bytes << std::endl;
+                size = bytes;
+                return false;
+                break;
+
+            case 'm':
+                NOTIMP;
+                break;
+
+            case 'X':
+                NOTIMP;
+                break;
+
+            case 'e':
+                s >> msg_;
+                eckit::Log::error() << msg_ << " [" << name_ << "]" << std::endl;
+                error_ = true;
+                done_ = true;
+                return false;
+                break;
+
+            case 'y':    /* retry */
+                NOTIMP;
+                break;
+
+            case 'I': /* info */
+                s >> msg;
+                eckit::Log::info() << msg << " [" << name_ << "]" << std::endl;
+				if(foreward_) {
+					eckit::Log::userInfo() << msg << " [" << name_ << "]" << std::endl;
+				}
+                break;
+
+            case 'W': /* warning */
+                s >> msg;
+                eckit::Log::warning() << msg << " [" << name_ << "]" << std::endl;
+				if(foreward_) {
+					eckit::Log::userWarning() << msg << " [" << name_ << "]" << std::endl;
+				}
+                break;
+
+            case 'D': /* debug */
+                s >> msg;
+                eckit::Log::debug() << msg << " [" << name_ << "]" << std::endl;
+				if(foreward_) {
+					eckit::Log::userInfo() << msg << " [" << name_ << "]" << std::endl;
+				}
+                break;
+
+            case 'E': /* error */
+                s >> msg;
+                eckit::Log::error() << msg << " [" << name_ << "]" << std::endl;
+				if(foreward_) {
+					eckit::Log::userError() << msg << " [" << name_ << "]" << std::endl;
+				}
+                break;
+
+            case 'N': /* notification */
+                NOTIMP;
+                break;
+
+            case 'p': /* ping */
+                s << 'p';
+                break;
+
+            case 's': /* statistics */
+                {
+                    int n;
+                    s >> n;
+                    std::string key, value;
+                    for(int i = 0; i < n; i++)
+                    {
+                        s >> key >> value;
+                        eckit::Log::info() << "DHSProtocol:s " << key << "=" << value << std::endl;
+                    }
+                }
+                break;
+
+            case 'S': /* notification start */
+                NOTIMP;
+                break;
+
+            case 't': /* new timeout */
+                NOTIMP;
+                break;
+
+            default:
+                throw eckit::Exception(std::string("Unknown code [") + code + "]");
+                break;
+        }
+    }
+}
+
+}
diff --git a/metkit/src/metkit/DHSProtocol.h b/metkit/src/metkit/DHSProtocol.h
new file mode 100644
index 0000000..d804aba
--- /dev/null
+++ b/metkit/src/metkit/DHSProtocol.h
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File DHSProtocol.h
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef DHSProtocol_H
+#define DHSProtocol_H
+
+#include "metkit/BaseProtocol.h"
+#include "eckit/net/TCPServer.h"
+#include "eckit/net/TCPSocket.h"
+#include "metkit/MarsRequest.h"
+#include "metkit/ClientTask.h"
+
+namespace metkit {
+
+class DHSProtocol : public BaseProtocol {
+public:
+	DHSProtocol(const std::string& name, const std::string& host, int port, bool forewardMessages = false);
+	~DHSProtocol();
+
+private:
+// No copy allowed
+	DHSProtocol(const DHSProtocol&);
+	DHSProtocol& operator=(const DHSProtocol&);
+
+// -- Members
+    eckit::TCPServer   callback_;
+    eckit::TCPSocket   socket_;
+    std::string      name_;
+    std::string      host_;
+    int         port_;
+    std::string      msg_;
+    bool        done_;
+    bool        error_;
+    bool        sending_;
+    std::auto_ptr<ClientTask> task_;
+    bool        foreward_;
+
+// -- Methods
+    bool wait(eckit::Length&);
+
+// -- Overridden methods
+	// From BaseProtocol
+    eckit::Length retrieve(const MarsRequest& request);
+    void archive(const MarsRequest& request, const eckit::Length&);
+    long read(void* buffer, long len);
+    long write(const void* buffer, long len);
+    void cleanup();
+	void print(std::ostream&) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const DHSProtocol& p)
+	//	{ p.print(s); return s; }
+};
+
+}
+
+#endif
diff --git a/metkit/src/metkit/MarsExpension.cc b/metkit/src/metkit/MarsExpension.cc
new file mode 100644
index 0000000..60012ff
--- /dev/null
+++ b/metkit/src/metkit/MarsExpension.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <set>
+#include <list>
+
+#include "eckit/types/Types.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/utils/MD5.h"
+#include "eckit/parser/StringTools.h"
+
+#include "metkit/MarsExpension.h"
+#include "metkit/types/Type.h"
+#include "metkit/MarsLanguage.h"
+
+using namespace eckit;
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+FlattenCallback::~FlattenCallback() {}
+
+ExpandCallback::~ExpandCallback() {}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MarsExpension::MarsExpension(bool inherit):
+    inherit_(inherit) {
+
+}
+
+MarsExpension::~MarsExpension() {
+    for (std::map<std::string, MarsLanguage* >::iterator j = languages_.begin(); j != languages_.end(); ++j) {
+        delete (*j).second;
+    }
+}
+
+void MarsExpension::reset() {
+    for (std::map<std::string, MarsLanguage* >::iterator j = languages_.begin(); j != languages_.end(); ++j) {
+        (*j).second->reset();
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+MarsLanguage& MarsExpension::language(const std::string& verb) {
+
+    std::string v = MarsLanguage::expandVerb(verb);
+
+    std::map<std::string, MarsLanguage*>::iterator j = languages_.find(v);
+    if (j == languages_.end()) {
+        languages_.insert(std::make_pair(v, new MarsLanguage(v)));
+        j = languages_.find(v);
+    }
+    return *(*j).second;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+std::vector<MarsRequest> MarsExpension::expand(const std::vector<MarsRequest>& requests) {
+    std::vector<MarsRequest> result;
+
+    // Implement inheritence
+    for (std::vector<MarsRequest>::const_iterator j = requests.begin(); j != requests.end(); ++j) {
+
+        MarsLanguage& lang = language((*j).verb());
+        MarsRequest r = lang.expand(*j, inherit_);
+
+
+        result.push_back(r);
+
+    }
+
+    return result;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+void MarsExpension::expand(const MarsRequest& request, ExpandCallback& callback) {
+    MarsRequest r = language(request.verb()).expand(request, inherit_);
+    callback(r);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+void MarsExpension::flatten(const MarsRequest& request,
+                            FlattenCallback& callback) {
+    language(request.verb()).flatten(request, callback);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsExpension.h b/metkit/src/metkit/MarsExpension.h
new file mode 100644
index 0000000..ebb2e4f
--- /dev/null
+++ b/metkit/src/metkit/MarsExpension.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Manuel Fuentes
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+/// @date Sep 96
+
+#ifndef metkit_MarsExpension_H
+#define metkit_MarsExpension_H
+
+#include "metkit/MarsRequest.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace metkit {
+
+class MarsLanguage;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class FlattenCallback {
+public:
+    virtual ~FlattenCallback();
+    virtual void operator()(const MarsRequest&) = 0;
+};
+
+class ExpandCallback {
+public:
+    virtual ~ExpandCallback();
+    virtual void operator()(const MarsRequest&) = 0;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MarsExpension : public eckit::NonCopyable {
+public:
+// -- Contructors
+
+    MarsExpension(bool inherit);
+    ~MarsExpension();
+
+    void reset();
+
+    std::vector<MarsRequest> expand(const std::vector<MarsRequest>&);
+
+    void expand(const MarsRequest& request,
+                ExpandCallback& cb);
+
+
+    void flatten(const MarsRequest& request,
+                 FlattenCallback& callback);
+
+
+private: // members
+
+    MarsLanguage& language(const std::string& verb);
+
+    std::map<std::string, MarsLanguage*> languages_;
+    bool inherit_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/MarsHandle.cc b/metkit/src/metkit/MarsHandle.cc
new file mode 100644
index 0000000..4f3616a
--- /dev/null
+++ b/metkit/src/metkit/MarsHandle.cc
@@ -0,0 +1,260 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/config/Resource.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/Log.h"
+#include "eckit/serialisation/HandleStream.h"
+
+#include "metkit/MarsHandle.h"
+
+using namespace eckit;
+
+
+const unsigned long startCRC = 0xffffffffL;
+
+eckit::ClassSpec MarsHandle::classSpec_ = {&TCPHandle::classSpec(),"MarsHandle",};
+Reanimator<MarsHandle> MarsHandle::reanimator_;
+
+class MarsHandleStream : public HandleStream {
+
+    MarsHandle& handle_;
+
+public:
+
+    MarsHandleStream(MarsHandle& handle):
+        HandleStream(handle), handle_(handle)
+        { handle_.streamMode_ = true; }
+
+    ~MarsHandleStream()
+        { handle_.streamMode_ = false; }
+};
+
+void MarsHandle::encode(eckit::Stream& s) const
+{
+    TCPHandle::encode(s);
+    s << clientID_;
+    s << doCRC_;
+}
+
+MarsHandle::MarsHandle(eckit::Stream& s)
+    : TCPHandle(s),
+      length_(0),
+      total_(0),
+      receiving_(false),
+      streamMode_(false),
+      doCRC_(false),
+      crc_(startCRC) {
+    s >> clientID_;
+
+    if(s.endObjectFound())
+    {
+        Log::info() << "Got old metkit without CRC" << std::endl;
+        return;
+    }
+
+    s >> doCRC_;
+    if(doCRC_) Log::info() << "Got new metkit with CRC" << std::endl;
+}
+
+MarsHandle::MarsHandle(const std::string& host, int port, unsigned long long clientID)
+    : TCPHandle(host, port),
+      clientID_(clientID),
+      length_(0),
+      total_(0),
+      receiving_(false),
+      streamMode_(false),
+      doCRC_(false),
+      crc_(startCRC) {}
+
+MarsHandle::~MarsHandle()
+{
+}
+
+std::string MarsHandle::title() const
+{
+    std::ostringstream os;
+
+    os << "Client[" ;
+
+    os << TCPSocket::hostName(host_);
+
+    os << ":" << port_ << "]";
+    return os.str();
+}
+
+Length MarsHandle::openForRead()
+{
+    static long size = eckit::Resource<long>("archiveSocketBufferSize",0);
+
+    connection_.bufferSize(size);
+
+    TCPHandle::openForRead();
+
+    MarsHandleStream s(*this);
+
+
+    s << clientID_;     // Send info
+    s << 'r';     // Send the request
+    s >> length_; // Get the length back
+
+    Log::status() << "Receiving " << Bytes(length_) << std::endl;
+
+    total_     = 0;
+    receiving_ = true;
+    crc_       = startCRC;
+
+    return length_;
+}
+
+void MarsHandle::openForWrite(const Length& length)
+{
+    TCPHandle::openForWrite(length);
+
+    MarsHandleStream s(*this);
+
+    length_ = length;
+
+    s << clientID_; // Send info
+    s << 'w';
+    s << length_; // Send the length
+
+    Log::status() << "Sending " << Bytes(length_) << std::endl;
+
+    total_     = 0;
+    receiving_ = false;
+}
+
+void MarsHandle::openForAppend(const Length&)
+{
+    NOTIMP;
+}
+
+long MarsHandle::read(void *buffer,long length)
+{
+    if(streamMode_) return TCPHandle::read(buffer,length);
+
+    long long  left = length_ - total_;
+
+    if(left < length) {
+        length = left;
+        if(length == 0) return 0;
+    }
+
+    long len = TCPHandle::read(buffer,length);
+
+    if(doCRC_) updateCRC(buffer,len);
+
+    total_ += len;
+
+    return len;
+}
+
+long MarsHandle::write(const void *buffer,long length)
+{
+    long len = TCPHandle::write(buffer,length);
+    total_ += len;
+    return len;
+}
+
+void MarsHandle::close()
+{
+    bool gotCRC   = false;
+    unsigned long version = 0;
+    unsigned long crc = 0;
+
+    if(length_ > 0 && total_ != length_)
+    {
+        TCPHandle::close();
+
+		Log::error() << "Recieved/Sent " << total_
+					 << " bytes instead of " << length_ << std::endl;
+        if(Exception::throwing())
+        {
+            Log::error() << "A expection is already active" << std::endl;
+            return;
+        }
+        throw ShortFile("Bad total in MarsHandle");
+	}
+
+    if(receiving_)
+    {
+
+        crc_ ^= 0xffffffff;
+
+        length_ = 0;
+
+        // Try to read CRC
+
+
+        try {
+            MarsHandleStream s(*this);
+            s >> version;
+
+            unsigned long long c;
+            s >> c;
+
+            crc = ((unsigned long)(c));
+
+            gotCRC = true;
+
+        }
+        catch(std::exception& e)
+        {
+            Log::warning() << "Cannot read crc: " << e.what() << std::endl;
+        }
+
+    }
+
+
+    if(doCRC_ && gotCRC)
+    {
+        Log::info() << "Local CRC " << crc_ << ", remote CRC " << crc << std::endl;
+        ASSERT(version == 1);
+        if(crc != crc_) {
+
+            {
+                FILE *p = popen("mail mab mar","w");
+                if(p) {
+                    fprintf(p,"CRC error\n");
+                    pclose(p);
+                }
+            }
+
+            {
+                PathName lock("~/locks/pause_if_crc_error");
+                while(lock.exists())
+                {
+                    Log::status() << "**** CRC ERROR ****" << std::endl;
+                    ::sleep(120);
+                }
+            }
+
+            TCPHandle::close();
+
+            throw eckit::SeriousBug("Invalide checksum");
+        }
+    }
+
+    TCPHandle::close();
+
+}
+
+Length MarsHandle::estimate()
+{
+    return length_;
+}
+
+
+void MarsHandle::updateCRC(void* buffer,long length)
+{
+}
diff --git a/metkit/src/metkit/MarsHandle.h b/metkit/src/metkit/MarsHandle.h
new file mode 100644
index 0000000..81541ec
--- /dev/null
+++ b/metkit/src/metkit/MarsHandle.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsHandle.h
+// Baudouin Raoult - ECMWF Oct 96
+
+#ifndef MarsHandle_H
+#define MarsHandle_H
+
+#include "eckit/io/Length.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/io/TCPHandle.h"
+
+class MarsHandle : public eckit::TCPHandle {
+public:
+
+// -- Contructors
+
+	MarsHandle(const std::string& host, int port, unsigned long long);
+	MarsHandle(eckit::Stream&);
+
+// -- Destructor
+
+	~MarsHandle();
+
+// -- Overridden methods
+
+	// From eckit::DataHandle
+
+    virtual eckit::Length openForRead();
+    virtual void openForWrite(const eckit::Length&);
+    virtual void openForAppend(const eckit::Length&);
+
+	virtual void close();
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+
+	virtual eckit::Length estimate();
+	virtual std::string title() const;
+    virtual bool moveable() const { return true; }
+
+	// From Streamable
+
+	virtual void encode(eckit::Stream&) const;
+	virtual const eckit::ReanimatorBase& reanimator() const { return reanimator_; }
+
+// -- Class methods
+
+	static  const eckit::ClassSpec&  classSpec()         { return classSpec_;}
+
+private:
+
+// -- Members
+
+	unsigned long long  clientID_;
+	eckit::Length         length_;
+	eckit::Length         total_;
+	bool           receiving_;
+	bool           streamMode_;
+	bool           doCRC_;
+	unsigned long  crc_;
+
+// -- Methods
+
+	void updateCRC(void*,long);
+
+// -- Class members
+
+    static  eckit::ClassSpec               classSpec_;
+	static  eckit::Reanimator<MarsHandle>  reanimator_;
+
+	friend class MarsHandleStream;
+
+};
+
+#endif
diff --git a/metkit/src/metkit/MarsLanguage.cc b/metkit/src/metkit/MarsLanguage.cc
new file mode 100644
index 0000000..17833fe
--- /dev/null
+++ b/metkit/src/metkit/MarsLanguage.cc
@@ -0,0 +1,348 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <set>
+#include <list>
+
+#include "eckit/types/Types.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/utils/MD5.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/memory/ScopedPtr.h"
+
+#include "metkit/MarsLanguage.h"
+#include "eckit/parser/JSONParser.h"
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/Type.h"
+#include "metkit/MarsExpension.h"
+#include "eckit/log/Timer.h"
+
+using namespace eckit;
+
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static Value languages_;
+static std::vector<std::string> verbs_;
+
+static void init() {
+
+    eckit::PathName language("~metkit/etc/language.json");
+
+    std::ifstream in(language.asString().c_str());
+    if (!in) {
+        throw eckit::CantOpenFile(language);
+    }
+
+    eckit::JSONParser parser(in);
+
+    languages_ =  parser.parse();
+    Value verbs = languages_.keys();
+    for (size_t i = 0; i < verbs.size(); ++i) {
+        verbs_.push_back(verbs[i]);
+    }
+
+}
+
+
+
+namespace metkit {
+
+MarsLanguage::MarsLanguage(const std::string& verb):
+    verb_(verb) {
+    pthread_once(&once, init);
+    Value lang = languages_[verb];
+    Value params = lang.keys();
+
+    for (size_t i = 0; i < params.size(); ++i) {
+        std::string keyword = params[i];
+        Value settings = lang[keyword];
+        types_[keyword] = TypesFactory::build(keyword, settings);
+        types_[keyword]->attach();
+        keywords_.push_back(keyword);
+
+        if (settings.contains("aliases")) {
+            Value aliases = settings["aliases"];
+            for (size_t j = 0; j < aliases.size(); ++j) {
+                aliases_[aliases[j]] = keyword;
+                keywords_.push_back(aliases[j]);
+            }
+        }
+
+    }
+}
+
+MarsLanguage::~MarsLanguage() {
+    for (std::map<std::string, Type* >::iterator j = types_.begin(); j != types_.end(); ++j) {
+        (*j).second->detach();
+    }
+}
+
+
+
+void MarsLanguage::reset() {
+    for (std::map<std::string, Type* >::iterator j = types_.begin(); j != types_.end(); ++j) {
+        (*j).second->reset();
+    }
+}
+
+eckit::Value MarsLanguage::jsonFile(const std::string& name) {
+    // TODO: cache
+
+    eckit::PathName path = std::string("~metkit/etc/" + name);
+
+    std::ifstream in(path.asString().c_str());
+    if (!in) {
+        throw eckit::CantOpenFile(path);
+    }
+
+    eckit::JSONParser parser(in);
+
+    return parser.parse();
+}
+
+std::string MarsLanguage::bestMatch(const std::string& name,
+                                    const std::vector<std::string>& values,
+                                    bool fail,
+                                    const std::map<std::string, std::string>& aliases) {
+
+    size_t score = 0;
+    std::vector<std::string> best;
+
+    for (size_t i = 0; i < values.size(); ++i) {
+        const std::string& value = values[i];
+
+        size_t len = std::min(name.length(), value.length());
+        size_t s = 0;
+
+        for (size_t j = 0; j < len; ++j) {
+            if (::tolower(name[j]) == ::tolower(value[j])) {
+                s++;
+            }
+            else {
+                break;
+            }
+        }
+
+        if (s == value.length() && s == name.length()) {
+            if (aliases.find(value) != aliases.end()) {
+                return aliases.find(value)->second;
+            }
+            return value;
+        }
+
+        if (s > 0 && s >= score) {
+            if (s > score) {
+                best.clear();
+            }
+            best.push_back(value);
+            score = s;
+        }
+    }
+
+    size_t max = 3;
+    if (best.size() > 0 && score < max) {
+        std::cerr << "Matching '"
+                  << name
+                  << "' with "
+                  << best
+                  << " "
+                  << "Please give at least " << max << " first letters"
+                  << std::endl;
+    }
+
+
+    if (best.size() == 1) {
+        if (aliases.find(best[0]) != aliases.end()) {
+            return aliases.find(best[0])->second;
+        }
+        return best[0];
+    }
+
+    if (best.empty()) {
+
+        if (!fail) {
+            static std::string empty;
+            return empty;
+        }
+
+        std::ostringstream oss;
+        oss << "Cannot match '" << name << "' in " << values;
+        throw eckit::UserError(oss.str());
+    }
+
+    std::set<std::string> names;
+    for (std::vector<std::string>::const_iterator j = best.begin(); j != best.end(); ++j) {
+        std::map<std::string, std::string>::const_iterator k = aliases.find(*j);
+        if (k == aliases.end()) {
+            names.insert(*j);
+        }
+        else {
+            names.insert((*k).second);
+        }
+    }
+
+    if (names.size() == 1) {
+        return best[0];
+    }
+
+    std::ostringstream oss;
+    oss << "Ambiguous value '" << name << "' could be " << best;
+    throw eckit::UserError(oss.str());
+}
+
+std::string MarsLanguage::expandVerb(const std::string& verb) {
+    pthread_once(&once, init);
+    // std::map<std::string, std::string>::iterator c = cache_.find(verb);
+    // if(c != cache_.end()) {
+    //     return (*c).second;
+    // }
+
+    // return cache_[verb] = bestMatch(verb, verbs_, true);
+    return bestMatch(verb, verbs_, true);
+}
+
+Type* MarsLanguage::type(const std::string& name) const {
+    std::map<std::string, Type* >::const_iterator k = types_.find(name);
+    if (k == types_.end()) {
+        throw eckit::SeriousBug("Cannot find a type for '" + name + "'");
+    }
+    return (*k).second;
+}
+
+
+MarsRequest MarsLanguage::expand(const MarsRequest& r, bool inherit)  {
+    // std::cout << r << std::endl;
+
+    MarsRequest result(verb_);
+
+    std::vector<std::string> params = r.params();
+    std::set<std::string> seen;
+
+    for (std::vector<std::string>::iterator j = params.begin(); j != params.end(); ++j) {
+        std::string p;
+
+
+        std::map<std::string, std::string>::iterator c = cache_.find(*j);
+        if (c != cache_.end()) {
+            p = (*c).second;
+        } else {
+            p =  cache_[*j] = bestMatch(*j, keywords_, true, aliases_);
+        }
+
+        // if (seen.find(p) != seen.end()) {
+        //     std::cout << "Duplicate " << p << " " << *j << std::endl;
+        //     std::cout << r << std::endl;
+        //     if (result.countValues(p)) {
+        //         std::cout << result.values(p) << std::endl;
+        //     }
+        //     else {
+        //         std::cout << "off" << std::endl;
+        //     }
+        //     std::cout << r.values(*j) << std::endl;
+        // }
+
+        // seen.insert(p);
+
+        std::vector<std::string> values = r.values(*j);
+
+        if (values.size() == 1) {
+            const std::string& s = values[0];
+            if (s == "off" || s == "OFF") {
+                result.unsetValues(p);
+                type(p)->clearDefaults();
+                continue;
+            }
+        }
+
+        type(p)->expand(values);
+        result.setValuesTyped(type(p), values);
+
+        // result.setValues(p, values);
+
+    }
+
+
+
+    if (inherit) {
+        for (std::map<std::string, Type*>::iterator k = types_.begin(); k != types_.end(); ++k) {
+            const std::string& name = (*k).first;
+            if (result.countValues(name) == 0) {
+                (*k).second->setDefaults(result);
+            }
+        }
+
+        result.getParams(params);
+        for (std::vector<std::string>::const_iterator k = params.begin(); k != params.end(); ++k) {
+            type(*k)->setDefaults(result.values(*k));
+        }
+    }
+
+    result.getParams(params);
+    for (std::vector<std::string>::const_iterator k = params.begin(); k != params.end(); ++k) {
+        type(*k)->finalise(result);
+    }
+
+    return result;
+}
+
+
+const std::string& MarsLanguage::verb() const {
+    return verb_;
+}
+
+
+void MarsLanguage::flatten(const MarsRequest& request,
+                           const std::vector<std::string>& params,
+                           size_t i,
+                           MarsRequest& result,
+                           FlattenCallback& callback) {
+
+    if (i == params.size()) {
+        callback(result);
+        return;
+    }
+
+    const std::string& param = params[i];
+
+    Type* t = type(param);
+    if (!t->flatten()) {
+        flatten(request, params, i + 1, result, callback);
+        return;
+    }
+
+    const std::vector<std::string>& values = t->flattenValues(request);
+
+    for (std::vector<std::string>::const_iterator j = values.begin(); j != values.end(); ++j) {
+        result.setValue(param, *j);
+        flatten(request, params, i + 1, result, callback);
+    }
+
+}
+
+void MarsLanguage::flatten(const MarsRequest & request,
+                           FlattenCallback & callback) {
+    std::vector<std::string> params;
+    request.getParams(params);
+
+    MarsRequest result(request);
+    flatten(request, params, 0, result, callback);
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsLanguage.h b/metkit/src/metkit/MarsLanguage.h
new file mode 100644
index 0000000..7dacd59
--- /dev/null
+++ b/metkit/src/metkit/MarsLanguage.h
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Manuel Fuentes
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+/// @date Sep 96
+
+#ifndef metkit_MarsLanguage_H
+#define metkit_MarsLanguage_H
+
+#include "metkit/MarsRequest.h"
+#include "eckit/memory/NonCopyable.h"
+
+
+namespace metkit {
+
+class Type;
+class FlattenCallback;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MarsLanguage : private eckit::NonCopyable {
+    typedef std::map<std::string, std::string> StringMap;
+
+public:
+
+    MarsLanguage(const std::string& verb);
+    ~MarsLanguage();
+
+    MarsRequest expand(const MarsRequest& r, bool inherit);
+
+    void reset();
+
+    const std::string& verb() const;
+
+    void flatten(const MarsRequest& request,
+                 FlattenCallback& callback);
+
+
+// - Class methds
+
+    static std::string expandVerb(const std::string& verb);
+    static std::string bestMatch(const std::string& what,
+                                 const std::vector<std::string>& values,
+                                 bool fail,
+                                 const StringMap& aliases = StringMap());
+
+    static eckit::Value jsonFile(const std::string& name);
+
+
+private:
+// -- Contructors
+
+    std::string verb_;
+    std::map<std::string, Type* > types_;
+    std::vector<std::string> keywords_;
+    StringMap aliases_;
+
+    mutable StringMap cache_;
+
+private: // Methods
+
+    void flatten(const MarsRequest& request,
+                 const std::vector<std::string>& params,
+                 size_t i,
+                 MarsRequest& result,
+                 FlattenCallback& callback);
+
+    Type* type(const std::string& name) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/MarsLocation.cc b/metkit/src/metkit/MarsLocation.cc
new file mode 100644
index 0000000..f6f9a9d
--- /dev/null
+++ b/metkit/src/metkit/MarsLocation.cc
@@ -0,0 +1,109 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <set>
+
+#include "eckit/parser/JSON.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/utils/MD5.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/config/Configuration.h"
+#include "eckit/config/LocalConfiguration.h"
+
+#include "metkit/MarsLocation.h"
+
+using namespace eckit;
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+MarsLocation::MarsLocation(const Configuration& cfg) :
+    request_( static_cast<ValueMap>( cfg.getSubConfiguration("request").get() ) ),
+    hostname_(cfg.getString("server")),
+    port_(cfg.getInt("port"))
+{
+    Log::info() << "MarsLocation: " << *this << std::endl;
+}
+
+MarsLocation::MarsLocation(const MarsRequest& r, const std::string& hostname, int port) :
+       request_(r),
+       hostname_(hostname),
+       port_(port)
+{
+}
+
+MarsLocation::~MarsLocation()
+{
+}
+
+metkit::MarsLocation::operator eckit::Value() const
+{
+    Value dict = Value::makeMap();
+
+    dict["request"] = request_;
+    dict["server"]  = hostname_;
+    dict["port"]    = port_;
+
+    return dict;
+}
+
+MarsLocation::MarsLocation(eckit::Stream& s) :
+    request_(s)
+{
+    s >> hostname_;
+    s >> port_;
+}
+
+void MarsLocation::encode(eckit::Stream& s) const
+{
+    s << request_;
+    s << hostname_;
+    s << port_;
+}
+
+const MarsRequest& MarsLocation::request() const
+{
+    return request_;
+}
+
+std::string MarsLocation::hostname() const
+{
+    return hostname_;
+}
+
+int MarsLocation::port() const
+{
+    return port_;
+}
+
+void MarsLocation::print(std::ostream& s) const
+{
+    s << request_ << ',' << std::endl;
+    s << "hostname=" << hostname_ << ',' << std::endl;
+    s << "port=" << port_ << std::endl;
+}
+
+void MarsLocation::json(eckit::JSON& s) const
+{
+    s.startObject();
+    s << "request" << request_;
+    s << "hostname" << hostname_;
+    s << "port" << port_;
+    s.endObject();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsLocation.h b/metkit/src/metkit/MarsLocation.h
new file mode 100644
index 0000000..15d2612
--- /dev/null
+++ b/metkit/src/metkit/MarsLocation.h
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef metkit_MarsLocation_H
+#define metkit_MarsLocation_H
+
+
+#include "metkit/MarsRequest.h"
+
+namespace eckit {
+    class JSON;
+    class Configuration;
+    class Stream;
+}
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// MarsLocation represents a MarsRequest associated with the hostname and port.
+/// From this location, the data can be directly retrieved without going through a queueing system.
+/// It assumes all the data identified by the request can be got from the same location.
+/// This is useful to identify and retrieve data directly from memory.
+///
+/// If we consider that a MarsRequest is analogue to a URI, then a MarsLocation is an analogue to a URL
+
+class MarsLocation {
+
+public: // methods
+
+// - Constructors
+
+    MarsLocation(const eckit::Configuration&);
+    MarsLocation(const MarsRequest& r, const std::string& hostname, int port);
+    MarsLocation(eckit::Stream&);
+
+// -- Destructor
+
+    ~MarsLocation();
+
+// -- Operators
+
+    operator eckit::Value() const;
+
+    void json(eckit::JSON&) const;
+
+    const MarsRequest& request() const;
+
+    std::string hostname() const;
+
+    int port() const;
+
+
+private: // members
+
+    MarsRequest 	request_;
+    std::string     hostname_;
+    int             port_;
+
+private: // methods
+
+	void print(std::ostream&) const;
+    void encode(eckit::Stream&) const;
+
+// -- Class members
+
+
+    friend std::ostream& operator<<(std::ostream& s, const MarsLocation& r) {
+        r.print(s); return s;
+    }
+
+    friend eckit::JSON& operator<<(eckit::JSON& s, const MarsLocation& r) {
+        r.json(s); return s;
+    }
+
+    friend eckit::Stream& operator<<(eckit::Stream& s, const MarsLocation& r) {
+        r.encode(s); return s;
+    }
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/MarsParser.cc b/metkit/src/metkit/MarsParser.cc
new file mode 100644
index 0000000..2e91ea1
--- /dev/null
+++ b/metkit/src/metkit/MarsParser.cc
@@ -0,0 +1,216 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   MarsParser.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   Jun 2012
+
+#include "metkit/MarsParser.h"
+#include "eckit/utils/Translator.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+std::string MarsParser::parseString(char quote)
+{
+    consume(quote);
+    std::string s;
+    for (;;)
+    {
+        char c = next(true);
+        if (c == '\\')
+        {
+            c = next(true);
+            switch (c) {
+
+            case '"':
+                s += '"';
+                break;
+
+            case '\'':
+                s += '\'';
+                break;
+
+            case '\\':
+                s += '\\';
+                break;
+
+            case '/':
+                s += '/';
+                break;
+
+            case 'b':
+                s += '\b';
+                break;
+
+            case 'f':
+                s += '\f';
+                break;
+
+            case 'n':
+                s += '\n';
+                break;
+
+            case 'r':
+                s += '\r';
+                break;
+
+            case 't':
+                s += '\t';
+                break;
+
+            case 'u':
+                throw StreamParser::Error(std::string("JSONTokenizer::parseString \\uXXXX format not supported"));
+                break;
+            default:
+                throw StreamParser::Error(std::string("JSONTokenizer::parseString invalid \\ char '") + c + "'");
+                break;
+            }
+        }
+        else
+        {
+            if (c == quote)
+            {
+                return s; // void(s);
+            }
+            s += c;
+        }
+
+    }
+
+}
+
+static bool inindent(char c) {
+    return isalnum(c) || c == '_' || c == ':' || c == '-' || c == '.';
+}
+
+
+void MarsParser::quoted(std::ostream& out, const std::string& value) {
+    char quote = 0;
+    for(std::string::const_iterator j = value.begin(); j != value.end(); ++j) {
+        if(!inindent(*j)) {
+            quote = '"';
+            break;
+        }
+    }
+
+    if(quote) {
+        out << quote << value << quote;
+    }
+    else {
+        out << value;
+    }
+}
+
+
+std::string MarsParser::parseIndents() {
+    std::ostringstream oss;
+    oss << parseIndent();
+
+    for (;;) {
+        char c = peek(true);
+        while (c == ' ') {
+            next(true);
+            c = peek(true);
+        }
+
+        if (!inindent(c)) {
+            break;
+        }
+
+        oss << " " << parseIndent();
+    }
+
+    return oss.str();
+}
+
+std::string MarsParser::parseValue() {
+    char c = peek();
+
+    if (c ==  '\"' || c == '\'') {
+        return parseString(c);
+    }
+
+    return parseIndents();
+
+}
+
+std::vector<std::string> MarsParser::parseValues() {
+    std::vector<std::string> v(1, parseValue());
+    char c = peek();
+    while (c == '/') {
+        consume('/');
+        v.push_back(parseValue());
+        c = peek();
+    }
+    return v;
+}
+
+std::string MarsParser::parseIndent()
+{
+    char c = peek();
+    std::string s;
+    while (inindent(c)) {
+        s += next(true);
+        c = peek(true);
+    }
+    return s;
+}
+
+std::string MarsParser::parseVerb() {
+    char c = peek();
+    if (!isalpha(c) && c != '_') {
+        throw  StreamParser::Error(std::string("MarsParser::parseVerb invalid char '") + c + "'", line_ + 1);
+    }
+    return parseIndent();
+}
+
+MarsRequest MarsParser::parseRequest() {
+    MarsRequest r(parseVerb());
+    char c = peek();
+    while (c == ',') {
+        consume(',');
+        std::string key = parseIndents();
+        consume('=');
+        r.values(key, parseValues());
+        c = peek();
+    }
+    return r;
+}
+
+MarsParser::MarsParser(std::istream &in):
+    StreamParser(in, true, "*#")
+{
+}
+
+std::vector<MarsRequest> MarsParser::parse()
+{
+    std::vector<MarsRequest> result;
+
+    char c;
+    while ((c = peek()) != 0) {
+        result.push_back(parseRequest());
+    }
+
+    return result;
+}
+
+void MarsParser::parse(MarsParserCallback& cb) {
+    char c;
+    while ((c = peek()) != 0) {
+        cb(parseRequest());
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsParser.h b/metkit/src/metkit/MarsParser.h
new file mode 100644
index 0000000..e727d00
--- /dev/null
+++ b/metkit/src/metkit/MarsParser.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @date Jun 2012
+
+#ifndef eckit_JSONParser_h
+#define eckit_JSONParser_h
+
+#include "eckit/parser/StreamParser.h"
+#include "eckit/types/Types.h"
+#include "metkit/MarsRequest.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MarsParserCallback {
+public:
+    virtual void operator()(const MarsRequest&) = 0;
+};
+
+class MarsParser : public eckit::StreamParser {
+
+public: // methods
+
+    MarsParser(std::istream& in);
+
+    std::vector<MarsRequest> parse();
+
+    void parse(MarsParserCallback& cb);
+
+    static void quoted(std::ostream& out, const std::string& value);
+
+private: // methods
+
+    MarsRequest parseRequest();
+    std::string parseVerb();
+    std::string parseKeyword();
+    std::vector<std::string> parseValues();
+    std::string parseValue();
+    std::string parseIndent();
+    std::string parseIndents();
+
+    std::string parseString(char c);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/MarsRequest.cc b/metkit/src/metkit/MarsRequest.cc
new file mode 100644
index 0000000..f0533e9
--- /dev/null
+++ b/metkit/src/metkit/MarsRequest.cc
@@ -0,0 +1,436 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <algorithm>
+#include <set>
+#include <list>
+
+#include "eckit/types/Types.h"
+#include "eckit/parser/JSON.h"
+#include "eckit/log/Log.h"
+#include "eckit/config/Resource.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/utils/MD5.h"
+#include "eckit/parser/StringTools.h"
+
+#include "metkit/MarsRequest.h"
+#include "metkit/types/TypeAny.h"
+#include "metkit/MarsParser.h"
+
+using namespace eckit;
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class UndefinedType : public Type {
+    virtual void print( std::ostream &out ) const  {
+        out << "<undefined type>";
+    }
+
+    virtual bool filter(const std::vector< std::string >& filter, std::vector<std::string>& values) {
+        NOTIMP;
+    }
+
+
+public:
+    UndefinedType() : Type("<undefined>", eckit::Value()) { attach(); }
+};
+
+
+static UndefinedType undefined;
+
+
+Parameter::Parameter():
+    type_(&undefined) {
+    type_->attach();
+}
+
+Parameter::~Parameter() {
+    type_->detach();
+}
+
+Parameter::Parameter(const std::vector<std::string>& values, Type* type):
+    type_(type),
+    values_(values) {
+    if (!type) {
+        type_ = &undefined;
+    }
+    type_->attach();
+}
+
+
+Parameter::Parameter(const Parameter& other):
+    type_(other.type_),
+    values_(other.values_) {
+    type_->attach();
+}
+
+Parameter& Parameter::operator=(const Parameter& other) {
+    Type *old = type_;
+    type_ = other.type_;
+    type_->attach();
+    old->detach();
+
+    values_ = other.values_;
+    return *this;
+}
+
+void Parameter::values(const std::vector<std::string>& values) {
+    values_ = values;
+}
+
+bool Parameter::filter(const std::vector<std::string> &filter)  {
+    return type_->filter(filter, values_);
+}
+
+
+bool Parameter::matches(const std::vector<std::string> &match) const {
+    return type_->matches(match, values_);
+}
+
+
+const std::string& Parameter::name() const {
+    return type_->name();
+}
+
+size_t Parameter::count() const {
+    return type_->count(values_);
+}
+
+bool Parameter::operator<(const Parameter& other) const {
+    if (name() != other.name()) {
+        return name() < other.name();
+    }
+    return values_ < other.values_;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+MarsRequest::MarsRequest()
+{
+}
+
+
+MarsRequest::MarsRequest(const std::string& s):
+    verb_(s)
+{
+}
+
+MarsRequest::MarsRequest(eckit::Stream& s)
+{
+    int size;
+
+
+    s >> verb_;
+    s >> size;
+
+    for (int i = 0; i < size; i++)
+    {
+        std::string param;
+        int    count;
+
+        s >> param;
+        s >> count;
+
+        std::vector<std::string> v;
+        v.reserve(count);
+
+        for (int k = 0; k < count; k++)
+        {
+            std::string value;
+            s >> value;
+            v.push_back(value);
+        }
+
+        params_.push_back(Parameter(v, new TypeAny(param)));
+    }
+}
+
+void MarsRequest::encode(eckit::Stream& s) const
+{
+    s << verb_;
+    int size = params_.size();
+    s << size;
+
+
+    for (std::list<Parameter>::const_iterator i = params_.begin(); i != params_.end(); ++i)
+    {
+        s << (*i).name();
+
+        const std::vector<std::string>& v = (*i).values();
+
+        int size = v.size(); // For backward compatibility
+        s << size;
+
+        for (std::vector<std::string>::const_iterator k = v.begin(); k != v.end(); ++k) {
+            s << *k;
+        }
+    }
+}
+
+MarsRequest::MarsRequest(const eckit::ValueMap&) {
+    NOTIMP;
+}
+
+bool MarsRequest::empty() const {
+    return params_.empty();
+}
+
+
+void MarsRequest::print(std::ostream& s) const
+{
+    dump(s, "", "");
+}
+
+void MarsRequest::dump(std::ostream& s, const char* cr, const char* tab) const {
+
+    std::list<Parameter>::const_iterator begin = params_.begin();
+    std::list<Parameter>::const_iterator end = params_.end();
+
+
+    s << verb_ ;
+
+    if (begin != end) {
+        s << ',' << cr << tab;
+
+        int a = 0;
+        for (std::list<Parameter>::const_iterator i = begin; i != end; ++i)
+        {
+            if (a++) {
+                s << ',';
+                s << cr << tab;
+            }
+
+            int b = 0;
+            s  << (*i).name()
+//               << "." << (*i).second.type()
+               << "=";
+            const std::vector<std::string>& v = (*i).values();
+
+            for (std::vector<std::string>::const_iterator k = v.begin();
+                    k != v.end(); ++k)
+            {
+                if (b++) {
+                    s << '/';
+                }
+                MarsParser::quoted(s, *k);
+            }
+        }
+    }
+
+    s << cr << cr;
+}
+
+void MarsRequest::json(eckit::JSON& s) const
+{
+    s.startObject();
+    s << "verb" << verb_;
+    std::list<Parameter>::const_iterator begin = params_.begin();
+    std::list<Parameter>::const_iterator end = params_.end();
+
+    for (std::list<Parameter>::const_iterator i = begin; i != end; ++i)
+    {
+        s << (*i).name();
+        const std::vector<std::string>& v = (*i).values();
+
+        if (v.size() != 1) {
+            s.startList();
+        }
+
+        for (std::vector<std::string>::const_iterator k = v.begin(); k != v.end(); ++k) {
+            s << (*k) ;
+        }
+
+        if (v.size() != 1) {s.endList();}
+    }
+
+    s.endObject();
+}
+
+void MarsRequest::md5(eckit::MD5& md5) const
+{
+    std::ostringstream oss;
+    oss << *this;
+    md5.add(oss.str());
+}
+
+MarsRequest::~MarsRequest()
+{
+}
+
+void MarsRequest::unsetValues(const std::string& name)
+{
+    std::list<Parameter>::iterator i = find(name);
+    if (i != params_.end()) {
+        params_.erase(i);
+    }
+}
+
+void MarsRequest::setValuesTyped(Type* type, const std::vector<std::string>& values) {
+    std::list<Parameter>::iterator i = find(type->name());
+    if (i != params_.end()) {
+        (*i) = Parameter(values, type);
+    }
+    else {
+        params_.push_back(Parameter(values, type));
+    }
+}
+
+bool MarsRequest::filter(const MarsRequest &filter) {
+    for (std::list<Parameter>::iterator i = params_.begin(); i != params_.end(); ++i) {
+
+        std::list<Parameter>::const_iterator j = filter.find((*i).name());
+        if (j == filter.params_.end()) {
+            continue;
+        }
+
+        if (!(*i).filter((*j).values())) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MarsRequest::matches(const MarsRequest &matches) const {
+
+    std::vector<std::string> params = matches.params();
+    for (std::vector<std::string>::const_iterator j = params.begin(); j != params.end(); ++j) {
+        std::list<Parameter>::const_iterator k = find(*j);
+        if (k == params_.end()) {
+            return false;
+        }
+
+        if (!(*k).matches(matches.values(*j))) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void MarsRequest::values(const std::string& name, const std::vector<std::string>& v)
+{
+    std::list<Parameter>::iterator i = find(name);
+    if (i != params_.end()) {
+        (*i).values(v);
+    }
+    else {
+        params_.push_back(Parameter(v, new TypeAny(name)));
+    }
+}
+
+
+size_t MarsRequest::countValues(const std::string& name) const
+{
+    std::list<Parameter>::const_iterator i = find(name);
+    if (i != params_.end()) {
+        return (*i).values().size();
+    }
+    return 0;
+}
+
+bool MarsRequest::is(const std::string& name, const std::string& value) const
+{
+    std::list<Parameter>::const_iterator i = find(name);
+    if (i != params_.end()) {
+        const std::vector<std::string>& v = (*i).values();
+        return v.size() == 1 && v[0] == value;
+    }
+    return false;
+}
+
+
+const std::vector<std::string>& MarsRequest::values(const std::string& name, bool emptyOk) const
+{
+    std::list<Parameter>::const_iterator i = find(name);
+    if (i == params_.end()) {
+
+        if (emptyOk) {
+            static std::vector<std::string> empty;
+            return empty;
+        }
+
+        std::ostringstream oss;
+        oss << "No parameter called '" << name << "' in request " << *this;
+        throw eckit::UserError(oss.str());
+    }
+    return (*i).values();
+}
+
+
+void MarsRequest::getParams(std::vector<std::string>& p) const
+{
+    p.clear();
+    for (std::list<Parameter>::const_iterator i = params_.begin(); i != params_.end(); ++i) {
+        p.push_back((*i).name());
+    }
+
+}
+
+size_t MarsRequest::count() const
+{
+    size_t result = 1;
+    for (std::list<Parameter>::const_iterator i = params_.begin(); i != params_.end(); ++i) {
+        result *= (*i).count();
+    }
+    return result;
+}
+
+
+std::vector<std::string> MarsRequest::params() const
+{
+    std::vector<std::string> p;
+    getParams(p);
+    return p;
+}
+
+MarsRequest::operator eckit::Value() const {
+    NOTIMP;
+}
+
+void MarsRequest::merge(const MarsRequest &other) {
+    NOTIMP;
+}
+
+
+void MarsRequest::verb(const std::string &verb) {
+    verb_ = verb;
+}
+
+bool MarsRequest::operator<(const MarsRequest& other) const {
+    if (verb_ != other.verb_) {
+        return verb_ < other.verb_;
+    }
+    return params_ < other.params_;
+}
+
+
+std::list<Parameter>::const_iterator MarsRequest::find(const std::string& name) const {
+    for (std::list<Parameter>::const_iterator i = params_.begin(); i != params_.end(); ++i) {
+        if ((*i).name() == name) {
+            return i;
+        }
+    }
+    return params_.end();
+}
+
+std::list<Parameter>::iterator MarsRequest::find(const std::string & name) {
+    for (std::list<Parameter>::iterator i = params_.begin(); i != params_.end(); ++i) {
+        if ((*i).name() == name) {
+            return i;
+        }
+    }
+    return params_.end();
+}
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsRequest.h b/metkit/src/metkit/MarsRequest.h
new file mode 100644
index 0000000..91938c8
--- /dev/null
+++ b/metkit/src/metkit/MarsRequest.h
@@ -0,0 +1,182 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Manuel Fuentes
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+/// @date Sep 96
+
+#ifndef metkit_MarsRequest_H
+#define metkit_MarsRequest_H
+
+#include "eckit/types/Date.h"
+#include "eckit/types/Double.h"
+#include "eckit/types/Time.h"
+#include "eckit/value/Value.h"
+
+namespace eckit {
+class JSON;
+class MD5;
+}
+
+namespace metkit {
+
+class Type;
+class MarsRequest;
+
+class Parameter {
+    Type* type_;
+    std::vector<std::string> values_;
+public:
+    Parameter();
+    ~Parameter();
+
+    Parameter(const std::vector<std::string>& values, Type* = 0);
+    Parameter(const Parameter&);
+    Parameter& operator=(const Parameter&);
+    bool operator<(const Parameter&) const;
+
+    const std::vector< std::string >& values() const { return values_; }
+    void values(const std::vector< std::string >& values);
+
+    bool filter(const std::vector< std::string >& filter);
+    bool matches(const std::vector< std::string >& matches) const;
+
+    Type& type() const { return *type_; }
+    const std::string& name() const;
+
+    size_t count() const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class MarsRequest {
+
+public: // methods
+
+// -- Contructors
+
+    MarsRequest();
+    MarsRequest(const std::string&);
+    MarsRequest(eckit::Stream&);
+
+    MarsRequest(const eckit::ValueMap&);
+
+// -- Copy
+
+    // MarsRequest(const MarsRequest&);
+    // MarsRequest& operator=(const MarsRequest&);
+
+// -- Destructor
+
+    ~MarsRequest();
+
+// -- Operators
+
+    bool operator<(const MarsRequest& other) const;
+
+    // eckit::Value&        operator[](const std::string&);
+    // const eckit::Value&  operator[](const std::string&) const;
+
+    operator eckit::Value() const;
+
+    /* DEPRECATED METHODS */
+
+    // These methods are required for ODB_API 0.15.x
+    // to maintain temporary compatibility with metkit 0.3.0
+
+    void name(const std::string& v) { verb(v); }
+    void setValues(const std::string& name, const std::vector<std::string>& v) { values(name, v); }
+
+    /* END DEPRECATED METHODS */
+
+// -- Methods
+
+    const std::string& verb() const { return verb_; }
+
+    size_t countValues(const std::string&) const;
+
+    bool is(const std::string& param, const std::string& value) const;
+
+    const std::vector<std::string> &values(const std::string&, bool emptyOk = false) const;
+
+    void getParams(std::vector<std::string>&) const;
+    std::vector<std::string> params() const;
+
+    void verb(const std::string&);
+
+    void values(const std::string&, const std::vector<std::string>&);
+
+    template<class T>
+    void setValue(const std::string& name, const T& value)
+    { std::vector<T> v(1, value); values(name, v); }
+
+    void setValue(const std::string& name, const char* value)
+    { std::string v(value); setValue(name, v); }
+
+    void unsetValues(const std::string&);
+
+    /// Merges one MarsRequest into another
+    /// @todo Improve performance -- uses O(N^2) search / merge in std::list's
+    void merge(const MarsRequest& other);
+
+    void json(eckit::JSON&) const;
+
+    void md5(eckit::MD5&) const;
+
+    void dump(std::ostream&, const char* cr = "\n", const char* tab = "\t") const;
+
+    void setValuesTyped(Type*, const std::vector<std::string>&);
+
+    bool filter(const MarsRequest& filter);
+    bool matches(const MarsRequest& filter) const;
+    bool empty() const;
+
+    size_t count() const;
+
+
+private: // members
+
+    std::string         verb_;
+    std::list<Parameter> params_;
+
+private: // methods
+
+    void print(std::ostream&) const;
+    void encode(eckit::Stream&) const;
+
+    std::list<Parameter>::const_iterator find(const std::string&) const;
+    std::list<Parameter>::iterator find(const std::string&);
+
+// -- Class members
+
+    static eckit::ClassSpec                 classSpec_;
+    static eckit::Reanimator<MarsRequest>   reanimator_;
+
+    friend std::ostream& operator<<(std::ostream& s, const MarsRequest& r) {
+        r.print(s); return s;
+    }
+
+    friend eckit::JSON& operator<<(eckit::JSON& s, const MarsRequest& r) {
+        r.json(s); return s;
+    }
+
+    friend eckit::Stream& operator<<(eckit::Stream& s, const MarsRequest& r) {
+        r.encode(s); return s;
+    }
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/MarsRequestHandle.cc b/metkit/src/metkit/MarsRequestHandle.cc
new file mode 100644
index 0000000..6c53a5f
--- /dev/null
+++ b/metkit/src/metkit/MarsRequestHandle.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsRequestHandle.cc
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#include "metkit/MarsRequestHandle.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/types/Types.h"
+
+namespace metkit {
+
+bool shortName(const std::string& prefix, const std::string& s)
+{
+    if (prefix.size() > s.size())
+        return false;
+    return std::equal(prefix.begin(), prefix.end(), s.begin());
+}
+
+MarsRequestHandle::MarsRequestHandle(const MarsRequest& request, BaseProtocol* protocol)
+: request_(request),
+  protocol_(protocol)
+{
+    eckit::Log::debug() << "MarsRequestHandle::MarsRequestHandle: request: " << request << " protocol: " << protocol << std::endl;
+    ASSERT(protocol);
+}
+
+MarsRequestHandle::~MarsRequestHandle() {}
+
+eckit::Length MarsRequestHandle::openForRead()
+{
+    eckit::Log::debug() << "MarsRequestHandle::openForRead: request_: " << request_ << std::endl;
+
+    const std::string v (eckit::StringTools::lower(request_.verb()));
+    ASSERT(v == "retrieve" || v == "stage" || v == "list");
+
+    return protocol_->retrieve(request_);
+}
+
+void MarsRequestHandle::openForWrite(const eckit::Length& size)
+{
+    eckit::Log::debug() << "MarsRequestHandle::openForWrite: request_.name()=" << request_.verb() << std::endl;
+
+    ASSERT(eckit::StringTools::lower(request_.verb()) == "archive");
+    protocol_->archive(request_, size);
+}
+
+void MarsRequestHandle::openForAppend(const eckit::Length&)
+{
+    NOTIMP;
+}
+
+long MarsRequestHandle::read(void* buffer, long len)
+{
+    return protocol_->read(buffer, len);
+}
+
+long MarsRequestHandle::write(const void* buffer, long len)
+{
+    return protocol_->write(buffer, len);
+}
+
+void MarsRequestHandle::close()
+{
+    protocol_->cleanup();
+}
+
+void MarsRequestHandle::print(std::ostream& s) const
+{
+    s << "MarsRequestHandle["<< *protocol_ << "," << request_ << "]";
+}
+
+} // namespace metkit
diff --git a/metkit/src/metkit/MarsRequestHandle.h b/metkit/src/metkit/MarsRequestHandle.h
new file mode 100644
index 0000000..bd1bf0e
--- /dev/null
+++ b/metkit/src/metkit/MarsRequestHandle.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MarsRequestHandle.h
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef MarsRequestHandle_H
+#define MarsRequestHandle_H
+
+#include "eckit/io/DataHandle.h"
+
+#include "metkit/BaseProtocol.h"
+#include "metkit/MarsRequest.h"
+
+namespace metkit {
+
+class MarsRequestHandle : public eckit::DataHandle {
+public:
+	MarsRequestHandle(const metkit::MarsRequest& request, metkit::BaseProtocol* protocol);
+	~MarsRequestHandle();
+
+
+private:
+    metkit::MarsRequest request_;
+    std::auto_ptr<BaseProtocol> protocol_;
+
+// -- Overridden methods
+	// From data handle
+	void print(std::ostream&) const;
+
+    eckit::Length openForRead();
+    void openForWrite(const eckit::Length&);
+    void openForAppend(const eckit::Length&);
+    long read(void*, long );
+    long write(const void*, long);
+    void close();
+};
+
+}
+
+#endif
diff --git a/metkit/src/metkit/RequestEnvironment.cc b/metkit/src/metkit/RequestEnvironment.cc
new file mode 100644
index 0000000..aa5ed6e
--- /dev/null
+++ b/metkit/src/metkit/RequestEnvironment.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File RequestEnvironment.cc
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#include <unistd.h>
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "metkit/RequestEnvironment.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+
+using namespace eckit;
+
+namespace metkit {
+
+static Mutex local_mutex;
+RequestEnvironment::RequestEnvironment():
+    request_("environ")
+{
+	char buf[1024];
+	if(gethostname(buf,sizeof(buf)) != 0)
+		throw SeriousBug("Cannot establish current hostname");
+
+    request_.setValue("host", std::string(buf));
+
+
+	struct passwd *pw;
+	setpwent();
+
+	if((pw = getpwuid(getuid())) == NULL)
+		throw SeriousBug("Cannot establish current user");
+
+    request_.setValue("user", std::string(pw->pw_name));
+
+	endpwent();
+
+
+//    request_.setValue("pid",long(::getpid()));
+}
+
+RequestEnvironment::~RequestEnvironment()
+{
+}
+
+
+void RequestEnvironment::print(std::ostream&) const
+{
+}
+
+RequestEnvironment& RequestEnvironment::instance()
+{
+    AutoLock<Mutex> lock(local_mutex);
+    {
+        static RequestEnvironment e;
+        return e;
+    }
+}
+
+}
diff --git a/metkit/src/metkit/RequestEnvironment.h b/metkit/src/metkit/RequestEnvironment.h
new file mode 100644
index 0000000..b005852
--- /dev/null
+++ b/metkit/src/metkit/RequestEnvironment.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File RequestEnvironment.h
+// Baudouin Raoult - (c) ECMWF Feb 12
+
+#ifndef RequestEnvironment_H
+#define RequestEnvironment_H
+
+#include "eckit/memory/NonCopyable.h"
+#include "metkit/MarsRequest.h"
+
+namespace metkit {
+
+class RequestEnvironment : private eckit::NonCopyable {
+public:
+
+// -- Contructors
+
+	RequestEnvironment();
+
+// -- Destructor
+
+	~RequestEnvironment();
+
+// -- Methods
+
+    const MarsRequest& request() const { return request_; }
+
+// -- Class members
+
+    static RequestEnvironment& instance();
+
+protected:
+
+// -- Methods
+
+	void print(std::ostream&) const;
+
+private:
+
+// -- Members
+
+    MarsRequest request_;
+
+};
+
+}
+
+#endif
diff --git a/metkit/src/metkit/config/LibMetkit.cc b/metkit/src/metkit/config/LibMetkit.cc
new file mode 100644
index 0000000..5728caa
--- /dev/null
+++ b/metkit/src/metkit/config/LibMetkit.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#include "metkit/config/LibMetkit.h"
+
+#include "eckit/config/Resource.h"
+
+#include "metkit/metkit_version.h"
+
+using namespace eckit;
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static LibMetkit libmetkit;
+
+LibMetkit::LibMetkit() : Library("metkit") {}
+
+const LibMetkit& LibMetkit::instance()
+{
+    return libmetkit;
+}
+
+const void* LibMetkit::addr() const { return this; }
+
+std::string LibMetkit::version() const {
+    return metkit_version_str();
+}
+
+std::string LibMetkit::gitsha1(unsigned int count) const {
+    std::string sha1(metkit_git_sha1());
+    if (sha1.empty()) {
+        return "not available";
+    }
+
+    return sha1.substr(0, std::min(count, 40u));
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace mir
+
diff --git a/metkit/src/metkit/config/LibMetkit.h b/metkit/src/metkit/config/LibMetkit.h
new file mode 100644
index 0000000..ad743fb
--- /dev/null
+++ b/metkit/src/metkit/config/LibMetkit.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   August 2016
+
+#ifndef mir_LibMetkit_H
+#define mir_LibMetkit_H
+
+#include "eckit/system/Library.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class LibMetkit : public eckit::system::Library {
+public:
+
+    LibMetkit();
+
+    static eckit::PathName cacheDir();
+
+    static const LibMetkit& instance();
+
+protected:
+
+    const void* addr() const;
+
+    virtual std::string version() const;
+
+    virtual std::string gitsha1(unsigned int count) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/EmosFile.cc b/metkit/src/metkit/grib/EmosFile.cc
new file mode 100644
index 0000000..a883f85
--- /dev/null
+++ b/metkit/src/metkit/grib/EmosFile.cc
@@ -0,0 +1,102 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Dec 2015
+
+#include "grib_api.h"
+
+#include "eckit/io/Buffer.h"
+#include "eckit/io/BufferedHandle.h"
+#include "eckit/io/MoverHandle.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/config/Resource.h"
+#include "eckit/log/Log.h"
+
+#include "metkit/grib/EmosFile.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static long readcb(void *data, void *buffer, long len)
+{
+    DataHandle *handle = reinterpret_cast<eckit::DataHandle*>(data);
+    return handle->read(buffer,len);
+}
+
+EmosFile::EmosFile(const PathName& path, bool buffered):
+    handle_(
+        buffered
+            ? new BufferedHandle(path.fileHandle(), 64*1024*1024)
+            : path.fileHandle()
+        )
+{
+    handle_->openForRead();
+}
+
+EmosFile::EmosFile( DataHandle& dh ):
+	handle_(new BufferedHandle( dh, 64*1024*1024))
+{
+    handle_->openForRead();
+}
+
+Offset EmosFile::position()
+{
+    return handle_->position();
+}
+
+void EmosFile::rewind()
+{
+	handle_->rewind();
+}
+
+void EmosFile::seek(const Offset& where)
+{
+    handle_->seek(where);
+}
+
+EmosFile::~EmosFile()
+{
+    handle_->close();
+}
+
+long EmosFile::read(Buffer& buffer)
+{
+	size_t len = buffer.size();
+	int e    = wmo_read_any_from_stream(handle_.get(),&readcb,buffer,&len);
+
+	if(e == GRIB_SUCCESS)  return len;
+	if(e == GRIB_END_OF_FILE) return 0;
+
+	throw ReadError("in EmosFile::read");
+}
+
+long EmosFile::readSome(Buffer& buffer)
+{
+	size_t len = buffer.size();
+	int e    = wmo_read_any_from_stream(handle_.get(),&readcb,buffer,&len);
+
+	if(e == GRIB_SUCCESS || e == GRIB_BUFFER_TOO_SMALL)  return len;
+	if(e == GRIB_END_OF_FILE)           return 0;
+
+	throw ReadError("in EmosFile::readSome");
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+
diff --git a/metkit/src/metkit/grib/EmosFile.h b/metkit/src/metkit/grib/EmosFile.h
new file mode 100644
index 0000000..707361a
--- /dev/null
+++ b/metkit/src/metkit/grib/EmosFile.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Manuel Fuentes
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef grib_EmosFile_H
+#define grib_EmosFile_H
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/memory/ScopedPtr.h"
+
+// Forward declarations
+
+namespace eckit { class Buffer; }
+namespace eckit { class PathName; }
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Previously existed in mars-server code as metkit/EmosFile
+
+class EmosFile : private eckit::NonCopyable {
+
+public: // methods
+
+    /// Contructor
+
+    EmosFile(const eckit::PathName&, bool buffered = true);
+
+    /// Contructor, does not take ownership of eckit::DataHandle
+
+    EmosFile( eckit::DataHandle& dh );
+
+    /// Destructor
+
+    ~EmosFile();
+
+public: // methods
+
+    long read(eckit::Buffer&);
+
+    // Don't fail if buffer is too small
+    // FIXME: Why are we not failing if the buffer is too small? As is, the
+    // caller is required to check whether the buffer was sufficiently large
+    long readSome(eckit::Buffer&);
+
+    eckit::Offset position();
+
+    void rewind();
+    void seek(const eckit::Offset&);
+
+private: // members
+
+    eckit::ScopedPtr<eckit::DataHandle> handle_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribAccessor.cc b/metkit/src/metkit/grib/GribAccessor.cc
new file mode 100644
index 0000000..b9a21b9
--- /dev/null
+++ b/metkit/src/metkit/grib/GribAccessor.cc
@@ -0,0 +1,93 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/grib/GribAccessor.h"
+
+#include "grib_api.h"
+
+#include "metkit/grib/GribHandle.h"
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static void check_error_code( const std::string& name, int err, bool quiet = false )
+{
+	if(err && !quiet) {
+	   eckit::Log::error() << "GribAccessor(" << name << "): " << grib_get_error_message(err) << std::endl;
+	}
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, double& x, bool quiet) const
+{
+	x = 0;
+	int err = grib_get_double(h.raw(), name.c_str(), &x);
+	check_error_code(name,err,quiet);
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, unsigned long& x, bool quiet) const
+{
+	long y = 0;
+	int err = grib_get_long(h.raw(), name.c_str(), &y);
+	check_error_code(name,err,quiet);
+	x = y;
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, long& x, bool quiet) const
+{
+	x = 0;
+	int err = grib_get_long(h.raw(), name.c_str(), &x);
+	check_error_code(name,err,quiet);
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name,  bool& x, bool quiet) const
+{
+   x = true;
+   long xd = 0;
+   int err = grib_get_long(h.raw(), name.c_str(), &xd);
+   check_error_code(name,err,quiet);
+   if (xd == 0) x = false;
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, std::string& x, bool quiet) const
+{
+	char buf[1024];
+	size_t s = sizeof(buf);
+	buf[0] = 0;
+	int err = grib_get_string(h.raw(), name.c_str(), buf, &s);
+	check_error_code(name,err,quiet);
+	x = buf;
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, std::vector<long>& x, bool quiet) const
+{
+	int err = 0;
+	size_t sz = 0;
+	err = grib_get_size(h.raw(),name.c_str(),&sz); check_error_code(name,err,quiet);
+	x.resize(sz);
+	err = grib_get_long_array(h.raw(),name.c_str(),&x[0],&sz); check_error_code(name,err,quiet);
+	ASSERT( x.size() == sz );
+}
+
+void GribAccessorBase::grib_get_value(const GribHandle& h, const std::string& name, std::vector<double>& x, bool quiet) const
+{
+	int err = 0;
+	size_t sz = 0;
+	err = grib_get_size(h.raw(),name.c_str(),&sz); check_error_code(name,err,quiet);
+	x.resize(sz);
+	err = grib_get_double_array(h.raw(),name.c_str(),&x[0],&sz); check_error_code(name,err,quiet);
+	ASSERT( x.size() == sz );
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribAccessor.h b/metkit/src/metkit/grib/GribAccessor.h
new file mode 100644
index 0000000..96964ad
--- /dev/null
+++ b/metkit/src/metkit/grib/GribAccessor.h
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef grib_GribAccessor_H
+#define grib_GribAccessor_H
+
+#include "eckit/log/Log.h"
+#include "eckit/exception/Exceptions.h"
+#include "metkit/grib/GribHandle.h"
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class GribAccessorBase {
+
+protected:
+
+    void grib_get_value(const GribHandle& h, const std::string& name, double& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  unsigned long& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  long& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  bool& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  std::string& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  std::vector<long>& x, bool quiet = false) const;
+
+    void grib_get_value(const GribHandle& h, const std::string& name,  std::vector<double>& x, bool quiet = false) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+class GribAccessor : private GribAccessorBase {
+
+private: // members
+
+	std::string name_;
+
+public: // methods
+
+	GribAccessor(const std::string& name): name_(name) {}
+
+	T value(const GribHandle& h) const
+	{
+		T value;
+		grib_get_value(h, name_, value);
+		return value;
+	}
+
+	T value(const GribHandle& h,T def) const
+	{
+		T value = def;
+		grib_get_value(h, name_, value, true);
+		return value;
+	}
+
+	T operator()(const GribHandle& h) const
+	{
+		return value(h);
+	}
+
+	T operator()(const GribHandle& h, T def) const
+	{
+		return value(h, def);
+	}
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribDataBlob.cc b/metkit/src/metkit/grib/GribDataBlob.cc
new file mode 100644
index 0000000..825554c
--- /dev/null
+++ b/metkit/src/metkit/grib/GribDataBlob.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "grib_api.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/parser/StringTools.h"
+
+#include "metkit/grib/GribDataBlob.h"
+#include "metkit/grib/GribHandle.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+// -------------------------------------------------------------------------------------------------
+
+// Construct GribDataBlob builder object, to self-register this type with the
+// DataBlobFactory
+namespace {
+    DataBlobBuilder<GribDataBlob> gribBlobBuilder("grib");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+GribDataBlob::GribDataBlob(const void* data, size_t length) :
+    eckit::DataBlob(data, length),
+    metadata_(data, length)
+{
+    size_t len = metadata_.length();
+    ASSERT(len <= buffer_.size());
+    actualLength_ = len;
+}
+
+GribDataBlob::GribDataBlob(DataHandle& dh, size_t length) :
+    eckit::DataBlob(dh, length),
+    metadata_(buffer(), length)
+{
+    size_t len = metadata_.length();
+    ASSERT(len <= buffer_.size());
+    actualLength_ = len;
+}
+
+GribDataBlob::~GribDataBlob() {
+}
+
+const Metadata &GribDataBlob::metadata() const {
+    return metadata_;
+}
+
+void GribDataBlob::print(std::ostream& os) const {
+    os << "GribDataBlob[size=" << Bytes(buffer_.size())
+       << ",metadata=" << metadata_
+       << "]";
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribDataBlob.h b/metkit/src/metkit/grib/GribDataBlob.h
new file mode 100644
index 0000000..0ad75ed
--- /dev/null
+++ b/metkit/src/metkit/grib/GribDataBlob.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Manuel Fuentes
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef grib_GribDataBlob_H
+#define grib_GribDataBlob_H
+
+#include "eckit/io/DataBlob.h"
+
+#include "metkit/grib/GribMetaData.h"
+
+namespace metkit {
+namespace grib {
+
+class GribHandle;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+///
+///
+class GribDataBlob : public eckit::DataBlob {
+
+public: // methods
+
+    GribDataBlob(const void* data, size_t length);
+    GribDataBlob(eckit::DataHandle& dh, size_t length);
+
+	virtual ~GribDataBlob();
+
+    virtual const eckit::Metadata& metadata() const;
+
+private: // members
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+    grib::GribMetaData metadata_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribFile.cc b/metkit/src/metkit/grib/GribFile.cc
new file mode 100644
index 0000000..4636a2a
--- /dev/null
+++ b/metkit/src/metkit/grib/GribFile.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date Jan 2016
+
+#include <string>
+
+#include "grib_api.h"
+
+#include "eckit/exception/Exceptions.h"
+
+#include "metkit/grib/GribFile.h"
+#include "metkit/grib/GribHandle.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+GribFile::GribFile(const PathName& path) :
+    path_(path),
+    file_(path_) {
+}
+
+GribFile::~GribFile() {
+}
+
+GribHandle* GribFile::next() {
+
+    int err = 0;
+    grib_handle* h = grib_handle_new_from_file(NULL, file_, &err);
+
+    if(err == GRIB_SUCCESS)
+        return new GribHandle(h);
+
+    if(err == GRIB_END_OF_FILE)
+        return NULL;
+
+    std::ostringstream msg;
+    msg << "Error reading GRIB file " << path_
+        << " : " << grib_get_error_message(err);
+    throw ReadError(msg.str(), Here());
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+
diff --git a/metkit/src/metkit/grib/GribFile.h b/metkit/src/metkit/grib/GribFile.h
new file mode 100644
index 0000000..71e9ad0
--- /dev/null
+++ b/metkit/src/metkit/grib/GribFile.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+
+/// @date Jan 2016
+
+#ifndef grib_GribFile_H
+#define grib_GribFile_H
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/io/StdFile.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace eckit { class PathName; }
+
+namespace metkit {
+namespace grib {
+
+  class GribHandle;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class GribFile : private eckit::NonCopyable {
+
+public: // methods
+
+    /// Contructor
+
+    GribFile(const eckit::PathName&);
+
+    /// Destructor
+
+    ~GribFile();
+
+    GribHandle* next();
+
+private: // members
+
+    eckit::PathName path_;
+
+    eckit::StdFile file_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribHandle.cc b/metkit/src/metkit/grib/GribHandle.cc
new file mode 100644
index 0000000..ebe018d
--- /dev/null
+++ b/metkit/src/metkit/grib/GribHandle.cc
@@ -0,0 +1,227 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/grib/GribHandle.h"
+
+#include "grib_api.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/StdFile.h"
+
+#include "metkit/grib/GribMetaData.h"
+#include "metkit/grib/GribAccessor.h"
+#include "metkit/grib/GribDataBlob.h"
+#include "metkit/grib/GribMetaData.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void grib_call(int code, const char *msg, const eckit::CodeLocation& where)
+{
+	if(code)
+	{
+        std::ostringstream os;
+        os << msg << " : " << grib_get_error_message(code);
+        throw Exception(os.str(), where);
+	}
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+GribHandle::GribHandle(const PathName& path) :
+    handle_(NULL),
+    owned_(true)
+{
+	StdFile f(path);
+
+	int err = 0;
+	grib_handle* h = grib_handle_new_from_file(0,f,&err);
+	if(err != 0)
+	{
+        std::ostringstream os;
+        os << "GribHandle() failed to build from path " << path;
+        throw Exception(os.str(), Here());
+	}
+
+	ASSERT(h);
+	handle_ = h;
+}
+
+GribHandle::GribHandle(grib_handle* h) :
+    handle_(NULL),
+    owned_(true)
+{
+	ASSERT(h);
+    handle_ = h;
+}
+
+GribHandle::GribHandle(grib_handle& h) :
+    handle_(&h),
+    owned_(false)
+{
+}
+
+GribHandle::GribHandle(const Buffer& buffer, bool copy)
+	: handle_(NULL)
+{
+	const char *message = buffer;
+	ASSERT(strncmp(message,"GRIB", 4) == 0);
+
+	grib_handle *h = 0;
+
+	if(copy)
+	{
+		h = grib_handle_new_from_message_copy(0,const_cast<char*>(message),buffer.size());
+	}
+	else
+	{
+		h = grib_handle_new_from_message(0,const_cast<char*>(message),buffer.size());
+	}
+
+	ASSERT(h);
+	handle_ = h;
+}
+
+GribHandle::~GribHandle()
+{
+    if( handle_ && owned_ )
+	{
+		GRIB_CALL( grib_handle_delete(handle_) );
+		handle_ = 0;
+    }
+}
+
+GribDataBlob* GribHandle::message() const
+{
+    size_t length;
+    const void* message;
+    GRIB_CALL( grib_get_message(handle_, &message, &length) );
+    return new GribDataBlob(message, length);
+}
+
+std::string GribHandle::gridType() const
+{
+	return GribAccessor<std::string>("gridType")(*this);
+}
+
+std::string GribHandle::geographyHash() const
+{
+   // The following key is edition independent
+   return GribAccessor<std::string>("md5GridSection")(*this);
+}
+
+long GribHandle::edition() const
+{
+	return GribAccessor<long>("edition")(*this);
+}
+
+size_t GribHandle::getDataValuesSize() const
+{
+    size_t count = 0;
+	GRIB_CALL(grib_get_size(raw(), "values", &count));
+    return count;
+}
+
+void GribHandle::getDataValues(double* values, const size_t& count) const
+{
+    ASSERT(values);
+    size_t n = count;
+	GRIB_CALL(grib_get_double_array(raw(),"values",values,&n));
+	ASSERT(n == count);
+}
+
+double* GribHandle::getDataValues(size_t& count) const
+{
+    count = getDataValuesSize();
+
+    double* values = new double[count];
+    getDataValues(values,count);
+    return values;
+}
+
+void GribHandle::setDataValues(const double *values, size_t count)
+{
+    ASSERT(values);
+	GRIB_CALL(grib_set_double_array(raw(),"values",values,count));
+}
+
+void GribHandle::write( DataHandle& handle )
+{
+	const void* message = NULL;
+	size_t length = 0;
+
+	GRIB_CALL(grib_get_message(raw(), &message, &length));
+
+	ASSERT( message );
+	ASSERT( length );
+
+    ASSERT( length = long(length) && handle.write(message, length) == long(length) );
+}
+
+size_t GribHandle::write( Buffer& buff )
+{
+    size_t len = buff.size();
+	GRIB_CALL( grib_get_message_copy( raw(), buff, &len )); // will issue error if buffer too small
+	return len;
+}
+
+double GribHandle::latitudeOfFirstGridPointInDegrees() const
+{
+	return GribAccessor<double>("latitudeOfFirstGridPointInDegrees")(*this);
+}
+
+double GribHandle::longitudeOfFirstGridPointInDegrees() const
+{
+	return GribAccessor<double>("longitudeOfFirstGridPointInDegrees")(*this);
+}
+
+double GribHandle::latitudeOfLastGridPointInDegrees() const
+{
+	return GribAccessor<double>("latitudeOfLastGridPointInDegrees")(*this);
+}
+
+double GribHandle::longitudeOfLastGridPointInDegrees() const
+{
+	return GribAccessor<double>("longitudeOfLastGridPointInDegrees")(*this);
+}
+
+GribHandle* GribHandle::clone() const
+{
+	grib_handle* h = grib_handle_clone(raw());
+    if(!h)
+        throw eckit::WriteError( std::string("failed to clone output grib") );
+
+	return new GribHandle(h);
+}
+
+string GribHandle::shortName() const
+{
+	return GribAccessor<std::string>("shortName")(*this);
+}
+
+size_t GribHandle::numberOfPoints() const
+{
+	return GribAccessor<long>("numberOfDataPoints")(*this);
+}
+
+bool GribHandle::hasKey(const char* key) const {
+    return (grib_is_defined(handle_,key) != 0);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribHandle.h b/metkit/src/metkit/grib/GribHandle.h
new file mode 100644
index 0000000..8c1acba
--- /dev/null
+++ b/metkit/src/metkit/grib/GribHandle.h
@@ -0,0 +1,120 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef grib_GribHandle_H
+#define grib_GribHandle_H
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/log/CodeLocation.h"
+#include "eckit/memory/Owned.h"
+
+struct grib_handle;
+
+namespace eckit {
+    class DataHandle;
+}
+
+namespace metkit {
+namespace grib {
+
+    class GribMetaData;
+    class GribDataBlob;
+    class GribAccessorBase;
+    class GribMutatorBase;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+void grib_call( int code, const char* msg, const eckit::CodeLocation& where );
+
+#define GRIB_CALL(a) grib::grib_call(a, #a, Here())
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class GribHandle : public eckit::Owned {
+
+public: // types
+
+   /// constructor from file path, creates grib_handle and takes ownership
+   /// @note currently this only handles local paths
+   explicit GribHandle(const eckit::PathName&);
+
+    /// constructor taking ownership of a grib_handle pointer
+    GribHandle(grib_handle*);
+
+    /// constructor not taking
+    GribHandle(grib_handle&);
+
+   /// constructor creating a grib_handle from a buffer
+   explicit GribHandle(const eckit::Buffer&, bool copy = true);
+
+   /// destructor will delete the grib_handle if we own it
+   ~GribHandle();
+
+public: // methods
+
+   /// @returns a new GribDataBlob
+   GribDataBlob* message() const;
+
+   std::string gridType() const;
+
+   std::string geographyHash() const;
+
+   GribHandle* clone() const;
+
+   std::string shortName() const;
+
+   size_t numberOfPoints() const;
+
+   size_t  getDataValuesSize() const;
+   double* getDataValues(size_t&) const;
+   void    getDataValues(double*, const size_t &) const;
+
+   void setDataValues(const double*, size_t);
+
+   void   write( eckit::DataHandle& );
+   size_t write( eckit::Buffer& );
+
+   double latitudeOfFirstGridPointInDegrees()  const;
+   double longitudeOfFirstGridPointInDegrees() const;
+   double latitudeOfLastGridPointInDegrees()   const;
+   double longitudeOfLastGridPointInDegrees()  const;
+
+   bool hasKey(const char*) const;
+
+protected: // methods
+
+   friend class GribDataBlob;
+   friend class GribMetaData;
+   friend class GribAccessorBase;
+   friend class GribMutatorBase;
+
+   /// To be used by friends since this is rather dangerous
+   /// Don't delete this pointer, use with care :)
+   /// @returns the raw grib_handle so client code can call grib directly
+   grib_handle* raw() const { return handle_; }
+
+   /// Client code shouldn't care if GRIB edition
+   long edition() const;
+
+private: // members
+
+   grib_handle* handle_;
+
+   bool owned_;
+
+};
+
+//------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribIndex.cc b/metkit/src/metkit/grib/GribIndex.cc
new file mode 100644
index 0000000..7f872a6
--- /dev/null
+++ b/metkit/src/metkit/grib/GribIndex.cc
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/grib/GribMetaData.h"
+#include "metkit/grib/GribIndex.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+GribIndex::~GribIndex()
+{
+    for( size_t i = 0; i < handle_.size(); ++i ) {
+		GribMetaData* h = handle_[i];
+		delete h;
+	}
+}
+
+void GribIndex::readFrom( eckit::Stream& s )
+{
+    ASSERT( length_.size() == 0);
+    ASSERT( offset_.size() == 0);
+    ASSERT( handle_.size() == 0);
+
+    unsigned long count;
+
+    s >> count;
+
+    length_.resize(count);
+    offset_.resize(count);
+    handle_.resize(count);
+
+    for( size_t i = 0; i < count; ++i )
+    {
+        unsigned long long x;
+        s >> x; offset_[i] = x;
+        s >> x; length_[i] = x;
+        handle_[i] = new GribMetaData(s);
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribIndex.h b/metkit/src/metkit/grib/GribIndex.h
new file mode 100644
index 0000000..d5a5a2a
--- /dev/null
+++ b/metkit/src/metkit/grib/GribIndex.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Manuel Fuentes
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef grib_GribIndex_H
+#define grib_GribIndex_H
+
+#include "eckit/types/Types.h"
+#include "eckit/io/Offset.h"
+#include "eckit/serialisation/Stream.h"
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class GribMetaData;
+
+struct GribIndex
+{
+    ~GribIndex();
+
+    void readFrom( eckit::Stream& s );
+
+    eckit::OffsetList         offset_;
+    eckit::LengthList         length_;
+    std::vector<GribMetaData*>  handle_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribMetaData.cc b/metkit/src/metkit/grib/GribMetaData.cc
new file mode 100644
index 0000000..ab4fe72
--- /dev/null
+++ b/metkit/src/metkit/grib/GribMetaData.cc
@@ -0,0 +1,203 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "grib_api.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/config/Resource.h"
+
+#include "metkit/grib/GribMetaData.h"
+#include "metkit/grib/GribHandle.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+GribMetaData::GribMetaData( eckit::Stream& s )
+{
+    bool b, more;
+    std::string key;
+    std::string str;
+    long ival;
+    double dval;
+
+    s >> more;
+    while(more)
+    {
+        s >> key;
+
+        s >> b;
+        if(b) {
+            s >> str;
+            stringValues_[key] = str;
+        }
+
+        s >> b;
+        if(b) {
+            s >> ival;
+            longValues_[key] = ival;
+        }
+
+        s >> b;
+        if(b) {
+            s >> dval;
+            doubleValues_[key] = dval;
+        }
+        s >> more;
+    }
+}
+
+GribMetaData::GribMetaData(const void *buffer, size_t length)
+{
+    size_t len;
+    char val[80] = {0,};
+    double d;
+    long l;
+
+    /// JIRA TICKET >> const void*
+    grib_handle* h = grib_handle_new_from_message(NULL, const_cast<void*>(buffer), length);
+    ASSERT(h);
+
+    static std::string gribToRequestNamespace = eckit::Resource<std::string>("gribToRequestNamespace", "mars");
+
+    grib_keys_iterator* ks = grib_keys_iterator_new(h, GRIB_KEYS_ITERATOR_ALL_KEYS, gribToRequestNamespace.c_str());
+    ASSERT(ks);
+
+    while(grib_keys_iterator_next(ks))
+    {
+        const char* name = grib_keys_iterator_get_name(ks);
+
+        if(name[0] == '_') {
+            continue;
+        }
+
+        // in method parameters() we assume that all keys have a string representation
+
+        len = sizeof(val);
+        ASSERT( grib_keys_iterator_get_string(ks,val,&len) == 0 );
+        stringValues_[name] = val;
+
+        len = 1;
+        if(grib_keys_iterator_get_double(ks,&d,&len) == 0)
+            doubleValues_[name] = d;
+
+        len = 1;
+        if(grib_keys_iterator_get_long(ks,&l,&len) == 0)
+            longValues_[name] = l;
+
+    }
+
+    {
+        char value[1024];
+        size_t len = sizeof(value);
+
+        if(grib_get_string(h, "paramId", value, &len) == 0) {
+            stringValues_["param"] = value;
+        }
+    }
+
+    ASSERT(grib_get_long(h, "totalLength", &length_) == 0);
+
+#if 0
+    const char *extra[] = {"editionNumber", "table2Version", "indicatorOfParameter", 0};
+    int i  = 0;
+
+    while(extra[i]) {
+        long value;
+        if(grib_get_long(h,extra[i],&value) == 0)
+            longValues_[std::string(extra[i])] = value;
+        i++;
+    }
+#endif
+
+    grib_keys_iterator_delete(ks);
+
+    grib_handle_delete(h);
+}
+
+GribMetaData::~GribMetaData() {
+}
+
+std::vector<std::string> GribMetaData::keywords() const {
+    std::vector<std::string> res;
+    res.reserve(stringValues_.size());
+    for(string_store::const_iterator itr = stringValues_.begin(); itr != stringValues_.end(); ++itr) {
+        res.push_back(itr->first);
+    }
+    return res;
+}
+
+bool GribMetaData::has(const std::string& key) const
+{
+    return
+        stringValues_.find(key) != stringValues_.find(key) ||
+        doubleValues_.find(key) != doubleValues_.end() ||
+                                   longValues_.find(key)   != longValues_.end();
+}
+
+void GribMetaData::get(const std::string &name, std::string &value) const
+{
+    getValue(name, value);
+}
+
+void GribMetaData::get(const std::string &name, long &value) const
+{
+    getValue(name, value);
+}
+
+void GribMetaData::get(const std::string &name, double &value) const
+{
+    getValue(name, value);
+}
+
+std::string GribMetaData::substitute(const std::string& pattern) const
+{
+    return StringTools::substitute(pattern,stringValues_);
+}
+
+size_t GribMetaData::length() const
+{
+    return length_;
+}
+
+void GribMetaData::getValue(const std::string& key,double& value) const
+{
+    std::map<std::string,double>::const_iterator j = doubleValues_.find(key);
+    if(j == doubleValues_.end()) throw eckit::UserError(std::string("GribMetaData::getDouble failed for [") + key + "]");
+    value = (*j).second;
+}
+
+void GribMetaData::getValue(const std::string& key,long& value) const
+{
+    std::map<std::string,long>::const_iterator j = longValues_.find(key);
+    if(j == longValues_.end()) throw eckit::UserError(std::string("GribMetaData::getLong failed for [") + key + "]");
+    value = (*j).second;
+}
+
+void GribMetaData::getValue(const std::string& key,std::string& value) const
+{
+    std::map<std::string,std::string>::const_iterator j = stringValues_.find(key);
+    if(j == stringValues_.end()) throw eckit::UserError(std::string("GribMetaData::getString failed for [") + key + "]");
+    value = (*j).second;
+}
+
+void GribMetaData::print(std::ostream& os) const {
+    os << "GribMetaData[]";
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribMetaData.h b/metkit/src/metkit/grib/GribMetaData.h
new file mode 100644
index 0000000..cd876d7
--- /dev/null
+++ b/metkit/src/metkit/grib/GribMetaData.h
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Manuel Fuentes
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef grib_GribMetaData_H
+#define grib_GribMetaData_H
+
+#include "eckit/io/Buffer.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/serialisation/Stream.h"
+#include "eckit/types/Metadata.h"
+
+namespace metkit {
+namespace grib {
+
+    class GribHandle;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Previously existed in mars-server code as metkit/GribHandle
+
+class GribMetaData : public eckit::Metadata {
+
+public: // methods
+
+    GribMetaData(eckit::Stream&);
+    GribMetaData(const void* buffer, size_t length);
+
+	virtual ~GribMetaData();
+
+// -- from Metadata
+
+    virtual std::vector<std::string> keywords() const;
+
+    virtual bool has(const std::string& key) const;
+
+    virtual void get(const std::string& name, std::string& value) const;
+    virtual void get(const std::string& name, long& value) const;
+    virtual void get(const std::string& name, double& value) const;
+
+    void getValue(const std::string& name, std::string& value) const;
+    void getValue(const std::string& name, long& value) const;
+    void getValue(const std::string& name, double& value) const;
+
+	std::string substitute(const std::string& pattern) const;
+
+    size_t length() const;
+
+protected: // members
+
+    virtual void print(std::ostream&) const;
+
+private: // members
+
+	typedef std::map<std::string, std::string> string_store;
+
+	string_store stringValues_;
+
+	std::map<std::string, long>        longValues_;
+	std::map<std::string, double>      doubleValues_;
+
+    long length_;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribMutator.cc b/metkit/src/metkit/grib/GribMutator.cc
new file mode 100644
index 0000000..25ee2fc
--- /dev/null
+++ b/metkit/src/metkit/grib/GribMutator.cc
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/grib/GribMutator.h"
+
+#include "grib_api.h"
+
+#include "eckit/exception/Exceptions.h"
+
+#include "metkit/grib/GribHandle.h"
+
+//----------------------------------------------------------------------------------------------------------------------
+
+namespace metkit {
+namespace grib {
+
+static void check_error_code( const std::string& name, int err, bool quiet = false )
+{
+	if(err && !quiet)
+		eckit::Log::error() << "grib_set_value(" << name << "): " << grib_get_error_message(err) << std::endl;
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, double x, bool quiet)
+{
+	int err = grib_set_double(h.raw(), name.c_str(), x);
+	check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, unsigned long x, bool quiet)
+{
+	int err = grib_set_long(h.raw(), name.c_str(), x);
+	check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, long x, bool quiet)
+{
+	int err = grib_set_long(h.raw(), name.c_str(), x);
+	check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, bool xy, bool quiet)
+{
+   long x = 0;
+   if (xy) x = 1;
+   int err = grib_set_long(h.raw(), name.c_str(), x);
+   check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, std::string x, bool quiet)
+{
+	size_t s = x.size();
+	int err = grib_set_string(h.raw(), name.c_str(), x.c_str(), &s );
+	check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, std::vector<long> x, bool quiet)
+{
+	int err = grib_set_long_array(h.raw(),name.c_str(),&x[0],x.size());
+	check_error_code(name,err,quiet);
+}
+
+void GribMutatorBase::grib_set_value(GribHandle& h, const std::string& name, std::vector<double> x, bool quiet)
+{
+	int err = grib_set_double_array(h.raw(),name.c_str(),&x[0],x.size());
+	check_error_code(name,err,quiet);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribMutator.h b/metkit/src/metkit/grib/GribMutator.h
new file mode 100644
index 0000000..00dafa0
--- /dev/null
+++ b/metkit/src/metkit/grib/GribMutator.h
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef grib_GribMutator_H
+#define grib_GribMutator_H
+
+#include "metkit/grib/GribHandle.h"
+
+struct grib_handle;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class GribMutatorBase {
+
+void grib_set_value(GribHandle& h, const std::string& name, double x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, unsigned long x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, long x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, bool x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, std::string x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, std::vector<long> x, bool quiet = false);
+
+void grib_set_value(GribHandle& h, const std::string& name, std::vector<double> x, bool quiet = false);
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+template<class T>
+class GribMutator : private GribMutatorBase {
+
+private: // members
+
+	std::string name_;
+
+public: // methods
+
+	GribMutator(const std::string& name): name_(name) {}
+
+	void set(GribHandle& h, T value) const
+	{
+		grib_set_value(h, name_, value);
+	}
+
+	void operator() (GribHandle& h, T value) const
+	{
+		grib_set_value(h, name_, value);
+	}
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/grib/GribToRequest.cc b/metkit/src/metkit/grib/GribToRequest.cc
new file mode 100644
index 0000000..e2d9789
--- /dev/null
+++ b/metkit/src/metkit/grib/GribToRequest.cc
@@ -0,0 +1,192 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/grib/GribToRequest.h"
+
+#include "grib_api.h"
+
+#include "eckit/config/Resource.h"
+#include "eckit/config/ResourceMgr.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/StringTools.h"
+
+#include "metkit/MarsRequest.h"
+
+using namespace eckit;
+
+namespace metkit {
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static void upper_case(char *p)
+{
+	while(*p)
+	{
+		if(islower(*p)) *p = toupper(*p);
+		p++;
+	}
+
+}
+
+void GribToRequest::handleToRequest(grib_handle * const g, MarsRequest& req) {
+
+	grib_keys_iterator* ks;
+
+	std::string name;
+
+	char value[80];
+	size_t len = sizeof(value);
+	int e;
+
+	ASSERT(g);
+
+    static std::string gribToRequestNamespace = eckit::Resource<std::string>("gribToRequestNamespace", "mars");
+
+    ks  = grib_keys_iterator_new(g, GRIB_KEYS_ITERATOR_ALL_KEYS, gribToRequestNamespace.c_str());
+
+	while(grib_keys_iterator_next(ks))
+	{
+
+		name = grib_keys_iterator_get_name(ks);
+
+		if((e = grib_keys_iterator_get_string(ks,value,&len))) {
+			std::ostringstream oss;
+			oss << "Cannot get "<<name<<" as string "<<e<<" ("<<grib_get_error_message(e)<<")";
+			throw eckit::Exception(oss.str(),Here());
+		}
+
+		name = StringTools::upper(name);
+
+		if( name == "EXPVER" ) {
+			upper_case(value);
+		}
+
+		req.setValue(name, std::string(value));
+		len = sizeof(value);
+	}
+
+
+	name = "identifier";
+	len = sizeof(value);
+	if((e = grib_get_string(g,name.c_str(),value,&len))) {
+		std::ostringstream oss;
+		oss << "Cannot get "<<name<<" as string "<<e<<" ("<<grib_get_error_message(e)<<")";
+		throw eckit::Exception(oss.str(),Here());
+	}
+
+	if(strcmp(value,"GRIB") != 0) {
+		std::ostringstream oss;
+		oss << "Unexpected message type ("<< value<<")";
+		throw eckit::Exception(oss.str(),Here());
+	}
+
+#if 0
+
+	const char *stream = get_value(r,"STREAM",0);
+	const char *number = get_value(r,"NUMBER",0);
+
+
+	{
+		mars.pseudogrib = (strcmp(value,"BUDG") == 0) || (strcmp(value,"TIDE") == 0);
+		if(mars.pseudogrib)
+		{
+			marslog(LOG_WARN,"Pseudo GRIB encountered (%s)",value);
+			if(stream == NULL)
+				stream = getenv("MARS_PSEUDOGRIB_STREAM");
+
+			if(stream != NULL)
+			{
+				marslog(LOG_DBUG,"Setting STREAM to '%s'",stream);
+				set_value(r,"STREAM","%s",stream);
+			}
+
+			if(number != NULL)
+			{
+				marslog(LOG_DBUG,"Setting NUMBER to '%s'",number);
+				set_value(r,"NUMBER","%s",number);
+			}
+		}
+		else {
+			if(strcmp(value,"GRIB") != 0)
+				marslog(LOG_EXIT,"Unexpected message type (%s)",value);
+			else
+			{
+				/* Get the edition */
+				long edition = 0;
+				if(e = grib_get_long(g,"edition",&edition))
+				{
+					marslog(LOG_EXIT,"Cannot get edition as long: %d (%s)",e,
+							grib_get_error_message(e));
+				}
+				set_value(r,"_EDITION","%ld",edition);
+			}
+		}
+	}
+
+	if(grib_get_long(g,"localDefinitionNumber",&local) ==  0 && local == 191) /* TODO: Not grib2 compatible, but speed-up process */
+	if(grib_get_size(g,"freeFormData",&len) ==  0 && len != 0)
+	{
+		char buffer[10240];
+		len = sizeof(buffer);
+		if(e = grib_get_bytes(g,"freeFormData",buffer,&len))
+			marslog(LOG_EROR,"Cannot get freeFormData %d (%s)",name,e,
+					grib_get_error_message(e));
+		else {
+			request* s = decode_free_format_request(buffer,len);
+
+			if(mars.debug)
+			{
+				marslog(LOG_DBUG,"Free format request:");
+				print_all_requests(s);
+			}
+			/* set_value(r,"PARAM","%d.%d",s1->parameter,s1->version); */
+
+			reset_language(mars_language());
+			mars.expflags = EXPAND_MARS | EXPAND_NO_DEFAULT;
+			s = expand_mars_request(s);
+			if( s == NULL)
+			{
+				/* if(mars.exit_on_failed_expand) */
+				{
+					e = -2;
+					marslog(LOG_EROR,"Failed to expand request");
+				}
+			}
+			mars.expflags = EXPAND_MARS;
+			reqcpy(r,s);
+			free_all_requests(s);
+
+		}
+
+	}
+#endif
+
+	grib_keys_iterator_delete(ks);
+}
+
+void GribToRequest::handleToRequest(const grib::GribHandle& grib, MarsRequest& req) {
+	NOTIMP;
+}
+
+void GribToRequest::gribToRequest(const void* buffer, size_t length, MarsRequest& req) {
+
+    grib_handle* grib = grib_handle_new_from_message(0, const_cast<void*>(buffer), length);
+
+	GribToRequest::handleToRequest(grib, req);
+
+	grib_handle_delete(grib);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
diff --git a/metkit/src/metkit/grib/GribToRequest.h b/metkit/src/metkit/grib/GribToRequest.h
new file mode 100644
index 0000000..bdde17f
--- /dev/null
+++ b/metkit/src/metkit/grib/GribToRequest.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Baudouin Raoult
+/// @author Manuel Fuentes
+/// @author Tiago Quintino
+
+/// @date Dec 2015
+
+#ifndef metkit_GribToRequest_H
+#define metkit_GribToRequest_H
+
+struct grib_handle;
+
+#include "metkit/grib/GribHandle.h"
+
+namespace metkit {
+
+class MarsRequest;
+
+namespace grib {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+/// Utility class to build MarsRequest from GribHandle
+
+/// Part of this code is taken from mars-metkit grib.c
+
+class GribToRequest {
+
+public: // methods
+
+	static void handleToRequest(grib_handle * const grib, MarsRequest& req);
+
+	static void handleToRequest(const grib::GribHandle& grib, MarsRequest& req);
+
+    static void gribToRequest(const void* buffer, size_t length, MarsRequest& req);
+
+private: // methods
+
+	GribToRequest();
+
+	~GribToRequest();
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace grib
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/metkit_config.h.in b/metkit/src/metkit/metkit_config.h.in
new file mode 100644
index 0000000..d78da12
--- /dev/null
+++ b/metkit/src/metkit/metkit_config.h.in
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+#ifndef metkit_config_h
+#define metkit_config_h
+
+#include "metkit_ecbuild_config.h" /* generated by ecbuild */
+
+#define METKIT_VERSION_STR      "@METKIT_VERSION_STR@"
+
+/* TODO: METKIT_VERSION is defined in metkit.h from the 3 version components below */
+
+#define METKIT_MAJOR_VERSION    @METKIT_MAJOR_VERSION@
+#define METKIT_MINOR_VERSION    @METKIT_MINOR_VERSION@
+#define METKIT_REVISION_VERSION @METKIT_PATCH_VERSION@
+
+/* packages */
+
+#endif /* metkit_config_h */
diff --git a/metkit/src/metkit/metkit_version.cc.in b/metkit/src/metkit/metkit_version.cc.in
new file mode 100644
index 0000000..1a31586
--- /dev/null
+++ b/metkit/src/metkit/metkit_version.cc.in
@@ -0,0 +1,12 @@
+#include "metkit/metkit_version.h"
+
+const char * metkit_version()     { return METKIT_VERSION; }
+const char * metkit_version_str() { return METKIT_VERSION_STR; }
+
+unsigned int metkit_version_int()
+{
+  return 10000*METKIT_MAJOR_VERSION + 100*METKIT_MINOR_VERSION + 1*METKIT_PATCH_VERSION;
+}
+
+const char * metkit_git_sha1() { return "@METKIT_GIT_SHA1@"; }
+
diff --git a/metkit/src/metkit/metkit_version.h.in b/metkit/src/metkit/metkit_version.h.in
new file mode 100644
index 0000000..e6c728e
--- /dev/null
+++ b/metkit/src/metkit/metkit_version.h.in
@@ -0,0 +1,19 @@
+#ifndef metkit_version_h
+#define metkit_version_h
+
+#define METKIT_VERSION_STR "@METKIT_VERSION_STR@"
+#define METKIT_VERSION     "@METKIT_VERSION@"
+
+#define METKIT_MAJOR_VERSION @METKIT_MAJOR_VERSION@
+#define METKIT_MINOR_VERSION @METKIT_MINOR_VERSION@
+#define METKIT_PATCH_VERSION @METKIT_PATCH_VERSION@
+
+const char * metkit_version();
+
+unsigned int metkit_version_int();
+
+const char * metkit_version_str();
+
+const char * metkit_git_sha1();
+
+#endif // metkit_version_h
diff --git a/metkit/src/metkit/types/Type.cc b/metkit/src/metkit/types/Type.cc
new file mode 100644
index 0000000..07dcc12
--- /dev/null
+++ b/metkit/src/metkit/types/Type.cc
@@ -0,0 +1,243 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "metkit/types/Type.h"
+#include "metkit/MarsRequest.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Type::Type(const std::string &name, const eckit::Value& settings) :
+    name_(name),
+    flatten_(true) {
+
+    // if (settings.contains("multiple")) {
+    //     flatten_ = settings["multiple"];
+    // }
+
+    if (settings.contains("flatten")) {
+        flatten_ = settings["flatten"];
+    }
+
+    if (settings.contains("default")) {
+        eckit::Value d = settings["default"];
+        if (d.isList()) {
+            size_t len = d.size();
+            for (size_t i = 0; i < len; i++) {
+                defaults_.push_back(d[i]);
+            }
+        }
+        else {
+            defaults_.push_back(d);
+        }
+    }
+
+    originalDefaults_ = defaults_;
+
+    if (settings.contains("only")) {
+        eckit::Value d = settings["only"];
+
+        size_t len = d.size();
+        for (size_t i = 0; i < len; i++) {
+            eckit::Value a = d[i];
+            eckit::Value keys = a.keys();
+
+            for (size_t j = 0; j < keys.size(); j++) {
+                std::string key = keys[i];
+                eckit::Value v = a[key];
+
+                if (v.isList())
+                {
+                    for (size_t k = 0; k < v.size(); k++) {
+                        only_[key].insert(v[k]);
+                    }
+                }
+                else {
+                    only_[key].insert(v);
+                }
+            }
+        }
+    }
+
+    if (settings.contains("never")) {
+        eckit::Value d = settings["never"];
+
+        size_t len = d.size();
+        for (size_t i = 0; i < len; i++) {
+            eckit::Value a = d[i];
+            eckit::Value keys = a.keys();
+
+            for (size_t j = 0; j < keys.size(); j++) {
+                std::string key = keys[i];
+                eckit::Value v = a[key];
+
+                if (v.isList())
+                {
+                    for (size_t k = 0; k < v.size(); k++) {
+                        never_[key].insert(v[k]);
+                    }
+                }
+                else {
+                    never_[key].insert(v);
+                }
+            }
+        }
+    }
+
+}
+
+Type::~Type() {
+}
+
+bool Type::flatten() const {
+    return flatten_;
+}
+
+size_t Type::count(const std::vector<std::string>& values) const {
+    return flatten_ ? values.size() : 1;
+}
+
+class NotInSet {
+
+    std::set<std::string> set_;
+public:
+
+    NotInSet(const std::vector<std::string>& f):
+        set_(f.begin(), f.end()) {}
+
+    bool operator()(const std::string& s) const {
+        return set_.find(s) == set_.end();
+    }
+};
+
+bool Type::filter(const std::vector<std::string> &filter, std::vector<std::string> &values) const {
+
+    NotInSet not_in_set(filter);
+
+    values.erase(std::remove_if(values.begin(), values.end(), not_in_set), values.end());
+
+    return !values.empty();
+
+}
+
+class InSet {
+
+    std::set<std::string> set_;
+public:
+
+    InSet(const std::vector<std::string>& f):
+        set_(f.begin(), f.end()) {}
+
+    bool operator()(const std::string& s) const {
+        return set_.find(s) != set_.end();
+    }
+};
+
+bool Type::matches(const std::vector<std::string> &match, const std::vector<std::string> &values) const {
+    InSet in_set(match);
+    return std::find_if(values.begin(), values.end(), in_set) !=  values.end();
+}
+
+std::string Type::tidy(const std::string &value) const  {
+    return value;
+}
+
+std::ostream &operator<<(std::ostream &s, const Type &x) {
+    x.print(s);
+    return s;
+}
+
+void Type::expand(std::vector<std::string>& values) const {
+    std::vector<std::string> newvals;
+
+    for (std::vector<std::string>::const_iterator j = values.begin(); j != values.end(); ++j) {
+        newvals.push_back(tidy(*j));
+    }
+
+    // std::cout << "expand " << name_ << " " << values << " " << newvals << std::endl;
+
+    std::swap(newvals, values);
+
+}
+
+void Type::setDefaults(MarsRequest& request) {
+    if (!defaults_.empty()) {
+        request.setValuesTyped(this, defaults_);
+    }
+}
+
+void Type::setDefaults(const std::vector<std::string>& defaults) {
+    defaults_ = defaults;
+}
+
+const std::vector<std::string>& Type::flattenValues(const MarsRequest& request) {
+    return request.values(name_);
+}
+
+
+void Type::clearDefaults() {
+    defaults_.clear();
+}
+
+void Type::reset() {
+    defaults_ = originalDefaults_;
+}
+
+const std::string& Type::name() const {
+    return name_;
+}
+
+void Type::finalise(MarsRequest& request) {
+    bool ok = true;
+
+    const std::vector<std::string>& values = request.values(name_, true);
+    if (values.size() == 1 && values[0] == "off") {
+        ok = false;
+    }
+
+    for (std::map<std::string, std::set<std::string> >::const_iterator
+            j = only_.begin(); ok && j != only_.end(); ++j) {
+
+        const std::string& name = (*j).first;
+        const std::set<std::string>& only = (*j).second;
+
+        const std::vector<std::string>& values = request.values(name, true);
+        for (std::vector<std::string>::const_iterator k = values.begin(); ok && k != values.end(); ++k) {
+            if (only.find(*k) == only.end()) {
+                ok = false;
+            }
+        }
+    }
+
+    for (std::map<std::string, std::set<std::string> >::const_iterator
+            j = never_.begin(); ok && j != never_.end(); ++j) {
+
+        const std::string& name = (*j).first;
+        const std::set<std::string>& never = (*j).second;
+
+        const std::vector<std::string>& values = request.values(name, true);
+        for (std::vector<std::string>::const_iterator k = values.begin(); ok && k != values.end(); ++k) {
+            if (never.find(*k) != never.end()) {
+                ok = false;
+            }
+        }
+    }
+
+    if (!ok) {
+        request.unsetValues(name_);
+    }
+
+}
+
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/Type.h b/metkit/src/metkit/types/Type.h
new file mode 100644
index 0000000..23de94c
--- /dev/null
+++ b/metkit/src/metkit/types/Type.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   Type.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_Type_H
+#define metkit_Type_H
+
+#include <string>
+
+#include "eckit/memory/Counted.h"
+#include "eckit/types/Types.h"
+#include "eckit/value/Value.h"
+
+
+namespace metkit {
+
+class MarsRequest;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Type : public eckit::Counted {
+
+public: // methods
+
+    Type(const std::string &name, const eckit::Value& settings);
+
+
+    virtual std::string tidy(const std::string &value) const ;
+
+    virtual void expand(std::vector<std::string>& values) const;
+    virtual void setDefaults(MarsRequest& request);
+    virtual void setDefaults(const std::vector<std::string>& defaults);
+    virtual void clearDefaults();
+    virtual void reset();
+
+    virtual void finalise(MarsRequest& request);
+
+    virtual const std::vector<std::string>& flattenValues(const MarsRequest& request);
+    virtual bool flatten() const;
+
+    virtual bool filter(const std::vector< std::string >& filter, std::vector<std::string>& values) const;
+    virtual bool matches(const std::vector< std::string >& filter, const std::vector<std::string>& values) const;
+
+    const std::string& name() const;
+
+    friend std::ostream &operator<<(std::ostream &s, const Type &x);
+
+    virtual size_t count(const std::vector<std::string>& values) const;
+
+
+public: // class methods
+
+    static const Type &lookup(const std::string &keyword);
+
+protected: // members
+
+    std::string name_;
+    std::vector<std::string> defaults_;
+    bool flatten_;
+    std::vector<std::string> originalDefaults_;
+
+    std::map<std::string, std::set<std::string> > only_;
+    std::map<std::string, std::set<std::string> > never_;
+
+protected: // methods
+
+    virtual ~Type();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const = 0;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeAny.cc b/metkit/src/metkit/types/TypeAny.cc
new file mode 100644
index 0000000..9fb1e27
--- /dev/null
+++ b/metkit/src/metkit/types/TypeAny.cc
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeAny.h"
+
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeAny::TypeAny(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+}
+
+TypeAny::~TypeAny() {
+}
+
+void TypeAny::print(std::ostream &out) const {
+    out << "TypeAny[name=" << name_ << "]";
+}
+
+static TypeBuilder<TypeAny> type("any");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeAny.h b/metkit/src/metkit/types/TypeAny.h
new file mode 100644
index 0000000..a8cd6e1
--- /dev/null
+++ b/metkit/src/metkit/types/TypeAny.h
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeAny.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeAny_H
+#define metkit_TypeAny_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeAny : public Type {
+
+public: // methods
+
+    TypeAny(const std::string &name, const eckit::Value& settings = eckit::Value());
+
+    virtual ~TypeAny();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeDate.cc b/metkit/src/metkit/types/TypeDate.cc
new file mode 100644
index 0000000..995e9f1
--- /dev/null
+++ b/metkit/src/metkit/types/TypeDate.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "eckit/types/Date.h"
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeDate.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeDate::TypeDate(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+}
+
+TypeDate::~TypeDate() {
+}
+
+
+std::string TypeDate::tidy(const std::string &value) const {
+    if (!value.empty() && (value[0] == '0' || value[0] == '-')) {
+        eckit::Translator<std::string, long> t;
+        long n = t(value);
+        if (n <= 0) {
+            eckit::Date now(n);
+            eckit::Translator<long, std::string> t;
+            return t(now.yyyymmdd());
+        }
+    }
+    return value;
+
+}
+
+void TypeDate::print(std::ostream & out) const {
+    out << "TypeDate[name=" << name_ << "]";
+}
+
+static TypeBuilder<TypeDate> type("date");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeDate.h b/metkit/src/metkit/types/TypeDate.h
new file mode 100644
index 0000000..b7d8563
--- /dev/null
+++ b/metkit/src/metkit/types/TypeDate.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeDate.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeDate_H
+#define metkit_TypeDate_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeDate : public Type {
+
+public: // methods
+
+    TypeDate(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeDate();
+
+    virtual std::string tidy(const std::string &value) const ;
+
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeEnum.cc b/metkit/src/metkit/types/TypeEnum.cc
new file mode 100644
index 0000000..5f13ab4
--- /dev/null
+++ b/metkit/src/metkit/types/TypeEnum.cc
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeEnum.h"
+#include "metkit/MarsLanguage.h"
+#include "eckit/parser/JSONParser.h"
+
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeEnum::TypeEnum(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings),
+    multiple_(false) {
+
+    if (settings.contains("multiple")) {
+        multiple_ = settings["multiple"];
+    }
+
+    eckit::Value values = settings["values"];
+
+    if (!values.isList()) {
+        values = MarsLanguage::jsonFile(values);
+        ASSERT(values.isList());
+    }
+
+    for (size_t i = 0; i < values.size(); ++i) {
+
+        const eckit::Value& val = values[i];
+
+        if (val.isList()) {
+            ASSERT(val.size() > 0);
+
+            std::string first = val[0];
+
+            for (size_t j = 0; j < val.size(); ++j) {
+                std::string v = val[j];
+
+                if(mapping_.find(v) != mapping_.end()) {
+                    std::ostringstream oss;
+                    oss << "Redefined enum '" << v << "', '" << first << "' and '" << mapping_[v] << "'";
+                    throw eckit::SeriousBug(oss.str());
+                }
+
+                mapping_[v] = first;
+                values_.push_back(v);
+            }
+        }
+        else {
+            std::string v = val;
+                if(mapping_.find(v) != mapping_.end()) {
+                    std::ostringstream oss;
+                    oss << "Redefined enum '" << v << "' and '" << mapping_[v] << "'";
+                    throw eckit::SeriousBug(oss.str());
+                }
+            mapping_[v] = v;
+            values_.push_back(v);
+        }
+    }
+
+
+}
+
+TypeEnum::~TypeEnum() {
+}
+
+void TypeEnum::print(std::ostream &out) const {
+    out << "TypeEnum[name=" << name_ << "]";
+}
+
+bool TypeEnum::expand(std::vector<std::string>& values, bool fail) const {
+
+    std::vector<std::string> newval;
+    newval.reserve(values.size());
+
+    for (std::vector<std::string>::const_iterator j = values.begin(); j != values.end(); ++j) {
+
+        std::map<std::string, std::string>::iterator c = cache_.find(*j);
+        if (c != cache_.end()) {
+            newval.push_back((*c).second);
+            continue;
+        }
+
+        std::string v = MarsLanguage::bestMatch((*j), values_, fail, mapping_);
+        if (v.empty()) {
+            return false;
+        }
+        std::map<std::string, std::string>::const_iterator k = mapping_.find(v);
+        ASSERT(k != mapping_.end());
+        newval.push_back((*k).second);
+        cache_[*j] = (*k).second;
+    }
+    std::swap(values, newval);
+
+    if (!multiple_ && values.size() > 1) {
+        throw eckit::UserError("Only one value passible for '" + name_ + "'");
+    }
+
+    return true;
+}
+
+
+void TypeEnum::expand(std::vector<std::string>& values) const {
+    expand(values, true);
+}
+
+void TypeEnum::reset() {
+    // cache_.clear();
+    Type::reset();
+}
+
+static TypeBuilder<TypeEnum> type("enum");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeEnum.h b/metkit/src/metkit/types/TypeEnum.h
new file mode 100644
index 0000000..30c1c08
--- /dev/null
+++ b/metkit/src/metkit/types/TypeEnum.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeEnum.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeEnum_H
+#define metkit_TypeEnum_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeEnum : public Type {
+
+public: // methods
+
+    TypeEnum(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeEnum();
+
+    virtual void expand(std::vector<std::string>& values) const;
+
+protected:
+    virtual bool expand(std::vector<std::string>& values, bool fail) const;
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual void reset();
+
+
+    std::map<std::string, std::string> mapping_;
+    std::vector<std::string> values_;
+    bool multiple_;
+
+    mutable std::map<std::string, std::string> cache_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeExpver.cc b/metkit/src/metkit/types/TypeExpver.cc
new file mode 100644
index 0000000..7e67483
--- /dev/null
+++ b/metkit/src/metkit/types/TypeExpver.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "eckit/types/Date.h"
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeExpver.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeExpver::TypeExpver(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+}
+
+TypeExpver::~TypeExpver() {
+}
+
+std::string TypeExpver::tidy(const std::string &value) const {
+    std::ostringstream oss;
+    oss << std::setfill('0') << std::setw(4) << value;
+    return oss.str();
+}
+
+void TypeExpver::print(std::ostream &out) const {
+    out << "TypeExpver[name=" << name_ << "]";
+}
+
+static TypeBuilder<TypeExpver> type("expver");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeExpver.h b/metkit/src/metkit/types/TypeExpver.h
new file mode 100644
index 0000000..fd6a323
--- /dev/null
+++ b/metkit/src/metkit/types/TypeExpver.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeExpver.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeExpver_H
+#define metkit_TypeExpver_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeExpver : public Type {
+
+public: // methods
+
+    TypeExpver(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeExpver();
+
+    virtual std::string tidy(const std::string &value) const ;
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeFloat.cc b/metkit/src/metkit/types/TypeFloat.cc
new file mode 100644
index 0000000..4824d8c
--- /dev/null
+++ b/metkit/src/metkit/types/TypeFloat.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeFloat.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeFloat::TypeFloat(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+}
+
+TypeFloat::~TypeFloat() {
+}
+
+std::string TypeFloat::tidy(const std::string &value) const  {
+
+    for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) {
+        switch (*j) {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case '-':
+        case '.':
+            break;
+
+
+        default:
+            throw eckit::UserError(name_ + ": invalid float '" + value + "'");
+            break;
+        }
+    }
+
+    static eckit::Translator<std::string, double> s2d;
+    static eckit::Translator<double, std::string> d2s;
+    return d2s(s2d(value));
+}
+
+
+void TypeFloat::print(std::ostream &out) const {
+    out << "TypeFloat[name=" << name_ << "]";
+}
+
+static TypeBuilder<TypeFloat> type("float");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeFloat.h b/metkit/src/metkit/types/TypeFloat.h
new file mode 100644
index 0000000..9710d35
--- /dev/null
+++ b/metkit/src/metkit/types/TypeFloat.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeFloat.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeFloat_H
+#define metkit_TypeFloat_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeFloat : public Type {
+
+public: // methods
+
+    TypeFloat(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeFloat();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual std::string tidy(const std::string &value) const ;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeInteger.cc b/metkit/src/metkit/types/TypeInteger.cc
new file mode 100644
index 0000000..51728fd
--- /dev/null
+++ b/metkit/src/metkit/types/TypeInteger.cc
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeInteger.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeInteger::TypeInteger(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+}
+
+TypeInteger::~TypeInteger() {
+}
+
+void TypeInteger::print(std::ostream &out) const {
+    out << "TypeInteger[name=" << name_ << "]";
+}
+
+bool TypeInteger::ok(const std::string &value, long& n) const {
+    n = 0;
+    long sign = 1;
+    for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) {
+        switch (*j) {
+        case '-':
+            if (j == value.begin()) {
+                sign = -1;
+            }
+            else {
+                return false;
+            }
+            break;
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            n *= 10;
+            n += (*j) - '0';
+            break;
+
+        default:
+            return false;
+            break;
+        }
+    }
+    n *= sign;
+    return true;
+}
+
+std::string TypeInteger::tidy(const std::string &value) const  {
+    long n = 0;
+    if (ok(value, n)) {
+        static eckit::Translator<long, std::string> l2s;
+        return l2s(n);
+    }
+    throw eckit::UserError(name_ + ": invalid integer '" + value + "'");
+}
+
+
+static TypeBuilder<TypeInteger> type("integer");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeInteger.h b/metkit/src/metkit/types/TypeInteger.h
new file mode 100644
index 0000000..6598f4e
--- /dev/null
+++ b/metkit/src/metkit/types/TypeInteger.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeInteger.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeInteger_H
+#define metkit_TypeInteger_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeInteger : public Type {
+
+public: // methods
+
+    TypeInteger(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeInteger();
+
+protected:
+
+    bool ok(const std::string &value, long& n) const;
+    virtual std::string tidy(const std::string &value) const;
+
+private: // methods
+
+
+    virtual void print( std::ostream &out ) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeMixed.cc b/metkit/src/metkit/types/TypeMixed.cc
new file mode 100644
index 0000000..508137f
--- /dev/null
+++ b/metkit/src/metkit/types/TypeMixed.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeMixed.h"
+
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeMixed::TypeMixed(const std::string &name, const eckit::Value& settings) :
+    TypeEnum(name, settings) {
+    eckit::Value types = settings["more"];
+    for (size_t i = 0; i < types.size(); ++i) {
+        Type *k = TypesFactory::build(name, types[i]);
+        k->attach();
+        types_.push_back(k);
+    }
+}
+
+TypeMixed::~TypeMixed() {
+    for (std::vector<Type*>::iterator j = types_.begin(); j != types_.end(); ++j) {
+         (*j)->detach();
+    }
+}
+
+void TypeMixed::print(std::ostream &out) const {
+    out << "TypeMixed[name=" << name_ << "]";
+}
+
+std::string TypeMixed::tidy(const std::string &value) const {
+    NOTIMP;
+}
+
+void TypeMixed::expand(std::vector<std::string>& values) const {
+    if (!TypeEnum::expand(values, false)) {
+        for (std::vector<Type*>::const_iterator j = types_.begin(); j != types_.end(); ++j) {
+            (*j)->expand(values);
+        }
+    }
+}
+
+
+static TypeBuilder<TypeMixed> type("enum-or-more");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeMixed.h b/metkit/src/metkit/types/TypeMixed.h
new file mode 100644
index 0000000..26cb96e
--- /dev/null
+++ b/metkit/src/metkit/types/TypeMixed.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeMixed.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeMixed_H
+#define metkit_TypeMixed_H
+
+#include "metkit/types/TypeEnum.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeMixed : public TypeEnum {
+
+public: // methods
+
+    TypeMixed(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeMixed();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual std::string tidy(const std::string &value) const ;
+    virtual void expand(std::vector<std::string>& values) const;
+
+    std::vector<Type*> types_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeParam.cc b/metkit/src/metkit/types/TypeParam.cc
new file mode 100644
index 0000000..7fc38e3
--- /dev/null
+++ b/metkit/src/metkit/types/TypeParam.cc
@@ -0,0 +1,178 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeParam.h"
+#include "metkit/MarsLanguage.h"
+#include "eckit/parser/JSONParser.h"
+#include "eckit/types/Types.h"
+#include "eckit/parser/StringTools.h"
+
+#include "eckit/thread/AutoLock.h"
+
+static eckit::Mutex *local_mutex = 0;
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+static std::map<std::string, std::string> *m = 0;
+
+static void init() {
+
+    local_mutex = new eckit::Mutex();
+    m = new std::map<std::string, std::string>();
+
+    eckit::PathName language("~metkit/etc/param.json");
+
+    std::ifstream in(language.asString().c_str());
+    if (!in) {
+        throw eckit::CantOpenFile(language);
+    }
+
+    eckit::JSONParser parser(in);
+
+    const eckit::Value values = parser.parse();
+    std::map<std::string, std::string>& mapping = *m;
+
+    for (size_t i = 0; i < values.size(); ++i) {
+
+        const eckit::Value& val = values[i];
+
+        ASSERT (val.isList()) ;
+        ASSERT(val.size() > 0);
+
+        std::string first = val[0];
+
+        for (size_t j = 0; j < val.size(); ++j) {
+            std::string v = val[j];
+
+            if (mapping.find(v) != mapping.end()) {
+                std::cerr << "Redefined param '" << v << "', '" << first << "' and '" << mapping[v] << "'" << std::endl;
+                continue;
+            }
+
+            mapping[v] = first;
+        }
+    }
+}
+
+static const std::string& lookup(const std::string & s, bool fail) {
+    pthread_once(&once, init);
+    eckit::AutoLock<eckit::Mutex> lock(local_mutex);
+
+    std::map<std::string, std::string>& mapping = *m;
+
+    std::map<std::string, std::string>::const_iterator j = mapping.find(s);
+    if (j != mapping.end()) {
+        return (*j).second;
+    }
+
+    std::string low = eckit::StringTools::lower(s);
+    j = mapping.find(low);
+    if (j != mapping.end()) {
+        mapping[low] = (*j).second;
+        return (*j).second;
+    }
+
+    size_t table = 0;
+    size_t param = 0;
+    size_t *n = ¶m;
+    bool ok = true;
+
+    for (std::string::const_iterator k = low.begin(); k != low.end(); ++k) {
+        switch (*k) {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            (*n) *= 10;
+            (*n) += (*k) - '0';
+             break;
+
+        case '.':
+            if(n == &param) {
+                n = &table;
+            }
+            else {
+                ok = false;
+            }
+            break;
+
+        default:
+            ok = false;
+            break;
+        }
+    }
+
+    if(ok && param > 0) {
+        std::ostringstream oss;
+        oss <<  table * 1000 + param;
+        mapping[s] = oss.str();
+        return mapping[s];
+
+    }
+
+    if(fail) {
+        throw eckit::UserError("Invalid parameter '" + s + "'");
+    }
+
+    static std::string empty;
+    return empty;
+}
+
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeParam::TypeParam(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings) {
+
+
+
+}
+
+TypeParam::~TypeParam() {
+}
+
+void TypeParam::print(std::ostream &out) const {
+    out << "TypeParam[name=" << name_ << "]";
+}
+
+bool TypeParam::expand(std::vector<std::string>& values, bool fail) const {
+
+
+    for (std::vector<std::string>::iterator j = values.begin(); j != values.end(); ++j) {
+        std::string& s = (*j);
+        s = ::lookup(s, fail);
+    }
+
+    return true;
+}
+
+
+void TypeParam::expand(std::vector<std::string>& values) const {
+    expand(values, true);
+}
+
+void TypeParam::reset() {
+    // cache_.clear();
+    Type::reset();
+}
+
+static TypeBuilder<TypeParam> type("param");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeParam.h b/metkit/src/metkit/types/TypeParam.h
new file mode 100644
index 0000000..747ac17
--- /dev/null
+++ b/metkit/src/metkit/types/TypeParam.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeParam.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeParam_H
+#define metkit_TypeParam_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeParam : public Type {
+
+public: // methods
+
+    TypeParam(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeParam();
+
+    virtual void expand(std::vector<std::string>& values) const;
+
+protected:
+    virtual bool expand(std::vector<std::string>& values, bool fail) const;
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual void reset();
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeRange.cc b/metkit/src/metkit/types/TypeRange.cc
new file mode 100644
index 0000000..68a204b
--- /dev/null
+++ b/metkit/src/metkit/types/TypeRange.cc
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeRange.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeRange::TypeRange(const std::string &name, const eckit::Value& settings) :
+    TypeToByList(name, settings) {
+}
+
+TypeRange::~TypeRange() {
+}
+
+void TypeRange::print(std::ostream &out) const {
+    out << "TypeRange[name=" << name_ << "]";
+}
+
+std::string TypeRange::tidy(const std::string &value) const  {
+
+   long p = 0;
+    if (ok(value, p)) {
+        static eckit::Translator<long, std::string> l2s;
+        return l2s(p);
+    }
+
+    long a = 0;
+    long b = 0;
+
+    long *n = &a;
+
+    for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) {
+        switch (*j) {
+        case '-':
+            if (j != value.begin()) {
+                if (n == &b) {
+                    throw eckit::UserError(name_ + ": invalid integer range '" + value + "' (a)");
+
+                }
+                n = &b;
+            }
+            else {
+                throw eckit::UserError(name_ + ": invalid integer range '" + value + "' (b)");
+            }
+            break;
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            (*n) *= 10;
+            (*n) += (*j) - '0';
+            break;
+
+
+        default:
+
+            throw eckit::UserError(name_ + ": invalid integer range '" + value + "' (c)");
+               break;
+        }
+    }
+
+    if (n == &a) {
+        std::ostringstream oss;
+        oss << a;
+
+        return oss.str();
+    }
+
+    std::ostringstream oss;
+    oss << a << "-" << b;
+
+    return oss.str();
+}
+
+
+static TypeBuilder<TypeRange> type("range");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeRange.h b/metkit/src/metkit/types/TypeRange.h
new file mode 100644
index 0000000..b3674c1
--- /dev/null
+++ b/metkit/src/metkit/types/TypeRange.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeRange.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeRange_H
+#define metkit_TypeRange_H
+
+#include "metkit/types/TypeToByList.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeRange : public TypeToByList {
+
+public: // methods
+
+    TypeRange(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeRange();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual std::string tidy(const std::string &value) const;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeTime.cc b/metkit/src/metkit/types/TypeTime.cc
new file mode 100644
index 0000000..ddaa092
--- /dev/null
+++ b/metkit/src/metkit/types/TypeTime.cc
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+
+#include "eckit/types/Date.h"
+#include "metkit/MarsRequest.h"
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeTime.h"
+#include "eckit/parser/StringTools.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeTime::TypeTime(const std::string &name, const eckit::Value& settings) :
+    Type(name, settings), by_(6) {
+}
+
+TypeTime::~TypeTime() {
+}
+
+std::string TypeTime::tidy(const std::string &value) const {
+
+    long n = 0;
+    int colon = 0;
+
+    for (std::string::const_iterator j = value.begin(); j != value.end(); ++j) {
+        switch (*j) {
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            n *= 10;
+            n += (*j) - '0';
+            break;
+
+        case ':':
+            colon++;
+            break;
+
+        default:
+            throw eckit::UserError(name_ + ": invalid time '" + value + "'");
+            break;
+        }
+    }
+
+    if (colon == 2) {
+        n /= 100;
+    }
+
+    if (n < 100) {
+        n *= 100;
+    }
+
+    std::ostringstream oss;
+    oss << std::setfill('0') << std::setw(4) << n;
+    return oss.str();
+}
+
+
+void TypeTime::expand(std::vector<std::string>& values) const {
+
+     static eckit::Translator<std::string, long> s2l;
+    static eckit::Translator<long, std::string> l2s;
+
+    if (values.size() == 3) {
+        if (eckit::StringTools::lower(values[1])[0] == 't') {
+            long from = s2l(tidy(values[0]));
+            long to = s2l(tidy(values[2]));
+            long by = by_;
+            values.clear();
+            values.reserve((to - from) / by + 1);
+            for (long i = from; i <= to; i += by) {
+                values.push_back(tidy(l2s(i)));
+            }
+            return;
+        }
+    }
+
+    if (values.size() == 5) {
+        if (eckit::StringTools::lower(values[1])[0] == 't' && eckit::StringTools::lower((values[3])) == "by") {
+            long from = s2l(tidy(values[0]));
+            long to = s2l(tidy(values[2]));
+            long by = s2l(tidy(values[4]));
+            values.clear();
+            values.reserve((to - from) / by + 1);
+
+            for (long i = from; i <= to; i += by) {
+                values.push_back(tidy(l2s(i)));
+            }
+            return;
+        }
+    }
+
+    Type::expand(values);
+}
+
+void TypeTime::print(std::ostream &out) const {
+    out << "TypeTime[name=" << name_ << "]";
+}
+
+static TypeBuilder<TypeTime> type("time");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeTime.h b/metkit/src/metkit/types/TypeTime.h
new file mode 100644
index 0000000..db3f742
--- /dev/null
+++ b/metkit/src/metkit/types/TypeTime.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeTime.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeTime_H
+#define metkit_TypeTime_H
+
+#include "metkit/types/Type.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeTime : public Type {
+
+public: // methods
+
+    TypeTime(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeTime();
+
+    virtual std::string tidy(const std::string &value) const ;
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual void expand(std::vector<std::string>& values) const;
+
+    long by_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypeToByList.cc b/metkit/src/metkit/types/TypeToByList.cc
new file mode 100644
index 0000000..16e4b36
--- /dev/null
+++ b/metkit/src/metkit/types/TypeToByList.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeToByList.h"
+
+#include "eckit/utils/Translator.h"
+#include "eckit/parser/StringTools.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypeToByList::TypeToByList(const std::string &name, const eckit::Value& settings) :
+    TypeInteger(name, settings),
+    by_(settings["by"]) {
+}
+
+TypeToByList::~TypeToByList() {
+}
+
+void TypeToByList::print(std::ostream &out) const {
+    out << "TypeToByList[name=" << name_ << "]";
+}
+
+void TypeToByList::expand(std::vector<std::string>& values) const {
+
+    static eckit::Translator<std::string, long> s2l;
+    static eckit::Translator<long, std::string> l2s;
+
+    if (values.size() == 3) {
+        if (eckit::StringTools::lower(values[1])[0] == 't') {
+            long from = s2l(tidy(values[0]));
+            long to = s2l(tidy(values[2]));
+            long by = by_;
+            values.clear();
+            values.reserve((to - from) / by + 1);
+            for (long i = from; i <= to; i += by) {
+                values.push_back(l2s(i));
+            }
+            return;
+        }
+    }
+
+    if (values.size() == 5) {
+        if (eckit::StringTools::lower(values[1])[0] == 't' && eckit::StringTools::lower((values[3])) == "by") {
+                long from = s2l(tidy(values[0]));
+                long to = s2l(tidy(values[2]));
+                long by = s2l(tidy(values[4]));
+                values.clear();
+                values.reserve((to - from) / by + 1);
+
+                for (long i = from; i <= to; i += by) {
+                    values.push_back(l2s(i));
+                }
+                return;
+        }
+    }
+
+    TypeInteger::expand(values);
+}
+
+static TypeBuilder<TypeToByList> type("to-by-list");
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypeToByList.h b/metkit/src/metkit/types/TypeToByList.h
new file mode 100644
index 0000000..60782e8
--- /dev/null
+++ b/metkit/src/metkit/types/TypeToByList.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypeToByList.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypeToByList_H
+#define metkit_TypeToByList_H
+
+#include "metkit/types/TypeInteger.h"
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class TypeToByList : public TypeInteger {
+
+public: // methods
+
+    TypeToByList(const std::string &name, const eckit::Value& settings);
+
+    virtual ~TypeToByList();
+
+private: // methods
+
+    virtual void print( std::ostream &out ) const;
+    virtual void expand(std::vector<std::string>& values) const;
+
+    long by_;
+
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/metkit/types/TypesFactory.cc b/metkit/src/metkit/types/TypesFactory.cc
new file mode 100644
index 0000000..e67d13e
--- /dev/null
+++ b/metkit/src/metkit/types/TypesFactory.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/value/Value.h"
+
+#include "metkit/types/TypesFactory.h"
+
+using namespace eckit;
+
+namespace metkit {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+TypesFactory::TypesFactory(const std::string &name) :
+    name_(name) {
+    TypesRegistry::instance().add(name, this);
+}
+
+TypesFactory::~TypesFactory() {
+    TypesRegistry::instance().remove(name_);
+}
+
+Type* TypesRegistry::build(const std::string &keyword, const eckit::Value& settings) {
+
+    std::string name = settings["type"];
+
+    AutoLock<Mutex> lock(mutex_);
+
+    std::map<std::string, TypesFactory *>::const_iterator j = m_.find(name);
+
+    if (j == m_.end()) {
+        eckit::Log::error() << "No TypesFactory for [" << name << "]" << std::endl;
+        eckit::Log::error() << "KeywordTypes are:" << std::endl;
+        for (j = m_.begin() ; j != m_.end() ; ++j)
+            eckit::Log::error() << "   " << (*j).first << std::endl;
+        throw eckit::SeriousBug(std::string("No TypesFactory called ") + name);
+    }
+
+    return (*j).second->make(keyword, settings);
+}
+
+void TypesRegistry::list(std::ostream& s) {
+
+    AutoLock<Mutex> lock(mutex_);
+
+    s << "[";
+
+    bool first = true;
+    std::map<std::string, TypesFactory*>::const_iterator j = m_.begin();
+    while (j != m_.end()) {
+        if (!first) s << ",";
+        s << (j++)->first;
+        first = false;
+    }
+
+    s << "]";
+}
+
+Type* TypesFactory::build(const std::string &keyword, const eckit::Value& settings) {
+    return TypesRegistry::instance().build(keyword, settings);
+}
+
+void TypesFactory::list(std::ostream& s) {
+    TypesRegistry::instance().list(s);
+}
+
+TypesRegistry& TypesRegistry::instance()
+{
+    static TypesRegistry instance;
+    return instance;
+}
+
+void TypesRegistry::add(const std::string& name, TypesFactory* f) {
+
+    AutoLock<Mutex> lock(mutex_);
+
+    ASSERT(m_.find(name) == m_.end());
+    m_[name] = f;
+}
+
+void TypesRegistry::remove(const std::string& name) {
+
+    AutoLock<Mutex> lock(mutex_);
+    m_.erase(name);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
diff --git a/metkit/src/metkit/types/TypesFactory.h b/metkit/src/metkit/types/TypesFactory.h
new file mode 100644
index 0000000..06b6346
--- /dev/null
+++ b/metkit/src/metkit/types/TypesFactory.h
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   TypesFactory.h
+/// @author Baudouin Raoult
+/// @author Tiago Quintino
+/// @date   April 2016
+
+#ifndef metkit_TypesFactory_H
+#define metkit_TypesFactory_H
+
+#include <string>
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/types/Types.h"
+
+namespace eckit {
+class Value;
+}
+
+namespace metkit {
+
+class Type;
+class TypesFactory;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+
+class TypesRegistry : private eckit::NonCopyable {
+
+    eckit::Mutex mutex_;
+    std::map<std::string, TypesFactory*> m_;
+
+public:
+
+    static TypesRegistry& instance();
+
+    void add(const std::string& name, TypesFactory* f);
+    void remove(const std::string& name);
+
+    Type* build(const std::string &keyword, const eckit::Value&);
+
+    void list(std::ostream& s);
+};
+
+/// A self-registering factory for producing TypesFactory instances
+
+class TypesFactory {
+public:
+
+    virtual Type *make(const std::string &keyword, const eckit::Value& settings) const = 0 ;
+
+    static Type* build(const std::string &keyword, const eckit::Value& settings);
+
+    static void list(std::ostream& s);
+
+protected:
+
+    TypesFactory(const std::string &);
+    virtual ~TypesFactory();
+
+    std::string name_;
+
+};
+
+/// Templated specialisation of the self-registering factory,
+/// that does the self-registration, and the construction of each object.
+
+template< class T>
+class TypeBuilder : public TypesFactory {
+
+    virtual Type *make(const std::string &keyword, const eckit::Value& settings) const {
+        return new T(keyword, settings);
+    }
+
+public:
+    TypeBuilder(const std::string &name) : TypesFactory(name) {}
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+} // namespace metkit
+
+#endif
diff --git a/metkit/src/tests/CMakeLists.txt b/metkit/src/tests/CMakeLists.txt
new file mode 100644
index 0000000..ef38a05
--- /dev/null
+++ b/metkit/src/tests/CMakeLists.txt
@@ -0,0 +1,35 @@
+# (C) Copyright 1996-2017 ECMWF.
+#
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+# In applying this licence, ECMWF does not waive the privileges and immunities
+# granted to it by virtue of its status as an intergovernmental organisation
+# nor does it submit to any jurisdiction.
+
+ecbuild_get_test_multidata( TARGET grib_get_data
+                            DIRNAME grib_api/data
+                            NAMES latlon.grib )
+
+ecbuild_add_test( TARGET        metkit_test_emosfile
+                  ARGS          --log_level=message
+                  BOOST
+                  CONDITION     HAVE_GRIB
+                  INCLUDES      "${ECKIT_INCLUDE_DIRS}"
+                  SOURCES       test_emosfile.cc
+                  LIBS          metkit
+                  TEST_DEPENDS  grib_get_data
+)
+
+ecbuild_add_test( TARGET        metkit_test_multihandle
+                  BOOST
+                  CONDITION     HAVE_GRIB
+                  INCLUDES      "${GRIB_API_INCLUDE_DIRS}"
+                  SOURCES       test_multihandle.cc
+                  LIBS          metkit
+                  TEST_DEPENDS  grib_get_data
+)
+
+ecbuild_add_test( TARGET    test_typesfactory
+                  SOURCES   test_typesfactory.cc
+                  INCLUDES  "${ECKIT_INCLUDE_DIRS}"
+                  LIBS      metkit )
diff --git a/metkit/src/tests/test_emosfile.cc b/metkit/src/tests/test_emosfile.cc
new file mode 100644
index 0000000..0d8f70b
--- /dev/null
+++ b/metkit/src/tests/test_emosfile.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   test_emosfile.cc
+/// @date   Jan 2016
+/// @author Florian Rathgeber
+
+#define BOOST_TEST_MODULE metkit_grib_EmosFile
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/Buffer.h"
+
+#include "eckit/testing/Setup.h"
+
+#include "metkit/grib/EmosFile.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+namespace metkit {
+namespace grib {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const size_t GRIB_SIZE = 858;
+
+struct F {
+    F() : file("latlon.grib") {}
+    EmosFile file;
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( metkit_grib_EmosFile )
+
+BOOST_FIXTURE_TEST_CASE( test_read, F ) {
+    Buffer buf(1024);
+    size_t len = file.read(buf);
+    BOOST_CHECK_LT(len, buf.size());
+    BOOST_CHECK_EQUAL(len, GRIB_SIZE);
+}
+
+BOOST_FIXTURE_TEST_CASE( test_read_some, F ) {
+    Buffer buf(1024);
+    size_t len = file.readSome(buf);
+    BOOST_CHECK_LT(len, buf.size());
+    BOOST_CHECK_EQUAL(len, GRIB_SIZE);
+}
+
+BOOST_FIXTURE_TEST_CASE( test_read_some_smallbuff, F ) {
+    Buffer buf(512);
+    size_t len = file.readSome(buf);
+    BOOST_CHECK_GT(len, buf.size());
+    BOOST_CHECK_EQUAL(len, GRIB_SIZE);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace grib
+} // namespace metkit
+
diff --git a/metkit/src/tests/test_multihandle.cc b/metkit/src/tests/test_multihandle.cc
new file mode 100644
index 0000000..8f77854
--- /dev/null
+++ b/metkit/src/tests/test_multihandle.cc
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2015 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation
+ * nor does it submit to any jurisdiction.
+ */
+
+/// @file   test_emosfile.cc
+/// @date   Jan 2016
+/// @author Florian Rathgeber
+
+#define BOOST_TEST_MODULE metkit_grib_multihandle
+#include "ecbuild/boost_test_framework.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/Buffer.h"
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/io/FileHandle.h"
+
+#include "eckit/testing/Setup.h"
+
+#include "metkit/grib/EmosFile.h"
+
+#include "grib_api.h"
+
+using namespace eckit;
+using namespace eckit::testing;
+
+BOOST_GLOBAL_FIXTURE(Setup);
+
+namespace metkit {
+namespace grib {
+namespace test {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+static const size_t GRIB_SIZE = 858;
+
+struct F {
+};
+
+//----------------------------------------------------------------------------------------------------------------------
+
+BOOST_AUTO_TEST_SUITE( metkit_grib_multihandle )
+
+BOOST_FIXTURE_TEST_CASE( fopen, F ) {
+
+    MultiHandle mh;
+
+    mh += new FileHandle("latlon.grib");
+    mh += new FileHandle("latlon.grib");
+
+    FILE* f = mh.openf("r");
+
+    grib_handle* h;
+
+    int err = 0;
+    size_t count = 0;
+    while( (h =  grib_handle_new_from_file(0, f, &err))) {
+        count++;
+        grib_handle_delete(h);
+    }
+
+    fclose(f);
+    BOOST_CHECK_EQUAL(count, 2);
+    BOOST_CHECK_EQUAL(err, GRIB_SUCCESS);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace grib
+} // namespace metkit
+
diff --git a/metkit/src/tests/test_typesfactory.cc b/metkit/src/tests/test_typesfactory.cc
new file mode 100644
index 0000000..917f01e
--- /dev/null
+++ b/metkit/src/tests/test_typesfactory.cc
@@ -0,0 +1,91 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file   test_typesfactory.cc
+/// @author Simon Smart
+/// @date   April 2017
+
+#include "metkit/types/TypesFactory.h"
+#include "metkit/types/TypeDate.h"
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/value/Value.h"
+
+using namespace eckit;
+using namespace metkit;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+// We use eckit::Tool for the tests as it ensures that the Main environment is set up
+// correctly, avoiding any unexpected segfaults inside eckit.
+
+class TypesFactoryTest : public Tool {
+
+public: // methods
+
+    TypesFactoryTest(int argc, char** argv);
+    virtual ~TypesFactoryTest();
+
+    virtual void run();
+
+private: // methods
+
+    void test_list_types();
+
+    void test_build();
+};
+
+
+TypesFactoryTest::TypesFactoryTest(int argc, char** argv) :
+    Tool(argc, argv) {}
+
+
+TypesFactoryTest::~TypesFactoryTest() {}
+
+
+void TypesFactoryTest::test_list_types() {
+
+    std::stringstream ss;
+    TypesFactory::list(ss);
+    ASSERT(ss.str() == std::string("[any,date,enum,enum-or-more,expver,float,integer,param,range,time,to-by-list]"));
+}
+
+
+void TypesFactoryTest::test_build() {
+
+    ValueMap settings;
+    settings["type"] = "date";
+
+    Type* t1(TypesFactory::build("abcd", Value(settings)));
+
+    ASSERT(t1 != 0);
+    t1->attach();
+
+    // Check that we have obtained the correct type
+    ASSERT(dynamic_cast<TypeDate*>(t1) != 0);
+
+    // Clean up, taking into account that ~Type is protected.
+    t1->detach();
+}
+
+
+void TypesFactoryTest::run() {
+    test_list_types();
+    test_build();
+}
+
+
+int main(int argc, char** argv) {
+    TypesFactoryTest tests(argc, argv);
+    return tests.start();
+}
+
+//----------------------------------------------------------------------------------------------------------------------
diff --git a/metkit/src/tools/CMakeLists.txt b/metkit/src/tools/CMakeLists.txt
new file mode 100644
index 0000000..521819d
--- /dev/null
+++ b/metkit/src/tools/CMakeLists.txt
@@ -0,0 +1,29 @@
+### provides MARS requests from GRIB files
+
+ecbuild_add_executable(
+
+    TARGET   grib-to-mars-request
+
+    CONDITION HAVE_GRIB
+
+    SOURCES  grib-to-mars-request.cc
+
+    INCLUDES
+        ${ECKIT_INCLUDE_DIRS}
+
+    LIBS     metkit
+)
+
+ecbuild_add_executable(
+
+    TARGET   parse-mars-request
+
+    # CONDITION HAVE_GRIB
+
+    SOURCES  parse-mars-request.cc
+
+    INCLUDES
+        ${ECKIT_INCLUDE_DIRS}
+
+    LIBS     metkit
+)
diff --git a/metkit/src/tools/grib-to-mars-request.cc b/metkit/src/tools/grib-to-mars-request.cc
new file mode 100644
index 0000000..59f316b
--- /dev/null
+++ b/metkit/src/tools/grib-to-mars-request.cc
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/io/Offset.h"
+
+#include "metkit/grib/EmosFile.h"
+
+#include "metkit/grib/GribToRequest.h"
+#include "metkit/MarsRequest.h"
+
+using namespace eckit;
+using namespace metkit;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class Grib2Request : public Tool {
+public:
+
+    Grib2Request(int argc,char **argv) :
+        Tool(argc,argv) {
+
+        path_ = eckit::Resource<std::string>("-in","input.grib"); ///< @todo Move to use Option
+
+    }
+
+    virtual ~Grib2Request() {}
+
+    virtual void run();
+
+private: // members
+
+     eckit::PathName path_;
+};
+
+void Grib2Request::run()
+{
+    Log::debug() << "Opening GRIB file : " << path_ << std::endl;
+
+    static long gribBufferSize = eckit::Resource<long>("gribBufferSize", 64*1024*1024);
+
+    Buffer buffer(gribBufferSize);
+
+    long len = 0;
+
+    grib::EmosFile file( path_ );
+
+    metkit::MarsRequest onereq("GRIB");
+
+    size_t nMsg = 0;
+    while( (len = file.readSome(buffer)) != 0 )
+    {
+        metkit::MarsRequest req("GRIB");
+
+        grib::GribToRequest::gribToRequest(buffer, len, req);
+
+        // Log::info() << req << std::endl;
+
+        ++nMsg;
+
+        onereq.merge(req);
+    }
+
+    Log::info() << onereq << std::endl;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc,char **argv)
+{
+    Grib2Request tool(argc,argv);
+    return tool.start();
+}
diff --git a/metkit/src/tools/parse-mars-request.cc b/metkit/src/tools/parse-mars-request.cc
new file mode 100644
index 0000000..75204a7
--- /dev/null
+++ b/metkit/src/tools/parse-mars-request.cc
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2017 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/io/Buffer.h"
+#include "eckit/io/Offset.h"
+
+#include "metkit/grib/EmosFile.h"
+
+#include "metkit/grib/GribToRequest.h"
+#include "metkit/MarsRequest.h"
+#include "metkit/MarsParser.h"
+#include "metkit/MarsExpension.h"
+
+using namespace eckit;
+using namespace metkit;
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ParseRequest : public Tool {
+public:
+
+    ParseRequest(int argc, char **argv) :
+        Tool(argc, argv) {
+
+        // path_ = eckit::Resource<std::string>("-in", "/Users/baudouin/Dropbox/diss/EC/EC1/req/curr/C1"); ///< @todo Move to use Option
+        // path_ = eckit::Resource<std::string>("-in", "/Users/baudouin/Dropbox/diss/XS/NA4/req/curr/N2");
+        // path_ = eckit::Resource<std::string>("-in", "/Users/baudouin/Dropbox/diss/CZ/CZM/req/curr/CJ");
+        // path_ = eckit::Resource<std::string>("-in", "/Users/baudouin/Dropbox/diss/FR/FRA/req/curr/FR");
+        path_ = eckit::Resource<std::string>("-in", "/Users/baudouin/Dropbox/diss");
+
+    }
+
+    virtual ~ParseRequest() {}
+
+private: // methods
+
+    virtual void run();
+    void process(const eckit::PathName& path);
+
+private: // members
+
+    eckit::PathName path_;
+
+
+};
+
+void ParseRequest::run() {
+    process(path_);
+}
+
+void ParseRequest::process(const eckit::PathName& path)
+{
+
+    if (path.isDir()) {
+        std::vector<PathName> files;
+        std::vector<PathName> directories;
+
+        path.children(files, directories);
+
+        std::sort(files.begin(), files.end());
+        std::sort(directories.begin(), directories.end());
+
+        for (std::vector<PathName>::const_iterator j = files.begin(); j != files.end(); ++j) {
+            process(*j);
+        }
+
+        for (std::vector<PathName>::const_iterator j = directories.begin(); j != directories.end(); ++j) {
+            process(*j);
+        }
+        return;
+    }
+
+
+    std::cout << "============= " << path << std::endl;
+    std::ifstream in(path.asString().c_str());
+    MarsParser parser(in);
+    MarsExpension expand(true);
+
+    std::vector<MarsRequest> p = parser.parse();
+    for (std::vector<MarsRequest>::const_iterator j = p.begin(); j != p.end(); ++j) {
+        (*j).dump(std::cout);
+    }
+
+    std::vector<MarsRequest> v = expand.expand(p);
+
+    for (std::vector<MarsRequest>::const_iterator j = v.begin(); j != v.end(); ++j) {
+        (*j).dump(std::cout);
+    }
+
+    class Print : public FlattenCallback {
+        virtual void operator()(const MarsRequest& request)  {
+            std::cout << request << std::endl;
+        }
+
+    };
+
+
+    Print cb;
+
+    // for (std::vector<MarsRequest>::const_iterator j = v.begin(); j != v.end(); ++j) {
+    //     expand.flatten(*j, cb, filter);
+    // }
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+    ParseRequest tool(argc, argv);
+    return tool.start();
+}
diff --git a/odb_api/.gitignore b/odb_api/.gitignore
new file mode 100644
index 0000000..14b93b5
--- /dev/null
+++ b/odb_api/.gitignore
@@ -0,0 +1,8 @@
+*.swp
+*.sublime-workspace
+.tags*
+CMakeLists.txt.user*
+*.autosave
+*.sublime-workspace
+.*.sw*
+.*un~
diff --git a/odb_api/CMakeLists.txt b/odb_api/CMakeLists.txt
new file mode 100644
index 0000000..e9a3bbe
--- /dev/null
+++ b/odb_api/CMakeLists.txt
@@ -0,0 +1,138 @@
+############################################################################################
+# cmake options:
+#
+#       -DCMAKE_BUILD_TYPE=Debug|RelWithDebInfo|Release|Production
+#       -DCMAKE_INSTALL_PREFIX=/path/to/install
+#
+#       -DCMAKE_MODULE_PATH=/path/to/ecbuild/cmake
+#
+#       -DCMAKE_C_COMPILER=gcc
+#       -DCMAKE_C_COMPILER=g++
+#
+#       -DCMAKE_PREFIX_PATH=/path/to/any/package/out/of/place
+#       -DBUILD_SHARED_LIBS=ON
+
+cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR )
+
+project( odb_api CXX Fortran )
+
+# Add path for custom modules
+set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
+
+include( ecbuild_system NO_POLICY_SCOPE )
+
+ecbuild_requires_macro_version( 1.9 )
+
+###############################################################################
+# local project
+
+ecbuild_use_package( PROJECT eckit VERSION 0.9 REQUIRED )
+ecbuild_use_package( PROJECT metkit VERSION 0.3 REQUIRED )
+
+ecbuild_declare_project()
+
+ecbuild_add_resources( TARGET ${PROJECT_NAME}_no_odb DONT_PACK_REGEX "*.odb" )
+ecbuild_add_resources( TARGET ${PROJECT_NAME}_no_oda DONT_PACK_REGEX "*.oda" )
+
+ecbuild_dont_pack(REGEX "*.odb")
+ecbuild_dont_pack(REGEX ".gdbinit")
+
+###############################################################################
+# some variables/options of this project
+
+ecbuild_add_option( FEATURE MIGRATOR
+                    DESCRIPTION "whether or not to build the ODB migrator tool"
+                    DEFAULT OFF
+                    REQUIRED_PACKAGES "PROJECT odb VERSION 1.0 QUIET" )
+
+ecbuild_add_option( FEATURE FORTRAN
+                    DESCRIPTION "whether or not to build the Fortran interface"
+                    DEFAULT OFF )
+
+ecbuild_add_option( FEATURE NETCDF
+                    DESCRIPTION "whether or not to build the odb2netcdf tool"
+                    DEFAULT OFF
+                    REQUIRED_PACKAGES "NetCDF COMPONENTS CXX" )
+
+ecbuild_add_option( FEATURE ODB_API_SERVER_SIDE
+                    DESCRIPTION "Support for keyword SERVER_SIDE"
+                    DEFAULT OFF )
+
+ecbuild_add_option( FEATURE ODB_SERVER_TIME_FORMAT_FOUR_DIGITS
+                    DESCRIPTION "ODB Server uses four digits format of time"
+                    DEFAULT OFF )
+
+SET(ODB_API_SCHEMA_PATH "" CACHE STRING "Path to schema file that will be loaded before executing SQL")
+
+if( HAVE_FORTRAN OR HAVE_MIGRATOR )
+
+    set( Fortran Fortran )
+    ecbuild_enable_fortran( REQUIRED MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/module )
+    ecbuild_find_fortranlibs( REQUIRED )
+    message( "Fortran libraries: [${FORTRAN_LIBRARIES}]" )
+
+endif()
+
+#if( HAVE_MIGRATOR )
+    #if(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
+    #    ecbuild_add_fortran_flags("-r8")
+    #    elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+    #    # NOTE that if we add -fdefault-real-8 then we NEED -fdefault-double-8 to avoid quadmath
+    #    ecbuild_add_fortran_flags("-fdefault-real-8 -fdefault-double-8")
+    #endif()
+#endif()
+
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
+  ecbuild_add_fortran_flags("-fPIC -ffree-line-length-none")
+endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  ecbuild_add_cxx_flags("-fPIC -Wl,--as-needed")
+endif()
+
+
+ecbuild_add_option( FEATURE OMP
+                    DESCRIPTION "Support for OpenMP threaded parallelism"
+                    REQUIRED_PACKAGES "OMP COMPONENTS CXX ${Fortran}" )
+
+if( HAVE_OMP )
+  ecbuild_enable_omp()
+else()
+  ecbuild_enable_ompstubs()
+endif()
+
+ecbuild_add_option( FEATURE PYTHON
+                    DESCRIPTION "whether or not to build the Python interface"
+                    DEFAULT OFF
+                    REQUIRED_PACKAGES "Python VERSION 2.7 NO_LIBS" SWIG )
+
+###############################################################################
+# contents
+
+set_directory_properties( PROPERTIES COMPILE_DEFINITIONS "${ECKIT_DEFINITIONS}" )
+
+get_directory_property( ODB_API_DEFINITIONS COMPILE_DEFINITIONS )
+
+set( ODB_API_INCLUDE_DIRS   ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src )
+set( ODB_API_LIBRARIES      Odb )
+
+if( HAVE_FORTRAN )
+  list( APPEND ODB_API_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/module )
+  list( INSERT ODB_API_LIBRARIES 0 Odb_fortran )
+endif()
+
+add_subdirectory( src )
+add_subdirectory( tests )
+
+############################################################################################
+# finalize
+
+ecbuild_pkgconfig( NAME ${PROJECT_NAME}
+                   DESCRIPTION "ECMWF coding, decoding and processing of observational data"
+                   URL "https://software.ecmwf.int/wiki/display/ODB/ODB+API"
+                   LIBRARIES Odb )
+
+ecbuild_install_project( NAME OdbAPI )
+
+ecbuild_print_summary()
+
diff --git a/odb_api/INSTALL b/odb_api/INSTALL
new file mode 100644
index 0000000..8c7ec56
--- /dev/null
+++ b/odb_api/INSTALL
@@ -0,0 +1,148 @@
+An up to date version of this document can be found at following address:
+
+   https://software.ecmwf.int/wiki/display/ODBAPI/ODB+API+Installation
+
+
+Following software packages are required to build ODB API library and command line tool:
+
+ - cmake 2.8.1 or newer,
+ - bison 2.3 or newer,
+ - flex (all known versions should be OK),
+ - a C++ compiler.
+
+Optionally, to build:
+
+ - ODB API Fortran bindings, a Fortran compiler is required,
+ - odb_migrator, a program to convert old ODB databases to the new format, it is required to have a valid installation of the old
+   ODB, and a Fortran compiler (the same as the one used to build the old ODB installation),
+ - ODB API Python bindings, SWIG 1.3.40 or newer and Python 2.7.
+
+After unpacking ODB API source create a build directory called build/production or build/debug inside the source's
+directory. This is where cmake will build binaries - we call it an external build because all the object files and binaries will
+be created outside of the source directory. Next step is to call cmake from within the directory (build/production of build/debug).
+
+It is recommended to create a shell script which calls cmake with appropriate options. In case of the example shell session below
+this script is called configure_ecmwf_ecgate.sh. 
+
+First parameter passed to cmake in case of an external build must be path to source code directory, in our case: ../.. All other
+parameters are optional. Detailed information on available cmake options follows the example.
+
+Cmake creates Makefiles only, so the next step is to call make and actually build the binaries:
+
+  $ pwd
+  /tmp/test
+  $ tar zxf OdbAPI-0.10.1-dev-Source.tar.gz
+  $ cd OdbAPI-0.10.1-dev-Source
+  $ mkdir -p build/production
+  $ cd build/production
+  $ ../../configure_ecmwf_ecgate.sh
+  $ make
+  $ make install
+
+
+Cmake options.
+
+In case of an external build the first parameter passed to cmake must be path to source code directory, in our case: ../.. 
+
+Other, optional, cmake options should be passed to cmake with prepended '-D' and followed by '=' and option's value. 
+For omitted options cmake will try to find appropriate values. We have included a script configure_example.sh which 
+can be used to build ODB API on ECMWF ecgate system.
+
+
+	CMAKE_C_COMPILER
+
+C compiler.
+
+
+	CMAKE_CXX_COMPILER
+
+C++ compiler.
+
+
+	CMAKE_Fortran_COMPILER
+
+Fortran compiler.
+
+
+	CMAKE_BUILD_TYPE
+
+Build type. Possible values: Debug, Production (case insensitive). At ECMWF we use following snippet to use build directory name
+as build type:
+
+  -DCMAKE_BUILD_TYPE=$(basename $(pwd) | sed 's/\W[a-zA-Z0-9]*//')
+
+
+    CMAKE_INSTALL_PREFIX
+
+Installation directory. The following shell command can be used to set this to a directory with added extra subdirectory with a
+name equal to the current version of the software:
+
+  -DCMAKE_INSTALL_PREFIX=/usr/local/apps/odb_api/`cat ../../VERSION.cmake|awk '{print $3}'|sed 's/["]//g'`/
+
+
+    CMAKE_PREFIX_PATH
+
+A comma separated list of directories where cmake will try to find libraries and tools.
+
+
+	PGI_PATH
+
+Path to Portland Group Fortran compiler installation.
+
+
+    ODB_PATH
+
+Path to old ODB installation. Normally it should be set to $ODB_ROOT.
+
+
+    CMAKE_MODULE_PATH
+
+Path to ECMWF cmake macros. The macros are supplied with ODB API source. For example, if the ODB API source code was unpacked in
+directory /tmp/test then CMAKE_MODULE_PATH should be set to /tmp/test/OdbAPI-0.10.1-dev-Source/ecbuild/cmake
+
+
+    ECKIT_SOURCE
+
+Path to EcKit source. EcKit is supplied with ODB API source. For example, if the ODB API source code was unpacked in directory
+/tmp/test then ECKIT_SOURCE should be set to /tmp/test/OdbAPI-0.10.1-dev-Source/eckit
+
+
+    BUILD_SHARED_LIBS
+
+Possible values: ON or OFF
+
+
+    ENABLE_MIGRATOR
+
+Spcifies if odb_migrator should be built. Possible values: ON or OFF
+
+
+    ENABLE_FORTRAN
+
+Specifies if ODB API Fortran interface should be built. Possiblle values: ON or OFF
+
+
+    ENABLE_PYTHON
+
+Specifies if ODB API Python interface should be built. Possiblle values: ON or OFF
+
+
+    SWIG_EXECUTABLE
+
+Path to swig executable. This option is only needed if a different swig executable should be needed than the one available on PATH
+
+
+    XLF_PATH
+
+Path to the IBM's XLF compiler
+
+
+    BISON_EXECUTABLE
+
+Path to bison executable.
+
+
+    FLEX_EXECUTABLE
+
+Path to flex executable.
+
diff --git a/odb_api/LICENSE b/odb_api/LICENSE
new file mode 100644
index 0000000..824a42c
--- /dev/null
+++ b/odb_api/LICENSE
@@ -0,0 +1,190 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   Copyright 1996-2012 ECMWF
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/odb_api/VERSION.cmake b/odb_api/VERSION.cmake
new file mode 100644
index 0000000..27c15ce
--- /dev/null
+++ b/odb_api/VERSION.cmake
@@ -0,0 +1 @@
+set( ${PROJECT_NAME}_VERSION_STR  "0.17.0" )
diff --git a/odb_api/bamboo/CLANG-env.sh b/odb_api/bamboo/CLANG-env.sh
new file mode 100644
index 0000000..14e05f9
--- /dev/null
+++ b/odb_api/bamboo/CLANG-env.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# No module environment on the Mac
+[[ $(uname) == "Darwin" ]] && return
+
+# Initialise module environment if it is not
+if [[ ! $(command -v module > /dev/null 2>&1) ]]; then
+  . /usr/local/apps/module/init/bash
+fi
+module unload grib_api
+module switch gnu clang/3.6.2
diff --git a/odb_api/bamboo/INTEL-env.sh b/odb_api/bamboo/INTEL-env.sh
new file mode 100644
index 0000000..260e756
--- /dev/null
+++ b/odb_api/bamboo/INTEL-env.sh
@@ -0,0 +1,11 @@
+# Initialise module environment if it is not
+if [[ ! $(command -v module > /dev/null 2>&1) ]]; then
+  . /usr/local/apps/module/init/bash
+fi
+# Unload modules not available for Intel
+module unload grib_api
+module unload eccodes
+module unload emos
+module unload fftw
+module unload libemos
+module switch gnu intel/16.0.3
diff --git a/odb_api/bamboo/env.sh b/odb_api/bamboo/env.sh
new file mode 100644
index 0000000..d6827b6
--- /dev/null
+++ b/odb_api/bamboo/env.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+ctest_parallel="no"
+
diff --git a/odb_api/bamboo/flags.cmake b/odb_api/bamboo/flags.cmake
new file mode 100644
index 0000000..9892d75
--- /dev/null
+++ b/odb_api/bamboo/flags.cmake
@@ -0,0 +1 @@
+SET(ENABLE_MIGRATOR ON CACHE BOOL "Build migrator tool")
diff --git a/odb_api/etc/ODA2RequestTool.cfg b/odb_api/etc/ODA2RequestTool.cfg
new file mode 100644
index 0000000..ecb1693
--- /dev/null
+++ b/odb_api/etc/ODA2RequestTool.cfg
@@ -0,0 +1,8 @@
+CLASS: class
+DATE: andate
+TIME: antime
+TYPE: type
+OBSGROUP: groupid at hdr
+REPORTYPE: reportype
+STREAM: stream
+EXPVER: expver
diff --git a/odb_api/etc/README.TXT b/odb_api/etc/README.TXT
new file mode 100644
index 0000000..80e2c3f
--- /dev/null
+++ b/odb_api/etc/README.TXT
@@ -0,0 +1,13 @@
+This directory is a copy of /usr/local/lib/metaps/lib/odalib/current/etc/ on ECMWF systems.
+made at some point in time and is not being synced regularly.
+
+Additionally, the file rtablel_2031 from directory defined normally in 
+
+  ODB_RTABLE_PATH=/usr/local/apps/odb/CY37R3.001/pgf90/LP64/include
+
+has been copied here - it is required by SQL function RggBox (see TestFunctionRggBox).
+
+The files here are only used for testing purposes - we set ODB_API_HOME to point to parent directory of this one
+when we run unit tests on our test machines.
+
+
diff --git a/odb_api/etc/class.table b/odb_api/etc/class.table
new file mode 100644
index 0000000..cef5f50
--- /dev/null
+++ b/odb_api/etc/class.table
@@ -0,0 +1,49 @@
+0 0 Unknown
+1 od Operational archive
+2 rd Research department
+3 er REANALYSE
+4 cs ECSN
+5 e4 REANALYSE40
+6 dm DEMETER
+7 pv PROVOST
+8 el ELDAS
+9 to TOST
+10 co Cosmo Leps
+11 en ENSEMBLES
+12 ti TIGGE
+13 me MERSEA
+14 ei ERA Interim
+15 sr Short-Range Ensemble Prediction System
+16 dt Data Targeting System
+17 la LACE ALADIN
+18 yt YOTC
+19 mc MACC
+20 pe Permanent experiments
+21 em ERA-CLIM model integration for the 20th-century
+22 e2 ERA-CLIM pilot reanalysis of the 20th-century using surface observations only
+23 ea ERA-CLIM pilot reanalysis of the satellite era
+24 ep ERA-CLIM pilot reanalysis of the pre-satellite era
+99 te Test
+100 at Austria
+101 be Belgium
+102 hr Croatia
+103 dk Denmark
+104 fi Finland
+105 fr France
+106 de Germany
+107 gr Greece
+108 hu Hungary
+109 is Iceland
+110 ie Ireland
+111 it Italy
+112 nl Netherlands
+113 no Norway
+114 pt Portugal
+115 si Slovenia
+116 es Spain
+117 se Sweden
+118 ch Switzerland
+119 tr Turkey
+120 uk United Kingdom
+121 ms Member States projects
+199 ma Metaps
diff --git a/odb_api/etc/group.txt b/odb_api/etc/group.txt
new file mode 100644
index 0000000..e0f427a
--- /dev/null
+++ b/odb_api/etc/group.txt
@@ -0,0 +1,60 @@
+       id↑    ; name    ; kind_id    ; marsname    ; description    ;    
+1  ; HIRS  ; 2  ; HIRS  ;    ;    
+2  ; AMSUA  ; 2  ; AMSUA  ;    ;    
+3  ; AMSUB  ; 2  ; AMSUB  ;    ;    
+4  ; MHS  ; 2  ; MHS  ;    ;    
+5  ; GEOS  ; 2  ; GEOS  ;    ;    
+6  ; RESAT  ; 2  ; RESAT  ;    ;    
+7  ; MERIS  ; 2  ; MERIS  ;    ;    
+8  ; GPSRO  ; 2  ; GPSRO  ;    ;    
+9  ; SATOB  ; 3  ; SATOB  ;    ;    
+10  ; SCATT  ; 2  ; SCATT  ;    ;    
+11  ; SSMI All-sky  ; 2  ; SSMI_AS  ;    ;    
+12  ; IASI  ; 2  ; IASI  ;    ;    
+13  ; AIRS  ; 2  ; AIRS  ;    ;    
+14  ; SSMIS All-sky  ; 2  ; SSMIS_AS  ;    ;    
+15  ; TMI All-sky  ; 2  ; TMI_AS  ;    ;    
+16  ; AMSRE All-sky  ; 2  ; AMSRE_AS  ;    ;    
+17  ; CONV  ; 1  ; CONV  ;    ;    
+19  ; SMOS  ; 2  ; SMOS  ;    ;    
+20  ; WINDSAT All-sky  ; 2  ; WINDSAT_AS  ;    ;    
+21  ; SSMI  ; 2  ; SSMI  ;    ;    
+22  ; AMSUA All-sky  ; 2  ; AMSUA_AS  ;    ;    
+23  ; AMSRE  ; 2  ; AMSRE  ;    ;    
+24  ; TMI  ; 2  ; TMI  ;    ;    
+25  ; SSMIS  ; 2  ; SSMIS  ;    ;    
+26  ; GBRAD  ; 1  ; GBRAD  ;    ;    
+27  ; MWHS  ; 2  ; MWHS  ;    ;    
+28  ; MWTS  ; 2  ; MWTS  ;    ;    
+29  ; MWRI All-sky  ; 2  ; MWRI_AS  ;    ;    
+30  ; IRAS  ; 2  ; IRAS  ;    ;    
+31  ; MSU  ; 2  ; MSU  ;    ;    
+32  ; SSU  ; 2  ; SSU  ;    ;    
+33  ; VTPR1  ; 2  ; VTPR1  ;    ;    
+34  ; VTPR2  ; 2  ; VTPR2  ;    ;    
+35  ; ATMS  ; 2  ; ATMS  ;    ;    
+36  ; RESAT Averaging Kernels  ; 2  ; RESAT_AK  ;    ;    
+37  ; CRIS  ; 2  ; CRIS  ; Cross-track Infrared Sounder  ;    
+38  ; WAVE integrated Parameters  ; 3  ; WAVE_IP  ; WAVE integrated Parameters  ;    
+39  ; WAVE spectra  ; 3  ; WAVE_SP  ; WAVE spectra  ;    
+40  ; RAINGG  ; 5  ; RAINGG  ; Rain Gauge  ;    
+41  ; SURFACE MULTISENSOR  ; 1  ; SFC_MS  ; SURFACE MULTISENSOR  ;    
+42  ; AMSR-2 All-sky  ; 2  ; AMSR2_AS  ; AMSR-2 All-sky  ;    
+43  ; SAPHIR All-sky  ; 2  ; SAPHIR_AS  ; SAPHIR All-sky  ;    
+44  ; AMSUB All-sky  ; 2  ; AMSUB_AS  ; AMSUB All-sky  ;    
+45  ; MHS All-sky  ; 2  ; MHS_AS  ; MHS All-sky  ;    
+46  ; Doppler Wind Lidar  ; 2  ; DWL  ; Doppler Wind Lidar  ;    
+47  ; IRIS  ; 2  ; IRIS  ; Infrared Interferometer Spectrometer  ;    
+49  ; AATSR  ; 2  ; AATSR  ; Advanced Along Track Scanning Radiometer  ;    
+50  ; ATMS All-sky  ; 2  ; ATMS_AS  ; ATMS All-sky  ;    
+51  ; GMI All-sky  ; 2  ; GMI_AS  ; GMI All-sky  ;    
+52  ; GODAE Sea Surface Temperatures  ; 4  ; GODAE_SST  ; GODAE Sea Surface Temperatures  ;    
+53  ; ATOVS MULTISENSOR  ; 4  ; ATOVS_MS  ; ATOVS MULTISENSOR  ;    
+54  ; Atmospheric composition  ; 1  ; ATMOSPHERIC_COMPOSITION  ; Atmospheric composition  ;    
+55  ; NON-SURFACE MULTISENSOR  ; 4  ; NON_SFC_MS  ; NON-SURFACE MULTISENSOR  ;    
+56  ; MWTS2  ; 2  ; MWTS2  ; Microwave Temperature Sounder 2  ;    
+57  ; SSMI 1DVAR TCWV Cloudy-Sky  ; 2  ; SSMI_1D  ; SSMI 1DVAR TCWV Cloudy-Sky  ;    
+58  ; MWHS2 All-sky  ; 2  ; MWHS2_AS  ; Microwave Humidity Sounder 2  ;    
+59  ; SSMT2  ; 2  ; SSMT2  ; SSMT2  ;    
+60  ; SMAP  ; 2  ; SMAP  ; Soil Moisture Active and Passive  ;    
+99  ; TEST  ; 4  ; TEST  ;    ;           
diff --git a/odb_api/etc/rtablel_2031 b/odb_api/etc/rtablel_2031
new file mode 100644
index 0000000..b16d38a
--- /dev/null
+++ b/odb_api/etc/rtablel_2031
@@ -0,0 +1,34 @@
+ &NAMRGRI
+  NRGRI(0001)=   20,
+  NRGRI(0002)=   25,
+  NRGRI(0003)=   32,
+  NRGRI(0004)=   40,
+  NRGRI(0005)=   45,
+  NRGRI(0006)=   48,
+  NRGRI(0007)=   54,
+  NRGRI(0008)=   60,
+  NRGRI(0009)=   64,
+  NRGRI(0010)=   64,
+  NRGRI(0011)=   64,
+  NRGRI(0012)=   64,
+  NRGRI(0013)=   64,
+  NRGRI(0014)=   64,
+  NRGRI(0015)=   64,
+  NRGRI(0016)=   64,
+  NRGRI(0017)=   64,
+  NRGRI(0018)=   64,
+  NRGRI(0019)=   64,
+  NRGRI(0020)=   64,
+  NRGRI(0021)=   64,
+  NRGRI(0022)=   64,
+  NRGRI(0023)=   64,
+  NRGRI(0024)=   64,
+  NRGRI(0025)=   60,
+  NRGRI(0026)=   54,
+  NRGRI(0027)=   48,
+  NRGRI(0028)=   45,
+  NRGRI(0029)=   40,
+  NRGRI(0030)=   32,
+  NRGRI(0031)=   25,
+  NRGRI(0032)=   20,
+ /
diff --git a/odb_api/etc/stream.table b/odb_api/etc/stream.table
new file mode 100644
index 0000000..c9ec51d
--- /dev/null
+++ b/odb_api/etc/stream.table
@@ -0,0 +1,95 @@
+0 0 Unknown
+1022 fsob Forecast sensitivity to observations
+1023 fsow Forecast sensitivity to observations wave 
+1024 dahc Daily archive hindcast
+1025 oper Atmospheric model
+1026 scda Atmospheric model (short cutoff)
+1027 scwv Wave model (short cutoff)
+1028 dcda Atmospheric model (delayed cutoff)
+1029 dcwv Wave model (delayed cutoff)
+1030 enda Ensemble data assimilation
+1032 efho Ensemble forecast hindcast overlap
+1033 enfh Ensemble forecast hindcasts
+1034 efov Ensemble forecast overlap
+1035 enfo Ensemble prediction system
+1036 sens Sensitivity forecast
+1037 maed Multianalysis ensemble data
+1038 amap Analysis for multianalysis project
+1039 efhc Ensemble forecast hindcasts (obsolete)
+1040 efhs Ensemble forecast hindcast statistics
+1041 toga TOGA
+1042 cher Chernobyl
+1043 mnth Monthly means
+1044 supd Deterministic supplementary data
+1045 wave Wave model
+1046 ocea Ocean
+1047 fgge FGGE
+1050 egrr Bracknell
+1051 kwbc Washington
+1052 edzw Offenbach
+1053 lfpw Toulouse
+1054 rjtd Tokyo
+1055 cwao Montreal
+1056 ammc Melbourne
+1070 msdc Monthly standard deviation and covariance
+1071 moda Monthly means of daily means
+1072 monr Monthly means using G. Boer's step function
+1073 mnvr Monthly variance and covariance data using G. Boer's step function
+1074 msda Monthly standard deviation and covariance of daily means
+1075 mdfa Monthly means of daily forecast accumulations
+1076 dacl Daily climatology
+1077 wehs Wave ensemble forecast hindcast statistics
+1078 ewho Ensemble forecast wave hindcast overlap
+1079 enwh Ensemble forecast wave hindcasts
+1080 wamo Wave monthly means
+1081 waef Wave ensemble forecast
+1082 wasf Wave seasonal forecast
+1083 mawv Multianalysis wave data
+1084 ewhc Wave ensemble forecast hindcast (obsolete)
+1085 wvhc Wave hindcast
+1086 weov Wave ensemble forecast overlap
+1087 wavm Wave model (standalone)
+1088 ewda Ensemble wave data assimilation
+1089 dacw Daily climatology wave
+1090 seas Seasonal forecast
+1091 sfmm Seasonal forecast atmospheric monthly means
+1092 swmm Seasonal forecast wave monthly means
+1093 mofc Monthly forecast
+1094 mofm Monthly forecast means
+1095 wamf Wave monthly forecast
+1096 wmfm Wave monthly forecast means
+1097 smma Seasonal monthly means anomalies
+1110 seap Sensitive area prediction
+1200 mnfc Real-time
+1201 mnfh Hindcasts
+1202 mnfa Anomalies
+1203 mnfw Wave real-time
+1204 mfhw Monthly forecast hindcasts wave
+1205 mfaw Wave anomalies
+1206 mnfm Real-time means
+1207 mfhm Hindcast means
+1208 mfam Anomaly means
+1209 mfwm Wave real-time means
+1210 mhwm Wave hindcast means
+1211 mawm Wave anomaly means
+1220 mmsf Multi-model seasonal forecast
+1221 msmm Multi-model seasonal forecast atmospheric monthly means
+1222 wams Multi-model seasonal forecast wave
+1223 mswm Multi-model seasonal forecast wave monthly means
+1224 mmsa Multi-model seasonal forecast monthly anomalies
+1230 mmaf Multi-model multi-annual forecast
+1231 mmam Multi-model multi-annual forecast means
+1232 mmaw Multi-model multi-annual forecast wave
+1233 mmwm Multi-model multi-annual forecast wave means
+1240 esmm EUROSIP monthly means
+1241 ehmm EUROSIP hindcast monthly means
+1242 edmm Ensemble data assimilation monthly means
+1243 edmo Ensemble data assimilation monthly means of daily means
+1244 ewmo Ensemble wave data assimilation monthly means of daily means 
+1245 ewmm Ensemble wave data assimilation monthly means
+1246 espd Ensemble supplementary data
+1247 lwda Long window daily archive
+1248 lwwv Long window wave
+2231 cnrm Meteo France climate centre
+2232 mpic Max Plank Institute
+2233 ukmo UKMO climate centre
diff --git a/odb_api/etc/type.table b/odb_api/etc/type.table
new file mode 100644
index 0000000..7eccac5
--- /dev/null
+++ b/odb_api/etc/type.table
@@ -0,0 +1,66 @@
+0 0  Unknown
+1 fg First guess
+2 an Analysis
+3 ia Initialised analysis
+4 oi Oi analysis
+5 3v 3d variational analysis
+6 4v 4d variational analysis
+7 3g 3d variational gradients
+8 4g 4d variational gradients
+9 fc Forecast
+10 cf Control forecast
+11 pf Perturbed forecast
+12 ef Errors in first guess
+13 ea Errors in analysis
+14 cm Cluster means
+15 cs Cluster std deviations
+16 fp Forecast probability
+17 em Ensemble mean
+18 es Ensemble standard deviation
+19 fa Forecast accumulation
+20 cl Climatology
+21 si Climate simulation
+22 s3 Climate 30 days simulation
+23 ed Empirical distribution
+24 tu Tubes
+25 ff Flux forcing realtime
+26 of Ocean forward
+27 efi Extreme forecast index
+28 efic Extreme forecast index control
+29 pb Probability boundaries
+30 ep Event probability
+31 bf Bias-corrected Forecast
+32 cd Climate distribution
+33 4i 4D analysis increments
+34 go Gridded observations
+35 me Model errors
+36 pd Probability distribution
+37 ci Cluster information
+38 sot Shift of Tail
+40 im Images
+42 sim Simulated images
+43 wem Weighted ensemble mean
+44 wes Weighted ensemble standard deviation
+45 cr Cluster representative
+46 ses Scaled ensemble standard deviation
+47 taem Time average ensemble mean
+48 taes Time average ensemble standard deviation 
+50 sg Sensitivity gradient
+52 sf Sensitivity forecast
+60 pa Perturbed analysis
+61 icp Initial condition perturbation
+62 sv Singular vector
+63 as Adjoint singular vector
+64 svar Signal variance
+65 cv Calibration/Validation forecast
+70 or Ocean reanalysis
+71 fx Flux forcing
+80 fcmean Forecast mean
+81 fcmax Forecast maximum
+82 fcmin Forecast minimum
+83 fcstdev Forecast standard deviation
+84 emtm Ensemble mean of temporal mean
+85 estdtm Ensemble standard deviation of temporal mean
+86 hcmean Hindcast climate mean
+87 ssd Simulated satellite data
+
diff --git a/odb_api/project_summary.cmake b/odb_api/project_summary.cmake
new file mode 100644
index 0000000..fc2e613
--- /dev/null
+++ b/odb_api/project_summary.cmake
@@ -0,0 +1,30 @@
+if( SWIG_FOUND )
+  message( STATUS " SWIG command     : [${SWIG_EXECUTABLE}]" )
+endif()
+
+if(ODB_FOUND)
+  message( STATUS " ODB     include  : [${ODB_INCLUDE_DIRS}]" )
+  message( STATUS "         libs     : [${ODB_LIBRARIES}]" )
+  message( STATUS "     DL  lib      : [${DL_LIBRARIES}]" )
+endif()
+
+if( FORTRAN_LIBRARIES )
+  message( STATUS " FORTRAN_LIBRARIES: [${FORTRAN_LIBRARIES}]" )
+endif()
+
+#string( TOUPPER ${PROJECT_NAME} PNAME )
+#foreach( _tpl ${${PNAME}_TPLS} )
+#	string( TOUPPER ${_tpl} TPL )
+#	if( ${TPL}_FOUND )
+#		message( STATUS " ${_tpl} ${${_tpl}_VERSION}" )
+#		if( ${TPL}_INCLUDE_DIRS )
+#		  message( STATUS "      includes : [${${TPL}_INCLUDE_DIRS}]" )
+#		endif()
+#		if( ${TPL}_LIBRARIES )
+#		  message( STATUS "      libs     : [${${TPL}_LIBRARIES}]" )
+#		endif()
+#		if( ${TPL}_DEFINITIONS )
+#		  message( STATUS "      defs     : [${${TPL}_DEFINITIONS}]" )
+#		endif()
+#	endif()
+#endforeach()
diff --git a/odb_api/src/CMakeLists.txt b/odb_api/src/CMakeLists.txt
new file mode 100644
index 0000000..e96c5a4
--- /dev/null
+++ b/odb_api/src/CMakeLists.txt
@@ -0,0 +1,30 @@
+## check if all sources are used
+ecbuild_find_project_files()
+
+### config header
+
+ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api )
+
+configure_file( odb_api_config.h.in odb_api_config.h )
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/odb_api_config.h DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api )
+
+add_subdirectory( ecml )
+
+### include directories
+
+ecbuild_debug_var (ECML_INCLUDE_DIRS)
+
+include_directories( ${ODB_API_INCLUDE_DIRS} ${ECKIT_INCLUDE_DIRS} ${ECML_INCLUDE_DIRS} )
+
+### libs
+
+add_subdirectory( odb_api )
+add_subdirectory( odb_api/tools )
+add_subdirectory( odb_api/migrator )
+add_subdirectory( odb_api/odb2netcdf )
+add_subdirectory( odb_api/odb2_to_odb1 )
+add_subdirectory( fortran )
+add_subdirectory( python )
+add_subdirectory( api )
+
diff --git a/odb_api/src/api/CMakeLists.txt b/odb_api/src/api/CMakeLists.txt
new file mode 100644
index 0000000..0c293d7
--- /dev/null
+++ b/odb_api/src/api/CMakeLists.txt
@@ -0,0 +1,16 @@
+
+# Examples / tests of C API.
+ecbuild_add_executable( TARGET    odbql_c_example
+                        SOURCES   odbql_c_example.c odbql_c_test.c
+                        LIBS      Odb 
+                        LINKER_LANGUAGE CXX ) # ODB-325
+
+
+# Examples / tests of Fortran API.
+ecbuild_add_executable( TARGET   odbql_fortran_example 
+
+                        CONDITION  HAVE_FORTRAN
+
+                        SOURCES    odbql_fortran_example.f90
+                        LIBS       Odb_fortran )
+
diff --git a/odb_api/src/api/README.md b/odb_api/src/api/README.md
new file mode 100644
index 0000000..f19ad39
--- /dev/null
+++ b/odb_api/src/api/README.md
@@ -0,0 +1,27 @@
+# ODB API examples
+
+This directory contains examples of usage of ODB API.
+
+## C/C++ API
+
+The C API (Application Programming Interface) has been inspired by sqlite3 API. It consists of a set of C functions and constants defined in header file `odbql.h`. This API is intended to be used in C and C++ programs. It is also a basis of Fortran and Python ODB API interfaces' implementations.
+
+Generally, the ODB API functions that are part of public interface start with `odbql_` prefix. All of them have an equivalent in the public interface of sqlite3 that has a `sqlite3_` prefix and a simillar syntax (parameters) and semantics.
+
+Similarly, most status and error codes, as well as numerical codes denoting column types, defined in `odbql.h` with macros prefixed with `ODBQL_`, have an equivalent macro in sqlite3 that starts with `SQLITE_` and has the same meaning and numerical value. 
+However, due to differences between ODB API and sqlite3 it was necessary to add some ODB API specific status codes, at this time these are `ODBQL_METADATA_CHANGED` and `ODBQL_BITFIELD` (an ODB specific data type).
+
+See file `odbql_c_example.c` for complete examples of how to read, write and process data with ODB API SQL engine using ODB API C/C++ interface.
+
+## Fortran API
+
+Fortran ODB API is a set of Fortran 95 subroutines and functions wrapping the C API described above. The subroutines and functions are in a module `odbql_wrappers`, and have the same names as their C equivalents.
+
+See file `odbql_fortran_example.f90` for example programs using module `odbql_wrappers`.
+
+## Python API
+
+Python interface of ODB API implements PEP 249 -- Python Database API Specification v2.0.
+
+See `odbql_python_example.py` for example code using the module.
+
diff --git a/odb_api/src/api/odbql_c_example.c b/odb_api/src/api/odbql_c_example.c
new file mode 100644
index 0000000..6c94003
--- /dev/null
+++ b/odb_api/src/api/odbql_c_example.c
@@ -0,0 +1,219 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file examples.cc
+///
+/// This file contains examples of usage of public APIs.
+///
+/// @author Piotr Kuchta, ECMWF, July 2016
+
+#include "odb_api/odbql.h"
+
+// We need to include stdio.h for the fprintf declaration
+#include "stdio.h"
+
+#define checkRC(return_code, message, db) { \
+    if (return_code != ODBQL_OK) { \
+        fprintf(stderr, "%s: %s\n", message, odbql_errmsg(db)); \
+        odbql_close(db); \
+        return 1; \
+    } \
+}
+
+int odbql_example_insert_data()
+{
+    odbql *db;
+    odbql_stmt *stmt;
+    int i;
+
+    int rc = odbql_open("", &db);
+
+    checkRC(rc, "Cannot open database", db);
+    
+    rc = odbql_prepare_v2(db, "CREATE TYPE bf AS (f1 bit1, f2 bit2);\n"
+                              "CREATE TABLE foo AS"
+                              "   (x INTEGER, y REAL, v STRING, status bf)"
+                              " ON 'new_api_c_example.odb';",
+                          -1, 
+                          &stmt, 
+                          0);
+    checkRC(rc, "Failed to prepare DDL statements", db);
+
+    rc = odbql_prepare_v2(db, 
+            "INSERT INTO foo (x,y,v,status) VALUES (?,?,?,?);", 
+            -1, 
+            &stmt, 
+            0);
+    checkRC(rc, "Failed to prepare INSERT statement", db);
+
+    // Populate first row with NULLs
+    for (i = 0; i < 4; ++i) 
+        odbql_bind_null(stmt, i);
+    rc = odbql_step(stmt);
+
+    // Few more rows with some non-NULL values
+    for (i = 0 ; i < 4; ++i)
+    {
+        rc = odbql_bind_int(stmt, 0, 1 * i);
+        checkRC(rc, "Failed to bind int value", db);
+
+        rc = odbql_bind_double(stmt, 1, 0.1 * i);
+        checkRC(rc, "Failed to bind double value", db);
+
+        rc = odbql_bind_text(stmt, 2, i%2 ? "hello" : "HELLO", 5 /* strlen("hello") */, ODBQL_STATIC);
+        checkRC(rc, "Failed to bind string", db);
+
+        rc = odbql_bind_int(stmt, 3, 3 * i);
+        checkRC(rc, "Failed to bind bitfield value", db);
+
+        rc = odbql_step(stmt);
+        //checkRC(rc, "Failed to step and write row", db);
+    }
+    rc = odbql_finalize(stmt);
+    checkRC(rc, "odbql_finalize failed", db);
+
+    rc = odbql_close(db);
+    checkRC(rc, "odbql_close failed", db);
+
+    return 0;
+}
+
+int odbql_example_select_data_read_results()
+{
+    odbql *db;
+    odbql_stmt *res;
+    int i, rc, column, number_of_columns;
+    long long number_of_rows = 0, number_of_rows_in_current_dataset = 0;
+    
+    rc = odbql_open("new_api_c_example.odb", &db);
+                           //" ON 'mars://RETRIEVE,CLASS=OD,TYPE=MFB,STREAM=OPER,EXPVER=0001,DATE=20160720,TIME=1200,DATABASE=marsod';", &db);
+    checkRC(rc, "Cannot open file", db);
+    
+    //rc = odbql_prepare_v2(db, "SELECT x,y,v,status,status.* FROM foo;", -1, &res, 0);
+    rc = odbql_prepare_v2(db, "SELECT * FROM 'new_api_c_example.odb';", -1, &res, 0);
+    checkRC(rc, "Failed to prepare statement", db);
+   
+    // Print rows of data. 
+    while((rc = odbql_step(res)) != ODBQL_DONE) 
+    {
+        if (number_of_rows == 0 || rc == ODBQL_METADATA_CHANGED)
+        {
+            number_of_columns = odbql_column_count(res);
+
+            if (number_of_rows_in_current_dataset)
+                printf("Number of rows: %lld\n", number_of_rows_in_current_dataset);
+/*
+            // Print CSV header. Each field is a colon separated pair of column name and type.
+            printf("\nNew dataset. Number of columns: %d\n", number_of_columns);
+            for (i = 0; i < number_of_columns; ++i)
+                printf("%s:%d%s", odbql_column_name(res, i), 
+                                  odbql_column_type(res, i), 
+                                  ((i < number_of_columns - 1) ? "," : ""));
+            printf("\n");
+*/
+
+            number_of_rows_in_current_dataset = 0;
+        }
+
+        if (rc == ODBQL_ROW) 
+        {
+            column = 0;
+
+/*
+            for (; column < number_of_columns; ++column)
+            {
+                printf("%s%s", odbql_column_value(res, column) 
+                                ? odbql_column_text(res, column) 
+                                : (unsigned char *) "NULL",
+                               ((column < number_of_columns - 1) ? "," : ""));
+
+            }
+*/
+            if (number_of_rows == 0)
+            {
+                for (i = 0; i < number_of_columns; ++i)
+                    if (odbql_column_value(res, i)) return 100; // first row should be all NULLL
+            }
+            else
+            {
+                // Check first column
+                odbql_value* pv = odbql_column_value(res, 0);
+                if ( pv == 0 )
+                {
+                    fprintf (stderr, "\nUnexpected NULL in column %ld of row %lld\n", 0L, number_of_rows);
+                    return 101;
+                }
+
+                int iv = odbql_value_int(pv);
+                if ( iv != number_of_rows - 1)
+                {
+                    fprintf (stderr, "\nUnexpected value in column %ld of row %lld\n", 0L, number_of_rows);
+                    return 102;
+                }
+
+                // Check last column (bitfield)
+                // We have a NULL on the first column (set with odbql_bind_null)
+                // but also on the second line, because 0 in a bitfield column means NULL
+                // (ECMWF convention since ODB-1)
+                if (number_of_rows > 1)
+                {
+                    pv = odbql_column_value(res, 3);
+                    if ( pv == 0 )
+                    {
+                        fprintf (stderr, "\nUnexpected NULL in column %ld of row %lld\n", 3L, number_of_rows);
+                        return 103;
+                    }
+
+                    iv = odbql_value_int(pv);
+                    if ( iv != 3 * (number_of_rows - 1))
+                    {
+                        fprintf (stderr, "\nUnexpected value in column %ld of row %lld\n", 3L, number_of_rows);
+                        return 104;
+                    }
+                }
+            }
+
+            printf("\n");
+        }
+        ++ number_of_rows;
+        ++ number_of_rows_in_current_dataset;
+    }
+    if (number_of_rows_in_current_dataset)
+        printf("Number of rows: %lld\n", number_of_rows_in_current_dataset);
+
+    printf("\nProcessed %lld rows.\n", number_of_rows);
+    return 0;
+}
+
+int odbql_example_execute_embedded_ecml()
+{
+    odbql *db;
+    odbql_stmt *res;
+    int rc;
+
+    rc = odbql_open("new_api_c_example.odb", &db);
+    checkRC(rc, "Cannot open database", db);
+
+    rc = odbql_prepare_v2(db, " { compare, left = new_api_c_example.odb, right = new_api_c_example.odb }; ", -1, &res, 0);
+    checkRC(rc, "Failed to prepare embedded statement", db);
+
+    // Print rows of data. 
+    while((rc = odbql_step(res)) != ODBQL_DONE) 
+    {
+        printf("\n---\n");
+    }
+    
+    rc = odbql_finalize(res);
+    checkRC(rc, "odbql_finalize failed", db);
+    rc = odbql_close(db);
+    checkRC(rc, "odbql_close failed", db);
+
+    return 0;
+}
diff --git a/odb_api/src/api/odbql_c_test.c b/odb_api/src/api/odbql_c_test.c
new file mode 100644
index 0000000..3470094
--- /dev/null
+++ b/odb_api/src/api/odbql_c_test.c
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file examples.cc
+///
+/// This file contains examples of usage of public APIs.
+///
+/// @author Piotr Kuchta, ECMWF, July 2016
+
+/*
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+
+#include "odb_api/odbql.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/runtime/ContextBehavior.h"
+#include "eckit/runtime/Context.h"
+
+#include "odb_api/ODBBehavior.h"
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/TestRunnerApplication.h"
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ImportTool.h"
+*/
+
+#include <stdio.h>
+
+/*
+extern "C" {
+*/
+
+int odbql_example_insert_data();
+int odbql_example_select_data_read_results();
+int odbql_example_execute_embedded_ecml();
+/*
+}
+*/
+
+int main(int argc, char** argv) {
+    if (odbql_example_insert_data()) 
+        return fprintf(stderr, "odbql_example_insert_data FAILED\n"), 1;
+    
+    if (odbql_example_select_data_read_results()) 
+        return fprintf(stderr, "odbql_example_select_data_read_results FAILED\n"), 1;
+
+    if (odbql_example_execute_embedded_ecml()) 
+        return fprintf(stderr, "odbql_example_execute_embedded_ecml FAILED\n"), 1;
+
+    fprintf(stdout, "%s: All done.\n", argv[0]);
+    return 0;
+}
+
diff --git a/odb_api/src/api/odbql_fortran_example.f90 b/odb_api/src/api/odbql_fortran_example.f90
new file mode 100644
index 0000000..3221253
--- /dev/null
+++ b/odb_api/src/api/odbql_fortran_example.f90
@@ -0,0 +1,116 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+! Piotr Kuchta, ECMWF, July 2016
+
+
+program example_fortran_api
+  use odbql_wrappers
+  implicit none
+  character(len=10)                            :: version
+
+  call odbql_libversion(version)
+  write(0,*) "This program is linked to ODB API version: ", version
+
+  call odbql_fortran_example()
+
+  write(0,*) "That's all, folks!"
+contains
+
+subroutine odbql_fortran_example
+ implicit none
+ type(odbql)                                   :: db
+ type(odbql_stmt)                              :: stmt
+ type(odbql_value)                             :: val
+ integer(kind=C_INT)                           :: status, number_of_columns, column_no, row_no, int_val
+ character(len=30)                             :: string_val, column_name
+ character(len=1000)                           :: unparsed_sql
+ real(kind=C_DOUBLE)                           :: v, real_val
+
+!!!! Write to a file with SQL INSERT
+
+ call odbql_open("", db)
+ call odbql_prepare_v2(db, "CREATE TYPE BF_T AS (f1 bit1, f2 bit2);&
+ & CREATE TABLE foo AS (x INTEGER,y REAL,v STRING,status BF_T) ON 'fort.odb';",-1, stmt, unparsed_sql)
+ call odbql_prepare_v2(db, "INSERT INTO foo (x,y,v,status) VALUES (?,?,?,?);", -1, stmt, unparsed_sql)
+
+! Populate first row with NULLs
+ do column_no = 1,4
+     call odbql_bind_null(stmt, column_no)
+ end do
+ call odbql_step(stmt)
+
+! Write 3 rows with some values other than NULL
+ do row_no = 1,3
+    call odbql_bind_int(stmt, 1, 1 * row_no)
+    v = 0.1 * row_no
+    call odbql_bind_double(stmt, 2, v)
+    call odbql_bind_text(stmt, 3, "hello", 5)
+    call odbql_bind_int(stmt, 4, 1 * row_no)
+
+    call odbql_step(stmt)
+ end do
+
+! Write internal buffers to disk and close the file
+ call odbql_finalize(stmt)
+ call odbql_close(db)
+
+!!!! Read from a file with SQL SELECT
+
+! Associate table with a file name
+! call odbql_open("fort.odb", db)
+! You could as well retrieve some data directly from MARS instead:
+ call odbql_open("mars://RETRIEVE,CLASS=OD,TYPE=MFB,STREAM=OPER,EXPVER=0001,DATE=20160720,TIME=1200,DATABASE=marsod", db)
+ call odbql_prepare_v2(db, "SELECT *;", -1, stmt, unparsed_sql)
+ number_of_columns = odbql_column_count(stmt)
+ write(0,*) "Number of columns: ", number_of_columns 
+
+ row_no = 1
+ do 
+   call odbql_step(stmt, status)
+   if (status == ODBQL_DONE) exit
+   if (status /= ODBQL_ROW) stop 
+
+   write(6,*) 'Row ', row_no
+   do column_no = 1,number_of_columns
+
+       call odbql_column_name(stmt, column_no, column_name)
+       val = odbql_column_value(stmt, column_no)
+       if (.not. c_associated(val%this)) then
+           write(6,*) column_no, ' ', column_name, ': MISSING'
+       else
+           select case (odbql_column_type(stmt, column_no))
+           case (ODBQL_TEXT)
+               call odbql_column_text(stmt, column_no, string_val)
+               write(6,*) column_no, ' ', column_name, ':string = ', string_val
+           case (ODBQL_BITFIELD)
+               int_val = odbql_value_int(val)
+               write(6,*) column_no, ' ', column_name, ':integer = ', int_val
+           case (ODBQL_INTEGER)
+               int_val = odbql_value_int(val)
+               write(6,*) column_no, ' ', column_name, ':integer = ', int_val
+           case (ODBQL_FLOAT)
+               real_val = odbql_value_double(val)
+               write(6,*) column_no, ' ', column_name, ':float = ', real_val
+           end select 
+
+           ! Note, odbql_column_text can be called for columns of any type
+           call odbql_column_text(stmt, column_no, string_val)
+           write(6,*) column_no, ' ', column_name, ': ', string_val
+
+       end if
+   end do
+   row_no = row_no + 1
+ end do 
+
+ call odbql_finalize(stmt)
+ call odbql_close(db)
+
+end subroutine odbql_fortran_example
+
+end program example_fortran_api
diff --git a/odb_api/src/api/odbql_python_example.py b/odb_api/src/api/odbql_python_example.py
new file mode 100755
index 0000000..4347fee
--- /dev/null
+++ b/odb_api/src/api/odbql_python_example.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python 
+
+# (C) Copyright 1996-2012 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+#
+
+"""
+Examples of usage of ODB API Python interface.
+
+ at author Piotr Kuchta, ECMWF, August 2016 
+"""
+
+import odb
+
+### Example 1. Create a new ODB file.
+###
+
+### 1.1. Define metadata (column names and their types) 
+#    of a new table associated with a physical file 
+#    by passing some DDL (Data Definition Language):
+#    CREATE TYPE (to define bitfields) and CREATE TABLE
+#    statements to function connect.
+#
+#   Function connect returns a Connection object.
+
+conn = odb.connect("")
+
+### 1.2 Create a Cursor object. """
+c = conn.cursor()
+
+### 1.3 Populate table with data using SQL INSERT statement 
+#    and method Cursor.executemany. 
+
+c.execute('''
+    CREATE TYPE bf AS (f1 bit1, f2 bit2); 
+
+    CREATE TABLE foo AS 
+    ( x INTEGER, y DOUBLE, v STRING, status bf) 
+    ON 'new_api_example.odb';
+    ''')
+
+c.executemany('INSERT INTO foo (x,y,v,status) VALUES (?,?,?,?);', 
+                [[1,0.1, '  one   ', 1], 
+                 [2,0.2, '  two   ', 2], 
+                 [3,0.3, '  three ', 3], 
+                 [4,0.4, '  four  ', 4]])
+### 1.4 Flush buffers and write to file associated with the table. """
+conn.commit()
+
+
+### Example 2. Read contents of a file.
+#
+#   Note: table foo is associated with a file using CREATE TABLE
+#   DDL statement, see previous example.
+
+### 2.1 Create Cursor object and execute
+
+c = conn.cursor()
+c.execute('select * from foo;')
+
+### 2.2 Use Cursor.fetchall to retrieve whole result set of SELECT
+#    and print its rows using simple print 
+
+for row in c.fetchall():
+    print ",".join(str(v) for v in row)
+
+
+### Example 3. Read a file into Pandas DataFrame object.
+
+import pandas as pd
+c.execute('select * from foo;')
+d = pd.DataFrame.from_records(c.fetchall(), 
+                              columns = [d[0] for d in c.description],
+                              exclude = ['v at foo'])
+
+print 'Pandas DataFrame:\n', d
+
+### Example 4. Create numpy array.
+import numpy as np
+
+c.execute('select x, y, status.f1, status.f2 from foo;')
+a = np.array(c.fetchall())
+print 'numpy array:\n', a
+
+
+### Example 5. Load some data from MARS or ODB Server into Pandas DataFrame
+conn = odb.connect('''mars://RETRIEVE,
+                        DATABASE  = marsod,
+                        CLASS     = OD,
+                        TYPE      = MFB,
+                        STREAM    = OPER,
+                        EXPVER    = 0001,
+                        DATE      = 20160830,
+                        TIME      = 1200,
+                        REPORTYPE = 16001''')
+c = conn.cursor()
+c.execute('select *;')
+d = pd.DataFrame.from_records(c.fetchall(), 
+                              columns = [d[0] for d in c.description],
+                              exclude = ['expver','class','stream'])
+print d
+
+print "That's all, folks!"
diff --git a/odb_api/src/ecml/CMakeLists.txt b/odb_api/src/ecml/CMakeLists.txt
new file mode 100644
index 0000000..875bf1e
--- /dev/null
+++ b/odb_api/src/ecml/CMakeLists.txt
@@ -0,0 +1,176 @@
+
+set( ECML_INCLUDE_DIRS   ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/.. )
+set( ECML_LIBRARIES      ecml )
+
+include_directories( ${ECML_INCLUDE_DIRS} ${ECKIT_INCLUDE_DIRS} ${METKIT_INCLUDE_DIRS} )
+
+list( APPEND ecml_srcs
+# Parser
+parser/Cell.h
+parser/Cell.cc
+parser/CellPrinter.h
+parser/CellPrinter.cc
+parser/CellDotPrinter.h
+parser/CellDotPrinter.cc
+parser/List.h
+parser/List.cc
+parser/Request.h
+parser/Request.cc
+parser/RequestParser.h
+parser/RequestParser.cc
+
+# AST
+ast/FunctionDefinition.h
+ast/FunctionDefinition.cc
+ast/Closure.cc
+ast/Closure.h
+
+# Core language
+core/Environment.h
+core/Environment.cc
+core/ExecutionContext.h
+core/ExecutionContext.cc
+core/Interpreter.h
+core/Interpreter.cc
+core/Module.cc
+core/Module.h
+core/RequestHandler.h
+core/RequestHandler.cc
+core/SpecialFormHandler.h
+core/SpecialFormHandler.cc
+
+# Prelude
+prelude/Prelude.cc
+prelude/Prelude.h
+prelude/LetHandler.cc
+prelude/LetHandler.h
+prelude/UpdateHandler.h
+prelude/UpdateHandler.cc
+prelude/VariableLookupHandler.cc
+prelude/VariableLookupHandler.h
+prelude/DefineFunctionHandler.cc
+prelude/DefineFunctionHandler.h
+prelude/ApplyHandler.cc
+prelude/ApplyHandler.h
+prelude/ClosureHandler.h
+prelude/ClosureHandler.cc
+prelude/ListHandler.cc
+prelude/ListHandler.h
+prelude/SequenceHandler.cc
+prelude/SequenceHandler.h
+prelude/PrintHandler.cc
+prelude/PrintHandler.h
+prelude/TestHandler.cc
+prelude/TestHandler.h
+prelude/FirstHandler.cc
+prelude/FirstHandler.h
+prelude/RestHandler.cc
+prelude/RestHandler.h
+prelude/IfHandler.cc
+prelude/IfHandler.h
+prelude/TemporaryFileHandler.h
+prelude/TemporaryFileHandler.cc
+prelude/SystemHandler.h
+prelude/SystemHandler.cc
+prelude/GetenvHandler.h
+prelude/GetenvHandler.cc
+prelude/JoinStringsHandler.h
+prelude/JoinStringsHandler.cc
+prelude/QuoteHandler.h
+prelude/QuoteHandler.cc
+prelude/NullHandler.h
+prelude/NullHandler.cc
+prelude/RunHandler.h
+prelude/RunHandler.cc
+prelude/REPLHandler.h
+prelude/REPLHandler.cc
+prelude/Autocompleter.h
+prelude/Autocompleter.cc
+prelude/RangeHandler.h
+prelude/RangeHandler.cc
+prelude/ForHandler.cc
+prelude/ForHandler.h
+prelude/GlobHandler.cc
+prelude/GlobHandler.h
+prelude/MatchHandler.h
+prelude/MatchHandler.cc
+prelude/ReadTextFileHandler.h
+prelude/ReadTextFileHandler.cc
+prelude/TryHandler.h
+prelude/TryHandler.cc
+prelude/ThrowHandler.h
+prelude/ThrowHandler.cc
+
+# Data types' support
+data/DataHandleFactory.h
+data/DataHandleFactory.cc
+data/FileHandleFactory.h
+data/FileHandleFactory.cc
+data/PartFileHandleFactory.h
+data/PartFileHandleFactory.cc
+data/HttpHandle.h
+data/HttpHandle.cc
+data/HttpHandleFactory.h
+data/HttpHandleFactory.cc
+# requires  metkit
+data/MarsHandleFactory.cc
+data/MarsHandleFactory.h
+
+# Miscellaneous
+# Adapter for eckit::Parametrisation
+misc/DynamicParametrisation.h
+misc/DynamicParametrisation.cc
+misc/ParameterizedRequestHandler.h
+misc/ParameterizedRequestHandler.cc
+)
+
+ecbuild_generate_yy( YYPREFIX    request_
+                     YACC        parser/requesty
+                     YACC_TARGET requesty
+                     LEX         parser/requestl
+                     LEX_TARGET  requestl
+                     DEPENDANT   parser/RequestParser.cc )
+
+ecbuild_add_library( TARGET             ecml
+                     INSTALL_HEADERS    LISTED
+                     HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/ecml
+                     SOURCES            ${ecml_srcs}
+                     LIBS               eckit 
+                                        eckit_cmd # for UserInput
+                                        metkit
+                                        )
+
+ecbuild_add_executable( TARGET  ecml_test
+                        SOURCES tests/ecml_test.cc
+                        LIBS    ecml )
+
+ecbuild_add_executable( TARGET  ecml_unittests
+                        SOURCES tests/ecml_unittests.cc
+                        LIBS    ecml )
+
+add_subdirectory( tests )
+
+configure_file( prelude/prelude.ecml ${CMAKE_BINARY_DIR}/include/prelude.ecml COPYONLY)
+
+install(DIRECTORY src/ 
+        DESTINATION ${INSTALL_INCLUDE_DIR} 
+        FILES_MATCHING PATTERN "*.h")
+
+install(FILES       ${CMAKE_CURRENT_SOURCE_DIR}/prelude/prelude.ecml
+        DESTINATION ${INSTALL_INCLUDE_DIR} )
+
+configure_file(       ecml_config.h.in   ecml_config.h )
+install( FILES        ${CMAKE_CURRENT_BINARY_DIR}/ecml_config.h
+         DESTINATION  ${INSTALL_INCLUDE_DIR}/ecml/ )
+
+
+#ecbuild_pkgconfig( NAME ${PROJECT_NAME}
+#                   DESCRIPTION "ECML language interpreter"
+#                   URL "https://software.ecmwf.int/wiki/display/ODB/ODB+API"
+#                   LIBRARIES ecml )
+
+# export to other packages and define install targets
+#ecbuild_install_project( NAME ecml )
+
+#ecbuild_print_summary()
+
diff --git a/odb_api/src/ecml/README.txt b/odb_api/src/ecml/README.txt
new file mode 100644
index 0000000..842c978
--- /dev/null
+++ b/odb_api/src/ecml/README.txt
@@ -0,0 +1,70 @@
+ECML
+-----------------------------------------
+
+ECML is an embedded and extensible scripting language with a syntax 
+compatible with syntax of the ECMWF MARS language. 
+
+A script contains a sequence of requests, each beginning with a verb
+and followed by a list of attributes and their values.
+
+A value of an attribute is always a list, containing strings and
+other requests, separated with a slash ('/').
+
+Requests appearing on the list of values must be in parantheses.
+
+
+Evaluation rules of ECML
+-----------------------------------------
+
+Script is parsed and requests are executed in sequence.
+
+Execution is performed in an execution context.
+The most important part of execution context is environment,
+which is a stack of dictionaries mapping names to objects like data
+or functions (verbs).
+
+Evaluating a single request depends on whether its verb is a regular
+verb, or a special form.
+
+1) If this is a regular verb, for example archive, retrieve (implemented
+in C++ as a RequestHandler or defined with a 'function' request) then:
+
+ - values of attributes are first evaluated.
+   Lists of values produced by evaluated requests nested on the values 
+   lists are concatenated together.
+
+ - a new dictionary is put on the environment; the dictionary is populated
+   with bindings of atributes of the request and evaluated lists of values. 
+
+ - a RequestHandler::handle(ExecutionContext&) implementation mapped to 
+   the verb is executed. The C++ code may use context.environment() to lookup
+   values it needs to do something and return a value.
+   After executing body of the function the environment created
+   in previous step is popped from the environment.
+
+2) If this is a special form, for example 'let' then the interpreter
+passes the parsed, unevaluated request and execution context to
+a registered SpecialFormHandler(Request, ExecutionContext&).
+
+Currently there are two special forms: 'let' and 'function'.
+
+'Let' (implemented in LetHandler) pushes a new dictionary containing
+bindings of attributes to evaluated lists of values. The dictionary will
+stay on top of the environment stack till the last request of 
+the currently executed sequence of requests is executed (last request 
+in a script file for the top level requests, or a closing paren for requests 
+embedded on a values list).
+
+Let is simply a way to introduce new variables in the script.
+The variables can be retrieved from evironment using a regular verb
+'value', for example (value,of=x) returns value associated with variable x.
+
+'Function' is a special form used to define new verbs.
+It accepts one or two attributes.
+The first, optional, attribute 'of' contains a list of parameters
+of the new function.
+The second atribute is a name of the new verb. The value of the attribute
+is the code of the new function.
+The scope of a function defined with a 'function' request has the same scope
+as variables introduced with 'let'.
+
diff --git a/odb_api/src/ecml/ast/Closure.cc b/odb_api/src/ecml/ast/Closure.cc
new file mode 100644
index 0000000..2863fe1
--- /dev/null
+++ b/odb_api/src/ecml/ast/Closure.cc
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+
+using namespace std;
+
+namespace ecml {
+
+// E.g.: closure,name=f,parameters=x,captured=(environment,a=1,b=2),code=(println,values=FOO)
+Closure::Closure(Cell* request)
+: name_("anonymous")
+{
+    //Log::info() << "Closure::Closure: request: " << request << endl;
+    ASSERT(request->tag() == "_verb" && request->text() == "closure");
+
+    Request r (request->rest());
+
+    if (r && r->text() == "name")
+    {
+        vector<string> names (r->valueAsListOfStrings());
+        if (names.size() > 1)
+            throw eckit::UserError("Closure can only have one name");
+        if (names.size())
+            name_ = names[0];
+        r = r->rest();
+    }
+
+    if (r && r->text() == "parameters")
+    {
+        parameters_ = r->valueAsListOfStrings();
+        r = r->rest();
+    }
+
+    if (r && r->text() == "captured")
+    {
+        capturedEnvironment_ = Cell::clone(r->value());
+        r = r->rest();
+    } else capturedEnvironment_ = new Cell("_verb", "let", 0, 0);
+
+    if (r && r->text() == "code")
+    {
+        code_ = Cell::clone(r->value());
+        r = r->rest();
+    }
+}
+
+Closure::Closure(const FunctionDefinition& fun, ExecutionContext& context)
+: name_(fun.name()),
+  parameters_(fun.parameters()),
+  capturedEnvironment_(new Cell("_verb", "let", 0, 0)),
+  code_(Cell::clone(fun.code()))
+{
+    std::vector<std::string> vs(fun.capturedVariables());
+    for (size_t i(0); i < vs.size(); ++i)
+    {
+        capturedEnvironment_->append(new Cell("", vs[i], context.environment().lookup(vs[i]), 0));
+    }
+}
+
+Closure::~Closure()
+{
+    //delete capturedEnvironment_;
+    //delete code_;
+}
+
+// E.g.: closure,name=f,parameters=x,captured=(environment,a=1,b=2),code=(println,values=FOO)
+Closure::operator Cell*() const
+{
+    List names;
+    names.append(name_);
+
+    List parameters;
+    for (size_t i (0); i < parameters_.size(); ++i)
+        parameters.append(parameters_[i]);
+
+    List captured;
+    if (capturedEnvironment_->value() || capturedEnvironment_->rest())
+        captured.append(new Cell("_requests", "", Cell::clone(capturedEnvironment_), 0));
+
+    List code;
+    code.append(Cell::clone(code_));
+
+    Cell* r (new Cell("_verb", "closure", 0,
+                    new Cell("", "name", names,
+                    new Cell("", "parameters", parameters,
+                    new Cell("", "captured", captured,
+                    new Cell("", "code", code, 0))))));
+
+    return r;
+}
+
+Cell* Closure::capturedEnvironment() const { return capturedEnvironment_; }
+
+Cell* Closure::code() const { return code_; }
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/ast/Closure.h b/odb_api/src/ecml/ast/Closure.h
new file mode 100644
index 0000000..eddce03
--- /dev/null
+++ b/odb_api/src/ecml/ast/Closure.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_Closure_H
+#define eckit_ecml_Closure_H
+
+#include <vector>
+#include <string>
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+class FunctionDefinition;
+class ExecutionContext;
+
+// E.g.: closure,name=f,parameters=x,captured=(environment,a=1,b=2),code=(println,values=FOO)
+class Closure {
+public:
+    Closure(Cell*);
+    Closure(const FunctionDefinition&, ExecutionContext&);
+    ~Closure();
+
+    Cell* capturedEnvironment() const; 
+    Cell* code() const;
+
+    operator Cell*() const;
+
+private:
+    std::string name_;
+    std::vector<std::string> parameters_;
+    Cell* capturedEnvironment_;
+    Cell* code_;
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/ast/FunctionDefinition.cc b/odb_api/src/ecml/ast/FunctionDefinition.cc
new file mode 100644
index 0000000..0116980
--- /dev/null
+++ b/odb_api/src/ecml/ast/FunctionDefinition.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "FunctionDefinition.h"
+
+using namespace std;
+
+namespace ecml {
+
+// E.g.: function,of=x,capture=a/b,f=(println,values=FOO)
+FunctionDefinition::FunctionDefinition(Cell* request)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "function");
+
+    Request r (request->rest());
+
+    Request capturedVariables (0);
+
+    if (r && r->text() == "of")
+    {
+        parameters_ = r->valueAsListOfStrings(); 
+        r = r->rest();
+    }
+
+    if (r && r->text() == "capture")
+    {
+        capturedVariables_ = r->valueAsListOfStrings();
+        r = r->rest();
+    }
+
+    ASSERT(r && r->tag() == "" && r->text().size());
+    name_ = r->text();
+    code_ = r->value()->value();
+    ASSERT(code_->tag() == "_requests");
+}
+
+std::string FunctionDefinition::name() const { return name_; }
+std::vector<std::string> FunctionDefinition::parameters() const { return parameters_; }
+std::vector<std::string> FunctionDefinition::capturedVariables() const { return capturedVariables_; }
+Cell* FunctionDefinition::code() const { return code_; }
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/ast/FunctionDefinition.h b/odb_api/src/ecml/ast/FunctionDefinition.h
new file mode 100644
index 0000000..bd69edf
--- /dev/null
+++ b/odb_api/src/ecml/ast/FunctionDefinition.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_FunctionDefinition_H
+#define eckit_ecml_FunctionDefinition_H
+
+#include <vector>
+#include <string>
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+class FunctionDefinition {
+public:
+    FunctionDefinition(Cell*);
+    std::string name() const;
+    std::vector<std::string> parameters() const;
+    std::vector<std::string> capturedVariables() const;
+    Cell* code() const;
+
+private:
+    std::string name_;
+    std::vector<std::string> parameters_;
+    std::vector<std::string> capturedVariables_;
+    Cell* code_;
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/core/Environment.cc b/odb_api/src/ecml/core/Environment.cc
new file mode 100644
index 0000000..81282f7
--- /dev/null
+++ b/odb_api/src/ecml/core/Environment.cc
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Environment.cc
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#include <unistd.h>
+#include <map>
+
+#include "eckit/exception/Exceptions.h"
+
+#include "Environment.h"
+#include "Interpreter.h"
+#include "ExecutionContext.h"
+
+using namespace std;
+using namespace ecml;
+
+Environment::Environment(const Environment& other)
+: dictionary_(Cell::clone(other.dictionary_)),
+  parent_(other.parent_)
+{
+}
+
+Environment::Environment(Environment* parent, Request r)
+: dictionary_(r),
+  parent_(parent)
+{}
+
+Environment::~Environment()
+{
+    delete dictionary_;
+}
+
+void Environment::print(std::ostream& s) const
+{
+    s << dictionary_;
+    if (parent_)
+    {
+        s << "," << endl << "parent = (";
+        parent_->print(s);
+        s << ")";
+    }
+}
+
+void Environment::set(const std::string& name, Request request)
+{
+    dictionary_->value(name, request);
+}
+
+string Environment::lookup(const string& name, const string& defaultValue, ExecutionContext& context)
+{
+    Request r (lookupNoThrow(name));
+    if (! r)
+        return defaultValue;
+
+    ASSERT(r->tag() == "_list");
+    Request evaluated (context.interpreter().eval(r, context));
+    ASSERT(evaluated->tag() == "_list");
+    if (evaluated->rest())
+        throw eckit::UserError("Expected single string as value of " + name);
+    return evaluated->value()->text();
+}
+
+vector<string> Environment::lookupList(const string& name, ExecutionContext& context)
+{
+    Request r (context.interpreter().eval(lookup(name), context));
+
+    vector<string> list;
+    for (Request e (r); e; e = e->rest())
+    {
+        ASSERT(e->tag() == "_list");
+        ASSERT(e->value()->tag() == "");
+
+        list.push_back(e->value()->text());
+    }
+    return list;
+}
+
+Request Environment::lookupNoThrow(const string& name)
+{
+    Request r (dictionary_->valueOrDefault(name, 0));
+    if (r)
+        return r;
+
+    if (! parent_)
+        return 0;
+
+    return parent_->lookupNoThrow(name);
+}
+
+Request Environment::lookup(const string& name)
+{
+    Request r(lookupNoThrow(name));
+    if (! r)
+        throw eckit::UserError (name + " not defined");
+    return r;
+}
+
+void Environment::lookupVariablesRec(const string& pattern, vector<string>& r)
+{
+    dictionary_->lookupVariables(pattern, r);
+    if (parent_)
+        parent_->lookupVariablesRec(pattern, r);
+}
+
+vector<string> Environment::lookupVariables(const string& pattern)
+{
+    vector<string> r;
+    lookupVariablesRec (pattern, r);
+    return r;
+}
+
+Environment* Environment::parent() { return parent_; }
+
+Request Environment::currentFrame() { return dictionary_; }
+
diff --git a/odb_api/src/ecml/core/Environment.h b/odb_api/src/ecml/core/Environment.h
new file mode 100644
index 0000000..849ba76
--- /dev/null
+++ b/odb_api/src/ecml/core/Environment.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Environment.h
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#ifndef eckit_utils_Environment_H
+#define eckit_utils_Environment_H
+
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class Environment {
+public:
+    Environment(const Environment&);
+    Environment(Environment*, Request);
+    ~Environment(); 
+
+    void set(const std::string&, Request);
+    Request lookup(const std::string&);
+    Request lookupNoThrow(const std::string&);
+    std::vector<std::string> lookupVariables(const std::string&);
+
+    std::string lookup(const std::string&, const std::string&, ExecutionContext&);
+    std::vector<std::string> lookupList(const std::string&, ExecutionContext&);
+
+    Environment* parent();
+    Request currentFrame();
+
+    void print(std::ostream&) const; 	
+
+private:
+    void lookupVariablesRec(const std::string&, std::vector<std::string>& );
+
+    Cell* dictionary_;
+    Environment* parent_;
+
+friend std::ostream& operator<<(std::ostream& o, const Environment& e) { e.print(o); return o; }
+
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/core/ExecutionContext.cc b/odb_api/src/ecml/core/ExecutionContext.cc
new file mode 100644
index 0000000..4c87722
--- /dev/null
+++ b/odb_api/src/ecml/core/ExecutionContext.cc
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+
+#include "ExecutionContext.h"
+#include "Environment.h"
+#include "Module.h"
+#include "Interpreter.h"
+#include "RequestHandler.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/prelude/Prelude.h"
+
+using namespace std;
+
+namespace ecml {
+
+ExecutionContext::ExecutionContext()
+: environment_(new Environment(0, new Cell("_list", "", 0, 0))),
+  otherEnvironment_(0),
+  interpreter_(new Interpreter())
+{
+    Prelude().importInto(*this);
+}
+
+ExecutionContext::ExecutionContext(const ExecutionContext& other)
+: environment_(other.environment_),
+  otherEnvironment_(other.environment_),
+  interpreter_(new Interpreter())
+{
+}
+
+ExecutionContext::~ExecutionContext()
+{
+    if (otherEnvironment_ != environment_)
+        delete environment_;
+    delete interpreter_;
+}
+
+Environment& ExecutionContext::environment() { return *environment_; }
+
+void ExecutionContext::registerHandler(const char* name, ecml::RequestHandler& handler)
+{
+    registerHandler(string(name), handler);
+}
+
+void ExecutionContext::registerHandler(const std::string& name, ecml::RequestHandler& handler)
+{
+    environment().set(name, new Cell("_native", handler.name(), 0, 0));
+
+    // TODO: save pointer to handler in a map in the context so we don't have to protect lookup operation.
+    // ExecutionContext will not be shared between threads.
+}
+
+Values ExecutionContext::executeScriptFile(const string& fileName)
+{
+    return interpreter().eval(RequestParser::parseFile(fileName.c_str(), /*debug=*/ false), *this);
+}
+
+Values ExecutionContext::execute(const string& source)
+{
+    return interpreter().eval(RequestParser::parse(source, /*debug=*/ false), *this);
+}
+
+void ExecutionContext::import(Module& module)
+{
+    pushEnvironmentFrame();
+    module.importInto(*this);
+}
+
+void ExecutionContext::pushEnvironmentFrame(Request r)
+{
+    environment_ = new Environment(environment_, r);
+}
+
+void ExecutionContext::pushEnvironmentFrame()
+{
+    pushEnvironmentFrame(new Cell("_list", "", 0, 0));
+}
+
+void ExecutionContext::popEnvironmentFrame()
+{
+    ASSERT(environment_ && environment_->parent());
+
+    Environment *e (environment_);
+    environment_ = e->parent();
+    delete e;
+}
+
+
+void ExecutionContext::popEnvironmentFrame(Request r)
+{
+    while (environment_ && environment_->currentFrame() != r)
+        popEnvironmentFrame(); 
+    ASSERT(environment_);
+    popEnvironmentFrame(); 
+}
+
+void ExecutionContext::interpreter(Interpreter* interpreter)
+{
+    delete interpreter_;
+    interpreter_ = interpreter;
+}
+
+Interpreter& ExecutionContext::interpreter() const
+{ 
+    return *interpreter_;
+}
+
+Cell* ExecutionContext::copy() const
+{
+    // TODO:
+    NOTIMP;
+    return 0;
+}
+
+std::vector<std::string> ExecutionContext::getValueAsList(const std::string& keyword)
+{
+    std::vector<std::string> r;
+
+    Request v (environment().lookup(keyword));
+    ASSERT(v->tag() == "_list");
+
+    Request evaluated (interpreter().eval(v, *this));
+    for (Request p(evaluated); p; p = p->rest())
+        if (p->value())
+            r.push_back(p->value()->text());
+
+    return r;
+}
+
+} //namespace ecml
diff --git a/odb_api/src/ecml/core/ExecutionContext.h b/odb_api/src/ecml/core/ExecutionContext.h
new file mode 100644
index 0000000..ba3dd51
--- /dev/null
+++ b/odb_api/src/ecml/core/ExecutionContext.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_utils_ExecutionContext_H
+#define eckit_utils_ExecutionContext_H
+
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+class Environment;
+class Module;
+class Interpreter;
+class RequestHandler;
+
+class ExecutionContext {
+public:
+    ExecutionContext();
+    ExecutionContext(const ExecutionContext&);
+    ~ExecutionContext();
+
+    std::vector<std::string> getValueAsList(const std::string& keyword);
+
+    void registerHandler(const char*, ecml::RequestHandler&);
+    void registerHandler(const std::string&, ecml::RequestHandler&);
+
+    ecml::Values execute(const std::string&);
+    ecml::Values executeScriptFile(const std::string&);
+
+    void import(Module&);
+
+    /// Creates a new environment frame and pushes it onto stack.
+    void pushEnvironmentFrame();
+
+    void pushEnvironmentFrame(ecml::Request);
+    void popEnvironmentFrame();
+    void popEnvironmentFrame(ecml::Request);
+
+    Environment& environment();
+
+    void interpreter(Interpreter*);
+    Interpreter& interpreter() const;
+
+    Cell* copy() const;
+
+private:
+    Environment* environment_;
+    Environment* otherEnvironment_;
+    Interpreter* interpreter_;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/core/Interpreter.cc b/odb_api/src/ecml/core/Interpreter.cc
new file mode 100644
index 0000000..d7c8d83
--- /dev/null
+++ b/odb_api/src/ecml/core/Interpreter.cc
@@ -0,0 +1,202 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/ast/Closure.h"
+#include "SpecialFormHandler.h"
+#include "RequestHandler.h"
+#include "Interpreter.h"
+#include "ExecutionContext.h"
+#include "Environment.h"
+
+using namespace std;
+
+namespace ecml {
+
+Interpreter::Interpreter()
+: debug_(0)
+{}
+
+Interpreter::~Interpreter() {}
+
+Values Interpreter::eval(const Request request, ExecutionContext& context)
+{
+    if (request->tag() == "") return request;
+    if (request->tag() == "_requests") return evalRequests(request, context);
+    if (request->tag() == "_list") return evalList(request, context);
+
+    ASSERT((request->tag() == "_verb" || request->tag() == "_macro") && request->text().size());
+
+    string verb (request->text());
+    Request object (context.environment().lookup(verb));
+    string tag (object->tag());
+
+    if (tag == "_list")
+    {
+        if (object->rest() == 0)
+        {
+            object = object->value();
+            tag = object->tag();
+        } else
+        {
+        }
+    }
+
+    Values r ( tag == ""          ? object
+             : tag == "_list"     ? object
+             : tag == "_native"   ? evalNative(object, request, context)
+             : tag == "_verb" 
+               && object->text() == "let" ? object
+             : tag == "_verb"     ? evalClosure(object, request, context)
+             : tag == "_macro"    ? evalMacro(object, request, context)
+             : tag == "_requests" ? eval(object, context)
+             : tag == "_request"  ? eval(object, context)
+             : 0 );
+    ASSERT(r);
+    return r;
+}
+
+Values Interpreter::evalList(const Request requests, ExecutionContext& context)
+{
+    ASSERT(requests->tag() == "_list");
+
+    List list; 
+
+    for (Request elt(requests); elt; elt = elt->rest())
+    {
+        if (! elt->tag().size())
+        {
+            eckit::Log::info() << "elt->tag() == " << elt->tag() <<  " " << endl;
+            eckit::Log::info() << "elt == " << elt <<  " ABORTING..." << endl;
+            NOTIMP;
+            list.append(Cell::clone(elt->value()));
+        } 
+        if (! elt->value())
+            continue; // empty list
+        if (elt->value()->tag() == "_requests")
+        {
+            Values sublist (evalRequests(elt->value(), context));
+
+            if (sublist->tag() == "_list")
+            {
+                for (Request e(sublist); e; e = e->rest())
+                    list.append(e->value());
+
+            } else if (sublist->tag() == "_native"
+                       || sublist->tag() == "_macro"
+                       || sublist->tag() == "_verb"
+                       || !sublist->tag().size())
+            {
+                list.append(sublist);
+            } else 
+            {
+                NOTIMP;
+            }
+        }
+        else if (elt->value()->tag() == "_list")
+        {
+            Values sublist (elt->value());
+            ASSERT(sublist->tag() == "_list");
+
+            for (Request e(sublist); e; e = e->rest())
+                list.append(e->value());
+        } else if (elt->value()->tag() == "")
+        {
+            list.append(elt->value());
+        } else if (elt->value()->tag() == "_verb"
+                    || elt->value()->tag() == "_macro")
+        {
+            Values sublist (elt->value());
+            list.append(sublist);
+        } else {
+            NOTIMP;
+        }
+    }
+
+    return list;
+}
+
+Values Interpreter::evalRequests(const Request requests, ExecutionContext& context)
+{
+    Values r(0);
+    for (Request elt(requests); elt; elt = elt->rest())
+    {
+        Request request( elt->value() );
+        delete r;
+
+        r = eval(request, context);
+    }
+    return r;
+}
+
+Values Interpreter::evalNative(const Request object, const Request request, ExecutionContext& context)
+{
+    RequestHandler& handler (RequestHandler::handler(object->text()));
+
+    Request evaluatedAttributes (evalAttributes(request, context));
+    context.pushEnvironmentFrame(evaluatedAttributes);
+
+    Values r (handler.handle(context));
+
+    context.popEnvironmentFrame(evaluatedAttributes);
+    return r;
+}
+
+Values Interpreter::evalMacro(const Request object, const Request request, ExecutionContext& context)
+{
+    SpecialFormHandler& h (SpecialFormHandler::handler(object->text()));
+    return h.handle(request, context);
+}
+
+Values Interpreter::evalClosure(const Request object, const Request request, ExecutionContext& context)
+{
+    ASSERT(object->text() == "closure");
+
+    Closure closure (object);
+
+    Request evaluatedAttributes (evalAttributes(request, context));
+
+    Cell* captured (closure.capturedEnvironment());
+    Cell* staticEnvironment (captured ? (captured->value() ? captured->value()->value() : 0) : 0);
+
+    context.pushEnvironmentFrame(evaluatedAttributes);
+    if (staticEnvironment) context.pushEnvironmentFrame(staticEnvironment);
+
+    Cell *r (eval(closure.code(), context));
+
+    if (staticEnvironment) context.popEnvironmentFrame(staticEnvironment);
+    context.popEnvironmentFrame(evaluatedAttributes);
+    return r;
+}
+
+Request Interpreter::evalAttributes(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb");
+    ASSERT(request->value() == 0);
+
+    Request r(new Cell(request->tag(), request->text(), 0, 0));
+
+    for (Request e(request->rest()); e; e = e->rest())
+    {
+        ASSERT(e->tag() == "");
+
+        const string& attributeName (e->text());
+        Values unevaluatedValues (e->value());
+
+        Values values (evalList(unevaluatedValues, context));
+
+        r->append(new Cell("", attributeName, values, 0));
+    }
+    return r;
+}
+
+} // namepsace eckit
diff --git a/odb_api/src/ecml/core/Interpreter.h b/odb_api/src/ecml/core/Interpreter.h
new file mode 100644
index 0000000..5086556
--- /dev/null
+++ b/odb_api/src/ecml/core/Interpreter.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_utils_Interpreter_H
+#define eckit_utils_Interpreter_H
+
+#include <list>
+
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class Interpreter {
+public:
+    Interpreter();
+    virtual ~Interpreter();
+
+    virtual Values eval(const Request, ExecutionContext&);
+    virtual Request evalAttributes(const Request, ExecutionContext&);
+
+    void debug(bool d) { debug_ = d; }
+
+    virtual Values evalRequests(const Request, ExecutionContext&);
+    virtual Values evalClosure(const Request object, const Request request, ExecutionContext&);
+    virtual Values evalNative(const Request object, const Request request, ExecutionContext&);
+protected:
+    virtual Values evalList(const Request, ExecutionContext&);
+    virtual Request evalMacro(const Request, const Request, ExecutionContext&);
+
+private:
+    bool debug_;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/core/Module.cc b/odb_api/src/ecml/core/Module.cc
new file mode 100644
index 0000000..4ebd135
--- /dev/null
+++ b/odb_api/src/ecml/core/Module.cc
@@ -0,0 +1,19 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Module.cc
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#include "Module.h"
+
+using namespace ecml;
+
+Module::~Module() {}
+
diff --git a/odb_api/src/ecml/core/Module.h b/odb_api/src/ecml/core/Module.h
new file mode 100644
index 0000000..0819e02
--- /dev/null
+++ b/odb_api/src/ecml/core/Module.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Module.h
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#ifndef Module_H
+#define Module_H
+
+namespace ecml {
+
+class ExecutionContext;
+
+/**
+ * Each software package/library X willing to add some functionality to the MARS language
+ * in forms of a new (native) function aka verb, should derive a class from ecml::RequestHandler
+ * and create instance of it, for example as a static local variable in <X>Module::importInto,
+ * which will register the new RequestHandler, and also insert reference to them in the
+ * context().environment, see ODBModule.cc in ODB API. 
+ *
+ * NOTE: this is a work in progress, no details how to insert reference here as it may
+ * get outdated qwuickly, see ODBModule::importInto for the current way of doing it.
+ * 
+ * After running importInto the new verbs ("native functions") should be avialable in the
+ * ExecutionContext.
+ */
+
+class Module {
+public:
+	virtual ~Module(); 
+
+    virtual void importInto(ExecutionContext&) = 0;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/core/RequestHandler.cc b/odb_api/src/ecml/core/RequestHandler.cc
new file mode 100644
index 0000000..ffb1e55
--- /dev/null
+++ b/odb_api/src/ecml/core/RequestHandler.cc
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+
+#include "RequestHandler.h"
+#include "ExecutionContext.h"
+#include "Environment.h"
+#include "Interpreter.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+std::map<std::string,RequestHandler*> RequestHandler::registeredHandlers_ = std::map<std::string,RequestHandler*>();
+
+RequestHandler::RequestHandler(const string& name)
+: name_(name)
+{
+    registeredHandlers_[name] = this;
+}
+
+RequestHandler& RequestHandler::handler(const std::string& name)
+{
+    if (registeredHandlers_.find(name) == registeredHandlers_.end())
+        throw eckit::UserError(string("RequestHandler ") + name + " not found");
+
+    return *registeredHandlers_[name];
+}
+
+std::string RequestHandler::name() const { return name_; }
+
+string RequestHandler::database(ExecutionContext& context) 
+{
+    string r (context.environment().lookup("database", "", context));
+    if (! r.size())
+        throw UserError("You must specify DATABASE explicitly");
+    return r;
+}
+
+string RequestHandler::database(Request request) 
+{
+    string r (request->valueAsString("database", ""));
+    if (! r.size())
+        throw UserError("You must specify DATABASE explicitly");
+    return r;
+}
+
+long RequestHandler::port(ExecutionContext& context) 
+{
+    string r (context.environment().lookup("port", "9000", context));
+    return atol(r.c_str());
+}
+
+long RequestHandler::port(Request request)
+{
+    string r (request->valueAsString("port", "9000"));
+    return atol(r.c_str());
+}
+
+vector<string> RequestHandler::pathNamesToStrings(const vector<PathName>& ps)
+{
+    vector<string> r;
+    for (size_t i(0); i < ps.size(); ++i)
+        r.push_back(ps[i]);
+    return r;
+}
+
+} //namespace ecml 
+
diff --git a/odb_api/src/ecml/core/RequestHandler.h b/odb_api/src/ecml/core/RequestHandler.h
new file mode 100644
index 0000000..5015bc6
--- /dev/null
+++ b/odb_api/src/ecml/core/RequestHandler.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_utils_RequestHandler_H
+#define eckit_utils_RequestHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+
+namespace eckit { class MultiHandle; }
+
+namespace ecml { 
+
+class ExecutionContext;
+
+class RequestHandler {
+public:
+    virtual ecml::Values handle(ecml::ExecutionContext&) = 0;
+
+    virtual std::string name() const;
+
+    static std::string database(ExecutionContext&);
+    static long port(ExecutionContext&);
+    static std::string database(Request);
+    static long port(ecml::Request);
+
+    static RequestHandler& handler(const std::string&);
+
+protected:
+    RequestHandler(const std::string&);
+
+    static std::vector<std::string> pathNamesToStrings(const std::vector<eckit::PathName>&);
+
+    std::string name_;
+    
+    static std::map<std::string,ecml::RequestHandler*> registeredHandlers_;
+};
+
+} //namespace ecml 
+
+#endif
diff --git a/odb_api/src/ecml/core/SpecialFormHandler.cc b/odb_api/src/ecml/core/SpecialFormHandler.cc
new file mode 100644
index 0000000..17e9e22
--- /dev/null
+++ b/odb_api/src/ecml/core/SpecialFormHandler.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "SpecialFormHandler.h"
+#include "ExecutionContext.h"
+#include "Environment.h"
+#include "Interpreter.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+std::map<std::string,SpecialFormHandler*> SpecialFormHandler::registeredHandlers_ = std::map<std::string,SpecialFormHandler*>();
+
+SpecialFormHandler::SpecialFormHandler(const string& name)
+: name_(name)
+{
+    registeredHandlers_[name] = this;
+}
+
+SpecialFormHandler& SpecialFormHandler::handler(const std::string& name)
+{
+    if (registeredHandlers_.find(name) == registeredHandlers_.end())
+        throw UserError(string("SpecialFormHandler ") + name + " not found");
+
+    return *registeredHandlers_[name];
+}
+
+std::string SpecialFormHandler::name() const { return name_; }
+
+} // namespace ecml 
diff --git a/odb_api/src/ecml/core/SpecialFormHandler.h b/odb_api/src/ecml/core/SpecialFormHandler.h
new file mode 100644
index 0000000..7b60244
--- /dev/null
+++ b/odb_api/src/ecml/core/SpecialFormHandler.h
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_utils_SpecialFormHandler_H
+#define eckit_utils_SpecialFormHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+
+namespace ecml { 
+
+class ExecutionContext;
+
+class SpecialFormHandler {
+public:
+    virtual Request handle(const Request, ExecutionContext&) = 0;
+
+    virtual std::string name() const;
+
+    static SpecialFormHandler& handler(const std::string&);
+
+protected:
+    SpecialFormHandler(const std::string&);
+
+    std::string name_;
+    
+    static std::map<std::string,SpecialFormHandler*> registeredHandlers_;
+};
+
+} //namespace ecml 
+
+#endif
diff --git a/odb_api/src/ecml/data/DataHandleFactory.cc b/odb_api/src/ecml/data/DataHandleFactory.cc
new file mode 100644
index 0000000..12cd8e5
--- /dev/null
+++ b/odb_api/src/ecml/data/DataHandleFactory.cc
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+#include <memory>
+
+#include "ecml/data/DataHandleFactory.h"
+#include "ecml/data/PartFileHandleFactory.h"
+#include "ecml/data/MarsHandleFactory.h"
+#include "ecml/data/FileHandleFactory.h"
+#include "ecml/data/HttpHandleFactory.h"
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/memory/ScopedPtr.h"
+
+using namespace eckit;
+
+namespace ecml {
+
+DataHandleFactory::DataHandleFactory(const std::string& prefix)
+: prefix_(prefix)
+{
+    factories()[prefix] = this;
+}
+
+DataHandleFactory::~DataHandleFactory()
+{
+    factories().erase(prefix_);
+}
+
+std::string DataHandleFactory::prefix() const { return prefix_; }
+
+DataHandleFactory::Storage &DataHandleFactory::factories()
+{
+    static Storage factories;
+    return factories;
+}
+
+DataHandle* DataHandleFactory::makeHandle(const std::string& prefix, const std::string& descriptor)
+{
+    if (factories().find(prefix) == factories().end())
+        throw UserError(std::string("No factory for '") + prefix + "://' data descriptors");
+
+    return factories()[prefix]->makeHandle(descriptor);
+}
+
+std::pair<std::string, std::string> DataHandleFactory::splitPrefix(const std::string& handleDescriptor)
+{
+    const std::string delimiter("://");
+    size_t pos (handleDescriptor.find(delimiter));
+    if (pos != std::string::npos)
+        return make_pair(handleDescriptor.substr(0, pos), handleDescriptor.substr(pos + delimiter.size()));
+
+    // Sugar.
+    if (StringTools::startsWith(StringTools::lower(StringTools::trim(handleDescriptor)), "retrieve,")
+        || StringTools::startsWith(StringTools::lower(StringTools::trim(handleDescriptor)), "stage,")
+        || StringTools::startsWith(StringTools::lower(StringTools::trim(handleDescriptor)), "list,")
+        || StringTools::startsWith(StringTools::lower(StringTools::trim(handleDescriptor)), "archive,"))
+        return std::make_pair(std::string("mars"), handleDescriptor);
+
+    return std::make_pair(std::string("file"), handleDescriptor);
+}
+
+void DataHandleFactory::buildMultiHandle(eckit::MultiHandle& mh, const std::string& dataDescriptor)
+{
+    registerFactories();
+    std::vector<std::string> ds;
+    ds.push_back(dataDescriptor);
+    buildMultiHandle(mh, ds);
+}
+
+void DataHandleFactory::buildMultiHandle(eckit::MultiHandle& mh, const std::vector<std::string>& dataDescriptors)
+{
+    registerFactories();
+    std::vector<DataHandle*> handles;
+    for (size_t i(0); i < dataDescriptors.size(); ++i)
+    {
+        std::pair<std::string,std::string> p (splitPrefix(dataDescriptors[i]));
+        mh += makeHandle(p.first, p.second);
+    }
+}
+
+DataHandle* DataHandleFactory::openForRead(const std::string& s)
+{
+    registerFactories();
+    std::pair<std::string,std::string> p (splitPrefix(s));
+    std::auto_ptr<DataHandle> d (makeHandle(p.first, p.second));
+    d->openForRead();
+    return d.release();
+}
+
+DataHandle* DataHandleFactory::openForWrite(const std::string& s, const eckit::Length& length)
+{
+    registerFactories();
+    std::pair<std::string,std::string> p (splitPrefix(s));
+    std::auto_ptr<DataHandle> d (makeHandle(p.first, p.second));
+    d->openForWrite(length);
+    return d.release();
+}
+
+void DataHandleFactory::registerFactories()
+{
+    static PartFileHandleFactory partFileHandleFactory;
+    static MarsHandleFactory marsHandleFactory;
+    static FileHandleFactory fileHandleFactory;
+    static HttpHandleFactory httpHandleFactory;
+}
+
+} //namespace ecml 
diff --git a/odb_api/src/ecml/data/DataHandleFactory.h b/odb_api/src/ecml/data/DataHandleFactory.h
new file mode 100644
index 0000000..74945f3
--- /dev/null
+++ b/odb_api/src/ecml/data/DataHandleFactory.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_utils_DataHandleFactory_H
+#define eckit_utils_DataHandleFactory_H
+
+#include <string>
+#include <map>
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+
+namespace eckit { 
+    class DataHandle;
+    class MultiHandle;
+    class Length; 
+}
+
+namespace ecml { 
+
+class DataHandleFactory {
+public:  // Types
+    typedef std::map<std::string, DataHandleFactory*> Storage;
+
+public:
+    virtual ~DataHandleFactory();
+
+    static eckit::DataHandle* openForRead(const std::string&);
+    static eckit::DataHandle* openForWrite(const std::string&, const eckit::Length& = eckit::Length(0));
+
+    static void buildMultiHandle(eckit::MultiHandle&, const std::vector<std::string>&);
+    static void buildMultiHandle(eckit::MultiHandle&, const std::string&);
+
+protected:
+    DataHandleFactory(const std::string&);
+
+    static std::pair<std::string,std::string> splitPrefix(const std::string&);
+
+    static eckit::DataHandle* makeHandle(const std::string&, const std::string&);
+
+    virtual eckit::DataHandle* makeHandle(const std::string&) const = 0;
+    std::string prefix() const;
+
+private:
+    DataHandleFactory();
+    static Storage& factories();
+    static void registerFactories();
+
+    std::string prefix_;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/data/FileHandleFactory.cc b/odb_api/src/ecml/data/FileHandleFactory.cc
new file mode 100644
index 0000000..0d9fd72
--- /dev/null
+++ b/odb_api/src/ecml/data/FileHandleFactory.cc
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "ecml/data/FileHandleFactory.h"
+#include "eckit/io/FileHandle.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace ecml {
+
+FileHandleFactory::FileHandleFactory()
+: DataHandleFactory("file")
+{}
+
+eckit::DataHandle* FileHandleFactory::makeHandle(const string& fileName) const
+{
+    return new FileHandle(fileName);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/data/FileHandleFactory.h b/odb_api/src/ecml/data/FileHandleFactory.h
new file mode 100644
index 0000000..0b76e03
--- /dev/null
+++ b/odb_api/src/ecml/data/FileHandleFactory.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_utils_FileHandleFactory_H
+#define eckit_utils_FileHandleFactory_H
+
+#include "DataHandleFactory.h"
+
+namespace ecml { 
+
+class FileHandleFactory : public DataHandleFactory
+{
+public:
+    FileHandleFactory();
+protected:
+    eckit::DataHandle* makeHandle(const std::string&) const;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/data/HttpHandle.cc b/odb_api/src/ecml/data/HttpHandle.cc
new file mode 100644
index 0000000..43e4251
--- /dev/null
+++ b/odb_api/src/ecml/data/HttpHandle.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "ecml/data/HttpHandle.h"
+
+namespace eckit {
+
+void HttpHandle::print(std::ostream& s) const
+{
+	s << "HttpHandle[" << url_ << ']';
+}
+
+void HttpHandle::encode(Stream& s) const
+{
+	NOTIMP;
+}
+
+std::string HttpHandle::parseHost(const std::string& url)
+{
+    // TODO:
+    return "localhost";
+}
+
+int HttpHandle::parsePort(const std::string& url)
+{
+    // TODO:
+    return 8000;
+}
+
+HttpHandle::HttpHandle(const std::string &url)
+: url_(url),
+  tcp_(parseHost(url), parsePort(url))
+{
+}
+
+HttpHandle::~HttpHandle()
+{
+}
+
+Length HttpHandle::openForRead()
+{
+	return 0;
+}
+
+void HttpHandle::openForWrite(const Length&)
+{
+}
+
+void HttpHandle::openForAppend(const Length&)
+{
+}
+
+long HttpHandle::read(void* buffer, long length)
+{
+    long n(0);
+    // TODO:
+	return n;
+}
+
+long HttpHandle::write(const void* buffer, long length)
+{
+    long n(0);
+    // TODO:
+	return n;
+}
+
+void HttpHandle::close()
+{
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/data/HttpHandle.h b/odb_api/src/ecml/data/HttpHandle.h
new file mode 100644
index 0000000..2e2b353
--- /dev/null
+++ b/odb_api/src/ecml/data/HttpHandle.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// @file   HttpHandle.h
+// @author Piotr Kuchta - ECMWF 27 Oct 2016
+
+#ifndef eckit_filesystem_HttpFileHandle_h
+#define eckit_filesystem_HttpFileHandle_h
+
+#include "eckit/io/TCPHandle.h"
+
+namespace eckit {
+
+class HttpHandle : public DataHandle {
+public:
+	HttpHandle(const std::string& url);
+	~HttpHandle();
+// -- Overridden methods
+	// From DataHandle
+    virtual Length openForRead();
+    virtual void openForWrite(const Length&);
+    virtual void openForAppend(const Length&);
+
+	virtual long read(void*,long);
+	virtual long write(const void*,long);
+	virtual void close();
+	virtual void print(std::ostream&) const;
+
+	// From Streamable
+	virtual void encode(Stream&) const;
+
+    static std::string parseHost(const std::string&);
+    static int parsePort(const std::string&);
+
+private:
+	const std::string url_;
+    eckit::TCPHandle tcp_;
+};
+
+} // namespace eckit
+
+#endif
diff --git a/odb_api/src/ecml/data/HttpHandleFactory.cc b/odb_api/src/ecml/data/HttpHandleFactory.cc
new file mode 100644
index 0000000..ab3e8f1
--- /dev/null
+++ b/odb_api/src/ecml/data/HttpHandleFactory.cc
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/data/HttpHandle.h"
+#include "ecml/data/HttpHandleFactory.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace ecml {
+
+HttpHandleFactory::HttpHandleFactory()
+: DataHandleFactory("http")
+{}
+
+eckit::DataHandle* HttpHandleFactory::makeHandle(const std::string& r) const
+{
+    return new HttpHandle(r);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/data/HttpHandleFactory.h b/odb_api/src/ecml/data/HttpHandleFactory.h
new file mode 100644
index 0000000..a6d302b
--- /dev/null
+++ b/odb_api/src/ecml/data/HttpHandleFactory.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, October 2016
+
+#ifndef ecml_mars_HttpHandleFactory_H
+#define ecml_mars_HttpHandleFactory_H
+
+#include "ecml/data/DataHandleFactory.h"
+
+namespace ecml {
+
+class HttpHandleFactory : public ecml::DataHandleFactory
+{
+public:
+    HttpHandleFactory();
+protected:
+    eckit::DataHandle* makeHandle(const std::string&) const;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/data/Makefile b/odb_api/src/ecml/data/Makefile
new file mode 100644
index 0000000..3b2036e
--- /dev/null
+++ b/odb_api/src/ecml/data/Makefile
@@ -0,0 +1,2 @@
+all:
+	m
diff --git a/odb_api/src/ecml/data/MarsHandleFactory.cc b/odb_api/src/ecml/data/MarsHandleFactory.cc
new file mode 100644
index 0000000..8584716
--- /dev/null
+++ b/odb_api/src/ecml/data/MarsHandleFactory.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/RequestHandler.h"
+
+#include "metkit/MarsRequestHandle.h"
+#include "metkit/DHSProtocol.h"
+
+#include "ecml/data/MarsHandleFactory.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace ecml {
+
+MarsHandleFactory::MarsHandleFactory()
+: DataHandleFactory("mars")
+{}
+
+bool shortName(const std::string& prefix, const std::string& s)
+{
+    if (prefix.size() > s.size())
+        return false;
+    return std::equal(prefix.begin(), prefix.end(), s.begin());
+}
+
+std::string verb(const ecml::Request request)
+{
+    std::string v (eckit::StringTools::lower(request->text()));
+
+    const char* verbs[] = {"retrieve", "stage", "list", "archive", 0};
+
+    for (size_t i (0); verbs[i]; ++i)
+        if (shortName(v, verbs[i])) 
+            return verbs[i];
+
+    throw eckit::UserError(std::string("Unknown request '") + v + "'");
+    return v;
+}
+
+DataHandle* MarsHandleFactory::makeHandle(const string& r) const
+{
+    Log::debug() << "MarsHandleFactory::makeHandle: parsing " << r << endl;
+
+    Request requests (ecml::RequestParser::parse(r));
+
+    Log::debug() << "MarsHandleFactory::makeHandle: requests = " << requests << endl;
+
+    Request request (requests->value());
+
+    Log::debug() << "MarsHandleFactory::makeHandle: request = " << request << endl;
+
+    if (requests->rest())
+        Log::warning() << "MarsHandleFactory: Only " << request << " used, skipped rest of " << requests << endl;
+
+    string host (RequestHandler::database(request));
+    long port (RequestHandler::port(request));
+
+    metkit::MarsRequest mr (verb(request));
+    ecml::convertToMarsRequest<metkit::MarsRequest> (request, mr);
+
+    return new metkit::MarsRequestHandle(mr, new metkit::DHSProtocol(host, host, port));
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/data/MarsHandleFactory.h b/odb_api/src/ecml/data/MarsHandleFactory.h
new file mode 100644
index 0000000..140669b
--- /dev/null
+++ b/odb_api/src/ecml/data/MarsHandleFactory.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef ecml_mars_MarsHandleFactory_H
+#define ecml_mars_MarsHandleFactory_H
+
+#include "ecml/data/DataHandleFactory.h"
+
+namespace ecml {
+
+class MarsHandleFactory : public ecml::DataHandleFactory
+{
+public:
+    MarsHandleFactory();
+protected:
+    eckit::DataHandle* makeHandle(const std::string&) const;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/data/PartFileHandleFactory.cc b/odb_api/src/ecml/data/PartFileHandleFactory.cc
new file mode 100644
index 0000000..eb07c45
--- /dev/null
+++ b/odb_api/src/ecml/data/PartFileHandleFactory.cc
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "ecml/data/PartFileHandleFactory.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/parser/StringTools.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace ecml {
+
+PartFileHandleFactory::PartFileHandleFactory()
+: DataHandleFactory("partfile")
+{}
+
+DataHandle* PartFileHandleFactory::makeHandle(const string& descriptor) const
+{
+    vector<string> ps (StringTools::split(":", descriptor));
+    string fileName (ps[0]);
+    vector<string> range (StringTools::split(",", ps[ps.size() - 1]));
+
+    Offset offset (atoi(range[0].c_str()));
+    Length length (atoi(range[1].c_str()));
+
+    return new PartFileHandle(fileName, offset, length);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/data/PartFileHandleFactory.h b/odb_api/src/ecml/data/PartFileHandleFactory.h
new file mode 100644
index 0000000..4bd5da1
--- /dev/null
+++ b/odb_api/src/ecml/data/PartFileHandleFactory.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_utils_PartFileHandleFactory_H
+#define eckit_utils_PartFileHandleFactory_H
+
+#include "DataHandleFactory.h"
+
+namespace ecml { 
+
+class PartFileHandleFactory : public DataHandleFactory
+{
+public:
+    PartFileHandleFactory();
+protected:
+    eckit::DataHandle* makeHandle(const std::string&) const;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/ecml_config.h.in b/odb_api/src/ecml/ecml_config.h.in
new file mode 100644
index 0000000..e12a768
--- /dev/null
+++ b/odb_api/src/ecml/ecml_config.h.in
@@ -0,0 +1,7 @@
+#ifndef ecml_config_h
+#define ecml_config_h
+
+#define ECML_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
+#define ECML_BUILD_DIR      "@CMAKE_BINARY_DIR@"
+
+#endif // ecml_config_h
diff --git a/odb_api/src/ecml/misc/DynamicParametrisation.cc b/odb_api/src/ecml/misc/DynamicParametrisation.cc
new file mode 100644
index 0000000..7e5d6b4
--- /dev/null
+++ b/odb_api/src/ecml/misc/DynamicParametrisation.cc
@@ -0,0 +1,107 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta
+/// @date June 2015
+
+#include <vector>
+
+#include "eckit/utils/Translator.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "ecml/misc/DynamicParametrisation.h"
+
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+DynamicParametrisation::DynamicParametrisation(ExecutionContext& context)
+: context_(context)
+{}
+
+DynamicParametrisation::~DynamicParametrisation() {}
+
+bool DynamicParametrisation::has(const std::string& name) const
+{
+    return context_.environment().lookupNoThrow(name);
+}
+
+bool DynamicParametrisation::get(const std::string& name, std::string& value) const
+{
+    bool r (has(name));
+    if (r)
+        value = context_.environment().lookup(name, "", context_);
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, bool& value) const
+{
+    bool r (has(name));
+    if (r)
+        value = Translator<string, bool>()(context_.environment().lookup(name, "", context_));
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, long& value) const
+{
+    bool r (has(name));
+    if (r)
+        value = Translator<string, long>()(context_.environment().lookup(name, "", context_));
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, size_t& value) const
+{
+    bool r (has(name));
+    if (r)
+        value = Translator<string, size_t>()(context_.environment().lookup(name, "", context_));
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, double& value) const
+{
+    bool r (has(name));
+    if (r)
+        value = Translator<string, double>()(context_.environment().lookup(name, "", context_));
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, std::vector<long>& value) const
+{
+    bool r (has(name));
+    if (r)
+    {
+        value.clear();
+        vector<string> values (context_.environment().lookupList(name, context_));
+        for (size_t i(0); i < values.size(); ++i)
+            value.push_back(Translator<string, long>()(values[i]));
+    }
+    return r;
+}
+
+bool DynamicParametrisation::get(const std::string& name, std::vector<double>& value) const
+{
+    bool r (has(name));
+    if (r)
+    {
+        value.clear();
+        vector<string> values (context_.environment().lookupList(name, context_));
+        for (size_t i(0); i < values.size(); ++i)
+            value.push_back(Translator<string, double>()(values[i]));
+    }
+    return r;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/misc/DynamicParametrisation.h b/odb_api/src/ecml/misc/DynamicParametrisation.h
new file mode 100644
index 0000000..5ee67be
--- /dev/null
+++ b/odb_api/src/ecml/misc/DynamicParametrisation.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta
+/// @date June 2015
+
+#ifndef eckit_config_DynamicParametrisation_H
+#define eckit_config_DynamicParametrisation_H
+
+#include "eckit/config/Parametrisation.h"
+
+#include <string>
+#include <vector>
+
+namespace ecml {
+
+class ExecutionContext;
+
+class DynamicParametrisation : public eckit::Parametrisation {
+public:
+
+    DynamicParametrisation(ExecutionContext&);
+    ~DynamicParametrisation();
+
+    virtual bool has(const std::string& name) const;
+
+    virtual bool get(const std::string& name, std::string& value) const;
+    virtual bool get(const std::string& name, bool& value) const;
+    virtual bool get(const std::string& name, long& value) const;
+    virtual bool get(const std::string& name, size_t& value) const;
+    virtual bool get(const std::string& name, double& value) const;
+
+    virtual bool get(const std::string& name, std::vector<long>& value) const;
+    virtual bool get(const std::string& name, std::vector<double>& value) const;
+
+private:
+    ExecutionContext& context_;
+};
+
+} // namespace ecml
+
+#endif
+
diff --git a/odb_api/src/ecml/misc/ParameterizedRequestHandler.cc b/odb_api/src/ecml/misc/ParameterizedRequestHandler.cc
new file mode 100644
index 0000000..cdb92de
--- /dev/null
+++ b/odb_api/src/ecml/misc/ParameterizedRequestHandler.cc
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+
+#include "ParameterizedRequestHandler.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+ParameterizedRequestHandler::ParameterizedRequestHandler(const string& name)
+: RequestHandler(name)
+{}
+
+Values ParameterizedRequestHandler::handle(ExecutionContext& context)
+{
+    DynamicParametrisation params(context);
+    vector<string> returnValues (handle(params));
+
+    if (returnValues.size() == 0) 
+        return new Cell("_list", "", 0, 0);
+
+    Values rv(0);
+    List list(rv);
+    for (size_t i(0); i < returnValues.size(); ++i)
+        list.append(returnValues[i]);
+    return rv;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/misc/ParameterizedRequestHandler.h b/odb_api/src/ecml/misc/ParameterizedRequestHandler.h
new file mode 100644
index 0000000..694fa6e
--- /dev/null
+++ b/odb_api/src/ecml/misc/ParameterizedRequestHandler.h
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, June 2015
+
+#ifndef eckit_utils_ParameterizedRequestHandler_H
+#define eckit_utils_ParameterizedRequestHandler_H
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+#include "ecml/misc/DynamicParametrisation.h"
+
+#include <string>
+
+namespace ecml { 
+
+class ExecutionContext;
+class MultiHandle; 
+
+class ParameterizedRequestHandler : public ecml::RequestHandler {
+public:
+    ParameterizedRequestHandler(const std::string&);
+    ~ParameterizedRequestHandler();
+
+    virtual Values handle(ExecutionContext&);
+
+    virtual std::vector<std::string> handle(eckit::Parametrisation&) = 0;
+};
+
+} //namespace ecml 
+
+#endif
diff --git a/odb_api/src/ecml/parser/Cell.cc b/odb_api/src/ecml/parser/Cell.cc
new file mode 100644
index 0000000..c247316
--- /dev/null
+++ b/odb_api/src/ecml/parser/Cell.cc
@@ -0,0 +1,216 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/utils/Regex.h"
+
+#include "Cell.h"
+#include "CellPrinter.h"
+#include "CellDotPrinter.h"
+
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+typedef StringTools S;
+
+Cell::Cell(const std::string& tag, const std::string& text, Cell* value, Cell* rest)
+: tag_(tag),
+  text_(text),
+  value_(value),
+  rest_(rest)
+{}
+
+Cell::Cell(const Cell* o)
+: tag_(o->tag_),
+  text_(o->text_),
+  value_(o->value_ ? new Cell(o->value_) : 0),
+  rest_(o->rest_ ? new Cell(o->rest_) : 0)
+{}
+
+Cell* Cell::clone(const Cell* c) { return c ? new Cell(c) : 0; }
+
+const std::string& Cell::tag() const { return tag_; }
+const std::string& Cell::text() const { return text_; }
+Cell* Cell::value() const { return value_; }
+Cell* Cell::rest() const { return rest_; }
+
+Cell* Cell::value(Cell* v) { return value_ = v; }
+Cell* Cell::rest(Cell* r) { return rest_ = r; }
+
+Cell* Cell::text(const std::string& s)
+{
+    text_ = s;
+    return this;
+}
+
+Cell* Cell::append(Cell* r)
+{ 
+    Cell* last (this);
+    for ( ; last->rest_; last = last->rest_)
+        ;
+    last->rest_ = r;
+    return this;
+}
+
+Cell* Cell::value(const std::string& keyword, const std::string& value)
+{
+   return this->value(keyword, new Cell("_list", "", new Cell("", value, 0, 0), 0));
+}
+
+Cell* Cell::value(const std::string& keyword, Cell* v) 
+{
+    //showGraph(string("value: request = ") + str() + ", keyword = " + keyword + ", v = " + v->str());
+    const std::string k (StringTools::lower(keyword));
+
+    for (Cell* r (this); r; r = r->rest())
+    {
+        if (StringTools::lower(r->text()) == k)
+            return r->value(v);
+
+        if (! r->rest())
+            return r->rest(new Cell("", keyword, v, 0))->value();
+    }
+
+    throw AssertionFailed("Should not reach here");
+    return 0;
+}
+
+void Cell::update(const std::string& keyword, Cell* v) 
+{
+    const std::string k (StringTools::lower(keyword));
+
+    Cell* lastMatch (0);
+    Cell* lastCell (this); 
+
+    for (Cell* r (this); r; r = r->rest())
+    {
+        if (StringTools::lower(r->text()) == k)
+            lastMatch = r;
+
+        lastCell = r;
+    }
+
+    if (! lastMatch) 
+        lastCell->rest(new Cell("", keyword, v, 0));
+    else
+    {
+        delete lastMatch->value();
+        lastMatch->value(v);
+    } 
+}
+
+Cell* Cell::valueOrDefault(const string& keyword, Cell* defaultValue) const
+{
+    //showGraph(string("valueOrDefault: request = ") + str() + ", keyword = " + keyword);
+    std::string k (StringTools::lower(keyword));
+    const Cell* p(0);
+    for (const Cell* r (this); r; r = r->rest())
+        if (StringTools::lower(r->text()) == k)
+            p = r;
+
+    if (p == 0)
+        return defaultValue;
+
+    return Cell::clone(p->value());
+}
+
+void Cell::lookupVariables(const std::string& pattern, std::vector<std::string>& ret)
+{
+    Regex re (StringTools::lower(pattern));
+
+    for (const Cell* r (this); r; r = r->rest())
+        if (re.match(StringTools::lower(r->text())))
+            ret.push_back(r->text());
+}
+
+std::string Cell::valueAsString(const string& keyword, const std::string& defaultValue) const
+{
+    //showGraph(string("valueAsString: request = ") + str() + ", keyword = " + keyword);
+
+    Cell* p (valueOrDefault(keyword, 0));
+
+    //showGraph(string("getValueAsString: found ") + p->str());
+
+    if (! p)
+        return defaultValue;
+
+    ASSERT(p->tag() == "_list");
+    string r (p->value()->text());
+
+    if (p->rest())
+        throw UserError(string("Expected only one value of ") + keyword + ", found: " + p->value()->str());
+
+    return r;
+}
+
+std::ostream& Cell::print(std::ostream& s) const
+{
+    return CellPrinter::print(s, this, 0);
+}
+
+ostream& Cell::dot(ostream& s, const string& label, bool detailed, bool clever) const
+{
+    return CellDotPrinter::dot(s, this, label, detailed, clever);
+}
+
+std::string Cell::str() const
+{
+    stringstream ss;
+    ss << this;
+    return ss.str();
+}
+
+void Cell::graph(const string& title) { return showGraph(title, true, true, /*clever*/ false); }
+void Cell::graph() { return showGraph(true, true, true); }
+void Cell::simpleGraph(const string& title) { return showGraph(title, true, true, false); }
+void Cell::simpleGraph() { return showGraph(true, true, false); }
+
+void Cell::showGraph(bool background, bool detailed, bool clever)
+{
+    showGraph(this->str(), background, detailed, clever);
+}
+
+void Cell::showGraph(const string& label, bool background, bool detailed, bool clever)
+{
+    stringstream ss;
+    dot(ss, label, detailed, clever);
+    string s(ss.str());
+    FileHandle f("graph.gv");
+    f.openForWrite(s.size());
+    f.write(s.c_str(), s.size());
+    
+    stringstream cmd;
+    cmd << "(dot -Tpng -o graph.png graph.gv && xv graph.png) ";
+    if (background)
+        cmd << "&";
+    system(cmd.str().c_str());
+}
+
+std::vector<std::string> Cell::valueAsListOfStrings() const
+{
+    std::vector<std::string> r;
+
+    for (const Cell* p(this->value()); p; p = p->rest())
+    {
+        // TODO: check it's just a string
+        if (p->value())
+            r.push_back(p->value()->text());
+    }
+
+    return r;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/parser/Cell.h b/odb_api/src/ecml/parser/Cell.h
new file mode 100644
index 0000000..2b9eb9a
--- /dev/null
+++ b/odb_api/src/ecml/parser/Cell.h
@@ -0,0 +1,88 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta - ECMWF March 2015
+
+#ifndef eckit_parser_Cell_H
+#define eckit_parser_Cell_H
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <ostream>
+
+namespace ecml {
+
+class Cell {
+public:
+    Cell(const std::string&, const std::string&, Cell*, Cell*);
+
+    static Cell* clone(const Cell*);
+
+    const std::string& tag() const;
+    const std::string& text() const;
+    Cell* value() const;
+    Cell* rest() const;
+
+    Cell* tag(const std::string&);
+    Cell* text(const std::string&);
+    Cell* value(Cell*);
+    Cell* rest(Cell*);
+
+    // Will throw if it's not just list of strings but also has subexpressions to be evaluated
+    std::vector<std::string> valueAsListOfStrings() const;
+
+    Cell* append(Cell*);
+
+    // Set value pointed by a keyword
+    Cell* value(const std::string& keyword, Cell* value); 
+    Cell* value(const std::string& keyword, const std::string& value); 
+
+    void update(const std::string& keyword, Cell*); 
+
+    // Get value pointed by a keyword
+    Cell* valueOrDefault(const std::string& keyword, Cell* defaultValue) const;
+    std::string valueAsString(const std::string& keyword, const std::string& defaultValue) const;
+
+    // Find names of variables matching regular expression
+    void lookupVariables(const std::string&, std::vector<std::string>& );
+
+    std::ostream& dot(std::ostream&, const std::string&, bool, bool) const;
+
+    std::string str() const;
+
+    void showGraph(bool background, bool detailed, bool clever);
+    void showGraph(const std::string&, bool background, bool detailed, bool clever);
+    void graph(const std::string&);
+    void graph();
+    void simpleGraph();
+    void simpleGraph(const std::string&);
+
+private:
+    Cell(const Cell*); // cloning
+
+    std::string tag_;
+    std::string text_;
+    Cell *value_;
+    Cell *rest_;
+
+    std::ostream& print(std::ostream&) const;
+
+    std::ostream& printDot(std::ostream&, bool, bool) const;
+    std::ostream& printDotList(std::ostream&, bool) const;
+    std::ostream& printDotVerb(std::ostream&, bool) const;
+
+    friend std::ostream& operator<< (std::ostream& s, const Cell *r) { return r->print(s); }
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/CellDotPrinter.cc b/odb_api/src/ecml/parser/CellDotPrinter.cc
new file mode 100644
index 0000000..8158374
--- /dev/null
+++ b/odb_api/src/ecml/parser/CellDotPrinter.cc
@@ -0,0 +1,145 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+#include "Cell.h"
+#include "CellPrinter.h"
+#include "CellDotPrinter.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+typedef StringTools S;
+
+ostream& CellDotPrinter::printDot(ostream& s, const Cell* p, bool detailed, bool clever)
+{
+    if (clever)
+    {
+        if (p->tag() == "_list" || p->tag() == "_requests") return printDotList(s, p, detailed);
+        if (p->tag() == "_verb") return printDotVerb(s, p, detailed);
+    }
+
+    s << "\"node" << (void*) p << "\" [ label=\"<f0>";
+    if (detailed) s << (void*) p;
+    s << " " << CellPrinter::quotedSnippet(p->text()) << " | <f1>value_ | <f2>rest_\" shape = \"record\" ];" << endl;
+    if (p->value())
+    {
+        s << "\"node" << (void*) p << "\":f1 -> \"node" << (void*) p->value() << "\":f0;" << endl;
+        printDot(s, p->value(), detailed, clever);
+    }
+    if (p->rest())
+    {
+        s << "\"node" << (void*) p << "\":f2 -> \"node" << (void*) p->rest() << "\":f0;" << endl;
+        printDot(s, p->rest(), detailed, clever);
+    }
+    return s;
+}
+
+bool oneElementList(const Cell* p)
+{
+    return p->tag() == "_list"
+         && p->value() 
+         && ! p->rest()
+         && ! (p->value()->tag().size())
+           ;
+}
+
+bool textElement(const Cell* p)
+{
+    return p
+        && p->text().size()
+        //&& ! p->tag().size()
+        && ! p->value()
+        && ! p->rest()
+        ;
+}
+
+ostream& CellDotPrinter::printDotVerb(ostream& s, const Cell* c, bool detailed)
+{
+    ASSERT(c->tag() == "_verb");
+
+    stringstream box, arrows;
+
+    box << "\"node" << "" << (void*) c << "\" [ label=\"<f0>";
+    if (detailed)
+        box << "\\\"" << (void*) c << "\\\"";
+    box << " " << CellPrinter::quotedSnippet(c->text()) << ",";
+
+    size_t i(1);
+    for (const Cell* p(c->rest()); p; ++i, p = p->rest())
+    {
+        box << " | <f" << i << "> ";
+        if (detailed)
+            box << "\\\"" << (void*) p << "\\\"";
+
+        box << " " << p->text() << " = "; 
+        if (oneElementList(p->value()))
+            box << CellPrinter::quotedSnippet(p->value()->value()->text());
+        else
+        {
+            arrows << "\"node" << (void*) c << "\":f" << i << " -> \"node" << (void*) p->value() << "\":f0;" << endl;
+            printDot(s, p->value(), detailed, true);
+        }
+
+    }
+    box << "\" shape=\"record\" ]; ";
+    return s << box.str() << endl << arrows.str();
+}
+
+ostream& CellDotPrinter::printDotList(ostream& s, const Cell* c, bool detailed) 
+{
+    ASSERT(c->tag() == "_list" || c->tag() == "_requests");
+
+    stringstream box, arrows;
+
+    box << "\"node" << (void*) c << "\" [ label=\"<f0> [" << c->tag() << "] ";
+
+    size_t i(0);
+    for (const Cell* p(c); p; ++i, p = p->rest())
+    {
+        ASSERT(p->tag() == "_list" || p->tag() == "_requests");
+
+        if (i) 
+            box << " | <f" << i << "> ";
+ 
+        if (detailed)
+            box << (void*) p;
+
+        if (textElement(p->value()))
+            box << "  " << CellPrinter::quotedSnippet(p->value()->text());
+        else
+        {
+            arrows << "\"node" << (void*) c << "\":f" << i << " -> \"node" << (void*) p->value() << "\":f0;" << endl;
+            if (p->value())
+                printDot(s, p->value(), detailed, true);
+        }
+
+    }
+    box << "\" shape=\"record\" ]; ";
+    return s << box.str() << endl << arrows.str();
+}
+
+ostream& CellDotPrinter::dot(ostream& s, const Cell* p, const string& label, bool detailed, bool clever) 
+{
+    s << "digraph g  {\n"
+        "graph [ rankdir = \"LR\" label=\"" << CellPrinter::quotedSnippet(label) << "\"];\n"
+        "node [ fontsize = \"16\" shape = \"ellipse\" ];\n"
+        "edge [ ];\n";
+    printDot(s, p, detailed, clever);
+    s << "}\n";
+    return s;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/parser/CellDotPrinter.h b/odb_api/src/ecml/parser/CellDotPrinter.h
new file mode 100644
index 0000000..8440f90
--- /dev/null
+++ b/odb_api/src/ecml/parser/CellDotPrinter.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta - ECMWF March 2015
+
+#ifndef eckit_parser_CellDotPrinter_H
+#define eckit_parser_CellDotPrinter_H
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <ostream>
+
+namespace ecml {
+
+class CellDotPrinter {
+public:
+    static std::ostream& dot(std::ostream&, const Cell*, const std::string&, bool, bool);
+    static std::ostream& printDot(std::ostream&, const Cell*, bool, bool);
+    static std::ostream& printDotList(std::ostream&, const Cell*, bool);
+    static std::ostream& printDotVerb(std::ostream&, const Cell*, bool);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/CellPrinter.cc b/odb_api/src/ecml/parser/CellPrinter.cc
new file mode 100644
index 0000000..4c69cbb
--- /dev/null
+++ b/odb_api/src/ecml/parser/CellPrinter.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+#include "Cell.h"
+#include "CellPrinter.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+typedef StringTools S;
+
+string CellPrinter::snippet(const string& s, size_t maxCharacters, size_t maxLines) 
+{
+    stringstream ss;
+
+    size_t i(0), nl(0);
+    for ( ;  i < s.size() && i < maxCharacters && nl < maxLines; ++i)
+    {
+        if (s[i] == '\n')
+            ++nl;
+        ss << s[i];
+    }
+
+    return ss.str() + (i < s.size() ? "..." : "");
+}
+
+string CellPrinter::quote(const string& s) 
+{
+    stringstream ss;
+
+    for (size_t i(0); i < s.size(); ++i)
+        switch (s[i]) {
+            case '"':
+                ss << "\\\"";
+                break;
+            case '{': 
+            case '}': 
+                ss << "\\" << s[i];
+                break;
+            case '\n':
+                ss << "\\n";
+                break;
+            default: 
+                ss << s[i];
+                break;
+        }
+
+    return ss.str();
+}
+
+string CellPrinter::quotedSnippet(const string& s) { return quote(snippet(s, 500, 3)); }
+
+vector<string> CellPrinter::quote(const vector<string>& v)
+{
+    vector<string> r;
+    for (size_t i(0); i < v.size(); ++i)
+        r.push_back("\"" + v[i] + "\"");
+    return r;
+}
+
+std::ostream& CellPrinter::printAttributes(std::ostream& s, const Cell* p, size_t depth) 
+{
+    for (Cell* r(p->rest()); r; r = r->rest())
+    {
+        s << ", " << r->text() << " = ";
+        printValues(s, r, depth + 1);
+    }
+    return s;
+}
+
+std::ostream& CellPrinter::printValues(std::ostream& s, const Cell* p, size_t depth)
+{
+    bool many (false);
+    for (Cell* lst(p->value()); lst; lst = lst->rest(), many = true)
+    {
+        ASSERT(lst->tag() == "_list");
+        if (many)
+            s << " / ";
+
+        if (! lst->value())
+            s << "(null)";
+        else
+        {
+            //if (lst->value()->text().size())
+            if (! lst->value()->tag().size())
+            {
+                s << "\"";
+                print(s, lst->value(), depth + 1);
+                s << "\"";
+            }
+            else
+                print(s, lst->value(), depth + 1);
+        }
+    }
+    return s;
+}
+
+std::ostream& CellPrinter::print(std::ostream& s, const Cell* p, size_t depth) 
+{
+    if (p->tag() == "_requests")
+    {
+        s << "(";
+        print(s, p->value(), 0);
+        for (Cell* lst(p->rest()); lst; lst = lst->rest())
+        {
+            ASSERT(lst->tag() == "_requests");
+            s << "\n";
+            ASSERT(lst->value());
+            print(s, lst->value(), 0);
+        }
+        s << ")";
+        return s;
+    } 
+
+    if (p->tag() == "_list")
+    {
+        if (p->value()) 
+            print(s, p->value(), depth + 1);
+        else
+            s << "(null)";
+        for (Cell* lst(p->rest()); lst; lst = lst->rest())
+        {
+            //ASSERT(lst->tag() == "_list");
+            if (lst->value())
+            {
+                s << " / ";
+                print(s, lst->value(), depth + 1);
+            }
+        }
+        return s;
+    } 
+
+    if (p->tag() == "_verb")
+    {
+        s << (depth ? "(" : "") << p->text();
+        printAttributes(s, p, depth + 1);
+        s << (depth ? ")" : "");
+        return s;
+    }
+
+    return s << p->text();
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/parser/CellPrinter.h b/odb_api/src/ecml/parser/CellPrinter.h
new file mode 100644
index 0000000..51c8d4f
--- /dev/null
+++ b/odb_api/src/ecml/parser/CellPrinter.h
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta - ECMWF March 2015
+
+#ifndef eckit_parser_CellPrinter_H
+#define eckit_parser_CellPrinter_H
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <ostream>
+
+namespace ecml {
+
+class Cell;
+
+class CellPrinter {
+public:
+    static std::ostream& print(std::ostream&, const Cell*, size_t depth=0);
+    static std::ostream& printAttributes(std::ostream&, const Cell*, size_t depth=0);
+    static std::ostream& printValues(std::ostream&, const Cell*, size_t depth=0);
+
+    static std::string quotedSnippet(const std::string&);
+    static std::string quote(const std::string&);
+    static std::vector<std::string> quote(const std::vector<std::string>&);
+    static std::string snippet(const std::string& s, size_t maxCharacters, size_t maxLines);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/List.cc b/odb_api/src/ecml/parser/List.cc
new file mode 100644
index 0000000..a18a59e
--- /dev/null
+++ b/odb_api/src/ecml/parser/List.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "List.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+typedef StringTools S;
+
+List::List(Cell*& c) : p_(0), cell_(c) {}
+
+List::List(): p_(new Cell("_list", "", 0, 0)), cell_(p_) {}
+
+size_t List::size() const
+{
+    size_t n(0);
+    for (Cell* r(cell_); r; r = r->rest())
+        ++n;
+    return n;
+}
+
+List& List::append(Cell* c)
+{
+    if (! cell_)
+        cell_ = new Cell("_list", "", c, 0); 
+    else
+    {
+        if (! cell_->value())
+            cell_->value(c);
+        else
+            cell_->append(new Cell("_list", "", c, 0));
+
+    }
+    return *this;
+}
+
+List& List::append(const string& s)
+{
+    return append(new Cell("", s, 0, 0));
+}
+
+std::ostream& List::print(std::ostream& s) const
+{
+    return s << cell_;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/parser/List.h b/odb_api/src/ecml/parser/List.h
new file mode 100644
index 0000000..18b39fd
--- /dev/null
+++ b/odb_api/src/ecml/parser/List.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta - ECMWF March 2015
+
+#ifndef eckit_parser_List_H
+#define eckit_parser_List_H
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <ostream>
+
+#include "ecml/parser/Cell.h"
+
+namespace ecml {
+
+class List {
+public:
+    List(Cell*&);
+    List();
+
+    size_t size() const;
+    List& append(Cell*);
+    List& append(const std::string&);
+
+    operator Cell*() const { return cell_; }
+
+    std::ostream& print(std::ostream&) const;
+
+private:
+    Cell* p_;
+    Cell*& cell_;
+
+    friend std::ostream& operator<<(std::ostream& s, const List& l) { return l.print(s); }
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/Makefile b/odb_api/src/ecml/parser/Makefile
new file mode 100644
index 0000000..3b2036e
--- /dev/null
+++ b/odb_api/src/ecml/parser/Makefile
@@ -0,0 +1,2 @@
+all:
+	m
diff --git a/odb_api/src/ecml/parser/Request.cc b/odb_api/src/ecml/parser/Request.cc
new file mode 100644
index 0000000..86d07e1
--- /dev/null
+++ b/odb_api/src/ecml/parser/Request.cc
@@ -0,0 +1,22 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "Request.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/parser/StringTools.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/parser/Request.h b/odb_api/src/ecml/parser/Request.h
new file mode 100644
index 0000000..75fb98c
--- /dev/null
+++ b/odb_api/src/ecml/parser/Request.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta - ECMWF March 2015
+
+#ifndef eckit_parser_Request_H
+#define eckit_parser_Request_H
+
+#include <string>
+#include <list>
+#include <vector>
+#include <map>
+#include <ostream>
+#include <iostream> 
+#include <sstream>
+
+#include "ecml/parser/Cell.h"
+#include "ecml/parser/List.h"
+
+namespace ecml {
+
+typedef Cell* Values;
+typedef Cell* Request;
+
+template <typename MARSREQUEST>
+static void convertToMarsRequest(const Request request, MARSREQUEST& marsRequest)
+{
+    marsRequest.name(request->text());
+//    marsRequest.verb(request->text());
+    std::cerr << "convertToMarsRequest: verb is " << request->text() << std::endl;
+    for (ecml::Request r(request->rest()); r; r = r->rest())
+    {
+        std::string key (r->text());
+        std::vector<std::string> vs;
+        for (ecml::Request v (r->value()); v; v = v->rest())
+        {
+            std::stringstream ss;
+            if (v->value()) 
+                ss << v->value(); 
+            vs.push_back(ss.str());
+        }
+          marsRequest.setValues(key, vs);
+//        marsRequest.values(key, vs);
+    }
+}
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/RequestParser.cc b/odb_api/src/ecml/parser/RequestParser.cc
new file mode 100644
index 0000000..e2be288
--- /dev/null
+++ b/odb_api/src/ecml/parser/RequestParser.cc
@@ -0,0 +1,153 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/parser/RequestParser.h"
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/log/Log.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/TmpFile.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/FileHandle.h"
+
+#include <fstream>
+#include <stdexcept> 
+#include <sstream>
+
+namespace ecml {
+
+struct RequestParserMutex : public eckit::AutoLock<eckit::Mutex> {
+    RequestParserMutex();
+private:
+    static eckit::Mutex mutex_;
+};
+
+static void reset_parser(FILE*,bool);
+static void do_parse_request_in_string(const char *);
+
+class RequestParseError : public eckit::Exception
+{
+public:
+    RequestParseError(const char*);
+};
+
+struct RequestParserResult { static Cell* result_; };
+
+Request RequestParser::parse(const std::string& s, bool debug)
+{
+    RequestParserMutex mutex;
+
+    std::stringstream ss;
+    // two zeroes are required by yy_scan_buffer (request__scan_buffer)
+    // I add this extra '\0' here in case we move to yy_scan_buffer at a point.
+    ss << s << "\0"; 
+
+    do_parse_request_in_string(ss.str().c_str()); 
+
+    Request result (RequestParserResult::result_);
+    RequestParserResult::result_ = 0;
+
+    return result;
+}
+
+std::string RequestParser::readFile(const eckit::PathName& fileName, bool logging)
+{
+	const size_t CHUNK_SIZE = 1024;
+	char buffer[CHUNK_SIZE]; 
+
+	eckit::FileHandle f(fileName);
+	eckit::Length estimated = f.openForRead();
+	
+	std::string ret;
+	size_t read, totalRead = 0;
+
+	while ( (read = f.read(buffer, sizeof(buffer) / sizeof(char))) > 0 )
+	{
+		totalRead += read;
+		ret.append(std::string(static_cast<char*>(buffer), read));
+	}
+
+	if (logging)
+		eckit::Log::info()  << "Read " << totalRead << " bytes from file " << fileName << "[" << ret << "]" << std::endl;
+
+	f.close();
+	return ret;
+}
+
+Request RequestParser::parseFile(const char* path, bool debug)
+{
+    RequestParserMutex mutex;
+    std::string source (readFile(path, debug));
+    return parse(source, debug);
+}
+
+typedef RequestParser ClientRequestParser;
+
+Request RequestParserResult::result_ = 0;
+
+eckit::Mutex RequestParserMutex::mutex_;
+RequestParserMutex::RequestParserMutex() : eckit::AutoLock<eckit::Mutex>(mutex_) {}
+
+RequestParseError::RequestParseError(const char* s)
+: Exception(s)
+{}
+
+namespace RequestYacc {
+
+    extern "C" 
+    {
+        int request_wrap(void);
+        void request_error(const char* msg);
+    }
+
+#include "ecml/requesty.c"
+
+    extern "C" 
+    {
+        int request_wrap(void) { return 1; }
+        void request_error(const char* msg)
+        { 
+            std::stringstream ss;
+            ss << std::string(msg) << " line " << request_lineno;
+            throw RequestParseError(ss.str().c_str());
+        }
+    }
+}
+
+void reset_parser(FILE* in, bool debug)
+{
+    RequestYacc::request_lineno = 0;
+    RequestYacc::request_in     = in;
+    RequestYacc::request_debug  = debug;
+    RequestYacc::request_restart(in);
+}
+
+void do_parse_request_in_string(const char *s)
+{
+    RequestYacc::YY_BUFFER_STATE buffer;
+    try { 
+        buffer = RequestYacc::request__scan_string(s);
+        RequestYacc::request_parse();
+        RequestYacc::request__delete_buffer(buffer);
+    }
+    catch (RequestParseError& e)
+    {
+        RequestYacc::request__delete_buffer(buffer);
+        throw eckit::UserError(e.what());
+    }
+    catch (...)
+    {
+        RequestYacc::request__delete_buffer(buffer);
+        throw;
+    }
+}
+
+} // namespace ecml 
diff --git a/odb_api/src/ecml/parser/RequestParser.h b/odb_api/src/ecml/parser/RequestParser.h
new file mode 100644
index 0000000..5e40ce6
--- /dev/null
+++ b/odb_api/src/ecml/parser/RequestParser.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File RequestParser.h
+// Manuel Fuentes - ECMWF Jan 97
+// Piotr Kuchta - ECMWF April 2015
+
+#ifndef eckit_parser_RequestParser_H
+#define eckit_parser_RequestParser_H
+
+#include "ecml/parser/Request.h"
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace ecml {
+
+class RequestParser : private eckit::NonCopyable
+{
+public:
+    static Request parse(const std::string&, bool debug=false);
+    static Request parseFile(const char*, bool debug=false);
+
+    static std::string readFile(const eckit::PathName& fileName, bool logging);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/parser/requestl.l b/odb_api/src/ecml/parser/requestl.l
new file mode 100755
index 0000000..550a6bd
--- /dev/null
+++ b/odb_api/src/ecml/parser/requestl.l
@@ -0,0 +1,55 @@
+%{
+
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#undef YYLMAX
+#define YYLMAX 2048
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+%}
+
+IDENT   [_0-9A-Za-z\$]+([_\.\-\+A-Za-z0-9:\t \$]*[_\.\-\+A-Za-z0-9\$]+)?
+NUMB    [\-\.]*[0-9]+[\.0-9]*[Ee]*[\-\+]*[0-9]*
+
+%%
+
+{IDENT}      { yylval.str = (const char *)yytext; return WORD; }
+{NUMB}       { yylval.str = (const char *)yytext; return WORD; }
+
+\"|\'  {
+           int c,q = yytext[0];
+           yyleng = 0;
+
+           while((c = yyinput()) && c != q)
+           {
+               if(c == '\\') yytext[yyleng++] = yyinput();
+               else yytext[yyleng++] =  c;
+            }
+
+            yytext[yyleng++] = 0;
+            yylval.str = (const char *)yytext;
+            return STRING;
+        }
+
+\#      {
+            int c;
+            while((c = yyinput()) && (c != '\n'))
+                    ;
+        }
+
+[ \t]*  ;
+\n      ; // TODO: FIXME: lineno++;
+
+.              return *yytext;
+
+%%
diff --git a/odb_api/src/ecml/parser/requesty.y b/odb_api/src/ecml/parser/requesty.y
new file mode 100755
index 0000000..c6b234b
--- /dev/null
+++ b/odb_api/src/ecml/parser/requesty.y
@@ -0,0 +1,75 @@
+%{
+
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#ifdef YYBISON
+#define YYSTYPE_IS_DECLARED
+int yylex();
+/*int yydebug;*/
+#endif
+
+struct YYSTYPE {
+    Cell* v;
+    std::string str;
+};
+
+%}
+
+%type <v>requests;
+
+%type <v>request
+%type <v>value
+%type <v>values
+%type <v>parameter
+%type <v>parameters
+
+%token <str>WORD
+%token <str>STRING
+%token <str>NUMB
+
+%start all
+
+%%
+
+all:	 requests                     { ecml::RequestParserResult::result_ = $1; }
+	;
+
+requests : request                    { $$ = new ecml::Cell("_requests", "", $1, 0); }
+		 | requests request           { $$ = ($1)->append(new ecml::Cell("_requests", "", $2, 0)); }
+
+request : WORD                        { $$ = new ecml::Cell("_verb", $1, 0, 0); }
+		| WORD ',' parameters         { $$ = new ecml::Cell("_verb", $1, 0, $3); }
+		| WORD '.'                    { $$ = new ecml::Cell("_verb", $1, 0, 0); }
+		| WORD ',' parameters '.'     { $$ = new ecml::Cell("_verb", $1, 0, $3); }
+		;
+
+parameters : parameter                { $$ = $1; }
+		   | parameters ',' parameter { $$ = ($1)->append($3); }
+		   ;
+
+parameter : WORD '=' values           { $$ = new ecml::Cell("", $1, $3, 0); }
+		  ;
+
+values : value                        { $$ = new ecml::Cell("_list", "", $1, 0); }
+	   | values '/' value             { $$ = ($1)->append(new ecml::Cell("_list", "", $3, 0)); }
+	   ;
+
+value: WORD                           { $$ = new ecml::Cell("", $1, 0, 0); } 
+	 | STRING                         { $$ = new ecml::Cell("", $1, 0, 0); }
+	 | NUMB                           { $$ = new ecml::Cell("", $1, 0, 0); }
+     | '(' requests ')'               { $$ = $2; }
+	 ;
+
+%%
+#include "requestl.c"
+
diff --git a/odb_api/src/ecml/parser/update.cc b/odb_api/src/ecml/parser/update.cc
new file mode 100644
index 0000000..1a25ef5
--- /dev/null
+++ b/odb_api/src/ecml/parser/update.cc
@@ -0,0 +1,24 @@
+
+void Cell::update(const std::string& keyword, Cell* v) 
+{
+    //showGraph(string("value: request = ") + str() + ", keyword = " + keyword + ", v = " + v->str());
+
+    const std::string k (StringTools::lower(keyword));
+
+    Cell* lastMatch (this); 
+
+    for (Cell* r (this); r; r = r->rest())
+    {
+        if (StringTools::lower(r->text()) == k)
+            lastMatch = r;
+
+        if (! r->rest())
+        {
+            r->rest(new Cell("", keyword, v, 0));
+            return; 
+        }
+    }
+
+    delete lastMatch->value();
+    lastMatch->value(v);
+}
diff --git a/odb_api/src/ecml/prelude/ApplyHandler.cc b/odb_api/src/ecml/prelude/ApplyHandler.cc
new file mode 100644
index 0000000..bf7506e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ApplyHandler.cc
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/ast/Closure.h"
+
+#include "ApplyHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+ApplyHandler::ApplyHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request ApplyHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "apply");
+
+    Request evaluatedAttributes (context.interpreter().evalAttributes(request, context));
+
+    Cell* p (evaluatedAttributes->rest());
+
+    if (! p)
+        throw eckit::UserError("apply needs at least one parameter; 'function' or 'closure'");
+
+    if (p->text() != "function" && p->text() != "closure")
+        throw eckit::UserError("First parameter of apply should be 'function' or 'closure'");
+
+    Cell* closure (p->value()); // it should be a list with one closure
+
+    if (! p->rest() || p->rest()->text() != "args" || !p->rest()->value())
+        throw eckit::UserError("apply: parameter 'args' is required");
+
+    Request paramsFrame (p->rest()->value()->value());
+    ASSERT(closure->rest() == 0);
+
+    Cell* r (0);
+
+    context.pushEnvironmentFrame(paramsFrame);
+
+    if (closure->value()->tag() == "_verb" && closure->value()->text() == "closure")
+        r = context.interpreter().evalClosure(closure->value(), paramsFrame, context);
+    else if (closure->value()->tag() == "_native")
+        r = context.interpreter().evalNative(closure->value(), paramsFrame, context);
+    else
+        r = context.interpreter().eval(closure->value(), context);
+
+    context.popEnvironmentFrame(paramsFrame);
+
+    return r;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/ApplyHandler.h b/odb_api/src/ecml/prelude/ApplyHandler.h
new file mode 100644
index 0000000..70cf773
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ApplyHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_ApplyHandler_H
+#define eckit_ecml_ApplyHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class ApplyHandler : public SpecialFormHandler {
+public:
+    ApplyHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/Autocompleter.cc b/odb_api/src/ecml/prelude/Autocompleter.cc
new file mode 100644
index 0000000..d0dbb00
--- /dev/null
+++ b/odb_api/src/ecml/prelude/Autocompleter.cc
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+#include "eckit/cmd/UserInput.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+#include "ecml/prelude/Autocompleter.h"
+
+using namespace std;
+
+namespace ecml {
+
+ExecutionContext* Autocompleter::context_ (0);
+
+bool Autocompleter::notInWord(char p)
+{
+    return p == ' '
+        || p == '\t'
+        || p == ','
+        || p == '('
+        || p == ')'
+        || p == '/'
+        || p == '='
+        ;
+}
+
+std::string Autocompleter::suffix(const char* line, int pos)
+{
+    char *p (const_cast<char*>(line) + pos);
+    while (p != line && !notInWord(*(p - 1)))
+        --p;
+    return p;
+}
+
+void Autocompleter::describe(const string& v, Cell* o)
+{
+    stringstream ss;
+    ss << v << ": " << (o ? o->str() : "NULL");
+
+    const string description (ss.str());
+
+    ::write(1, "\r\n", 2);
+    ::write(1, description.c_str(), description.size());
+    ::write(1, "\r\n", 2);
+}
+
+bool Autocompleter::completion(const char* line, int pos, char* insert, int insertmax)
+{
+    ExecutionContext& context (*Autocompleter::context_); // it would be nice to have it passed as a parameter here
+
+    const string prefix (suffix(line, pos));
+
+    const vector<string> matchedVars (context.environment().lookupVariables("^" + prefix));
+    const set<string> matched (matchedVars.begin(), matchedVars.end());
+
+    if (matched.empty())
+        return true;
+
+    if (matched.size() == 1)
+    {
+        const string& match (*matched.begin());
+        describe(match, context.environment().lookupNoThrow(match)); 
+
+        for (size_t i(prefix.size()), ii(0); ii < insertmax && i < match.size(); ++i)
+            insert[ii++] = match[i];
+
+        return true;
+    }
+
+    size_t i(0);
+    for (set<string>::const_iterator it (matched.begin()); it != matched.end(); ++it)
+    {
+        const string& ins (*it);
+        for (size_t j(0); i < insertmax && j < ins.size(); ++j)
+            insert[i++] = ins[j];
+
+        if (i < insertmax)
+            insert[i++] = ' ';
+    }
+
+    return false;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/Autocompleter.h b/odb_api/src/ecml/prelude/Autocompleter.h
new file mode 100644
index 0000000..0bf308e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/Autocompleter.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2016
+
+#ifndef eckit_ecml_Autocompleter_H
+#define eckit_ecml_Autocompleter_H
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class Autocompleter {
+
+private:
+    static bool completion(const char*, int, char*, int);
+    static void describe(const std::string&, Cell*);
+    static bool notInWord(char);
+    static std::string suffix(const char* line, int pos);
+
+    static ExecutionContext* context_;
+
+    friend class REPLHandler;
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/ClosureHandler.cc b/odb_api/src/ecml/prelude/ClosureHandler.cc
new file mode 100644
index 0000000..6812204
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ClosureHandler.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/parser/Request.h"
+#include "ecml/prelude/ClosureHandler.h"
+
+using namespace std;
+
+namespace ecml {
+
+ClosureHandler::ClosureHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request ClosureHandler::handle(const Request request, ExecutionContext&)
+{
+    return Cell::clone(request);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/ClosureHandler.h b/odb_api/src/ecml/prelude/ClosureHandler.h
new file mode 100644
index 0000000..a0beec5
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ClosureHandler.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_ClosureHandler_H
+#define eckit_ecml_ClosureHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class ClosureHandler : public SpecialFormHandler {
+public:
+    ClosureHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/DefineFunctionHandler.cc b/odb_api/src/ecml/prelude/DefineFunctionHandler.cc
new file mode 100644
index 0000000..6d3e97c
--- /dev/null
+++ b/odb_api/src/ecml/prelude/DefineFunctionHandler.cc
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+#include "ecml/prelude/DefineFunctionHandler.h"
+
+using namespace std;
+
+namespace ecml {
+
+DefineFunctionHandler::DefineFunctionHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request DefineFunctionHandler::handle(const Request request, ExecutionContext& context)
+{
+    FunctionDefinition f (request);
+    Closure closure (f, context);
+    Cell* closureWrapped (new Cell("_list", "", closure, 0));
+    context.pushEnvironmentFrame(new Cell("_verb", "let", 0, 
+                                            new Cell("", f.name(), closureWrapped, 0)));
+    return Cell::clone(closureWrapped);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/DefineFunctionHandler.h b/odb_api/src/ecml/prelude/DefineFunctionHandler.h
new file mode 100644
index 0000000..7dce336
--- /dev/null
+++ b/odb_api/src/ecml/prelude/DefineFunctionHandler.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_DefineFunctionHandler_H
+#define eckit_ecml_DefineFunctionHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class DefineFunctionHandler : public SpecialFormHandler {
+public:
+    DefineFunctionHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/FirstHandler.cc b/odb_api/src/ecml/prelude/FirstHandler.cc
new file mode 100644
index 0000000..25b7885
--- /dev/null
+++ b/odb_api/src/ecml/prelude/FirstHandler.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "FirstHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+namespace ecml {
+
+FirstHandler::FirstHandler(const std::string& name) : RequestHandler(name) {}
+
+Values FirstHandler::handle(ExecutionContext& context)
+{
+    Values vs (context.environment().lookup("of"));
+    return new Cell("_list", "", Cell::clone(vs->value()), 0);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/FirstHandler.h b/odb_api/src/ecml/prelude/FirstHandler.h
new file mode 100644
index 0000000..b083e1f
--- /dev/null
+++ b/odb_api/src/ecml/prelude/FirstHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, July 2015
+
+#ifndef eckit_ecml_FirstHandler_H
+#define eckit_ecml_FirstHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class FirstHandler : public ecml::RequestHandler {
+public:
+    FirstHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/ForHandler.cc b/odb_api/src/ecml/prelude/ForHandler.cc
new file mode 100644
index 0000000..8e4e452
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ForHandler.cc
@@ -0,0 +1,137 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/prelude/ForHandler.h"
+
+#ifdef _OPENMP
+# include <omp.h>
+#endif
+
+using namespace std;
+
+namespace ecml {
+
+ForHandler::ForHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+// for,x = 1/2/3,
+//     do = (println,_=($,_=x))
+// =>
+//   let,x=1
+//   println,_=($,_=x) 
+//
+//   let,x=2
+//   println,_=($,_=x) 
+//
+//   ...
+
+// for, x = 1/2/3,
+//      do = (for, y = a/b/c,
+//                 do = (println,_ = ($,_=x) / " " / ($,_=y)))
+
+Request ForHandler::handle(const Request r, ExecutionContext& context)
+{
+    Cell* loopVariable (r->rest());
+    if (! loopVariable)
+        throw eckit::UserError("for: first parameter must be name and values of loop variable");
+
+    string var (loopVariable->text());
+    Cell* values (context.interpreter().eval(loopVariable->value(), context));
+
+    Cell* loopBody (loopVariable->rest());
+    if (! loopBody || eckit::StringTools::lower(loopBody->text()) != "do" )
+        throw eckit::UserError("for: second parameter must be 'do' (body of the loop)");
+
+    Cell* loopBodyCode (loopBody->value()->value());
+    ASSERT(loopBodyCode->tag() == "_requests");
+
+    vector<Cell*> vvalues, vresult;
+
+    for (Cell* v (values); v; v = v->rest())
+    {
+        Cell* value (Cell::clone(v->value()));
+        vvalues.push_back(value);
+        vresult.push_back(0);
+    }
+
+    #pragma omp parallel
+    {
+#ifdef _OPENMP
+        eckit::Log::debug() << "omp_get_thread_num: " << omp_get_thread_num() << endl;
+#endif
+        #pragma omp for
+        for (size_t i=0; i < vvalues.size(); ++i)
+        {
+            ExecutionContext ctx (context);
+
+            Request frame (new Cell("_verb", "let", 0, 0));
+            frame->append(new Cell("", var, Cell::clone(vvalues[i]), 0));
+            ctx.pushEnvironmentFrame(frame);
+
+            try { vresult[i] = ctx.interpreter().evalRequests(loopBodyCode, ctx);
+            } catch (eckit::Exception e) {
+                vresult[i] = exceptionValue(e.what());
+            } catch (std::exception e) {
+                vresult[i] = exceptionValue(e.what());
+            } catch (...) {
+                vresult[i] = exceptionValue("exception");
+            }
+        }
+    }
+
+    List result;
+    for (size_t i(0); i < vresult.size(); ++i) {
+        Cell* elt (vresult[i]);
+        if (! elt->value()) {
+            result.append(elt);
+            continue;
+        }
+
+        if (elt->tag() != "_list")
+            result.append(elt);
+        else
+        {
+            for (Values l (elt); l; l = l->rest()) {
+                Values sublist (l->value());
+                if (sublist == 0) continue;
+
+                if (sublist->tag() != "_list")
+                    result.append(sublist);
+                else
+                    for (Request e(sublist); e; e = e->rest()) {
+                        ASSERT(e->tag() == "_list");
+                        result.append(e->value());
+                    }
+            }
+        }
+    }
+
+    return result;
+}
+
+/// This is returning a value encoding exception.
+/// For now it is only a string. 
+/// TODO: return something that can be detected as being an exception rather then just ordinary value.
+ecml::Cell* ForHandler::exceptionValue(const std::string& s)
+{
+    return new ecml::Cell("_list", "", new ecml::Cell("", s, 0, 0), 0);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/ForHandler.h b/odb_api/src/ecml/prelude/ForHandler.h
new file mode 100644
index 0000000..e79cf16
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ForHandler.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, December 2015
+
+#ifndef eckit_ecml_ForHandler_H
+#define eckit_ecml_ForHandler_H
+
+#include "eckit/eckit_config.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class ForHandler : public SpecialFormHandler {
+public:
+    ForHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+
+private:
+    static ecml::Cell* exceptionValue(const std::string&);
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/GetenvHandler.cc b/odb_api/src/ecml/prelude/GetenvHandler.cc
new file mode 100644
index 0000000..0b2972e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/GetenvHandler.cc
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "GetenvHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+GetenvHandler::GetenvHandler(const std::string& name) : RequestHandler(name) {}
+
+Values GetenvHandler::handle(ExecutionContext& context)
+{
+    List r;
+
+    vector<string> vars (context.getValueAsList("values"));
+    for (size_t i (0); i < vars.size(); ++i)
+    {
+        char *s (getenv(vars[i].c_str()));
+        r.append(! s ? string("") : string(getenv(vars[i].c_str())));
+    }
+
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/GetenvHandler.h b/odb_api/src/ecml/prelude/GetenvHandler.h
new file mode 100644
index 0000000..811eda7
--- /dev/null
+++ b/odb_api/src/ecml/prelude/GetenvHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_GetenvHandler_H
+#define eckit_ecml_GetenvHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class GetenvHandler : public ecml::RequestHandler {
+public:
+    GetenvHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/GlobHandler.cc b/odb_api/src/ecml/prelude/GlobHandler.cc
new file mode 100644
index 0000000..30c0c3a
--- /dev/null
+++ b/odb_api/src/ecml/prelude/GlobHandler.cc
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "eckit/parser/StringTools.h"
+
+#include "GlobHandler.h"
+
+#include <string.h>
+#include <glob.h>
+
+using namespace std;
+using namespace ecml;
+
+int glob_error_handler(const char* fn, int errnum)
+{
+    char buf[1024];
+    eckit::Log::warning() << "glob: error reading '" << fn << "': " << strerror_r(errnum, buf, sizeof(buf)) << endl;
+    return 0;
+}
+
+namespace ecml {
+
+GlobHandler::GlobHandler(const std::string& name) : RequestHandler(name) {}
+
+// glob, _ = <pattern> 
+Values GlobHandler::handle(ExecutionContext& context)
+{
+    vector<string> patterns (context.environment().lookupList("_", context));
+
+    glob_t globbuf;
+    int flags (0);
+    for (size_t i(0); i < patterns.size(); ++i)
+    {
+        const char* pattern (patterns[i].c_str());
+
+        int rc = glob(pattern, flags, glob_error_handler, &globbuf);
+        rc = rc;
+        //ASSERT(rc == 0);
+        //GLOB_NOSPACE for running out of memory,
+        //GLOB_ABORTED for a read error, and
+        //GLOB_NOMATCH for no found matches.
+
+        flags = flags | GLOB_APPEND;
+    }
+
+    List r;
+    for (size_t i(0); i < globbuf.gl_pathc; ++i)
+        r.append(eckit::StringTools::trim(string(globbuf.gl_pathv[i])));
+
+    globfree(&globbuf);
+
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/GlobHandler.h b/odb_api/src/ecml/prelude/GlobHandler.h
new file mode 100644
index 0000000..3695225
--- /dev/null
+++ b/odb_api/src/ecml/prelude/GlobHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, December 2015
+
+#ifndef eckit_ecml_GlobHandler_H
+#define eckit_ecml_GlobHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class GlobHandler : public ecml::RequestHandler {
+public:
+    GlobHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/IfHandler.cc b/odb_api/src/ecml/prelude/IfHandler.cc
new file mode 100644
index 0000000..f266472
--- /dev/null
+++ b/odb_api/src/ecml/prelude/IfHandler.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "IfHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+IfHandler::IfHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+/// Accepted parameters: condition, then, else
+Request IfHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "if");
+
+    Request condition (request->valueOrDefault("condition", 0));
+    if (! condition)
+        throw eckit::UserError("No 'condition' passed to 'if'");
+
+    Request then (request->valueOrDefault("then", 0));
+    Request _else (request->valueOrDefault("else", 0));
+    if (!then && !_else)
+        throw eckit::UserError("Either 'then' or 'else' must be passed to 'if'");
+
+    Values test (context.interpreter().eval(condition, context));
+    Request r(0);
+
+    if (! test
+        || (test->tag() == "_list" && !test->value())) // empty list
+    {
+        if (_else) r = _else;
+    }
+    else
+    {
+        if (then) r = then;
+    }
+
+    if (r)
+    {
+        r = context.interpreter().eval(r, context);
+        return Cell::clone(r);
+    }
+
+    return new Cell("_list", "", 0, 0);
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/IfHandler.h b/odb_api/src/ecml/prelude/IfHandler.h
new file mode 100644
index 0000000..862212b
--- /dev/null
+++ b/odb_api/src/ecml/prelude/IfHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, July 2015
+
+#ifndef eckit_ecml_IfHandler_H
+#define eckit_ecml_IfHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class IfHandler : public SpecialFormHandler {
+public:
+    IfHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/JoinStringsHandler.cc b/odb_api/src/ecml/prelude/JoinStringsHandler.cc
new file mode 100644
index 0000000..2818f0d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/JoinStringsHandler.cc
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "JoinStringsHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/prelude/PrintHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+JoinStringsHandler::JoinStringsHandler(const std::string& name) : RequestHandler(name) {}
+
+// join_strings, _ = <list_of_strings> [, separator = <separator>]
+Values JoinStringsHandler::handle(ExecutionContext& context)
+{
+    string separator;
+    if (context.environment().lookupNoThrow("separator"))
+    {
+        vector<string> separators (context.getValueAsList("separator"));
+        if (separators.size() > 1)
+            throw eckit::UserError("join_strings: separator must be a single string");
+
+        if (separators.size() == 1)
+            separator = separators[0];
+    }
+
+    List r;
+    stringstream ss;
+
+    Values vs (context.environment().lookup("_"));
+    PrintHandler::printList(ss, vs, separator, "");
+    delete vs;
+
+    r.append(ss.str());
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/JoinStringsHandler.h b/odb_api/src/ecml/prelude/JoinStringsHandler.h
new file mode 100644
index 0000000..730e05d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/JoinStringsHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_JoinStringsHandler_H
+#define eckit_ecml_JoinStringsHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class JoinStringsHandler : public ecml::RequestHandler {
+public:
+    JoinStringsHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/LetHandler.cc b/odb_api/src/ecml/prelude/LetHandler.cc
new file mode 100644
index 0000000..4c636b1
--- /dev/null
+++ b/odb_api/src/ecml/prelude/LetHandler.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "LetHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+LetHandler::LetHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request LetHandler::handle(const Request request, ExecutionContext& context)
+{
+    // TODO: try .. catch ... handle exceptions
+    Request marker (new Cell("_verb", "_let_marker", 0, 0));
+    context.pushEnvironmentFrame(marker);
+    Request evaluatedAttributes (context.interpreter().evalAttributes(request, context));
+    context.popEnvironmentFrame(marker);
+
+    Request frame (new Cell("_verb", "let", 0, 0));
+    for (Request e(evaluatedAttributes->rest()); e; e = e->rest())
+    {
+        ASSERT(e->tag() == "");
+        frame->append(new Cell("", e->text(), e->value(), 0));
+    }
+    context.pushEnvironmentFrame(frame);
+    return Cell::clone(frame);
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/LetHandler.h b/odb_api/src/ecml/prelude/LetHandler.h
new file mode 100644
index 0000000..bce0727
--- /dev/null
+++ b/odb_api/src/ecml/prelude/LetHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_LetHandler_H
+#define eckit_ecml_LetHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class LetHandler : public SpecialFormHandler {
+public:
+    LetHandler(const std::string&);
+
+    virtual ecml::Request handle(const Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/ListHandler.cc b/odb_api/src/ecml/prelude/ListHandler.cc
new file mode 100644
index 0000000..fa4b1b0
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ListHandler.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ListHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+namespace ecml {
+
+ListHandler::ListHandler(const std::string& name) : RequestHandler(name) {}
+
+Values ListHandler::handle(ExecutionContext& context)
+{
+    NOTIMP;
+    Values r (Cell::clone(context.environment().lookup("values")));
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/ListHandler.h b/odb_api/src/ecml/prelude/ListHandler.h
new file mode 100644
index 0000000..33cb604
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ListHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_ListHandler_H
+#define eckit_ecml_ListHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class ListHandler : public ecml::RequestHandler {
+public:
+    ListHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/Makefile b/odb_api/src/ecml/prelude/Makefile
new file mode 100644
index 0000000..887ca76
--- /dev/null
+++ b/odb_api/src/ecml/prelude/Makefile
@@ -0,0 +1,2 @@
+all:
+	mm
diff --git a/odb_api/src/ecml/prelude/MatchHandler.cc b/odb_api/src/ecml/prelude/MatchHandler.cc
new file mode 100644
index 0000000..28bfe2d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/MatchHandler.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/utils/Regex.h"
+
+#include "MatchHandler.h"
+
+#include <string.h>
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+MatchHandler::MatchHandler(const std::string& name) : RequestHandler(name) {}
+
+// match, regex = <pattern>, string = <strings>
+Values MatchHandler::handle(ExecutionContext& context)
+{
+    vector<string> patterns (context.environment().lookupList("regex", context));
+    vector<string> strings (context.environment().lookupList("strings", context));
+
+    List r;
+    for (size_t i(0); i < patterns.size(); ++i)
+    {
+        const string& pattern (patterns[i]);
+
+        for (size_t j(0); j < strings.size(); ++j)
+        {
+            // Should this trimming be optional
+            const string& s (eckit::StringTools::trim(strings[j]));
+
+            if (eckit::Regex(pattern).match(s))
+                r.append(s);
+        }
+    }
+
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/MatchHandler.h b/odb_api/src/ecml/prelude/MatchHandler.h
new file mode 100644
index 0000000..15c28e1
--- /dev/null
+++ b/odb_api/src/ecml/prelude/MatchHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, June 2016
+
+#ifndef eckit_ecml_MatchHandler_H
+#define eckit_ecml_MatchHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class MatchHandler : public ecml::RequestHandler {
+public:
+    MatchHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/NullHandler.cc b/odb_api/src/ecml/prelude/NullHandler.cc
new file mode 100644
index 0000000..66cd6a5
--- /dev/null
+++ b/odb_api/src/ecml/prelude/NullHandler.cc
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "NullHandler.h"
+
+#include "ecml/parser/Request.h"
+
+namespace ecml {
+
+NullHandler::NullHandler(const std::string& name) : RequestHandler(name) {}
+
+Values NullHandler::handle(ExecutionContext&)
+{
+    return new Cell("_list", "", 0, 0);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/NullHandler.h b/odb_api/src/ecml/prelude/NullHandler.h
new file mode 100644
index 0000000..aac615d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/NullHandler.h
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_NullHandler_H
+#define eckit_ecml_NullHandler_H
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class NullHandler : public ecml::RequestHandler {
+public:
+    NullHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/Prelude.cc b/odb_api/src/ecml/prelude/Prelude.cc
new file mode 100644
index 0000000..616070d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/Prelude.cc
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Prelude.cc
+// Piotr Kuchta - (c) ECMWF July 2015
+
+#include <string>
+
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/ecml_config.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "ListHandler.h"
+#include "SequenceHandler.h"
+#include "VariableLookupHandler.h"
+#include "PrintHandler.h"
+#include "LetHandler.h"
+#include "UpdateHandler.h"
+#include "DefineFunctionHandler.h"
+#include "ClosureHandler.h"
+#include "TestHandler.h"
+#include "FirstHandler.h"
+#include "RestHandler.h"
+#include "IfHandler.h"
+#include "TemporaryFileHandler.h"
+#include "SystemHandler.h"
+#include "GetenvHandler.h"
+#include "ApplyHandler.h"
+#include "Prelude.h"
+#include "JoinStringsHandler.h"
+#include "QuoteHandler.h"
+#include "NullHandler.h"
+#include "RunHandler.h"
+#include "REPLHandler.h"
+#include "RangeHandler.h"
+#include "ForHandler.h"
+#include "GlobHandler.h"
+#include "ReadTextFileHandler.h"
+#include "TryHandler.h"
+#include "ThrowHandler.h"
+#include "MatchHandler.h"
+
+namespace ecml {
+
+using namespace std;
+
+Prelude::Prelude() {}
+Prelude::~Prelude() {}
+
+// unused // static Request native(const string& name) { return new Cell("_native", name, 0, 0); }
+
+static Request macro(const string& name) { return new Cell("_macro", name, 0, 0); }
+
+std::string Prelude::preludePath()
+{
+    const std::string installPrefix (ECML_INSTALL_PREFIX),
+                      buildDir (ECML_BUILD_DIR);
+
+    std::string p (installPrefix + "/include/prelude.ecml" );
+
+    if (! eckit::PathName(p).exists())
+        p = buildDir + "/include/prelude.ecml";
+
+    eckit::Log::debug() << "preludePath: " << p << endl;
+
+    if (! eckit::PathName(p).exists()) 
+        eckit::Log::warning() << "ecml: cannot find prelude.ecml in either " 
+                       << installPrefix << " or " << buildDir << std::endl;
+    return p;
+}
+
+void Prelude::executePrelude(ExecutionContext& context)
+{
+    Values vs (context.executeScriptFile(preludePath()));
+    delete vs;
+}
+
+void Prelude::importInto(ExecutionContext& context)
+{
+    static DefineFunctionHandler function("function");
+    static ClosureHandler closure("closure");
+    static ListHandler list("list");
+    static SequenceHandler sequence("sequence");
+    static PrintHandler println("println", "\n");
+    static PrintHandler print("print", "");
+    static LetHandler let("let");
+    static UpdateHandler update("update");
+    static ApplyHandler apply("apply");
+    static VariableLookupHandler value("value", "of");
+    static VariableLookupHandler dollar("$", "_");
+    static TestHandler test("test");
+    static FirstHandler first("first");
+    static RestHandler rest("rest");
+    static TemporaryFileHandler temporary_file("temporary_file");
+    static IfHandler _if("if");
+    static SystemHandler _system("system");
+    static GetenvHandler _getenv("getenv");
+    static JoinStringsHandler join_strings("join_strings");
+    static QuoteHandler quote("quote");
+    static NullHandler null("null");
+    static RunHandler run("run");
+    static REPLHandler repl("repl");
+    static RangeHandler range("range");
+    static ForHandler _for("for");
+    static GlobHandler _glob("glob");
+    static ReadTextFileHandler read_text_file("read_text_file");
+    static TryHandler _try("try");
+    static ThrowHandler _throw("throw");
+    static MatchHandler match("match");
+
+    Environment& e(context.environment());
+    e.set("let", macro(let.name()));
+    e.set("update", macro(update.name()));
+    e.set("function", macro(function.name()));
+    e.set("apply", macro(apply.name()));
+    e.set("closure", macro(closure.name()));
+    e.set("test", macro(test.name()));
+    e.set("if", macro(_if.name()));
+    e.set("quote", macro(quote.name()));
+    e.set("run", macro(run.name()));
+    e.set("repl", macro(repl.name()));
+    e.set("for", macro(_for.name()));
+    e.set("try", macro(_try.name()));
+
+    context.registerHandler("list", list);
+    context.registerHandler("sequence", sequence);
+    context.registerHandler("value", value);
+    context.registerHandler("$", dollar);
+    context.registerHandler("print", print);
+    context.registerHandler("println", println);
+    context.registerHandler("first", first);
+    context.registerHandler("rest", rest);
+    context.registerHandler("temporary_file", temporary_file);
+    context.registerHandler("system", _system);
+    context.registerHandler("getenv", _getenv);
+    context.registerHandler("join_strings", join_strings);
+    context.registerHandler("null", null);
+    context.registerHandler("range", range);
+    context.registerHandler("glob", _glob);
+    context.registerHandler("read_text_file", read_text_file);
+    context.registerHandler("throw", _throw);
+    context.registerHandler("match", match);
+
+    executePrelude(context);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/Prelude.h b/odb_api/src/ecml/prelude/Prelude.h
new file mode 100644
index 0000000..98b1b93
--- /dev/null
+++ b/odb_api/src/ecml/prelude/Prelude.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Prelude.h
+// Piotr Kuchta - (c) ECMWF July 2015
+
+#ifndef eckit_ecml_Prelude_H
+#define eckit_ecml_Prelude_H
+
+#include "ecml/core/Module.h"
+#include "ecml/core/ExecutionContext.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace ecml {
+
+class Prelude : public Module {
+public:
+    Prelude();
+    ~Prelude();
+    void importInto(ExecutionContext&);
+
+    static std::string preludePath();
+private:
+    void executePrelude(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/PrintHandler.cc b/odb_api/src/ecml/prelude/PrintHandler.cc
new file mode 100644
index 0000000..fa8445e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/PrintHandler.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "PrintHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+PrintHandler::PrintHandler(const string& name, const string& end, std::ostream& o)
+: RequestHandler(name),
+  end_(end),
+  out_(&o)
+{}
+
+PrintHandler::PrintHandler(const string& name, const string& end)
+: RequestHandler(name),
+  end_(end),
+  out_(&cout)
+{}
+
+Values PrintHandler::handle(ExecutionContext& context)
+{
+    string separator (" ");
+    if (context.environment().lookupNoThrow("separator"))
+    {
+        vector<string> separators (context.getValueAsList("separator"));
+        if (separators.size() > 1)
+            throw eckit::UserError("join_strings: separator must be a single string");
+
+        if (separators.size() == 1)
+            separator = separators[0];
+    }
+
+    // TODO: this is a leak I think
+    Values r (Cell::clone(context.environment().lookupNoThrow("values")));
+    if (!r )
+        r = new Cell("_list", "", 0, 0);
+    else
+        printList(out(), r, separator, end_);
+
+    out() << end_;
+    return r;
+}
+
+void PrintHandler::printList(ostream& out, Values values, const std::string& separator, const std::string& end)
+{
+    for (Cell* e(values); e; e = e->rest())
+    {
+        if (e->value())
+            out << e->value();
+        else
+            out << "(null)";
+
+        if (e->rest())
+            out << separator;
+        else
+            out << end;
+    }
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/PrintHandler.h b/odb_api/src/ecml/prelude/PrintHandler.h
new file mode 100644
index 0000000..ad0fd5d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/PrintHandler.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_PrintHandler_H
+#define eckit_ecml_PrintHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class PrintHandler : public ecml::RequestHandler {
+public:
+    PrintHandler(const std::string&, const std::string&, std::ostream&);
+    PrintHandler(const std::string&, const std::string&);
+    virtual Values handle(ExecutionContext&);
+
+    static void printList(std::ostream&, Values, const std::string& separator, const std::string& end);
+
+protected:
+    std::ostream& out() { return *out_; }
+    void out(std::ostream& o) { out_ = &o; }
+
+private:
+    const std::string end_;
+    std::ostream* out_;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/QuoteHandler.cc b/odb_api/src/ecml/prelude/QuoteHandler.cc
new file mode 100644
index 0000000..1db4a3c
--- /dev/null
+++ b/odb_api/src/ecml/prelude/QuoteHandler.cc
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+#include "ecml/prelude/QuoteHandler.h"
+
+using namespace std;
+
+namespace ecml {
+
+QuoteHandler::QuoteHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request QuoteHandler::handle(const Request request, ExecutionContext& context)
+{
+    Cell* c(request->valueOrDefault("_", 0));
+    if (! c) eckit::UserError("quote: _ not set");
+    return Cell::clone(c);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/QuoteHandler.h b/odb_api/src/ecml/prelude/QuoteHandler.h
new file mode 100644
index 0000000..15a913d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/QuoteHandler.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_QuoteHandler_H
+#define eckit_ecml_QuoteHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class QuoteHandler : public SpecialFormHandler {
+public:
+    QuoteHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/REPLHandler.cc b/odb_api/src/ecml/prelude/REPLHandler.cc
new file mode 100644
index 0000000..2f29237
--- /dev/null
+++ b/odb_api/src/ecml/prelude/REPLHandler.cc
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+#include "eckit/cmd/UserInput.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+#include "ecml/prelude/REPLHandler.h"
+#include "ecml/prelude/Autocompleter.h"
+
+using namespace std;
+
+namespace ecml {
+
+REPLHandler::REPLHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request REPLHandler::handle(const Request, ExecutionContext& context)
+{
+    repl(context);
+
+    return new Cell("_list", "", 0, 0);
+}
+
+
+string REPLHandler::historyFile()
+{
+    return string (getenv("HOME")) + "/.ecml_history";
+}
+
+void REPLHandler::readHistory()
+{
+    eckit::UserInput::loadHistory(historyFile().c_str());
+}
+
+
+void REPLHandler::writeHistory()
+{
+    eckit::UserInput::saveHistory(historyFile().c_str());
+}
+
+string REPLHandler::readLine(ExecutionContext& context)
+{
+    // It would be nicer to pass it as a third param to getUserInput
+    Autocompleter::context_ = &context; 
+    const char* line (eckit::UserInput::getUserInput("ecml> ", eckit::UserInput::completion_proc (&Autocompleter::completion)));
+    if (line == 0)
+        return "quit";
+    return line;
+}
+
+void REPLHandler::repl(ExecutionContext& context)
+{
+    readHistory();
+    string cmd; 
+    while (true)
+    {
+        cmd += readLine(context);
+
+        if (cin.eof()
+            || cmd == "quit"
+            || cmd == "bye")
+        {
+            writeHistory();
+            cout << "Bye." << endl;
+            break;
+        }
+
+        if (! cmd.size()) continue;
+
+        if (cmd.rfind("\\") == cmd.size() - 1)
+        {
+            cmd.erase(cmd.size() - 1);
+            cmd += '\n';
+            continue;
+        }
+
+        try {
+            Values r (context.execute(cmd));
+            cout << " => " << r << endl;
+            if (showResultGraph(context))
+                r->graph();
+            delete r;
+        } catch (eckit::Exception e) {
+            // error message is already printed by Exception ctr
+            //cout << "*** error: " << e.what() << endl;
+        }
+        cmd = "";
+    }
+}
+
+bool REPLHandler::showResultGraph(ExecutionContext& context)
+{
+    if (! context.environment().lookupNoThrow("show_dot"))
+        return false;
+    else
+    {
+        vector<string> vs (context.environment().lookupList("show_dot", context));
+        return vs.size() == 1 && vs[0] == "true";
+    }
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/REPLHandler.h b/odb_api/src/ecml/prelude/REPLHandler.h
new file mode 100644
index 0000000..7444df6
--- /dev/null
+++ b/odb_api/src/ecml/prelude/REPLHandler.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_REPLHandler_H
+#define eckit_ecml_REPLHandler_H
+
+#include "eckit/eckit_config.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace ecml {
+
+class REPLHandler : public SpecialFormHandler {
+public:
+    REPLHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+
+    static void repl(ExecutionContext&);
+
+private:
+    static std::string readLine(ExecutionContext&);
+    static void readHistory();
+    static std::string historyFile();
+    static void writeHistory();
+
+    static bool showResultGraph(ExecutionContext&);
+};
+
+} // namespace ecml
+
+
+#endif
diff --git a/odb_api/src/ecml/prelude/RangeHandler.cc b/odb_api/src/ecml/prelude/RangeHandler.cc
new file mode 100644
index 0000000..fcbd87a
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RangeHandler.cc
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "RangeHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include <vector>
+
+using namespace std;
+
+namespace ecml {
+
+RangeHandler::RangeHandler(const std::string& name) : RequestHandler(name) {}
+
+Values RangeHandler::handle(ExecutionContext& context)
+{
+    const string from (context.environment().lookup("from", "", context));
+    const string to (context.environment().lookup("to", "", context));
+    const string below (context.environment().lookup("below", "", context));
+
+    if (! from.size() || (to.size() == 0 && below.size() == 0) 
+                      || (to.size() != 0 && below.size() != 0)) 
+        throw eckit::UserError("range: one value of 'from' and one value of 'to' or 'below' must be given");
+
+    long lf (atol(from.c_str()));
+    long lt (to.size() 
+             ? atol(to.c_str())
+             : atol(below.c_str()) - 1);
+
+    List r;
+
+    for (long i (lf); i <= lt; ++i)
+    {
+        stringstream ss;
+        ss << i;
+        r.append(ss.str());
+    }
+
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/RangeHandler.h b/odb_api/src/ecml/prelude/RangeHandler.h
new file mode 100644
index 0000000..ad51045
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RangeHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, November 2015
+
+#ifndef eckit_ecml_RangeHandler_H
+#define eckit_ecml_RangeHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class RangeHandler : public ecml::RequestHandler {
+public:
+    RangeHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/ReadTextFileHandler.cc b/odb_api/src/ecml/prelude/ReadTextFileHandler.cc
new file mode 100644
index 0000000..89feb34
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ReadTextFileHandler.cc
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "eckit/io/FileHandle.h"
+
+#include "ReadTextFileHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+ReadTextFileHandler::ReadTextFileHandler(const std::string& name) : RequestHandler(name) {}
+
+// read_text_file, _ = <path> 
+Values ReadTextFileHandler::handle(ExecutionContext& context)
+{
+    vector<string> paths (context.environment().lookupList("_", context));
+
+    List r;
+
+    for (size_t i(0); i < paths.size(); ++i)
+        r.append(readFile(paths[i]));
+
+    return r;
+}
+
+std::string ReadTextFileHandler::readFile(const eckit::PathName& fileName)
+{
+	const size_t CHUNK_SIZE = 1024 * 4;
+	char buffer[CHUNK_SIZE]; 
+
+	eckit::FileHandle f(fileName);
+
+    // unused // Length estimated =
+    f.openForRead();
+	
+	std::string ret;
+	size_t read, totalRead = 0;
+
+	while ( (read = f.read(buffer, sizeof(buffer) / sizeof(char))) > 0 )
+	{
+		totalRead += read;
+		ret.append(std::string(static_cast<char*>(buffer), read));
+	}
+
+	f.close();
+	return ret;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/ReadTextFileHandler.h b/odb_api/src/ecml/prelude/ReadTextFileHandler.h
new file mode 100644
index 0000000..bbd195f
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ReadTextFileHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, December 2015
+
+#ifndef eckit_ecml_ReadTextFileHandler_H
+#define eckit_ecml_ReadTextFileHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class ReadTextFileHandler : public ecml::RequestHandler {
+public:
+    ReadTextFileHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+private:
+    std::string readFile(const eckit::PathName&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/RestHandler.cc b/odb_api/src/ecml/prelude/RestHandler.cc
new file mode 100644
index 0000000..aa4047a
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RestHandler.cc
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "RestHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+namespace ecml {
+
+RestHandler::RestHandler(const std::string& name) : RequestHandler(name) {}
+
+Values RestHandler::handle(ExecutionContext& context)
+{
+    Values vs (context.environment().lookup("of"));
+
+    if (! vs->rest())
+        return new Cell("_list", "", 0, 0);
+
+    return Cell::clone(vs->rest());
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/RestHandler.h b/odb_api/src/ecml/prelude/RestHandler.h
new file mode 100644
index 0000000..c9b142a
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RestHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_RestHandler_H
+#define eckit_ecml_RestHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class RestHandler : public ecml::RequestHandler {
+public:
+    RestHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/RunHandler.cc b/odb_api/src/ecml/prelude/RunHandler.cc
new file mode 100644
index 0000000..06e90ec
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RunHandler.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/types/Types.h"
+//#include "eckit/filesystem/FileSpace.h"
+#include "eckit/io/FileHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+
+#include "RunHandler.h"
+
+using namespace std;
+
+namespace ecml {
+
+
+RunHandler::RunHandler(const std::string& name) : SpecialFormHandler(name) {}
+
+Values RunHandler::handle(const Request request, ExecutionContext& context)
+{
+    Cell* r(0); 
+
+    Request evaluatedAttributes (context.interpreter().evalAttributes(request, context));
+
+    std::vector<std::string> source;
+    Values vs (evaluatedAttributes->valueOrDefault("source", 0));
+    if (vs)
+        for (Cell* p (vs); p; p = p->rest())
+        {
+            Cell* v (p->value());
+            source.push_back(v->str());
+        }
+
+    eckit::Log::info() << "run: " << source << endl;
+
+    Request frame (new Cell("_verb", "let", 0, 0));
+    context.pushEnvironmentFrame(frame);
+
+    for (size_t i(0); i < source.size(); ++i)
+    {
+        eckit::PathName fileName (source[i]);
+
+        eckit::Log::info() << "* Run " << fileName << endl;
+
+        delete r;
+        r = context.executeScriptFile(fileName);
+    }
+
+    List rv;
+    rv.append(r);
+
+    return rv;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/RunHandler.h b/odb_api/src/ecml/prelude/RunHandler.h
new file mode 100644
index 0000000..cb8cc7d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/RunHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_RunHandler_H
+#define eckit_ecml_RunHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class RunHandler : public SpecialFormHandler {
+public:
+    RunHandler(const std::string&);
+    virtual Values handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/SequenceHandler.cc b/odb_api/src/ecml/prelude/SequenceHandler.cc
new file mode 100644
index 0000000..ebb3f40
--- /dev/null
+++ b/odb_api/src/ecml/prelude/SequenceHandler.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "SequenceHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+namespace ecml {
+
+SequenceHandler::SequenceHandler(const std::string& name) : RequestHandler(name) {}
+
+Values SequenceHandler::handle(ExecutionContext& context)
+{
+    Values r (Cell::clone(context.environment().lookup("values")));
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/SequenceHandler.h b/odb_api/src/ecml/prelude/SequenceHandler.h
new file mode 100644
index 0000000..ab66f97
--- /dev/null
+++ b/odb_api/src/ecml/prelude/SequenceHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_SequenceHandler_H
+#define eckit_ecml_SequenceHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class SequenceHandler : public RequestHandler {
+public:
+    SequenceHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/SystemHandler.cc b/odb_api/src/ecml/prelude/SystemHandler.cc
new file mode 100644
index 0000000..852ee6e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/SystemHandler.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "SystemHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/prelude/PrintHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+SystemHandler::SystemHandler(const std::string& name) : RequestHandler(name) {}
+
+Values SystemHandler::handle(ExecutionContext& context)
+{
+    List r;
+
+    Values vs (context.environment().lookup("values"));
+    stringstream ss;
+    PrintHandler::printList(ss, vs, " ", "");
+
+    eckit::Log::debug() << "Executing system(\"" << ss.str() << ")\"" << endl;
+
+    int rc (system (ss.str().c_str()));
+    stringstream rs;
+    rs << rc;
+    r.append(rs.str());
+
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/SystemHandler.h b/odb_api/src/ecml/prelude/SystemHandler.h
new file mode 100644
index 0000000..dacfbe1
--- /dev/null
+++ b/odb_api/src/ecml/prelude/SystemHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_SystemHandler_H
+#define eckit_ecml_SystemHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class SystemHandler : public ecml::RequestHandler {
+public:
+    SystemHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/TemporaryFileHandler.cc b/odb_api/src/ecml/prelude/TemporaryFileHandler.cc
new file mode 100644
index 0000000..b5863fc
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TemporaryFileHandler.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+#include "TemporaryFileHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "eckit/filesystem/FileSpace.h"
+
+namespace ecml {
+
+TemporaryFileHandler::TemporaryFileHandler(const std::string& name) : RequestHandler(name) {}
+
+Values TemporaryFileHandler::handle(ExecutionContext& context)
+{
+    //PathName path(PathName::unique(FileSpace::lookUp("temp").selectFileSystem()+ "/tmp"));
+    const char *tmpdir = ::getenv("TMPDIR");
+	if(!tmpdir)
+	{
+        tmpdir = "/tmp";
+    }
+
+    long max = pathconf(tmpdir, _PC_PATH_MAX);
+    char path[max];
+    sprintf(path, "%s/eckitXXXXXXXXXXX", tmpdir);
+    int fd;
+    SYSCALL(fd = ::mkstemp(path));
+
+    //PathName result(path);
+    //result.touch();
+
+    SYSCALL(::close(fd));
+
+    List r;
+    r.append(std::string(path));
+    return r;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/TemporaryFileHandler.h b/odb_api/src/ecml/prelude/TemporaryFileHandler.h
new file mode 100644
index 0000000..12690d8
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TemporaryFileHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, August 2015
+
+#ifndef eckit_ecml_TemporaryHandler_H
+#define eckit_ecml_TemporaryHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class TemporaryFileHandler : public ecml::RequestHandler {
+public:
+    TemporaryFileHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/TestHandler.cc b/odb_api/src/ecml/prelude/TestHandler.cc
new file mode 100644
index 0000000..fb43108
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TestHandler.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "TestHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+TestHandler::TestHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+/// Accepted parameters: label, do, expect
+Request TestHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "test");
+
+    string label (request->valueAsString("label", "NO_LABEL"));
+    Request code (request->valueOrDefault("do", 0));
+    if (! code) throw eckit::UserError("No test code (keyword 'do') given for test '" + label + "'");
+    Request expect (request->valueOrDefault("expect", 0));
+    if (! expect) throw eckit::UserError("No expected value (keyword 'expect') given for test '" + label + "'");
+
+    Values values (context.interpreter().eval(code, context));
+    ASSERT(values);
+
+    stringstream sv, se;
+    sv << values;
+    se << expect;
+
+    if (sv.str() != se.str())
+        throw eckit::UserError("ECML TEST '" + label + "' EXPECTED:[" + se.str() + "] GOT:[" + sv.str() + "]");
+
+    return new Cell("_list", "", 0, 0);
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/TestHandler.h b/odb_api/src/ecml/prelude/TestHandler.h
new file mode 100644
index 0000000..4c7ad7d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TestHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, July 2015
+
+#ifndef eckit_ecml_TestHandler_H
+#define eckit_ecml_TestHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class TestHandler : public SpecialFormHandler {
+public:
+    TestHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/ThrowHandler.cc b/odb_api/src/ecml/prelude/ThrowHandler.cc
new file mode 100644
index 0000000..d496cab
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ThrowHandler.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ThrowHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include <vector>
+#include "eckit/parser/StringTools.h"
+
+using namespace std;
+
+namespace ecml {
+
+ThrowHandler::ThrowHandler(const std::string& name) : RequestHandler(name) {}
+
+Values ThrowHandler::handle(ExecutionContext& context)
+{
+    string message;
+
+    if (context.environment().lookupNoThrow("what"))
+        message = eckit::StringTools::join("/", context.getValueAsList("what"));
+    else
+    {
+        const string currentException (context.environment().lookup("current_exception", "", context));
+        if (currentException.size())
+            message = currentException;
+    }
+
+    throw eckit::Exception(message);
+    return 0;
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/ThrowHandler.h b/odb_api/src/ecml/prelude/ThrowHandler.h
new file mode 100644
index 0000000..c59b145
--- /dev/null
+++ b/odb_api/src/ecml/prelude/ThrowHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, December 2015
+
+#ifndef eckit_ecml_ThrowHandler_H
+#define eckit_ecml_ThrowHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class ThrowHandler : public ecml::RequestHandler {
+public:
+    ThrowHandler(const std::string&);
+    virtual Values handle(ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/TryHandler.cc b/odb_api/src/ecml/prelude/TryHandler.cc
new file mode 100644
index 0000000..6bd3936
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TryHandler.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "TryHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+
+TryHandler::TryHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+/// try,
+//       do = (throw,what=problem), 
+//       catch = (println,values=exception caught)
+//       finally = (println,values=bye)
+Request TryHandler::handle(const Request request, ExecutionContext& context)
+{
+    auto_ptr<Cell> body (request->valueOrDefault("do", 0));
+    auto_ptr<Cell> _catch (request->valueOrDefault("catch", 0));
+    auto_ptr<Cell> finally (request->valueOrDefault("finally", 0));
+
+    if (! body.get()) throw eckit::UserError("No 'do' passed to 'try'");
+
+    bool exceptionThrown (false);
+    string what;
+
+    auto_ptr<Cell> value (0);
+    if (! _catch.get() && ! finally.get())
+        value.reset(context.interpreter().eval(body.get(), context));
+    else
+    try 
+    {
+        value.reset(context.interpreter().eval(body.get(), context));
+        if (finally.get()) 
+            context.interpreter().eval(finally.get(), context);
+
+    } catch (eckit::Exception e) 
+    {
+        exceptionThrown = true;
+        what = e.what(); // TODO: save type of the exception as a prefix
+    }
+
+    if (exceptionThrown)
+    {
+        List w;
+        w.append(what);
+        context.pushEnvironmentFrame(new Cell("_verb", "let", 0, new Cell("", "current_exception", w, 0)));
+
+        if (_catch.get()) 
+        {
+            try {
+                value.reset(context.interpreter().eval(_catch.get(), context));
+            } catch (eckit::Exception ec)
+            {
+                if (finally.get()) 
+                    context.interpreter().eval(finally.get(), context);
+                throw ec;
+            } 
+            if (finally.get()) 
+                context.interpreter().eval(finally.get(), context);
+        } else if (finally.get()) 
+        {
+            context.interpreter().eval(finally.get(), context);
+            throw eckit::Exception(what); // TODO: add info about type of exception
+        }
+    }
+    
+    if (! value.get())
+        return new Cell("_list", "", 0, 0);
+    return value.release();
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/TryHandler.h b/odb_api/src/ecml/prelude/TryHandler.h
new file mode 100644
index 0000000..8e6f82e
--- /dev/null
+++ b/odb_api/src/ecml/prelude/TryHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, December 2015
+
+#ifndef eckit_ecml_TryHandler_H
+#define eckit_ecml_TryHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class TryHandler : public SpecialFormHandler {
+public:
+    TryHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/UpdateHandler.cc b/odb_api/src/ecml/prelude/UpdateHandler.cc
new file mode 100644
index 0000000..24f67f8
--- /dev/null
+++ b/odb_api/src/ecml/prelude/UpdateHandler.cc
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "UpdateHandler.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace ecml {
+
+UpdateHandler::UpdateHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+// update, _ = (let, x = 1, y = 2), x = 10  =>  let, x = 10, y = 2
+
+Request UpdateHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "update");
+
+    Request evaluatedAttributes (context.interpreter().evalAttributes(request, context));
+
+    Cell* value (evaluatedAttributes->rest());
+    if (value->text() != "_") 
+        throw eckit::UserError("update: first parameter must be _ (a closure)");
+    value = value->value(); 
+    value = value->value();
+
+    evaluatedAttributes = evaluatedAttributes->rest();
+
+    Request newValue (Cell::clone(value));
+    for (Request e(evaluatedAttributes->rest()); e; e = e->rest())
+    {
+        ASSERT(e->tag() == "");
+
+        newValue->update(e->text(), Cell::clone(e->value()));
+    }
+
+    return newValue;
+}
+
+} // namespace ecml
+
diff --git a/odb_api/src/ecml/prelude/UpdateHandler.h b/odb_api/src/ecml/prelude/UpdateHandler.h
new file mode 100644
index 0000000..d79f620
--- /dev/null
+++ b/odb_api/src/ecml/prelude/UpdateHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_UpdateHandler_H
+#define eckit_ecml_UpdateHandler_H
+
+#include "eckit/filesystem/PathName.h"
+#include "ecml/parser/Request.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+namespace ecml {
+
+class ExecutionContext;
+
+class UpdateHandler : public SpecialFormHandler {
+public:
+    UpdateHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ExecutionContext&);
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/VariableLookupHandler.cc b/odb_api/src/ecml/prelude/VariableLookupHandler.cc
new file mode 100644
index 0000000..c9edd17
--- /dev/null
+++ b/odb_api/src/ecml/prelude/VariableLookupHandler.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "VariableLookupHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+using namespace std;
+
+namespace ecml {
+
+VariableLookupHandler::VariableLookupHandler(const string& name, const string& parameterName) 
+: RequestHandler(name),
+  of_(parameterName)
+{}
+
+// TODO: this needs to be a SpecialFormHandler so this works properly:
+// ecml> let,_='A'
+//  => let, _ = "A"
+//  ecml> $,_=_
+//   => _
+Values VariableLookupHandler::handle(ExecutionContext& context)
+{
+    vector<string> vars (context.getValueAsList(of_));
+    string var (vars[0]);
+    Values r (context.environment().lookup(var));
+
+    for (size_t i (1); i < vars.size(); ++i)
+    {
+        const string& key (vars[i]);
+
+        // r is (possibly) a list, let's assume it has only one element for now
+        // TODO: handle case where there is more elements 
+        Cell * v ( r->tag() == "_list" ? r->value() : r);
+
+        Cell * values (v->valueOrDefault(key, 0));
+
+        if (! values)
+            throw eckit::UserError(string("No '") + key + "' in " + r->str());
+
+        delete r;
+        r = values;
+    }
+
+    return Cell::clone(r);
+}
+
+} // namespace ecml
diff --git a/odb_api/src/ecml/prelude/VariableLookupHandler.h b/odb_api/src/ecml/prelude/VariableLookupHandler.h
new file mode 100644
index 0000000..24efeb0
--- /dev/null
+++ b/odb_api/src/ecml/prelude/VariableLookupHandler.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef eckit_ecml_VariableLookupHandler_H
+#define eckit_ecml_VariableLookupHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace ecml {
+
+class VariableLookupHandler : public ecml::RequestHandler {
+public:
+    VariableLookupHandler(const std::string&, const std::string&);
+    virtual Values handle(ExecutionContext&);
+private:
+    const std::string of_;
+};
+
+} // namespace ecml
+
+#endif
diff --git a/odb_api/src/ecml/prelude/graph.gv b/odb_api/src/ecml/prelude/graph.gv
new file mode 100644
index 0000000..7ac925d
--- /dev/null
+++ b/odb_api/src/ecml/prelude/graph.gv
@@ -0,0 +1,8 @@
+digraph g  {
+graph [ rankdir = "LR" label="let, foo = \"1\" / \"2\" / \"3\" / \"4\""];
+node [ fontsize = "16" shape = "ellipse" ];
+edge [ ];
+"node0x1e55aa0" [ label="<f0> [_list] 0x1e55aa0  1 | <f1> 0x1e55b00  2 | <f2> 0x1e55b60  3 | <f3> 0x1e55bc0  4" shape="record" ]; 
+"node0x1e55a40" [ label="<f0>\"0x1e55a40\" let, | <f1> \"0x1e55a70\" foo = " shape="record" ]; 
+"node0x1e55a40":f1 -> "node0x1e55aa0":f0;
+}
diff --git a/odb_api/src/ecml/prelude/graph.png b/odb_api/src/ecml/prelude/graph.png
new file mode 100644
index 0000000..552f862
Binary files /dev/null and b/odb_api/src/ecml/prelude/graph.png differ
diff --git a/odb_api/src/ecml/prelude/prelude.ecml b/odb_api/src/ecml/prelude/prelude.ecml
new file mode 100644
index 0000000..0daad2f
--- /dev/null
+++ b/odb_api/src/ecml/prelude/prelude.ecml
@@ -0,0 +1,35 @@
+let, ecml_version = '0.1'
+
+let, documentation = (
+  let,
+    $ = (let, description = Get value of a variable, parameters = (let, _ = "name of a variable, it can also be a list")),
+    apply = (let, description = Call a function with given parameters, parameters = (let, function = function to be called, args     = a dictionary with parameters)), 
+    closure = (let, description = "A special verb for encoding closures.  Normally it is created by verb function.  Evaluates to itself so it can be passed around as value", parameters = (let)),
+    first = (let),
+    for = (let),
+    function = (let),
+    getenv = (let),
+    glob = (let),
+    if = (let),
+    join_strings = (let),
+    let = (let),
+    list = (let),
+    null = (let),
+    print = (let),
+    println =  (let),
+    quote = (let),
+    range = (let),
+    read_text_file = (let),
+    repl = (let),
+    rest = (let),
+    run = (let),
+    sequence = (let),
+    system = (let),
+    temporary_file = (let),
+    test = (let),
+    throw = (let),
+    try = (let),
+    update = (let),
+    value = (let)
+)
+
diff --git a/odb_api/src/ecml/tests/CMakeLists.txt b/odb_api/src/ecml/tests/CMakeLists.txt
new file mode 100644
index 0000000..eab4cea
--- /dev/null
+++ b/odb_api/src/ecml/tests/CMakeLists.txt
@@ -0,0 +1,46 @@
+list( APPEND ecml_tests
+test_print.ecml
+test_let_and_value.ecml
+test_function.ecml
+test_one_letter_variables.ecml
+test_first.ecml
+test_rest.ecml
+test_lists.ecml
+test_recursion.ecml
+test_higher_order_functions.ecml
+test_map.ecml
+test_system.ecml
+test_getenv.ecml
+test_apply.ecml
+test_dictionary.ecml
+test_for.ecml
+test_match.ecml
+)
+
+# Get location of test runner executable (for the ECML tests)
+get_target_property ( ecml_test_runner_bin ecml_test LOCATION )
+
+# Get location of unit test runner executable (C++ tests)
+get_target_property ( ecml_unittests_bin ecml_unittests LOCATION )
+
+set( test_environment
+  ECML_HOME=${PROJECT_SOURCE_DIR}
+  #ECML_TEST_DATA_PATH=${CMAKE_CURRENT_BINARY_DIR}
+  PATH=${CMAKE_BINARY_DIR}/bin:$ENV{PATH})
+
+### Add ECML tests
+foreach( _ecml_test ${ecml_tests} )
+    ecbuild_add_test( TARGET       ${_ecml_test}
+                      COMMAND      ${ecml_test_runner_bin}
+                      ARGS         ${PROJECT_SOURCE_DIR}/src/ecml/tests/${_ecml_test}
+                      ENVIRONMENT  ${test_environment}
+                      LABELS       odb_api odb_api_ecml
+                      TEST_DEPENDS ${_dependencies} )
+endforeach()
+
+# Add C++ unit tests
+ecbuild_add_test( TARGET       ecml_cpptests
+                  COMMAND      ${ecml_unittests_bin}
+                  ENVIRONMENT  ${test_environment}
+                  LABELS       odb_api odb_api_ecml
+                  TEST_DEPENDS ${_dependencies} )
diff --git a/odb_api/src/ecml/tests/ecml_test.cc b/odb_api/src/ecml/tests/ecml_test.cc
new file mode 100644
index 0000000..27f8762
--- /dev/null
+++ b/odb_api/src/ecml/tests/ecml_test.cc
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/prelude/REPLHandler.h"
+
+#include "odb_api/odbcapi.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+class TestECML : public Tool {
+public:
+    TestECML (int argc, char **argv) : Tool(argc, argv) {}
+    ~TestECML() {}
+
+    virtual void run();
+
+protected:
+    virtual void runScript(const std::string& pathName);
+};
+
+void TestECML::run()
+{
+    if (argc() < 2)
+    {
+        ExecutionContext context;
+        REPLHandler::repl(context);
+    }
+
+    for (int  i = 1; i < argc(); ++i) {
+        runScript(argv(i));
+    }
+}
+
+void TestECML::runScript(const string& pathName)
+{
+    Log::info() << endl << " ** ecml_test: running " << pathName << endl;
+    ExecutionContext context;
+    context.executeScriptFile(pathName);
+}
+
+} // namespace ecml
+
+int main(int argc,char **argv)
+{
+    ecml::TestECML runner(argc, argv);
+    return runner.start();
+}
diff --git a/odb_api/src/ecml/tests/ecml_unittests.cc b/odb_api/src/ecml/tests/ecml_unittests.cc
new file mode 100644
index 0000000..75de4d7
--- /dev/null
+++ b/odb_api/src/ecml/tests/ecml_unittests.cc
@@ -0,0 +1,199 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/log/Log.h"
+#include "eckit/runtime/Tool.h"
+#include "eckit/io/PartFileHandle.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/Module.h"
+#include "ecml/prelude/PrintHandler.h"
+#include "ecml/prelude/DefineFunctionHandler.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/ast/FunctionDefinition.h"
+#include "ecml/ast/Closure.h"
+#include "ecml/data/DataHandleFactory.h"
+#include "ecml/data/PartFileHandleFactory.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace ecml {
+
+class ECMLUnitTests : public Tool {
+public:
+    ECMLUnitTests (int argc, char **argv) : Tool(argc, argv) {}
+    ~ECMLUnitTests() {}
+
+    virtual void run();
+protected:
+    virtual void runTests();
+};
+
+class TestPrintHandler : public PrintHandler
+{
+public:
+    TestPrintHandler(ostream& o, const string& name, const string& end)
+    : PrintHandler(name, end)
+    { out(o); }
+};
+
+struct ECMLTestModule : public Module {
+    ECMLTestModule()
+    : Module(),
+      s_(),
+      println_(s_, "tprintln", "\n"),
+      print_(s_, "tprint", "")
+    {}
+
+    void importInto(ExecutionContext& context)
+    {
+        context.registerHandler("print", print_);
+        context.registerHandler("println", println_);
+    }
+
+    string str() { return s_.str(); }
+
+    void clear()
+    {
+        s_.str("");
+        s_.clear();
+    }
+
+private:
+    stringstream s_;
+    TestPrintHandler println_;
+    TestPrintHandler print_;
+};
+
+void ECMLUnitTests::run() { runTests(); }
+
+void ECMLUnitTests::runTests()
+{
+    ostream& L (Log::info());
+
+    L << endl << " ** ecml_unittests **" << endl;
+
+    ECMLTestModule tm;
+    ExecutionContext context;
+    tm.importInto(context);
+
+    //Cell* c (context.copy());
+    //stringstream ss;
+    //ss << c << endl;
+    //cout << ss.str();
+
+    context.execute("print, values = Hello world");
+    ASSERT(tm.str() == "Hello world");
+    tm.clear();
+    context.execute("print, values = Hello again");
+    ASSERT(tm.str() == "Hello again"); // not sure where the trailing space come from
+
+
+    // Closure
+    Cell* requests (RequestParser::parse("function,of=x,capture=a/b,f=(println,values=FOO)"));
+    Cell* parsedFunction (requests->value());
+    //L << "parsedFunction: " << parsedFunction << endl;
+
+    FunctionDefinition funDef(parsedFunction);
+    //L << "funDef.parameters().size(): " << funDef.parameters().size() << endl;
+
+    ASSERT(funDef.parameters().size() == 1);
+    ASSERT(funDef.parameters()[0] == "x");
+    ASSERT(funDef.capturedVariables().size() == 2);
+    ASSERT(funDef.capturedVariables()[0] == "a");
+    ASSERT(funDef.capturedVariables()[1] == "b");
+    ASSERT(funDef.code()->str() == "(println, values = \"FOO\")");
+
+    Cell* rs (RequestParser::parse("closure,name=f,parameters=x,captured=(environment,a=1,b=2),code=(println,values=FOO)"));
+    //L << "rs: " << rs << endl;
+    Cell* cc (rs->value());
+    //L << "cc: " << cc << endl;
+    Closure clos (cc);
+    //L << "clos:" << (Cell*) clos << endl;
+
+    DefineFunctionHandler df("function");
+    context.execute("let, a=1,b=2");
+    Cell* cclosure (df.handle(parsedFunction, context));
+    cclosure = cclosure->value();
+    //L << "cclosure: " << cclosure << endl;
+    Closure closure (cclosure);
+    //L << "closure: " << (Cell*) closure << endl;
+    //((Cell*) closure)->graph();
+    //ASSERT(cclosure->str() == ((Cell*) closure)->str());
+    context.execute("function,of=name,capture=a/b,greetings=(print,values=Hello /(value,of=name)/(value,of=a)/(value,of=b))");
+    context.execute("greetings, name=Piotr");
+    context.execute(
+    "function, foo = ("
+    "    function, bar = ( println, values = BAR )"
+    "    value, of = bar"
+    ")"
+    );
+
+    // Cell* c;
+
+    //c = context.interpreter().eval(RequestParser::parse("foo"), context);
+    //c = context.interpreter().eval(RequestParser::parse("value, of = bar"), context);
+
+    Cell* parsed ( RequestParser::parse("let, f = ( foo ) ") );
+
+    //parsed->graph("parsed 'let, f = ( foo )': " + parsed->str());
+
+    context.interpreter().debug(true);
+
+    // c =
+    context.interpreter().eval(parsed, context);
+    //c->graph("evaluated: 'let, f = ( foo )' " + c->str());
+
+    // c =
+    context.interpreter().eval(RequestParser::parse("f"), context);
+
+    //c->graph("evaluated 'f': " + c->str());
+
+
+    context.execute("system, values = 'echo some test data for your testing pleasure > test_data_for_ecml_part_file_testing.txt'");
+    string descriptor ("partfile://test_data_for_ecml_part_file_testing.txt:0,10");
+    DataHandle* dh (DataHandleFactory::openForRead(descriptor));
+    Log::info () << "partfile: " << dh << endl;
+    PartFileHandle* pfh (dynamic_cast<PartFileHandle*>(dh));
+    ASSERT(pfh);
+    context.execute("system, values = 'rm -f test_data_for_ecml_part_file_testing.txt'");
+
+
+    context.execute("let,big_foobar=0/0/0");
+
+    context.execute("let,a=1");
+    context.execute("let,b=2");
+    context.execute("let,ab=12");
+    context.execute("let,abba=12221");
+
+    vector<string> vars (context.environment().lookupVariables("a"));
+    ASSERT(vars.size() >= 3); // we have variables from the prelude and possibly other libs in the environment
+    ASSERT(vars[0] == "abba");
+    ASSERT(vars[1] == "ab");
+    ASSERT(vars[2] == "a");
+}
+
+} // namespace ecml 
+
+int main(int argc,char **argv)
+{
+    //TODO: set a behaviour?
+    //Context::instance().behavior( new odb::ODBBehavior() );
+    // TODO: enable $DEBUG (Log::debug)
+
+    ecml::ECMLUnitTests runner(argc, argv);
+    return runner.start();
+}
+
diff --git a/odb_api/src/ecml/tests/s.ecml b/odb_api/src/ecml/tests/s.ecml
new file mode 100644
index 0000000..03e8be9
--- /dev/null
+++ b/odb_api/src/ecml/tests/s.ecml
@@ -0,0 +1 @@
+for,x=1/2/3,do=(x)
diff --git a/odb_api/src/ecml/tests/test_apply.ecml b/odb_api/src/ecml/tests/test_apply.ecml
new file mode 100644
index 0000000..fef37fc
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_apply.ecml
@@ -0,0 +1,34 @@
+test, label = "Execute apply with a closure, no parameters",
+do = (
+    apply, closure = (function, _ = (println, values = FOO)),
+           args = (let)
+), 
+EXPECT = FOO
+
+test, label = Apply parameters to closure,
+do = (
+    apply,
+        closure = (function, of = param, _ = (println, values = ($, _ = param))),
+        args = (let, param = FOO)
+), 
+expect = FOO
+
+test, label = Apply parameters to a deserialized closure,
+do = (
+    let, x = "some value of x in the dynamic environment"
+    apply,
+        closure = (closure,
+                        name = _,
+                        parameters = things / ideas, 
+                        captured = (let, x = "the captured value"), 
+                        code = (
+                                    println, values = "things: " / ($, _ = things) / " ideas: " / ($, _ = ideas) / " captured: " / ($,_ = x)
+                                )),
+        args    = (let, 
+                    things = 1 / 2, 
+                    ideas  = foo / bar)
+
+),
+expect = "things: " / 1 / 2 /  " ideas: " / foo / bar /  " captured: " / "the captured value"
+
+
diff --git a/odb_api/src/ecml/tests/test_dictionary.ecml b/odb_api/src/ecml/tests/test_dictionary.ecml
new file mode 100644
index 0000000..d186e8f
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_dictionary.ecml
@@ -0,0 +1,27 @@
+test, label = check verb let is there,
+do = (
+        println,values = (let, a = 1, b = 2, c = 3, d = 1/2/3/4)
+),
+expect = (let, a = 1, b = 2, c = 3, d = 1/2/3/4)
+
+
+test, label = check verb value can extract values from a dictionary,
+do = (
+        let, d = (let, a = 1, b = 2, c = 3)
+        $, _ = d / a
+),
+expect = 1
+
+
+test, label = 'check path syntax of verb value (also known as $ (dollar))',
+do = (
+        let, d = (let, 
+                     a = 1, 
+                     b = 2, 
+                     c = (let,
+                              k1 = 1,
+                              k2 = 1 / 2,
+                              k3 = 1 / 2 / 3))
+        $, _ = d/c/k3
+),
+expect = 1 / 2 / 3
diff --git a/odb_api/src/ecml/tests/test_first.ecml b/odb_api/src/ecml/tests/test_first.ecml
new file mode 100644
index 0000000..aa6e38a
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_first.ecml
@@ -0,0 +1,6 @@
+test,label=Test verb first,
+do = (
+        let, l = foo / bar / baz
+        println, values = (first, of = (value, of = l))
+     ),
+expect = foo
diff --git a/odb_api/src/ecml/tests/test_for.ecml b/odb_api/src/ecml/tests/test_for.ecml
new file mode 100644
index 0000000..48387cc
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_for.ecml
@@ -0,0 +1,51 @@
+test, label = Check verb for works 1,
+do = (
+    for, x = 1/2/3,
+    do = (
+        println,values = (x)
+    )
+),
+expect = 1/2/3
+
+test, label = Check verb for works 2,
+do = (
+    for, x = 1/2/3,
+    do = (
+        println,values = ($,_=x) / ($,_=x)
+    )
+),
+expect = 1/1/2/2/3/3
+
+test, label = Check nested for,
+do = (
+    for, x = 1/2/3,
+    do = (
+        for, y = (sequence,values = a/b/c),
+        do = (
+            println,values = ($,_=x) / ($,_=y)
+        )
+    )
+),
+expect = 1 / a / 1 / b / 1 / c / 2 / a / 2 / b / 2 / c / 3 / a / 3 / b / 3 / c
+
+test, label = Check nested for again,
+do = (
+    for, x = (for, y = a/b/c, do = (y)),
+    do = (sequence,values=(x) / (x))
+),
+expect = a / a / b / b / c / c
+
+test, label = Check nested for producing dicts,
+do = (
+    let, records = (
+        for, x = 1/2/3,
+        do = (
+            for, y = (sequence,values = a/b/c),
+            do = (let, number = (x),
+                       character = (y))
+        )
+    )
+
+    for, rec = (records), do = ($,_ = rec/number)
+),
+expect = 1/1/1 / 2/2/2 / 3/3/3
diff --git a/odb_api/src/ecml/tests/test_function.ecml b/odb_api/src/ecml/tests/test_function.ecml
new file mode 100644
index 0000000..8e6839e
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_function.ecml
@@ -0,0 +1,16 @@
+test, label = define and call a function,
+    do = (
+            let, ending = my friend
+
+            function, of = name, capture = ending,
+                greetings = (
+                    print, values = Hello / (value, of = name) / (value, of = ending)
+                ) 
+
+            let, ending = "!"
+
+            greetings, name = world
+         ),
+    expect = Hello / world / my friend
+
+    
diff --git a/odb_api/src/ecml/tests/test_getenv.ecml b/odb_api/src/ecml/tests/test_getenv.ecml
new file mode 100644
index 0000000..cf9ec9c
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_getenv.ecml
@@ -0,0 +1,7 @@
+test, label = Check getenv,
+do = (
+    println, values = Hello / (getenv, values = USER)
+    println, values = "Nice to meet you!"
+    
+),
+expect = "Nice to meet you!"
diff --git a/odb_api/src/ecml/tests/test_higher_order_functions.ecml b/odb_api/src/ecml/tests/test_higher_order_functions.ecml
new file mode 100644
index 0000000..f145d07
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_higher_order_functions.ecml
@@ -0,0 +1,18 @@
+test, label = Pass a function to another function and call it there,
+do = (
+    function, foo = ( println, values = FOO )
+    function, of = fun, bar = ( fun )
+    bar, fun = (value, of = foo)
+),
+expect = FOO
+
+test, label = return a function from another function and call it,
+do = (
+    function, foo = (
+        function, bar = ( println, values = BAR )
+    )
+    let, f = ( foo )
+    f
+),
+expect = BAR
+
diff --git a/odb_api/src/ecml/tests/test_let_and_value.ecml b/odb_api/src/ecml/tests/test_let_and_value.ecml
new file mode 100644
index 0000000..5a41a04
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_let_and_value.ecml
@@ -0,0 +1,8 @@
+test,
+    label = let defines a variable that we can lookup value of,
+    do = (
+            let, name = world
+            print, values = Hello / (value, of = name)
+         ),
+    expect = Hello / world
+
diff --git a/odb_api/src/ecml/tests/test_lists.ecml b/odb_api/src/ecml/tests/test_lists.ecml
new file mode 100644
index 0000000..550c819
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_lists.ecml
@@ -0,0 +1,6 @@
+test, label = sequences produced by expressions embedded in a sequence are concatenated together,
+do = (
+        let, x = 1 / 2 
+        println,values = ($,_ = x) / ($,_ = x)
+),
+expect = 1 / 2 / 1 / 2
diff --git a/odb_api/src/ecml/tests/test_map.ecml b/odb_api/src/ecml/tests/test_map.ecml
new file mode 100644
index 0000000..3cc6a03
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_map.ecml
@@ -0,0 +1,22 @@
+function, of = f / values, map = (
+    if, condition = (first, of = ($,_ = values)),
+        then = ( sequence, values = (f, values = (first, of = ($,_ = values))) 
+                                    / (map, f = ($,_ = f),
+                                            values = (rest, of = ($,_ = values))))
+)
+
+test, label = pass a native function to a map function,
+do = ( map, f = ($,_ = println), values = 1 / 2 / 3 ),
+expect = 1 / 2 / 3
+
+test, label = pass a user defined function to a map function,
+do = ( 
+        let, greeting = Hello
+        function, of = values, capture = greeting, foo = (
+            println, values = ($,_ = greeting ) / ($,_ = values) 
+        )
+        let, greeting = Hi
+        map, f = ($,_ = foo), values = 1 / 2 / 3
+     ),
+expect = Hello / 1 / Hello / 2 / Hello / 3
+
diff --git a/odb_api/src/ecml/tests/test_match.ecml b/odb_api/src/ecml/tests/test_match.ecml
new file mode 100644
index 0000000..0afefaa
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_match.ecml
@@ -0,0 +1,3 @@
+test, label = test verb match,
+do = (match, regex=foo, strings = foobar/foo/foobaz/bazbaz),
+expect = foobar / foo / foobaz
diff --git a/odb_api/src/ecml/tests/test_one_letter_variables.ecml b/odb_api/src/ecml/tests/test_one_letter_variables.ecml
new file mode 100644
index 0000000..77e886c
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_one_letter_variables.ecml
@@ -0,0 +1,9 @@
+test, label = variable name can be one letter long,
+    do = (
+            let, l = lexer
+            let, p = parser
+            println, values = (value,of=l) / and / (value,of=p)
+            value, of = l
+         ),
+    expect = lexer
+
diff --git a/odb_api/src/ecml/tests/test_parallel_map.ecml b/odb_api/src/ecml/tests/test_parallel_map.ecml
new file mode 100644
index 0000000..54be220
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_parallel_map.ecml
@@ -0,0 +1,10 @@
+test, label = check parallel map works,
+do = (
+    let, f = (function,
+                    of = values, 
+                    _ = (println, values = number / (value, of = values)))
+
+    map, values = 1 / 2 / 3,
+         closure = (value, of = f)
+),
+expect = number / 1 / number / 2 / number / 3
diff --git a/odb_api/src/ecml/tests/test_print.ecml b/odb_api/src/ecml/tests/test_print.ecml
new file mode 100644
index 0000000..8f736ea
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_print.ecml
@@ -0,0 +1,5 @@
+test,
+    label = Verb print returns values that it just printed,
+    do = ( print, values = Hello world ),
+    expect = Hello world
+
diff --git a/odb_api/src/ecml/tests/test_recursion.ecml b/odb_api/src/ecml/tests/test_recursion.ecml
new file mode 100644
index 0000000..6b96cc9
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_recursion.ecml
@@ -0,0 +1,18 @@
+test, label = test recursive function,
+do = (
+        function, of = names,
+            hello = (
+                if, condition = (rest, of = (value, of = names)), # if more then one element
+                then = (
+                    println, values = Hello / (first, of = (value, of = names))
+
+                    hello, names = (rest, of = (value, of = names))
+                ),
+                else = (
+                    println, values = Bye / (first, of = (value, of = names))
+                )
+            )
+
+          hello, names = foo / bar / 0 / baz
+     ),
+expect = Bye / baz
diff --git a/odb_api/src/ecml/tests/test_rest.ecml b/odb_api/src/ecml/tests/test_rest.ecml
new file mode 100644
index 0000000..18722a3
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_rest.ecml
@@ -0,0 +1,12 @@
+test, label = rest returns a copy of the sequence supplied in values parameter without its first element,
+do = (
+    println, values = (rest, of = foo / bar / baz)
+),
+expect = bar / baz
+
+test, label = check return value of rest is not embedded in an extra sequence,
+do = (
+    let, x = 1 / 2 / 3 / 4 / 5
+    println,values = (first,of = (rest, of = ($,_ = x)))
+),
+expect = 2
diff --git a/odb_api/src/ecml/tests/test_system.ecml b/odb_api/src/ecml/tests/test_system.ecml
new file mode 100644
index 0000000..57967d8
--- /dev/null
+++ b/odb_api/src/ecml/tests/test_system.ecml
@@ -0,0 +1,9 @@
+test, label = check verb system works,
+do = (
+    print, values = Today is
+    system, values = date
+
+    print, values = Current working directory is
+    system, values = pwd
+),
+expect = 0
diff --git a/odb_api/src/fortran/CMakeLists.txt b/odb_api/src/fortran/CMakeLists.txt
new file mode 100644
index 0000000..53f505f
--- /dev/null
+++ b/odb_api/src/fortran/CMakeLists.txt
@@ -0,0 +1,62 @@
+
+ecbuild_add_library(TARGET		Odb_fortran
+
+                    CONDITION   HAVE_FORTRAN
+
+                    SOURCES		
+                                # The new Fortran API:
+                                odbql_binding.f90 
+                                odbql_constants.f90
+                                odbql_wrappers.f90 
+
+                                # Legacy:
+
+                                odb_c_binding.f90
+
+                    LIBS		Odb )
+
+set( test_environment_fortran
+     TEST_DHSHOME=${PROJECT_SOURCE_DIR}/tests/dhshome/
+     ODB_API_SCHEMA_PATH=${CMAKE_CURRENT_BINARY_DIR}/../../tests/cma.hh
+)
+
+if( HAVE_FORTRAN )
+
+    list( APPEND fortran_tests
+
+        # Legacy API examples
+        legacy_fortran_api_examples
+        legacy_test_fortran_api_open_non_existing_file
+        # TODO: This test needs some data which is, presumebly, created/downloaded by
+        # an ODB Server test (in MARS server source). I need to make sure legacy_test_client_lib_fortran_local
+        # can work on its own, not just as a part of ODB Server bundle.
+        #legacy_test_client_lib_fortran_local
+        test_regression
+    )
+
+    foreach( _test ${fortran_tests} )
+        ecbuild_add_test( TARGET     ${_test}
+                          CONDITION  HAVE_FORTRAN
+                          SOURCES    ${_test}.f90
+                          LIBS       Odb_fortran ${ODB_LIBS} 
+                          ENVIRONMENT  ${test_environment_fortran} 
+
+                          LABELS     odb_api odb_api_fortran
+
+                          TEST_DEPENDS get_odb_api_test_data
+                                       get_mars_client_test_data_mo
+                                       get_mars_client_test_data_ec
+                                       test_ec_archiving.ecml
+                                       test_mo_archiving.ecml
+
+                          LINKER_LANGUAGE Fortran )
+    endforeach()
+
+# odbql, the new Fortran API:
+    install( FILES ${CMAKE_Fortran_MODULE_DIRECTORY}/${CMAKE_CFG_INTDIR}/odbql_binding.mod  DESTINATION ${INSTALL_INCLUDE_DIR} )
+    install( FILES ${CMAKE_Fortran_MODULE_DIRECTORY}/${CMAKE_CFG_INTDIR}/odbql_wrappers.mod DESTINATION ${INSTALL_INCLUDE_DIR} )
+    install( FILES ${CMAKE_Fortran_MODULE_DIRECTORY}/${CMAKE_CFG_INTDIR}/odbql_constants.mod DESTINATION ${INSTALL_INCLUDE_DIR} )
+
+# Legacy Fortran bindings:
+    install( FILES ${CMAKE_Fortran_MODULE_DIRECTORY}/${CMAKE_CFG_INTDIR}/odb_c_binding.mod DESTINATION ${INSTALL_INCLUDE_DIR} )
+endif()
diff --git a/odb_api/src/fortran/legacy_fortran_api_examples.f90 b/odb_api/src/fortran/legacy_fortran_api_examples.f90
new file mode 100644
index 0000000..3a087ad
--- /dev/null
+++ b/odb_api/src/fortran/legacy_fortran_api_examples.f90
@@ -0,0 +1,406 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+program example_fortran_api
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  implicit none
+
+  integer, parameter :: max_varlen = 128
+  integer(kind=4)    :: ncolumns = 5
+
+  write(0,*) "Calling odb_start..."
+
+  call odb_start()
+
+  call example_fortran_api_setup()
+  call example_fortran_api1()
+  call example_fortran_api2()
+  call example_fortran_api_append()
+
+contains
+
+subroutine example_fortran_api_append
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=max_varlen)        :: config = C_NULL_CHAR
+ character(kind=C_CHAR, len=max_varlen)        :: outputfile="example_fortran_api_append.odb"//achar(0)
+ integer(kind=4)                               :: i
+ integer(kind=C_INT)                           :: itype, c_ncolumns 
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=100)                            :: expver="fihn"//achar(0)
+
+ write(0,*) 'example_fortran_api_append'
+ c_ncolumns = ncolumns
+ odb_handle = odb_write_new(config, cerr)
+ odb_it = odb_write_iterator_new(odb_handle, outputfile, cerr);
+ 
+ cerr = odb_write_set_no_of_columns(odb_it, ncolumns)
+ cerr = odb_write_set_column(odb_it, 0, ODB_INTEGER, "ifoo"//achar(0))
+ cerr = odb_write_set_column(odb_it, 1, ODB_REAL, "nbar"//achar(0))
+ cerr = odb_write_set_bitfield(odb_it, 2, ODB_BITFIELD, "status"//achar(0), &
+                                     "active:passive:blacklisted:"//achar(0), &
+                                     "1:1:4:"//achar(0))
+ cerr = odb_write_set_column(odb_it, 3, ODB_STRING, "expver"//achar(0))
+ cerr = odb_write_set_column(odb_it, 4, ODB_DOUBLE, "dbar"//achar(0))
+
+ cerr = odb_write_set_missing_value(odb_it, 0, 1.0_8)
+ cerr = odb_write_header(odb_it)
+
+ allocate(one_row(ncolumns))
+ do i=1,10
+   one_row(1) = i
+   one_row(2) = i 
+   one_row(3) = 5 
+   one_row(4) = transfer(expver, one_row(4))
+   one_row(5) = 5
+   cerr = odb_write_set_next_row(odb_it, one_row, c_ncolumns)
+ enddo
+ deallocate(one_row)
+
+ cerr = odb_write_iterator_delete(odb_it)
+
+ odb_it = odb_append_iterator_new(odb_handle, outputfile, cerr);
+ 
+ cerr = odb_write_set_no_of_columns(odb_it, ncolumns)
+ cerr = odb_write_set_column(odb_it, 0, ODB_INTEGER, "ifoo"//achar(0))
+ cerr = odb_write_set_column(odb_it, 1, ODB_REAL, "nbar"//achar(0))
+ cerr = odb_write_set_bitfield(odb_it, 2, ODB_BITFIELD, "status"//achar(0), &
+                                     "active:passive:blacklisted:"//achar(0), &
+                                     "1:1:4:"//achar(0))
+ cerr = odb_write_set_column(odb_it, 3, ODB_STRING, "expver"//achar(0))
+ cerr = odb_write_set_column(odb_it, 4, ODB_DOUBLE, "dbar"//achar(0))
+
+ cerr = odb_write_set_missing_value(odb_it, 0, 1.0_8)
+ cerr = odb_write_header(odb_it)
+
+ allocate(one_row(ncolumns))
+ do i=1,10
+   one_row(1) = i
+   one_row(2) = i 
+   one_row(3) = 5 
+   one_row(4) = transfer(expver, one_row(4))
+   one_row(5) = 5 
+   cerr = odb_write_set_next_row(odb_it, one_row, c_ncolumns)
+ enddo
+ deallocate(one_row)
+
+ cerr = odb_write_iterator_delete(odb_it)
+ cerr = odb_write_delete(odb_handle)
+
+end subroutine example_fortran_api_append
+
+subroutine example_fortran_api_setup
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=max_varlen)        :: config = C_NULL_CHAR
+ character(kind=C_CHAR, len=max_varlen)        :: outputfile="test.odb"//achar(0)
+ integer(kind=4)                               :: i
+ integer(kind=C_INT)                           :: itype, c_ncolumns 
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=100)                            :: expver="fihn"//achar(0)
+
+ write(0,*) 'example_fortran_api_setup'
+ c_ncolumns = ncolumns
+ odb_handle = odb_write_new(config, cerr)
+ odb_it = odb_write_iterator_new(odb_handle, outputfile, cerr);
+ 
+ cerr = odb_write_set_no_of_columns(odb_it, ncolumns)
+ cerr = odb_write_set_column(odb_it, 0, ODB_INTEGER, "ifoo"//achar(0))
+ cerr = odb_write_set_column(odb_it, 1, ODB_REAL, "nbar"//achar(0))
+ cerr = odb_write_set_bitfield(odb_it, 2, ODB_BITFIELD, "status"//achar(0), &
+                                     "active:passive:blacklisted:"//achar(0), &
+                                     "1:1:4:"//achar(0))
+ cerr = odb_write_set_column(odb_it, 3, ODB_STRING, "expver"//achar(0))
+ cerr = odb_write_set_column(odb_it, 4, ODB_DOUBLE, "dbar"//achar(0))
+
+ cerr = odb_write_set_missing_value(odb_it, 0, 1.0_8)
+ cerr = odb_write_header(odb_it)
+
+ allocate(one_row(ncolumns))
+ do i=1,10
+   one_row(1) = i
+   one_row(2) = i 
+   one_row(3) = 5 
+   one_row(4) = transfer(expver, one_row(4))
+   one_row(5) = 5 
+   cerr = odb_write_set_next_row(odb_it, one_row, c_ncolumns)
+ enddo
+ deallocate(one_row)
+
+ cerr = odb_write_iterator_delete(odb_it)
+ cerr = odb_write_delete(odb_handle)
+
+end subroutine example_fortran_api_setup
+
+subroutine example_fortran_api1
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=max_varlen)        :: config = C_NULL_CHAR
+ character(kind=C_CHAR, len=max_varlen)        :: inputfile = "test.odb"//achar(0)
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=2, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ real(kind=C_DOUBLE)                           :: val
+ character(len=8)                              :: tmp_str
+
+ write(0,*) 'example_fortran_api1'
+ odb_handle = odb_read_new(config, cerr)
+ odb_it = odb_read_iterator_new(odb_handle, inputfile, cerr);
+ 
+ if (cerr /=0) STOP 1
+ 
+ cerr = odb_read_get_no_of_columns(odb_it, c_ncolumns)
+ if (cerr /=0) STOP 2
+ if (c_ncolumns /= ncolumns) STOP 3
+ 
+ cerr = odb_read_get_column_type(odb_it, 0, itype)
+ if (cerr /=0) STOP 4
+ if (itype /= ODB_INTEGER) STOP 5
+
+ val = -13
+ cerr = odb_read_get_missing_value(odb_it, 0, val)
+ if (cerr /=0) STOP 400
+ write(0,*) 'odb_read_get_missing_value: missing value of column 0 => ', val
+
+ cerr = odb_read_get_column_type(odb_it, 1, itype)
+ if (cerr /=0) STOP 6
+ if (itype /= ODB_REAL) STOP 7
+
+ cerr = odb_read_get_column_type(odb_it, 2, itype)
+ if (cerr /=0) STOP 8
+ if (itype /= ODB_BITFIELD) STOP 9
+
+ cerr = odb_read_get_bitfield(odb_it, 2, ptr_bitfield_names, ptr_bitfield_sizes, bitfield_names_size, bitfield_sizes_size)
+ write(0,*) 'odb_read_get_bitfield column 2 => ', cerr
+ if (cerr /=0) STOP 91
+ write(0,*) 'column 2 bitfield_names_size: ', bitfield_names_size
+ call C_F_POINTER(CPTR=ptr_bitfield_names, FPTR=f_ptr_bitfield_names, shape=(/bitfield_names_size/));
+ do i=1, bitfield_names_size
+    bitfield_names(i:i) = f_ptr_bitfield_names(i)
+ end do
+ write(0,*) 'column 2 bitfield_names: ', bitfield_names(1:bitfield_names_size)
+ if (bitfield_names(1:bitfield_names_size) /= 'active:passive:blacklisted:') STOP 92
+
+ write(0,*) 'column 2 bitfield_sizes_size: ', bitfield_sizes_size
+ call C_F_POINTER(CPTR=ptr_bitfield_sizes, FPTR=f_ptr_bitfield_sizes, shape=(/bitfield_sizes_size/));
+ do i=1, bitfield_sizes_size
+    bitfield_sizes(i:i) = f_ptr_bitfield_sizes(i)
+ end do
+ write(0,*) 'column 2 bitfield_sizes: ', bitfield_sizes(1:bitfield_sizes_size)
+ if (bitfield_sizes(1:bitfield_sizes_size) /= '1:1:4:') STOP 93
+
+ cerr = odb_read_get_column_type(odb_it, 3, itype)
+ if (cerr /=0) STOP 10
+ if (itype /= ODB_STRING) STOP 11
+
+ cerr = odb_read_get_column_type(odb_it, 4, itype)
+ if (cerr /=0) STOP 110
+ if (itype /= ODB_DOUBLE) STOP 111
+
+ cerr = odb_read_get_column_name(odb_it, 0, ptr_colname, size_name)
+ 
+ if (cerr /= 0) STOP 12
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 1 : ', colname(1:i)
+
+ cerr = odb_read_get_column_name(odb_it, 1, ptr_colname, size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 2 : ', colname(1:i)
+
+ cerr = odb_read_get_column_name(odb_it, 2, ptr_colname,size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 3 : ', colname(1:i)
+
+ cerr = odb_read_get_column_name(odb_it, 3, ptr_colname,size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 4 : ', colname(1:i)
+
+ cerr = odb_read_get_column_name(odb_it, 4, ptr_colname,size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 5 : ', colname(1:i)
+
+ allocate(one_row(c_ncolumns))
+ cerr = 0
+ i = 1
+ do 
+   cerr = odb_read_get_next_row(odb_it, c_ncolumns,  one_row, newdataset)
+   if ( cerr /= 0) exit
+   tmp_str = transfer(one_row(4), tmp_str)
+   write(0,*) i, ":", one_row(1), one_row(2), one_row(3), tmp_str
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ cerr = odb_read_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+
+end subroutine example_fortran_api1
+
+subroutine example_fortran_api2
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i
+ character(kind=C_CHAR, len=128)               :: sql='select * from "test.odb";'//achar(0)
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=3, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=8)                 :: tmp_str
+
+ write(0,*) 'example_fortran_api2'
+
+ odb_handle = odb_select_new(config, cerr)
+ if (cerr /=0) STOP 1
+
+ odb_it =  odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr /=0) STOP 1
+ 
+ cerr = odb_select_get_no_of_columns(odb_it, c_ncolumns)
+ if (cerr /=0) STOP 1
+ if (c_ncolumns /= ncolumns) STOP "Error number of columns is not 4"
+ 
+ cerr = odb_select_get_column_type(odb_it, 0, itype)
+ if (cerr /=0) STOP 1
+ if (itype /= ODB_INTEGER) STOP "Error type of column 1 is not integer!"
+
+ cerr = odb_select_get_column_type(odb_it, 1, itype)
+ if (cerr /=0) STOP 1
+ if (itype /= ODB_REAL) STOP "Error type of column 2 is not real!"
+
+ cerr = odb_select_get_column_type(odb_it, 2, itype)
+ if (cerr /=0) STOP 1
+ if (itype /= ODB_BITFIELD) STOP "Error type of column 3 is not bitfield!"
+
+ cerr = odb_select_get_column_type(odb_it, 3, itype)
+ if (cerr /=0) STOP 1
+ if (itype /= ODB_STRING) STOP "Error type of column 4 is not string!"
+
+ cerr = odb_select_get_column_type(odb_it, 4, itype)
+ if (cerr /=0) STOP 1
+ if (itype /= ODB_DOUBLE) STOP "Error type of column 5 is not double!"
+
+ cerr = odb_select_get_column_name(odb_it, 0, ptr_colname,size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name 
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 1 : ', colname(1:i)
+
+ cerr = odb_select_get_column_name(odb_it, 1, ptr_colname, size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 2 : ', colname(1:i)
+
+ cerr = odb_select_get_bitfield(odb_it, 2, ptr_bitfield_names, ptr_bitfield_sizes, bitfield_names_size, bitfield_sizes_size)
+ write(0,*) 'odb_select_get_bitfield column 2 => ', cerr
+ if (cerr /=0) STOP 191
+ write(0,*) 'column 2 bitfield_names_size: ', bitfield_names_size
+ call C_F_POINTER(CPTR=ptr_bitfield_names, FPTR=f_ptr_bitfield_names, shape=(/bitfield_names_size/));
+ do i=1, bitfield_names_size
+    bitfield_names(i:i) = f_ptr_bitfield_names(i)
+ end do
+ write(0,*) 'column 2 bitfield_names: ', bitfield_names(1:bitfield_names_size)
+ if (bitfield_names(1:bitfield_names_size) /= 'active:passive:blacklisted:') STOP 192
+
+ write(0,*) 'column 2 bitfield_sizes_size: ', bitfield_sizes_size
+ call C_F_POINTER(CPTR=ptr_bitfield_sizes, FPTR=f_ptr_bitfield_sizes, shape=(/bitfield_sizes_size/));
+ do i=1, bitfield_sizes_size
+    bitfield_sizes(i:i) = f_ptr_bitfield_sizes(i)
+ end do
+ write(0,*) 'column 2 bitfield_sizes: ', bitfield_sizes(1:bitfield_sizes_size)
+ if (bitfield_sizes(1:bitfield_sizes_size) /= '1:1:4:') STOP 193
+
+ cerr = odb_select_get_column_name(odb_it, 2, ptr_colname, size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 3 : ', colname(1:i)
+
+ cerr = odb_select_get_column_name(odb_it, 3, ptr_colname, size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 4 : ', colname(1:i)
+
+ cerr = odb_select_get_column_name(odb_it, 4, ptr_colname, size_name)
+ if (cerr /=0) STOP 1
+ call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+ do i=1, size_name
+    colname(i:i)  = f_ptr_colname(i)
+ end do
+ write(0,*) 'column name 5 : ', colname(1:i)
+
+ allocate(one_row(c_ncolumns))
+ cerr=0
+ i = 1
+ do 
+   cerr = odb_select_get_next_row(odb_it, c_ncolumns,  one_row, newdataset)
+   if ( cerr /= 0) exit
+   tmp_str = transfer(one_row(4), tmp_str)
+   write(0,*) i, ":",  one_row(1), one_row(2), one_row(3), tmp_str
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ cerr = odb_select_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+
+end subroutine example_fortran_api2
+end program example_fortran_api
diff --git a/odb_api/src/fortran/legacy_test_client_lib_fortran_local.ecml b/odb_api/src/fortran/legacy_test_client_lib_fortran_local.ecml
new file mode 100644
index 0000000..aab419e
--- /dev/null
+++ b/odb_api/src/fortran/legacy_test_client_lib_fortran_local.ecml
@@ -0,0 +1,6 @@
+test, label = Execute test_client_lib_fortran_local,
+do = (
+    sh, _ = (getenv, values = MARS_INSTALLATION_DIRECTORY)/"/bin/legacy_test_client_lib_fortran_local"
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/fortran/legacy_test_client_lib_fortran_local.f90 b/odb_api/src/fortran/legacy_test_client_lib_fortran_local.f90
new file mode 100644
index 0000000..506b0a2
--- /dev/null
+++ b/odb_api/src/fortran/legacy_test_client_lib_fortran_local.f90
@@ -0,0 +1,169 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+program test_client_lib_fortran_local
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  implicit none
+
+  integer, parameter :: max_varlen = 128
+  integer(kind=4)    :: ncolumns 
+
+  write(0,*) "Calling odb_start..."
+  call odb_start()
+
+  call example_fortran_api_stage_local()
+  call example_fortran_api_retrieve_part_local()
+  call example_fortran_api_stage_local()
+  call example_fortran_api_retrieve_part_local()
+
+contains
+
+
+subroutine example_fortran_api_stage_local
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i, ci
+
+ character(kind=C_CHAR, len=2048)              :: sql='select partition_number,number_of_rows from &
+ &"local://stage,class=OD,date=20151108,time=1200,type=OFB,&
+ &obsgroup=conv,reportype=16030,stream=oper,expver=qu12,&
+ &odbpathnameschema=''{date}/{time}/{reportype}.odb'',&
+ &odbserverroots=''~/data/root'',&
+ &partitionsinfo=''~/data/partitions_info.txt'',&
+ &n_parts=13,&
+ &database=localhost";'//achar(0)
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=2, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=8)                              :: tmp_str
+
+ write(0,*) 'example_fortran_api_stage_local: ', sql
+
+ odb_handle = odb_select_new(config, cerr)
+ if (cerr /=0) STOP 1
+
+ odb_it = odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr /=0) STOP 2
+ 
+ cerr = odb_select_get_no_of_columns(odb_it, ncolumns)
+ if (cerr /=0) STOP 1
+ write(0,*) '-=-=-=-=-= example_fortran_api_stage_local: number of columns: ', ncolumns
+ if (ncolumns /= 2) STOP 2
+ 
+ do ci=0, ncolumns - 1
+     cerr = odb_select_get_column_name(odb_it, ci, ptr_colname, size_name)
+     if (cerr /=0) STOP 1
+     call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+     do i=1, size_name 
+        colname(i:i)  = f_ptr_colname(i)
+     end do
+     write(0,*) ' : ', colname(1:i)
+ end do
+
+ allocate(one_row(c_ncolumns))
+ cerr=0
+ i = 0
+ do 
+   cerr = odb_select_get_next_row(odb_it, c_ncolumns, one_row, newdataset)
+   if ( cerr /= 0) exit
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ write(0,*) '-=-=-=-=-= example_fortran_api_stage_local: number of rows: ', i
+ if (i /= 13) STOP 22
+
+ cerr = odb_select_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+end subroutine example_fortran_api_stage_local
+
+subroutine example_fortran_api_retrieve_part_local
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i, ci
+
+ character(kind=C_CHAR, len=2048)              :: sql='select * from &
+ & "local://retrieve,class=OD,date=20151108,time=1200,type=OFB,&
+ & obsgroup=conv,reportype=16030,stream=oper,expver=qu12,&
+ & odbpathnameschema=''{date}/{time}/{reportype}.odb'',&
+ & odbserverroots=''~/data/root'',&
+ & partitionsinfo=''~/data/partitions_info.txt'',&
+ & n_parts=13,&
+ & part_number=0,&
+ & database=localhost";'//achar(0)
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=52, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=8)                              :: tmp_str
+
+ write(0,*) 'example_fortran_api_retrieve_part_local: ', sql
+
+ odb_handle = odb_select_new(config, cerr)
+ if (cerr /=0) STOP 1
+
+ odb_it = odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr /=0) STOP 1
+ 
+ cerr = odb_select_get_no_of_columns(odb_it, ncolumns)
+ if (cerr /=0) STOP 1
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve_part_local: number of columns: ', ncolumns
+ if (c_ncolumns /= ncolumns) STOP 2
+ 
+ do ci=0, ncolumns - 1
+     cerr = odb_select_get_column_name(odb_it, ci, ptr_colname, size_name)
+     if (cerr /=0) STOP 1
+     call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+     do i=1, size_name 
+        colname(i:i)  = f_ptr_colname(i)
+     end do
+     write(0,*) ' : ', colname(1:i)
+ end do
+
+ allocate(one_row(c_ncolumns))
+ cerr=0
+ i = 0
+ do 
+   cerr = odb_select_get_next_row(odb_it, c_ncolumns, one_row, newdataset)
+   if ( cerr /= 0) exit
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve_part_local: number of rows: ', i
+ if (i /= 9938) STOP 22
+
+ cerr = odb_select_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+
+end subroutine example_fortran_api_retrieve_part_local
+
+end program test_client_lib_fortran_local
diff --git a/odb_api/src/fortran/legacy_test_fortran_api_open_non_existing_file.f90 b/odb_api/src/fortran/legacy_test_fortran_api_open_non_existing_file.f90
new file mode 100644
index 0000000..2d8a38f
--- /dev/null
+++ b/odb_api/src/fortran/legacy_test_fortran_api_open_non_existing_file.f90
@@ -0,0 +1,46 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+program test_fortran_api
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  implicit none
+
+  integer, parameter :: max_varlen = 128
+  integer(kind=4)    :: ncolumns = 4
+
+  write(0,*) "Calling odb_start..."
+
+  call odb_start()
+
+  call test_fortran_api_non_existing_file()
+
+contains
+
+subroutine test_fortran_api_non_existing_file
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it, odb_select_handle, odb_select_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ character(kind=C_CHAR, len=max_varlen)        :: inputfile = "test_fortran_api_non_existing_file.odb"//achar(0)
+ character(kind=C_CHAR, len=128)               :: sql='select * from "test_fortran_api_non_existing_file.odb";'//achar(0)
+
+ write(0,*) 'test_fortran_api_non_existing_file'
+ odb_handle = odb_read_new(config, cerr)
+ odb_it = odb_read_iterator_new(odb_handle, inputfile, cerr);
+ ! This should fail
+ write(0,*) 'test_fortran_api_non_existing_file: odb_read_iterator_new => ', cerr
+ if (cerr == 0) STOP 1
+
+ odb_handle = odb_select_new(config, cerr)
+ odb_it = odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr == 0) STOP 1
+end subroutine test_fortran_api_non_existing_file
+
+end program test_fortran_api
diff --git a/odb_api/src/fortran/odb_c_binding.f90 b/odb_api/src/fortran/odb_c_binding.f90
new file mode 100644
index 0000000..8e38de0
--- /dev/null
+++ b/odb_api/src/fortran/odb_c_binding.f90
@@ -0,0 +1,304 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+!------------------------------------------------------------------------------
+! ECMWF
+!------------------------------------------------------------------------------
+!
+! MODULE: odb_c_binding
+!
+!> @author Anne Fouilloux, ECMWF
+!
+! DESCRIPTION: 
+!> Provides Fortran bindings for ODB API
+!
+! REVISION HISTORY:
+! DD Mmm YYYY - Initial Version
+! TODO_dd_mmm_yyyy - TODO_describe_appropriate_changes - TODO_name
+!------------------------------------------------------------------------------
+
+module odb_c_binding
+  use iso_c_binding
+  use, intrinsic :: iso_c_binding
+  implicit none
+  integer, parameter :: ODB_IGNORE=0
+  integer, parameter :: ODB_INTEGER=1
+  integer, parameter :: ODB_REAL=2
+  integer, parameter :: ODB_STRING=3
+  integer, parameter :: ODB_BITFIELD=4
+  integer, parameter :: ODB_DOUBLE=5
+interface 
+
+!> Initialize ODB API. This function must be called before any other function from the ODB API.
+   subroutine odb_start() bind(C, name = "odb_start")
+   end subroutine
+
+!> Counts number of rows in an ODB file.
+!>
+!> @param[in] filename  name of the file
+!> @return number       of rows on file
+   function odb_count(filename) bind(C, name="odb_count")
+     use, intrinsic                       :: iso_c_binding
+     character(kind=C_CHAR),dimension(*)  :: filename
+     real(kind=C_DOUBLE)                  :: odb_count
+   end function odb_count
+
+   function odb_select_new(config, err) bind(C, name = "odb_select_create")
+     use, intrinsic                       :: iso_c_binding
+     character(kind=C_CHAR),dimension(*)  :: config
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_select_new
+   end function odb_select_new
+
+   function odb_select_delete(odb) bind(C, name = "odb_select_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb
+     integer(kind=C_INT)                  :: odb_select_delete
+   end function odb_select_delete
+
+   function odb_read_new(config, err) bind(C, name = "odb_read_create")
+     use, intrinsic                       :: iso_c_binding
+     character(kind=C_CHAR),dimension(*)  :: config
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_read_new
+   end function odb_read_new
+
+   function odb_read_delete(odb) bind(C, name = "odb_read_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb
+     integer(kind=C_INT)                  :: odb_read_delete
+   end function odb_read_delete
+
+!> Create new read iterator.
+   function odb_read_iterator_new(odb, filename, err) bind(C, name="odb_create_read_iterator")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb
+     character(kind=C_CHAR),dimension(*)  :: filename
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_read_iterator_new
+   end function odb_read_iterator_new
+
+   function odb_read_iterator_delete(odb_iterator) bind(C, name="odb_read_iterator_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT)                  :: odb_read_iterator_delete
+   end function odb_read_iterator_delete
+
+   function odb_read_get_no_of_columns(odb_iterator, ncols) bind(C, name="odb_read_iterator_get_no_of_columns")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb_iterator
+     integer(kind=C_INT)                  :: ncols
+     integer(kind=C_INT)                  :: odb_read_get_no_of_columns ! return an error code
+   end function odb_read_get_no_of_columns
+
+   function odb_read_get_column_type(odb_iterator, n, type) bind(C, name="odb_read_iterator_get_column_type")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     integer(kind=C_INT)                  :: type
+     integer(kind=C_INT)                  :: odb_read_get_column_type
+   end function odb_read_get_column_type
+
+   function odb_read_get_column_name(odb_iterator, n, colname, nchar) bind(C, name="odb_read_iterator_get_column_name")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: n
+     integer(kind=C_INT)                  :: nchar
+     type(C_PTR)                          :: colname
+     integer(kind=C_INT)                  :: odb_read_get_column_name
+   end function odb_read_get_column_name
+
+   function odb_read_get_bitfield(odb_iterator, n, bitfield_names, bitfield_sizes, bitfield_names_size, bitfield_sizes_size) &
+   bind(C, name="odb_read_iterator_get_bitfield")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: n
+     type(C_PTR)                          :: bitfield_names
+     type(C_PTR)                          :: bitfield_sizes
+     integer(kind=C_INT)                  :: bitfield_names_size
+     integer(kind=C_INT)                  :: bitfield_sizes_size
+     integer(kind=C_INT)                  :: odb_read_get_bitfield
+   end function odb_read_get_bitfield
+
+   function odb_read_get_next_row(odb_iterator, count, data, new_dataset) bind(C, name="odb_read_iterator_get_next_row")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: count
+     integer(kind=C_INT)                  :: new_dataset
+     real(kind=C_DOUBLE),dimension(*)     :: data
+     integer(kind=C_INT)                  :: odb_read_get_next_row
+   end function odb_read_get_next_row
+
+! odb_read_iterator_get_missing_value(oda_read_iterator_ptr ri, int index, double* value)
+   function odb_read_get_missing_value(odb_iterator, n, v) &
+   bind(C, name="odb_read_iterator_get_missing_value")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     real(kind=C_DOUBLE)                  :: v
+     integer(kind=C_INT)                  :: odb_read_get_missing_value
+   end function odb_read_get_missing_value
+
+  
+! SELECT ITERATOR
+   function odb_select_iterator_new(odb, sql, err) bind(C, name="odb_create_select_iterator")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb
+     character(kind=C_CHAR),dimension(*)  :: sql
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_select_iterator_new
+   end function odb_select_iterator_new
+
+   function odb_select_iterator_new_from_file(odb, sql, filename, err) bind(C, name="odb_create_select_iterator_from_file")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb
+     character(kind=C_CHAR),dimension(*)  :: sql, filename
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_select_iterator_new_from_file
+   end function odb_select_iterator_new_from_file
+
+   function odb_select_iterator_delete(odb_iterator) bind(C, name="odb_select_iterator_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT)                  :: odb_select_iterator_delete
+   end function odb_select_iterator_delete
+
+   function odb_select_get_no_of_columns(odb_iterator, ncols) bind(C, name="odb_select_iterator_get_no_of_columns")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb_iterator
+     integer(kind=C_INT)                  :: ncols
+     integer(kind=C_INT)                  :: odb_select_get_no_of_columns ! return an error code
+   end function odb_select_get_no_of_columns
+
+   function odb_select_get_column_type(odb_iterator, n, type) bind(C, name="odb_select_iterator_get_column_type")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     integer(kind=C_INT)                  :: type
+     integer(kind=C_INT)                  :: odb_select_get_column_type
+   end function odb_select_get_column_type
+
+   function odb_select_get_column_name(odb_iterator, n, colname, nchar) bind(C, name="odb_select_iterator_get_column_name")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: n
+     type(C_PTR)                          :: colname
+     integer(kind=C_INT)                  :: nchar
+     integer(kind=C_INT)                  :: odb_select_get_column_name
+   end function odb_select_get_column_name
+
+   function odb_select_get_bitfield(odb_iterator, n, bitfield_names, bitfield_sizes, bitfield_names_size, bitfield_sizes_size) &
+   bind(C, name="odb_select_iterator_get_bitfield")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: n
+     type(C_PTR)                          :: bitfield_names
+     type(C_PTR)                          :: bitfield_sizes
+     integer(kind=C_INT)                  :: bitfield_names_size
+     integer(kind=C_INT)                  :: bitfield_sizes_size
+     integer(kind=C_INT)                  :: odb_select_get_bitfield
+   end function odb_select_get_bitfield
+
+   function odb_select_get_next_row(odb_iterator, count, data, new_dataset) bind(C, name="odb_select_iterator_get_next_row")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: count
+     integer(kind=C_INT)                  :: new_dataset
+     real(kind=C_DOUBLE),dimension(*)     :: data
+     integer(kind=C_INT)                  :: odb_select_get_next_row
+   end function odb_select_get_next_row
+  
+! WRITE
+   function odb_write_new(config, err) bind(C, name = "odb_writer_create")
+     use, intrinsic                       :: iso_c_binding
+     character(kind=C_CHAR),dimension(*)  :: config
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_write_new
+   end function odb_write_new
+
+   function odb_write_delete(odb) bind(C, name = "odb_writer_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb
+     integer(kind=C_INT)                  :: odb_write_delete
+   end function odb_write_delete
+
+! WRITE iterator
+   function odb_write_iterator_new(odb, filename, err) bind(C, name="odb_create_write_iterator")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb
+     character(kind=C_CHAR),dimension(*)  :: filename
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_write_iterator_new
+   end function odb_write_iterator_new
+
+   function odb_append_iterator_new(odb, filename, err) bind(C, name="odb_create_append_iterator")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb
+     character(kind=C_CHAR),dimension(*)  :: filename
+     integer(kind=C_INT)                  :: err
+     type(C_PTR)                          :: odb_append_iterator_new
+   end function odb_append_iterator_new
+
+   function odb_write_iterator_delete(odb_iterator) bind(C, name="odb_write_iterator_destroy")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT)                  :: odb_write_iterator_delete
+   end function odb_write_iterator_delete
+
+   function odb_write_set_no_of_columns(odb_iterator, ncols) bind(C, name="odb_write_iterator_set_no_of_columns")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR),VALUE                    :: odb_iterator
+     integer(kind=C_INT), VALUE           :: ncols
+     integer(kind=C_INT)                  :: odb_write_set_no_of_columns ! return an error code
+   end function odb_write_set_no_of_columns
+
+   function odb_write_set_column(odb_iterator, n, type, colname) bind(C, name="odb_write_iterator_set_column")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     integer(kind=C_INT),VALUE            :: type
+     character(kind=C_CHAR),dimension(*)  :: colname
+     integer(kind=C_INT)                  :: odb_write_set_column
+   end function odb_write_set_column
+
+   function odb_write_set_bitfield(odb_iterator, n, type, colname, bitfield_names, bitfield_sizes) &
+ bind(C, name="odb_write_iterator_set_bitfield")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     integer(kind=C_INT),VALUE            :: type
+     character(kind=C_CHAR),dimension(*)  :: colname
+     character(kind=C_CHAR),dimension(*)  :: bitfield_names, bitfield_sizes
+     integer(kind=C_INT)                  :: odb_write_set_bitfield
+   end function odb_write_set_bitfield
+
+   function odb_write_set_missing_value(odb_iterator, n, v) bind(C, name="odb_write_iterator_set_missing_value")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT),VALUE            :: n
+     real(kind=C_DOUBLE),VALUE            :: v
+     integer(kind=C_INT)                  :: odb_write_set_missing_value
+   end function odb_write_set_missing_value
+
+   function odb_write_set_next_row(odb_iterator, data, count) bind(C, name="odb_write_iterator_set_next_row")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT), VALUE           :: count
+     real(kind=C_DOUBLE),dimension(*)     :: data
+     integer(kind=C_INT)                  :: odb_write_set_next_row
+   end function odb_write_set_next_row
+
+   function odb_write_header(odb_iterator) bind(C, name="odb_write_iterator_write_header")
+     use, intrinsic                       :: iso_c_binding
+     type(C_PTR), VALUE                   :: odb_iterator
+     integer(kind=C_INT)                  :: odb_write_header
+   end function odb_write_header
+
+end interface
+end module odb_c_binding
diff --git a/odb_api/src/fortran/odbapi.fortran.doxyfile b/odb_api/src/fortran/odbapi.fortran.doxyfile
new file mode 100644
index 0000000..cb01ec5
--- /dev/null
+++ b/odb_api/src/fortran/odbapi.fortran.doxyfile
@@ -0,0 +1,1295 @@
+# Doxyfile 1.5.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that 
+# follow. The default is UTF-8 which is also the encoding used for all text before 
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into 
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of 
+# possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "ODB API Fortran bindings"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = ./dox
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, 
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, 
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, 
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = .
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_FOR_FORTRAN   = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 
+# include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted 
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}', 
+# where file will be replaced with the base name of the file that contains the anonymous 
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = .
+
+# This tag can be used to specify the character encoding of the source files that 
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default 
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. 
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the output. 
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, 
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH 
+# then you must also enable this option. If you don't then doxygen will produce 
+# a warning and turn it on anyway
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = NO
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to 
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to 
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to 
+# be found in the default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
+# generate a caller dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable caller graphs for selected 
+# functions only using the \callergraph command.
+
+CALLER_GRAPH           = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the number 
+# of direct children of the root node in a graph is already larger than 
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 90
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/odb_api/src/fortran/odbql_binding.f90 b/odb_api/src/fortran/odbql_binding.f90
new file mode 100644
index 0000000..cbdfe7a
--- /dev/null
+++ b/odb_api/src/fortran/odbql_binding.f90
@@ -0,0 +1,229 @@
+
+
+!!!!! THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY !!!!!
+!!    See fwrap.py
+
+module odbql_binding
+  use iso_c_binding
+  use, intrinsic :: iso_c_binding
+  implicit none
+interface
+
+!> const char * odbql_errmsg(odbql* db)
+
+    function odbql_errmsg_c (db) bind(C, name="odbql_errmsg")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: db
+     type(C_PTR)                                :: odbql_errmsg_c
+    end function odbql_errmsg_c
+
+
+    
+
+!> const char * odbql_libversion(void)
+
+    function odbql_libversion_c () bind(C, name="odbql_libversion")
+     use, intrinsic                             :: iso_c_binding
+     
+     type(C_PTR)                                :: odbql_libversion_c
+    end function odbql_libversion_c
+
+
+    
+
+!> error_code_t odbql_open(const char *filename, odbql **ppDb)
+
+    function odbql_open_c (filename,ppDb) bind(C, name="odbql_open")
+     use, intrinsic                             :: iso_c_binding
+     character(kind=C_CHAR), dimension(*)       :: filename
+     type(C_PTR)                                :: ppDb
+     integer(kind=C_INT)                        :: odbql_open_c
+    end function odbql_open_c
+
+
+    
+
+!> error_code_t odbql_close(odbql* db)
+
+    function odbql_close_c (db) bind(C, name="odbql_close")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: db
+     integer(kind=C_INT)                        :: odbql_close_c
+    end function odbql_close_c
+
+
+    
+
+!> error_code_t odbql_prepare_v2(odbql *db, const char *zSql, int nByte, odbql_stmt **ppStmt, const char **pzTail)
+
+    function odbql_prepare_v2_c (db,zSql,nByte,ppStmt,pzTail) bind(C, name="odbql_prepare_v2")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: db
+     character(kind=C_CHAR), dimension(*)       :: zSql
+     integer(kind=C_INT), value                 :: nByte
+     type(C_PTR)                                :: ppStmt
+     character(kind=C_CHAR), dimension(*)       :: pzTail
+     integer(kind=C_INT)                        :: odbql_prepare_v2_c
+    end function odbql_prepare_v2_c
+
+
+    
+
+!> error_code_t odbql_step(odbql_stmt* stmt)
+
+    function odbql_step_c (stmt) bind(C, name="odbql_step")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT)                        :: odbql_step_c
+    end function odbql_step_c
+
+
+    
+
+!> error_code_t odbql_bind_double(odbql_stmt* stmt, int iCol, double v)
+
+    function odbql_bind_double_c (stmt,iCol,v) bind(C, name="odbql_bind_double")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     real(kind=C_DOUBLE), value                 :: v
+     integer(kind=C_INT)                        :: odbql_bind_double_c
+    end function odbql_bind_double_c
+
+
+    
+
+!> error_code_t odbql_bind_int(odbql_stmt* stmt, int iCol, int v)
+
+    function odbql_bind_int_c (stmt,iCol,v) bind(C, name="odbql_bind_int")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT), value                 :: v
+     integer(kind=C_INT)                        :: odbql_bind_int_c
+    end function odbql_bind_int_c
+
+
+    
+
+!> error_code_t odbql_bind_null(odbql_stmt* stmt, int iCol)
+
+    function odbql_bind_null_c (stmt,iCol) bind(C, name="odbql_bind_null")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT)                        :: odbql_bind_null_c
+    end function odbql_bind_null_c
+
+
+    
+
+!> error_code_t odbql_bind_text(odbql_stmt* stmt, int iCol, const char* s, int n, void(*d)(void*))
+
+    function odbql_bind_text_c (stmt,iCol,s,n,d) bind(C, name="odbql_bind_text")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     character(kind=C_CHAR), dimension(*)       :: s
+     integer(kind=C_INT), value                 :: n
+     type(C_PTR), VALUE                         :: d
+     integer(kind=C_INT)                        :: odbql_bind_text_c
+    end function odbql_bind_text_c
+
+
+    
+
+!> const unsigned char *odbql_column_text(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_text_c (stmt,iCol) bind(C, name="odbql_column_text")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     type(C_PTR)                                :: odbql_column_text_c
+    end function odbql_column_text_c
+
+
+    
+
+!> error_code_t odbql_finalize(odbql_stmt *stmt)
+
+    function odbql_finalize_c (stmt) bind(C, name="odbql_finalize")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT)                        :: odbql_finalize_c
+    end function odbql_finalize_c
+
+
+    
+
+!> const char *odbql_column_name(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_name_c (stmt,iCol) bind(C, name="odbql_column_name")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     type(C_PTR)                                :: odbql_column_name_c
+    end function odbql_column_name_c
+
+
+    
+
+!> int odbql_column_type(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_type_c (stmt,iCol) bind(C, name="odbql_column_type")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT)                        :: odbql_column_type_c
+    end function odbql_column_type_c
+
+
+    
+
+!> odbql_value *odbql_column_value(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_value_c (stmt,iCol) bind(C, name="odbql_column_value")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     type(C_PTR)                                :: odbql_column_value_c
+    end function odbql_column_value_c
+
+
+    
+
+!> int odbql_column_count(odbql_stmt *stmt)
+
+    function odbql_column_count_c (stmt) bind(C, name="odbql_column_count")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: stmt
+     integer(kind=C_INT)                        :: odbql_column_count_c
+    end function odbql_column_count_c
+
+
+    
+
+!> double odbql_value_double(odbql_value* vp)
+
+    function odbql_value_double_c (vp) bind(C, name="odbql_value_double")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: vp
+     real(kind=C_DOUBLE)                        :: odbql_value_double_c
+    end function odbql_value_double_c
+
+
+    
+
+!> int odbql_value_int(odbql_value* vp)
+
+    function odbql_value_int_c (vp) bind(C, name="odbql_value_int")
+     use, intrinsic                             :: iso_c_binding
+     type(C_PTR), VALUE                         :: vp
+     integer(kind=C_INT)                        :: odbql_value_int_c
+    end function odbql_value_int_c
+
+
+    
+
+end interface
+end module odbql_binding
diff --git a/odb_api/src/fortran/odbql_constants.f90 b/odb_api/src/fortran/odbql_constants.f90
new file mode 100644
index 0000000..7b66d56
--- /dev/null
+++ b/odb_api/src/fortran/odbql_constants.f90
@@ -0,0 +1,366 @@
+
+module odbql_constants
+  implicit none
+
+character(len=*), parameter :: ODBQL_VERSION = "3.13.0" ! 
+integer, parameter :: ODBQL_VERSION_NUMBER = 3013000 ! 
+character(len=*), parameter :: ODBQL_SOURCE_ID = "2016-05-18 10:57:30 fc49f556e48970561d7ab6a2f24fdd7d9eb81ff2" ! 
+integer, parameter :: ODBQL_OK = 0 !  Successful result 
+integer, parameter :: ODBQL_ERROR = 1 !  SQL error or missing database 
+integer, parameter :: ODBQL_INTERNAL = 2 !  Internal logic error in SQLite 
+integer, parameter :: ODBQL_PERM = 3 !  Access permission denied 
+integer, parameter :: ODBQL_ABORT = 4 !  Callback routine requested an abort 
+integer, parameter :: ODBQL_BUSY = 5 !  The database file is locked 
+integer, parameter :: ODBQL_LOCKED = 6 !  A table in the database is locked 
+integer, parameter :: ODBQL_NOMEM = 7 !  A malloc() failed 
+integer, parameter :: ODBQL_READONLY = 8 !  Attempt to write a readonly database 
+integer, parameter :: ODBQL_INTERRUPT = 9 !  Operation terminated by odbql_interrupt()
+integer, parameter :: ODBQL_IOERR = 10 !  Some kind of disk I/O error occurred 
+integer, parameter :: ODBQL_CORRUPT = 11 !  The database disk image is malformed 
+integer, parameter :: ODBQL_NOTFOUND = 12 !  Unknown opcode in odbql_file_control() 
+integer, parameter :: ODBQL_FULL = 13 !  Insertion failed because database is full 
+integer, parameter :: ODBQL_CANTOPEN = 14 !  Unable to open the database file 
+integer, parameter :: ODBQL_PROTOCOL = 15 !  Database lock protocol error 
+integer, parameter :: ODBQL_EMPTY = 16 !  Database is empty 
+integer, parameter :: ODBQL_SCHEMA = 17 !  The database schema changed 
+integer, parameter :: ODBQL_TOOBIG = 18 !  String or BLOB exceeds size limit 
+integer, parameter :: ODBQL_CONSTRAINT = 19 !  Abort due to constraint violation 
+integer, parameter :: ODBQL_MISMATCH = 20 !  Data type mismatch 
+integer, parameter :: ODBQL_MISUSE = 21 !  Library used incorrectly 
+integer, parameter :: ODBQL_NOLFS = 22 !  Uses OS features not supported on host 
+integer, parameter :: ODBQL_AUTH = 23 !  Authorization denied 
+integer, parameter :: ODBQL_FORMAT = 24 !  Auxiliary database format error 
+integer, parameter :: ODBQL_RANGE = 25 !  2nd parameter to odbql_bind out of range 
+integer, parameter :: ODBQL_NOTADB = 26 !  File opened that is not a database file 
+integer, parameter :: ODBQL_NOTICE = 27 !  Notifications from odbql_log() 
+integer, parameter :: ODBQL_WARNING = 28 !  Warnings from odbql_log() 
+integer, parameter :: ODBQL_ROW = 100 !  odbql_step() has another row ready 
+integer, parameter :: ODBQL_DONE = 101 !  odbql_step() has finished executing 
+integer, parameter :: ODBQL_METADATA_CHANGED = 102 !  odbql_step(): metadata has changed 
+integer, parameter :: ODBQL_IOERR_READ = IOR(ODBQL_IOERR, LSHIFT(1,8)) ! (ODBQL_IOERR | (1<<8))  [266]
+integer, parameter :: ODBQL_IOERR_SHORT_READ = IOR(ODBQL_IOERR, LSHIFT(2,8)) ! (ODBQL_IOERR | (2<<8))  [522]
+integer, parameter :: ODBQL_IOERR_WRITE = IOR(ODBQL_IOERR, LSHIFT(3,8)) ! (ODBQL_IOERR | (3<<8))  [778]
+integer, parameter :: ODBQL_IOERR_FSYNC = IOR(ODBQL_IOERR, LSHIFT(4,8)) ! (ODBQL_IOERR | (4<<8))  [1034]
+integer, parameter :: ODBQL_IOERR_DIR_FSYNC = IOR(ODBQL_IOERR, LSHIFT(5,8)) ! (ODBQL_IOERR | (5<<8))  [1290]
+integer, parameter :: ODBQL_IOERR_TRUNCATE = IOR(ODBQL_IOERR, LSHIFT(6,8)) ! (ODBQL_IOERR | (6<<8))  [1546]
+integer, parameter :: ODBQL_IOERR_FSTAT = IOR(ODBQL_IOERR, LSHIFT(7,8)) ! (ODBQL_IOERR | (7<<8))  [1802]
+integer, parameter :: ODBQL_IOERR_UNLOCK = IOR(ODBQL_IOERR, LSHIFT(8,8)) ! (ODBQL_IOERR | (8<<8))  [2058]
+integer, parameter :: ODBQL_IOERR_RDLOCK = IOR(ODBQL_IOERR, LSHIFT(9,8)) ! (ODBQL_IOERR | (9<<8))  [2314]
+integer, parameter :: ODBQL_IOERR_DELETE = IOR(ODBQL_IOERR, LSHIFT(10,8)) ! (ODBQL_IOERR | (10<<8))  [2570]
+integer, parameter :: ODBQL_IOERR_BLOCKED = IOR(ODBQL_IOERR, LSHIFT(11,8)) ! (ODBQL_IOERR | (11<<8))  [2826]
+integer, parameter :: ODBQL_IOERR_NOMEM = IOR(ODBQL_IOERR, LSHIFT(12,8)) ! (ODBQL_IOERR | (12<<8))  [3082]
+integer, parameter :: ODBQL_IOERR_ACCESS = IOR(ODBQL_IOERR, LSHIFT(13,8)) ! (ODBQL_IOERR | (13<<8))  [3338]
+integer, parameter :: ODBQL_IOERR_CHECKRESERVEDLOCK = IOR(ODBQL_IOERR, LSHIFT(14,8)) ! (ODBQL_IOERR | (14<<8))  [3594]
+integer, parameter :: ODBQL_IOERR_LOCK = IOR(ODBQL_IOERR, LSHIFT(15,8)) ! (ODBQL_IOERR | (15<<8))  [3850]
+integer, parameter :: ODBQL_IOERR_CLOSE = IOR(ODBQL_IOERR, LSHIFT(16,8)) ! (ODBQL_IOERR | (16<<8))  [4106]
+integer, parameter :: ODBQL_IOERR_DIR_CLOSE = IOR(ODBQL_IOERR, LSHIFT(17,8)) ! (ODBQL_IOERR | (17<<8))  [4362]
+integer, parameter :: ODBQL_IOERR_SHMOPEN = IOR(ODBQL_IOERR, LSHIFT(18,8)) ! (ODBQL_IOERR | (18<<8))  [4618]
+integer, parameter :: ODBQL_IOERR_SHMSIZE = IOR(ODBQL_IOERR, LSHIFT(19,8)) ! (ODBQL_IOERR | (19<<8))  [4874]
+integer, parameter :: ODBQL_IOERR_SHMLOCK = IOR(ODBQL_IOERR, LSHIFT(20,8)) ! (ODBQL_IOERR | (20<<8))  [5130]
+integer, parameter :: ODBQL_IOERR_SHMMAP = IOR(ODBQL_IOERR, LSHIFT(21,8)) ! (ODBQL_IOERR | (21<<8))  [5386]
+integer, parameter :: ODBQL_IOERR_SEEK = IOR(ODBQL_IOERR, LSHIFT(22,8)) ! (ODBQL_IOERR | (22<<8))  [5642]
+integer, parameter :: ODBQL_IOERR_DELETE_NOENT = IOR(ODBQL_IOERR, LSHIFT(23,8)) ! (ODBQL_IOERR | (23<<8))  [5898]
+integer, parameter :: ODBQL_IOERR_MMAP = IOR(ODBQL_IOERR, LSHIFT(24,8)) ! (ODBQL_IOERR | (24<<8))  [6154]
+integer, parameter :: ODBQL_IOERR_GETTEMPPATH = IOR(ODBQL_IOERR, LSHIFT(25,8)) ! (ODBQL_IOERR | (25<<8))  [6410]
+integer, parameter :: ODBQL_IOERR_CONVPATH = IOR(ODBQL_IOERR, LSHIFT(26,8)) ! (ODBQL_IOERR | (26<<8))  [6666]
+integer, parameter :: ODBQL_IOERR_VNODE = IOR(ODBQL_IOERR, LSHIFT(27,8)) ! (ODBQL_IOERR | (27<<8))  [6922]
+integer, parameter :: ODBQL_IOERR_AUTH = IOR(ODBQL_IOERR, LSHIFT(28,8)) ! (ODBQL_IOERR | (28<<8))  [7178]
+integer, parameter :: ODBQL_LOCKED_SHAREDCACHE = IOR(ODBQL_LOCKED, LSHIFT(1,8)) ! (ODBQL_LOCKED |  (1<<8))  [262]
+integer, parameter :: ODBQL_BUSY_RECOVERY = IOR(ODBQL_BUSY, LSHIFT(1,8)) ! (ODBQL_BUSY   |  (1<<8))  [261]
+integer, parameter :: ODBQL_BUSY_SNAPSHOT = IOR(ODBQL_BUSY, LSHIFT(2,8)) ! (ODBQL_BUSY   |  (2<<8))  [517]
+integer, parameter :: ODBQL_CANTOPEN_NOTEMPDIR = IOR(ODBQL_CANTOPEN, LSHIFT(1,8)) ! (ODBQL_CANTOPEN | (1<<8))  [270]
+integer, parameter :: ODBQL_CANTOPEN_ISDIR = IOR(ODBQL_CANTOPEN, LSHIFT(2,8)) ! (ODBQL_CANTOPEN | (2<<8))  [526]
+integer, parameter :: ODBQL_CANTOPEN_FULLPATH = IOR(ODBQL_CANTOPEN, LSHIFT(3,8)) ! (ODBQL_CANTOPEN | (3<<8))  [782]
+integer, parameter :: ODBQL_CANTOPEN_CONVPATH = IOR(ODBQL_CANTOPEN, LSHIFT(4,8)) ! (ODBQL_CANTOPEN | (4<<8))  [1038]
+integer, parameter :: ODBQL_CORRUPT_VTAB = IOR(ODBQL_CORRUPT, LSHIFT(1,8)) ! (ODBQL_CORRUPT | (1<<8))  [267]
+integer, parameter :: ODBQL_READONLY_RECOVERY = IOR(ODBQL_READONLY, LSHIFT(1,8)) ! (ODBQL_READONLY | (1<<8))  [264]
+integer, parameter :: ODBQL_READONLY_CANTLOCK = IOR(ODBQL_READONLY, LSHIFT(2,8)) ! (ODBQL_READONLY | (2<<8))  [520]
+integer, parameter :: ODBQL_READONLY_ROLLBACK = IOR(ODBQL_READONLY, LSHIFT(3,8)) ! (ODBQL_READONLY | (3<<8))  [776]
+integer, parameter :: ODBQL_READONLY_DBMOVED = IOR(ODBQL_READONLY, LSHIFT(4,8)) ! (ODBQL_READONLY | (4<<8))  [1032]
+integer, parameter :: ODBQL_ABORT_ROLLBACK = IOR(ODBQL_ABORT, LSHIFT(2,8)) ! (ODBQL_ABORT | (2<<8))  [516]
+integer, parameter :: ODBQL_CONSTRAINT_CHECK = IOR(ODBQL_CONSTRAINT, LSHIFT(1,8)) ! (ODBQL_CONSTRAINT | (1<<8))  [275]
+integer, parameter :: ODBQL_CONSTRAINT_COMMITHOOK = IOR(ODBQL_CONSTRAINT, LSHIFT(2,8)) ! (ODBQL_CONSTRAINT | (2<<8))  [531]
+integer, parameter :: ODBQL_CONSTRAINT_FOREIGNKEY = IOR(ODBQL_CONSTRAINT, LSHIFT(3,8)) ! (ODBQL_CONSTRAINT | (3<<8))  [787]
+integer, parameter :: ODBQL_CONSTRAINT_FUNCTION = IOR(ODBQL_CONSTRAINT, LSHIFT(4,8)) ! (ODBQL_CONSTRAINT | (4<<8))  [1043]
+integer, parameter :: ODBQL_CONSTRAINT_NOTNULL = IOR(ODBQL_CONSTRAINT, LSHIFT(5,8)) ! (ODBQL_CONSTRAINT | (5<<8))  [1299]
+integer, parameter :: ODBQL_CONSTRAINT_PRIMARYKEY = IOR(ODBQL_CONSTRAINT, LSHIFT(6,8)) ! (ODBQL_CONSTRAINT | (6<<8))  [1555]
+integer, parameter :: ODBQL_CONSTRAINT_TRIGGER = IOR(ODBQL_CONSTRAINT, LSHIFT(7,8)) ! (ODBQL_CONSTRAINT | (7<<8))  [1811]
+integer, parameter :: ODBQL_CONSTRAINT_UNIQUE = IOR(ODBQL_CONSTRAINT, LSHIFT(8,8)) ! (ODBQL_CONSTRAINT | (8<<8))  [2067]
+integer, parameter :: ODBQL_CONSTRAINT_VTAB = IOR(ODBQL_CONSTRAINT, LSHIFT(9,8)) ! (ODBQL_CONSTRAINT | (9<<8))  [2323]
+integer, parameter :: ODBQL_CONSTRAINT_ROWID = IOR(ODBQL_CONSTRAINT, LSHIFT(10,8)) ! (ODBQL_CONSTRAINT |(10<<8))  [2579]
+integer, parameter :: ODBQL_NOTICE_RECOVER_WAL = IOR(ODBQL_NOTICE, LSHIFT(1,8)) ! (ODBQL_NOTICE | (1<<8))  [283]
+integer, parameter :: ODBQL_NOTICE_RECOVER_ROLLBACK = IOR(ODBQL_NOTICE, LSHIFT(2,8)) ! (ODBQL_NOTICE | (2<<8))  [539]
+integer, parameter :: ODBQL_WARNING_AUTOINDEX = IOR(ODBQL_WARNING, LSHIFT(1,8)) ! (ODBQL_WARNING | (1<<8))  [284]
+integer, parameter :: ODBQL_AUTH_USER = IOR(ODBQL_AUTH, LSHIFT(1,8)) ! (ODBQL_AUTH | (1<<8))  [279]
+integer, parameter :: ODBQL_OPEN_READONLY = 1 ! 0x00000001  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_READWRITE = 2 ! 0x00000002  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_CREATE = 4 ! 0x00000004  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_DELETEONCLOSE = 8 ! 0x00000008  VFS only 
+integer, parameter :: ODBQL_OPEN_EXCLUSIVE = 16 ! 0x00000010  VFS only 
+integer, parameter :: ODBQL_OPEN_AUTOPROXY = 32 ! 0x00000020  VFS only 
+integer, parameter :: ODBQL_OPEN_URI = 64 ! 0x00000040  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_MEMORY = 128 ! 0x00000080  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_MAIN_DB = 256 ! 0x00000100  VFS only 
+integer, parameter :: ODBQL_OPEN_TEMP_DB = 512 ! 0x00000200  VFS only 
+integer, parameter :: ODBQL_OPEN_TRANSIENT_DB = 1024 ! 0x00000400  VFS only 
+integer, parameter :: ODBQL_OPEN_MAIN_JOURNAL = 2048 ! 0x00000800  VFS only 
+integer, parameter :: ODBQL_OPEN_TEMP_JOURNAL = 4096 ! 0x00001000  VFS only 
+integer, parameter :: ODBQL_OPEN_SUBJOURNAL = 8192 ! 0x00002000  VFS only 
+integer, parameter :: ODBQL_OPEN_MASTER_JOURNAL = 16384 ! 0x00004000  VFS only 
+integer, parameter :: ODBQL_OPEN_NOMUTEX = 32768 ! 0x00008000  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_FULLMUTEX = 65536 ! 0x00010000  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_SHAREDCACHE = 131072 ! 0x00020000  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_PRIVATECACHE = 262144 ! 0x00040000  Ok for odbql_open_v2() 
+integer, parameter :: ODBQL_OPEN_WAL = 524288 ! 0x00080000  VFS only 
+integer, parameter :: ODBQL_IOCAP_ATOMIC = 1 ! 0x00000001 
+integer, parameter :: ODBQL_IOCAP_ATOMIC512 = 2 ! 0x00000002 
+integer, parameter :: ODBQL_IOCAP_ATOMIC1K = 4 ! 0x00000004 
+integer, parameter :: ODBQL_IOCAP_ATOMIC2K = 8 ! 0x00000008 
+integer, parameter :: ODBQL_IOCAP_ATOMIC4K = 16 ! 0x00000010 
+integer, parameter :: ODBQL_IOCAP_ATOMIC8K = 32 ! 0x00000020 
+integer, parameter :: ODBQL_IOCAP_ATOMIC16K = 64 ! 0x00000040 
+integer, parameter :: ODBQL_IOCAP_ATOMIC32K = 128 ! 0x00000080 
+integer, parameter :: ODBQL_IOCAP_ATOMIC64K = 256 ! 0x00000100 
+integer, parameter :: ODBQL_IOCAP_SAFE_APPEND = 512 ! 0x00000200 
+integer, parameter :: ODBQL_IOCAP_SEQUENTIAL = 1024 ! 0x00000400 
+integer, parameter :: ODBQL_IOCAP_UNDELETABLE_WHEN_OPEN = 2048 ! 0x00000800 
+integer, parameter :: ODBQL_IOCAP_POWERSAFE_OVERWRITE = 4096 ! 0x00001000 
+integer, parameter :: ODBQL_IOCAP_IMMUTABLE = 8192 ! 0x00002000 
+integer, parameter :: ODBQL_LOCK_NONE = 0 ! 
+integer, parameter :: ODBQL_LOCK_SHARED = 1 ! 
+integer, parameter :: ODBQL_LOCK_RESERVED = 2 ! 
+integer, parameter :: ODBQL_LOCK_PENDING = 3 ! 
+integer, parameter :: ODBQL_LOCK_EXCLUSIVE = 4 ! 
+integer, parameter :: ODBQL_SYNC_NORMAL = 2 ! 0x00002 
+integer, parameter :: ODBQL_SYNC_FULL = 3 ! 0x00003 
+integer, parameter :: ODBQL_SYNC_DATAONLY = 16 ! 0x00010 
+integer, parameter :: ODBQL_FCNTL_LOCKSTATE = 1 ! 
+integer, parameter :: ODBQL_FCNTL_GET_LOCKPROXYFILE = 2 ! 
+integer, parameter :: ODBQL_FCNTL_SET_LOCKPROXYFILE = 3 ! 
+integer, parameter :: ODBQL_FCNTL_LAST_ERRNO = 4 ! 
+integer, parameter :: ODBQL_FCNTL_SIZE_HINT = 5 ! 
+integer, parameter :: ODBQL_FCNTL_CHUNK_SIZE = 6 ! 
+integer, parameter :: ODBQL_FCNTL_FILE_POINTER = 7 ! 
+integer, parameter :: ODBQL_FCNTL_SYNC_OMITTED = 8 ! 
+integer, parameter :: ODBQL_FCNTL_WIN32_AV_RETRY = 9 ! 
+integer, parameter :: ODBQL_FCNTL_PERSIST_WAL = 10 ! 
+integer, parameter :: ODBQL_FCNTL_OVERWRITE = 11 ! 
+integer, parameter :: ODBQL_FCNTL_VFSNAME = 12 ! 
+integer, parameter :: ODBQL_FCNTL_POWERSAFE_OVERWRITE = 13 ! 
+integer, parameter :: ODBQL_FCNTL_PRAGMA = 14 ! 
+integer, parameter :: ODBQL_FCNTL_BUSYHANDLER = 15 ! 
+integer, parameter :: ODBQL_FCNTL_TEMPFILENAME = 16 ! 
+integer, parameter :: ODBQL_FCNTL_MMAP_SIZE = 18 ! 
+integer, parameter :: ODBQL_FCNTL_TRACE = 19 ! 
+integer, parameter :: ODBQL_FCNTL_HAS_MOVED = 20 ! 
+integer, parameter :: ODBQL_FCNTL_SYNC = 21 ! 
+integer, parameter :: ODBQL_FCNTL_COMMIT_PHASETWO = 22 ! 
+integer, parameter :: ODBQL_FCNTL_WIN32_SET_HANDLE = 23 ! 
+integer, parameter :: ODBQL_FCNTL_WAL_BLOCK = 24 ! 
+integer, parameter :: ODBQL_FCNTL_ZIPVFS = 25 ! 
+integer, parameter :: ODBQL_FCNTL_RBU = 26 ! 
+integer, parameter :: ODBQL_FCNTL_VFS_POINTER = 27 ! 
+integer, parameter :: ODBQL_FCNTL_JOURNAL_POINTER = 28 ! 
+integer, parameter :: ODBQL_GET_LOCKPROXYFILE = ODBQL_FCNTL_GET_LOCKPROXYFILE ! 
+integer, parameter :: ODBQL_SET_LOCKPROXYFILE = ODBQL_FCNTL_SET_LOCKPROXYFILE ! 
+integer, parameter :: ODBQL_LAST_ERRNO = ODBQL_FCNTL_LAST_ERRNO ! 
+integer, parameter :: ODBQL_ACCESS_EXISTS = 0 ! 
+integer, parameter :: ODBQL_ACCESS_READWRITE = 1 !  Used by PRAGMA temp_store_directory 
+integer, parameter :: ODBQL_ACCESS_READ = 2 !  Unused 
+integer, parameter :: ODBQL_SHM_UNLOCK = 1 ! 
+integer, parameter :: ODBQL_SHM_LOCK = 2 ! 
+integer, parameter :: ODBQL_SHM_SHARED = 4 ! 
+integer, parameter :: ODBQL_SHM_EXCLUSIVE = 8 ! 
+integer, parameter :: ODBQL_SHM_NLOCK = 8 ! 
+integer, parameter :: ODBQL_CONFIG_SINGLETHREAD = 1 !  nil 
+integer, parameter :: ODBQL_CONFIG_MULTITHREAD = 2 !  nil 
+integer, parameter :: ODBQL_CONFIG_SERIALIZED = 3 !  nil 
+integer, parameter :: ODBQL_CONFIG_MALLOC = 4 !  odbql_mem_methods* 
+integer, parameter :: ODBQL_CONFIG_GETMALLOC = 5 !  odbql_mem_methods* 
+integer, parameter :: ODBQL_CONFIG_SCRATCH = 6 !  void*, int sz, int N 
+integer, parameter :: ODBQL_CONFIG_PAGECACHE = 7 !  void*, int sz, int N 
+integer, parameter :: ODBQL_CONFIG_HEAP = 8 !  void*, int nByte, int min 
+integer, parameter :: ODBQL_CONFIG_MEMSTATUS = 9 !  boolean 
+integer, parameter :: ODBQL_CONFIG_MUTEX = 10 !  odbql_mutex_methods* 
+integer, parameter :: ODBQL_CONFIG_GETMUTEX = 11 !  odbql_mutex_methods* 
+integer, parameter :: ODBQL_CONFIG_LOOKASIDE = 13 !  int int 
+integer, parameter :: ODBQL_CONFIG_PCACHE = 14 !  no-op 
+integer, parameter :: ODBQL_CONFIG_GETPCACHE = 15 !  no-op 
+integer, parameter :: ODBQL_CONFIG_LOG = 16 !  xFunc, void* 
+integer, parameter :: ODBQL_CONFIG_URI = 17 !  int 
+integer, parameter :: ODBQL_CONFIG_PCACHE2 = 18 !  odbql_pcache_methods2* 
+integer, parameter :: ODBQL_CONFIG_GETPCACHE2 = 19 !  odbql_pcache_methods2* 
+integer, parameter :: ODBQL_CONFIG_COVERING_INDEX_SCAN = 20 !  int 
+integer, parameter :: ODBQL_CONFIG_SQLLOG = 21 !  xSqllog, void* 
+integer, parameter :: ODBQL_CONFIG_MMAP_SIZE = 22 !  odbql_int64, odbql_int64 
+integer, parameter :: ODBQL_CONFIG_WIN32_HEAPSIZE = 23 !  int nByte 
+integer, parameter :: ODBQL_CONFIG_PCACHE_HDRSZ = 24 !  int *psz 
+integer, parameter :: ODBQL_CONFIG_PMASZ = 25 !  unsigned int szPma 
+integer, parameter :: ODBQL_CONFIG_STMTJRNL_SPILL = 26 !  int nByte 
+integer, parameter :: ODBQL_DBCONFIG_LOOKASIDE = 1001 !  void* int int 
+integer, parameter :: ODBQL_DBCONFIG_ENABLE_FKEY = 1002 !  int int* 
+integer, parameter :: ODBQL_DBCONFIG_ENABLE_TRIGGER = 1003 !  int int* 
+integer, parameter :: ODBQL_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004 !  int int* 
+integer, parameter :: ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005 !  int int* 
+integer, parameter :: ODBQL_DENY = 1 !  Abort the SQL statement with an error 
+integer, parameter :: ODBQL_IGNORE = 2 !  Don't allow access, but don't generate an error 
+integer, parameter :: ODBQL_CREATE_INDEX = 1 !  Index Name      Table Name      
+integer, parameter :: ODBQL_CREATE_TABLE = 2 !  Table Name      NULL            
+integer, parameter :: ODBQL_CREATE_TEMP_INDEX = 3 !  Index Name      Table Name      
+integer, parameter :: ODBQL_CREATE_TEMP_TABLE = 4 !  Table Name      NULL            
+integer, parameter :: ODBQL_CREATE_TEMP_TRIGGER = 5 !  Trigger Name    Table Name      
+integer, parameter :: ODBQL_CREATE_TEMP_VIEW = 6 !  View Name       NULL            
+integer, parameter :: ODBQL_CREATE_TRIGGER = 7 !  Trigger Name    Table Name      
+integer, parameter :: ODBQL_CREATE_VIEW = 8 !  View Name       NULL            
+integer, parameter :: ODBQL_DELETE = 9 !  Table Name      NULL            
+integer, parameter :: ODBQL_DROP_INDEX = 10 !  Index Name      Table Name      
+integer, parameter :: ODBQL_DROP_TABLE = 11 !  Table Name      NULL            
+integer, parameter :: ODBQL_DROP_TEMP_INDEX = 12 !  Index Name      Table Name      
+integer, parameter :: ODBQL_DROP_TEMP_TABLE = 13 !  Table Name      NULL            
+integer, parameter :: ODBQL_DROP_TEMP_TRIGGER = 14 !  Trigger Name    Table Name      
+integer, parameter :: ODBQL_DROP_TEMP_VIEW = 15 !  View Name       NULL            
+integer, parameter :: ODBQL_DROP_TRIGGER = 16 !  Trigger Name    Table Name      
+integer, parameter :: ODBQL_DROP_VIEW = 17 !  View Name       NULL            
+integer, parameter :: ODBQL_INSERT = 18 !  Table Name      NULL            
+integer, parameter :: ODBQL_PRAGMA = 19 !  Pragma Name     1st arg or NULL 
+integer, parameter :: ODBQL_READ = 20 !  Table Name      Column Name     
+integer, parameter :: ODBQL_SELECT = 21 !  NULL            NULL            
+integer, parameter :: ODBQL_TRANSACTION = 22 !  Operation       NULL            
+integer, parameter :: ODBQL_UPDATE = 23 !  Table Name      Column Name     
+integer, parameter :: ODBQL_ATTACH = 24 !  Filename        NULL            
+integer, parameter :: ODBQL_DETACH = 25 !  Database Name   NULL            
+integer, parameter :: ODBQL_ALTER_TABLE = 26 !  Database Name   Table Name      
+integer, parameter :: ODBQL_REINDEX = 27 !  Index Name      NULL            
+integer, parameter :: ODBQL_ANALYZE = 28 !  Table Name      NULL            
+integer, parameter :: ODBQL_CREATE_VTABLE = 29 !  Table Name      Module Name     
+integer, parameter :: ODBQL_DROP_VTABLE = 30 !  Table Name      Module Name     
+integer, parameter :: ODBQL_FUNCTION = 31 !  NULL            Function Name   
+integer, parameter :: ODBQL_SAVEPOINT = 32 !  Operation       Savepoint Name  
+integer, parameter :: ODBQL_COPY = 0 !  No longer used 
+integer, parameter :: ODBQL_RECURSIVE = 33 !  NULL            NULL            
+integer, parameter :: ODBQL_LIMIT_LENGTH = 0 ! 
+integer, parameter :: ODBQL_LIMIT_SQL_LENGTH = 1 ! 
+integer, parameter :: ODBQL_LIMIT_COLUMN = 2 ! 
+integer, parameter :: ODBQL_LIMIT_EXPR_DEPTH = 3 ! 
+integer, parameter :: ODBQL_LIMIT_COMPOUND_SELECT = 4 ! 
+integer, parameter :: ODBQL_LIMIT_VDBE_OP = 5 ! 
+integer, parameter :: ODBQL_LIMIT_FUNCTION_ARG = 6 ! 
+integer, parameter :: ODBQL_LIMIT_ATTACHED = 7 ! 
+integer, parameter :: ODBQL_LIMIT_LIKE_PATTERN_LENGTH = 8 ! 
+integer, parameter :: ODBQL_LIMIT_VARIABLE_NUMBER = 9 ! 
+integer, parameter :: ODBQL_LIMIT_TRIGGER_DEPTH = 10 ! 
+integer, parameter :: ODBQL_LIMIT_WORKER_THREADS = 11 ! 
+integer, parameter :: ODBQL_INTEGER = 1 ! 
+integer, parameter :: ODBQL_FLOAT = 2 ! 
+integer, parameter :: ODBQL_BLOB = 4 ! 
+integer, parameter :: ODBQL_NULL = 5 ! 
+integer, parameter :: ODBQL_BITFIELD = 6 ! 
+integer, parameter :: ODBQL_TEXT = 3 ! 
+integer, parameter :: ODBQL_UTF8 = 1 !  IMP: R-37514-35566 
+integer, parameter :: ODBQL_UTF16LE = 2 !  IMP: R-03371-37637 
+integer, parameter :: ODBQL_UTF16BE = 3 !  IMP: R-51971-34154 
+integer, parameter :: ODBQL_UTF16 = 4 !  Use native byte order 
+integer, parameter :: ODBQL_ANY = 5 !  Deprecated 
+integer, parameter :: ODBQL_UTF16_ALIGNED = 8 !  odbql_create_collation only 
+integer, parameter :: ODBQL_DETERMINISTIC = 2048 ! 0x800 
+integer, parameter :: ODBQL_STATIC = 0  ! ((odbql_destructor_type)0)
+integer, parameter :: ODBQL_TRANSIENT = -1  ! ((odbql_destructor_type)-1)
+integer, parameter :: ODBQL_INDEX_SCAN_UNIQUE = 1 !  Scan visits at most 1 row 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_EQ = 2 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_GT = 4 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_LE = 8 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_LT = 16 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_GE = 32 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_MATCH = 64 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_LIKE = 65 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_GLOB = 66 ! 
+integer, parameter :: ODBQL_INDEX_CONSTRAINT_REGEXP = 67 ! 
+integer, parameter :: ODBQL_MUTEX_FAST = 0 ! 
+integer, parameter :: ODBQL_MUTEX_RECURSIVE = 1 ! 
+integer, parameter :: ODBQL_MUTEX_STATIC_MASTER = 2 ! 
+integer, parameter :: ODBQL_MUTEX_STATIC_MEM = 3 !  odbql_malloc() 
+integer, parameter :: ODBQL_MUTEX_STATIC_MEM2 = 4 !  NOT USED 
+integer, parameter :: ODBQL_MUTEX_STATIC_OPEN = 4 !  odbqlBtreeOpen() 
+integer, parameter :: ODBQL_MUTEX_STATIC_PRNG = 5 !  odbql_random() 
+integer, parameter :: ODBQL_MUTEX_STATIC_LRU = 6 !  lru page list 
+integer, parameter :: ODBQL_MUTEX_STATIC_LRU2 = 7 !  NOT USED 
+integer, parameter :: ODBQL_MUTEX_STATIC_PMEM = 7 !  odbqlPageMalloc() 
+integer, parameter :: ODBQL_MUTEX_STATIC_APP1 = 8 !  For use by application 
+integer, parameter :: ODBQL_MUTEX_STATIC_APP2 = 9 !  For use by application 
+integer, parameter :: ODBQL_MUTEX_STATIC_APP3 = 10 !  For use by application 
+integer, parameter :: ODBQL_MUTEX_STATIC_VFS1 = 11 !  For use by built-in VFS 
+integer, parameter :: ODBQL_MUTEX_STATIC_VFS2 = 12 !  For use by extension VFS 
+integer, parameter :: ODBQL_MUTEX_STATIC_VFS3 = 13 !  For use by application VFS 
+integer, parameter :: ODBQL_TESTCTRL_FIRST = 5 ! 
+integer, parameter :: ODBQL_TESTCTRL_PRNG_SAVE = 5 ! 
+integer, parameter :: ODBQL_TESTCTRL_PRNG_RESTORE = 6 ! 
+integer, parameter :: ODBQL_TESTCTRL_PRNG_RESET = 7 ! 
+integer, parameter :: ODBQL_TESTCTRL_BITVEC_TEST = 8 ! 
+integer, parameter :: ODBQL_TESTCTRL_FAULT_INSTALL = 9 ! 
+integer, parameter :: ODBQL_TESTCTRL_BENIGN_MALLOC_HOOKS = 10 ! 
+integer, parameter :: ODBQL_TESTCTRL_PENDING_BYTE = 11 ! 
+integer, parameter :: ODBQL_TESTCTRL_ASSERT = 12 ! 
+integer, parameter :: ODBQL_TESTCTRL_ALWAYS = 13 ! 
+integer, parameter :: ODBQL_TESTCTRL_RESERVE = 14 ! 
+integer, parameter :: ODBQL_TESTCTRL_OPTIMIZATIONS = 15 ! 
+integer, parameter :: ODBQL_TESTCTRL_ISKEYWORD = 16 ! 
+integer, parameter :: ODBQL_TESTCTRL_SCRATCHMALLOC = 17 ! 
+integer, parameter :: ODBQL_TESTCTRL_LOCALTIME_FAULT = 18 ! 
+integer, parameter :: ODBQL_TESTCTRL_EXPLAIN_STMT = 19 !  NOT USED 
+integer, parameter :: ODBQL_TESTCTRL_NEVER_CORRUPT = 20 ! 
+integer, parameter :: ODBQL_TESTCTRL_VDBE_COVERAGE = 21 ! 
+integer, parameter :: ODBQL_TESTCTRL_BYTEORDER = 22 ! 
+integer, parameter :: ODBQL_TESTCTRL_ISINIT = 23 ! 
+integer, parameter :: ODBQL_TESTCTRL_SORTER_MMAP = 24 ! 
+integer, parameter :: ODBQL_TESTCTRL_IMPOSTER = 25 ! 
+integer, parameter :: ODBQL_TESTCTRL_LAST = 25 ! 
+integer, parameter :: ODBQL_STATUS_MEMORY_USED = 0 ! 
+integer, parameter :: ODBQL_STATUS_PAGECACHE_USED = 1 ! 
+integer, parameter :: ODBQL_STATUS_PAGECACHE_OVERFLOW = 2 ! 
+integer, parameter :: ODBQL_STATUS_SCRATCH_USED = 3 ! 
+integer, parameter :: ODBQL_STATUS_SCRATCH_OVERFLOW = 4 ! 
+integer, parameter :: ODBQL_STATUS_MALLOC_SIZE = 5 ! 
+integer, parameter :: ODBQL_STATUS_PARSER_STACK = 6 ! 
+integer, parameter :: ODBQL_STATUS_PAGECACHE_SIZE = 7 ! 
+integer, parameter :: ODBQL_STATUS_SCRATCH_SIZE = 8 ! 
+integer, parameter :: ODBQL_STATUS_MALLOC_COUNT = 9 ! 
+integer, parameter :: ODBQL_DBSTATUS_LOOKASIDE_USED = 0 ! 
+integer, parameter :: ODBQL_DBSTATUS_CACHE_USED = 1 ! 
+integer, parameter :: ODBQL_DBSTATUS_SCHEMA_USED = 2 ! 
+integer, parameter :: ODBQL_DBSTATUS_STMT_USED = 3 ! 
+integer, parameter :: ODBQL_DBSTATUS_LOOKASIDE_HIT = 4 ! 
+integer, parameter :: ODBQL_DBSTATUS_LOOKASIDE_MISS_SIZE = 5 ! 
+integer, parameter :: ODBQL_DBSTATUS_LOOKASIDE_MISS_FULL = 6 ! 
+integer, parameter :: ODBQL_DBSTATUS_CACHE_HIT = 7 ! 
+integer, parameter :: ODBQL_DBSTATUS_CACHE_MISS = 8 ! 
+integer, parameter :: ODBQL_DBSTATUS_CACHE_WRITE = 9 ! 
+integer, parameter :: ODBQL_DBSTATUS_DEFERRED_FKS = 10 ! 
+integer, parameter :: ODBQL_DBSTATUS_MAX = 10 !  Largest defined DBSTATUS 
+integer, parameter :: ODBQL_STMTSTATUS_FULLSCAN_STEP = 1 ! 
+integer, parameter :: ODBQL_STMTSTATUS_SORT = 2 ! 
+integer, parameter :: ODBQL_STMTSTATUS_AUTOINDEX = 3 ! 
+integer, parameter :: ODBQL_STMTSTATUS_VM_STEP = 4 ! 
+integer, parameter :: ODBQL_CHECKPOINT_PASSIVE = 0 !  Do as much as possible w/o blocking 
+integer, parameter :: ODBQL_CHECKPOINT_FULL = 1 !  Wait for writers, then checkpoint 
+integer, parameter :: ODBQL_CHECKPOINT_RESTART = 2 !  Like FULL but wait for for readers 
+integer, parameter :: ODBQL_CHECKPOINT_TRUNCATE = 3 !  Like RESTART but also truncate WAL 
+integer, parameter :: ODBQL_VTAB_CONSTRAINT_SUPPORT = 1 ! 
+integer, parameter :: ODBQL_ROLLBACK = 1 ! 
+integer, parameter :: ODBQL_FAIL = 3 ! 
+integer, parameter :: ODBQL_REPLACE = 5 ! 
+integer, parameter :: ODBQL_SCANSTAT_NLOOP = 0 ! 
+integer, parameter :: ODBQL_SCANSTAT_NVISIT = 1 ! 
+integer, parameter :: ODBQL_SCANSTAT_EST = 2 ! 
+integer, parameter :: ODBQL_SCANSTAT_NAME = 3 ! 
+integer, parameter :: ODBQL_SCANSTAT_EXPLAIN = 4 ! 
+integer, parameter :: ODBQL_SCANSTAT_SELECTID = 5 ! 
+integer, parameter :: ODBQL_CHANGESET_DATA = 1 ! 
+integer, parameter :: ODBQL_CHANGESET_NOTFOUND = 2 ! 
+integer, parameter :: ODBQL_CHANGESET_CONFLICT = 3 ! 
+integer, parameter :: ODBQL_CHANGESET_CONSTRAINT = 4 ! 
+integer, parameter :: ODBQL_CHANGESET_FOREIGN_KEY = 5 ! 
+integer, parameter :: ODBQL_CHANGESET_OMIT = 0 ! 
+integer, parameter :: ODBQL_CHANGESET_REPLACE = 1 ! 
+integer, parameter :: ODBQL_CHANGESET_ABORT = 2 ! 
+
+end module odbql_constants
diff --git a/odb_api/src/fortran/odbql_wrappers.f90 b/odb_api/src/fortran/odbql_wrappers.f90
new file mode 100644
index 0000000..7031b8e
--- /dev/null
+++ b/odb_api/src/fortran/odbql_wrappers.f90
@@ -0,0 +1,481 @@
+
+
+!!!!! THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY !!!!!
+!!    See fwrap.py
+
+module odbql_wrappers
+  use odbql_binding
+  use odbql_constants
+  implicit none
+  
+  type odbql
+    type(c_ptr) :: this
+  end type
+
+  type odbql_stmt
+    type(c_ptr) :: this
+  end type
+
+  type odbql_value
+    type(c_ptr) :: this
+  end type
+
+contains
+
+
+
+!> Helper routine to convert C '\0' terminated strings to Fortran strings
+
+    subroutine C_to_F_string(c_string_pointer, out_string)
+
+      use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char
+
+      type(c_ptr), intent(in)                       :: c_string_pointer
+      character(len=*), intent(out)                 :: out_string
+      character(kind=c_char), dimension(:), pointer :: char_array_pointer
+      integer                                       :: i,length
+
+      char_array_pointer => null()
+      call c_f_pointer(c_string_pointer,char_array_pointer,[255])
+
+      if (.not.associated(char_array_pointer)) then
+          out_string = "NULL"
+          return
+      end if
+
+      out_string = " "
+      do i = 1, len(out_string)
+        if (char_array_pointer(i) == c_null_char) exit
+        out_string(i:i) = char_array_pointer(i)
+      end do
+
+    end subroutine
+
+
+
+
+
+!> const char * odbql_errmsg(odbql* db)
+
+    subroutine odbql_errmsg (db,return_value) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql), value                         :: db
+     character(len=*), intent(out)              :: return_value
+
+     
+
+     
+     call C_to_F_string(odbql_errmsg_c(db%this), return_value)
+     
+
+    end subroutine odbql_errmsg
+
+    
+
+!> const char * odbql_libversion(void)
+
+    subroutine odbql_libversion (return_value) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     
+     character(len=*), intent(out)              :: return_value
+
+     
+
+     
+     call C_to_F_string(odbql_libversion_c(), return_value)
+     
+
+    end subroutine odbql_libversion
+
+    
+
+!> error_code_t odbql_open(const char *filename, odbql **ppDb)
+
+    subroutine odbql_open (filename,ppDb,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     character(len=*), intent(in)               :: filename
+     type(odbql)                                :: ppDb
+     integer(kind=C_INT), intent(out), optional :: status
+
+     character(len=len_trim(filename)+1)        :: filename_tmp
+     integer(kind=c_int)                        :: rc
+
+     filename_tmp = filename//achar(0)
+     rc = odbql_open_c(filename_tmp,ppDb%this)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_open'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_open
+
+    
+
+!> error_code_t odbql_close(odbql* db)
+
+    subroutine odbql_close (db,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql), value                         :: db
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_close_c(db%this)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_close'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_close
+
+    
+
+!> error_code_t odbql_prepare_v2(odbql *db, const char *zSql, int nByte, odbql_stmt **ppStmt, const char **pzTail)
+
+    subroutine odbql_prepare_v2 (db,zSql,nByte,ppStmt,pzTail,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql), value                         :: db
+     character(len=*), intent(in)               :: zSql
+     integer(kind=C_INT), value                 :: nByte
+     type(odbql_stmt)                           :: ppStmt
+     character(len=*), intent(out)              :: pzTail
+     integer(kind=C_INT), intent(out), optional :: status
+
+     character(len=len_trim(zSql)+1)            :: zSql_tmp
+     integer(kind=c_int)                        :: rc
+
+     zSql_tmp = zSql//achar(0)
+     rc = odbql_prepare_v2_c(db%this,zSql_tmp,nByte,ppStmt%this,pzTail)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_prepare_v2'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_prepare_v2
+
+    
+
+!> error_code_t odbql_step(odbql_stmt* stmt)
+
+    subroutine odbql_step (stmt,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_step_c(stmt%this)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_step'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_step
+
+    
+
+!> error_code_t odbql_bind_double(odbql_stmt* stmt, int iCol, double v)
+
+    subroutine odbql_bind_double (stmt,iCol,v,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     real(kind=C_DOUBLE), value                 :: v
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_bind_double_c(stmt%this,iCol-1,v)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_bind_double'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_bind_double
+
+    
+
+!> error_code_t odbql_bind_int(odbql_stmt* stmt, int iCol, int v)
+
+    subroutine odbql_bind_int (stmt,iCol,v,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT), value                 :: v
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_bind_int_c(stmt%this,iCol-1,v)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_bind_int'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_bind_int
+
+    
+
+!> error_code_t odbql_bind_null(odbql_stmt* stmt, int iCol)
+
+    subroutine odbql_bind_null (stmt,iCol,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_bind_null_c(stmt%this,iCol-1)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_bind_null'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_bind_null
+
+    
+
+!> error_code_t odbql_bind_text(odbql_stmt* stmt, int iCol, const char* s, int n, void(*d)(void*))
+
+    subroutine odbql_bind_text (stmt,iCol,s,n,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     character(len=*), intent(in)               :: s
+     integer(kind=C_INT), value                 :: n
+     integer(kind=C_INT), intent(out), optional :: status
+
+     character(len=len_trim(s)+1)               :: s_tmp
+     type(C_PTR)                                :: d
+     integer(kind=c_int)                        :: rc
+
+     s_tmp = s//achar(0)
+     rc = odbql_bind_text_c(stmt%this,iCol-1,s_tmp,n,d)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_bind_text'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_bind_text
+
+    
+
+!> const unsigned char *odbql_column_text(odbql_stmt* stmt, int iCol)
+
+    subroutine odbql_column_text (stmt,iCol,return_value) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     character(len=*), intent(out)              :: return_value
+
+     
+
+     
+     call C_to_F_string(odbql_column_text_c(stmt%this,iCol-1), return_value)
+     
+
+    end subroutine odbql_column_text
+
+    
+
+!> error_code_t odbql_finalize(odbql_stmt *stmt)
+
+    subroutine odbql_finalize (stmt,status) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), intent(out), optional :: status
+
+     
+     integer(kind=c_int)                        :: rc
+
+     
+     rc = odbql_finalize_c(stmt%this)
+     
+     if (present(status)) then
+         status = rc ! let user handle the error
+     else
+         if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+             write (0,*) 'Error in odbql_finalize'
+             stop
+         end if
+     end if
+
+    end subroutine odbql_finalize
+
+    
+
+!> const char *odbql_column_name(odbql_stmt* stmt, int iCol)
+
+    subroutine odbql_column_name (stmt,iCol,return_value) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     character(len=*), intent(out)              :: return_value
+
+     
+
+     
+     call C_to_F_string(odbql_column_name_c(stmt%this,iCol-1), return_value)
+     
+
+    end subroutine odbql_column_name
+
+    
+
+!> int odbql_column_type(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_type (stmt,iCol) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     integer(kind=C_INT)                        :: odbql_column_type
+
+     
+
+     
+     odbql_column_type = odbql_column_type_c(stmt%this,iCol-1)
+     
+
+    end function odbql_column_type
+
+    
+
+!> odbql_value *odbql_column_value(odbql_stmt* stmt, int iCol)
+
+    function odbql_column_value (stmt,iCol) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT), value                 :: iCol
+     type(odbql_value)                          :: odbql_column_value
+
+     
+
+     
+     odbql_column_value%this = odbql_column_value_c(stmt%this,iCol-1)
+     
+
+    end function odbql_column_value
+
+    
+
+!> int odbql_column_count(odbql_stmt *stmt)
+
+    function odbql_column_count (stmt) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_stmt), value                    :: stmt
+     integer(kind=C_INT)                        :: odbql_column_count
+
+     
+
+     
+     odbql_column_count = odbql_column_count_c(stmt%this)
+     
+
+    end function odbql_column_count
+
+    
+
+!> double odbql_value_double(odbql_value* vp)
+
+    function odbql_value_double (vp) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_value), value                   :: vp
+     real(kind=C_DOUBLE)                        :: odbql_value_double
+
+     
+
+     
+     odbql_value_double = odbql_value_double_c(vp%this)
+     
+
+    end function odbql_value_double
+
+    
+
+!> int odbql_value_int(odbql_value* vp)
+
+    function odbql_value_int (vp) 
+     use odbql_binding
+     use, intrinsic                             :: iso_c_binding
+     type(odbql_value), value                   :: vp
+     integer(kind=C_INT)                        :: odbql_value_int
+
+     
+
+     
+     odbql_value_int = odbql_value_int_c(vp%this)
+     
+
+    end function odbql_value_int
+
+    
+
+end module odbql_wrappers
diff --git a/odb_api/src/fortran/test_regression.f90 b/odb_api/src/fortran/test_regression.f90
new file mode 100644
index 0000000..3355408
--- /dev/null
+++ b/odb_api/src/fortran/test_regression.f90
@@ -0,0 +1,85 @@
+! (C) Copyright 1996-2017 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+! Piotr Kuchta, ECMWF, April 2017
+
+
+program test_regression
+  use odbql_wrappers
+  implicit none
+  character(len=10)                            :: version
+
+  call odbql_libversion(version)
+  write(0,*) "This program is linked to ODB API version: ", version
+
+  call test_odb127()
+
+  write(0,*) "Phew, made it! All regression tests passed."
+contains
+
+subroutine test_odb127
+ implicit none
+ type(odbql)                                   :: db
+ type(odbql_stmt)                              :: stmt
+ type(odbql_value)                             :: val
+ integer(kind=C_INT)                           :: status, number_of_columns, column_no, row_no, int_val
+ character(len=30)                             :: string_val, column_name
+ character(len=1000)                           :: unparsed_sql
+ real(kind=C_DOUBLE)                           :: v, real_val
+
+!!!! Write to a file with SQL INSERT
+
+ call odbql_open("", db)
+ call odbql_prepare_v2(db, "CREATE TABLE foo AS (varno INTEGER,obsvalue REAL) ON 'odb127f.odb';", &
+ & -1, stmt, unparsed_sql)
+ call odbql_prepare_v2(db, "INSERT INTO foo (varno,obsvalue) VALUES (?,?);", -1, stmt, unparsed_sql)
+
+! Populate first row with NULLs
+ call odbql_bind_null(stmt, 1)
+ call odbql_bind_null(stmt, 2)
+ call odbql_step(stmt)
+
+! Write 3 rows with some values other than NULL
+ do row_no = 1,1000
+    call odbql_bind_int(stmt, 1, row_no)
+    v = 0.1 * row_no
+    call odbql_bind_double(stmt, 2, v)
+
+    call odbql_step(stmt)
+ end do
+
+! Write internal buffers to disk and close the file
+ call odbql_finalize(stmt)
+ call odbql_close(db)
+
+!!!! Read from a file with SQL SELECT
+
+! Associate table with a file name
+ call odbql_open("", db)
+ call odbql_prepare_v2(db, "SELECT varno FROM 'odb127f.odb' WHERE varno = $ps;", &
+  & -1, stmt, unparsed_sql, status)
+ if (status == ODBQL_DONE) stop 1
+ number_of_columns = odbql_column_count(stmt)
+ write(0,*) "Number of columns: ", number_of_columns 
+
+ call odbql_step(stmt, status)
+ if (status /= ODBQL_ROW) stop 2
+
+ val = odbql_column_value(stmt, 1)
+ if (.not. c_associated(val%this)) stop 3
+ 
+ int_val = odbql_value_double(val)
+ write(6,*) 'VALUE ', int_val
+ if (int_val /= 110 ) stop 4
+       
+ call odbql_finalize(stmt)
+ call odbql_close(db)
+
+end subroutine test_odb127
+
+end program test_regression
diff --git a/odb_api/src/odb_api/Archiver.cc b/odb_api/src/odb_api/Archiver.cc
new file mode 100755
index 0000000..a4792b1
--- /dev/null
+++ b/odb_api/src/odb_api/Archiver.cc
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/FileMapper.h"
+#include "odb_api/Odb2Hub.h"
+#include "odb_api/Archiver.h"
+#include "odb_api/Retriever.h"
+#include "odb_api/FileCollector.h"
+#include "odb_api/StringTool.h"
+
+#include <stdio.h>
+#include <fstream>
+
+using namespace std;
+using namespace eckit;
+
+std::ifstream::pos_type Archiver::fileSize(const PathName& path)
+{
+    std::ifstream in(path.asString().c_str(), std::ifstream::ate | std::ifstream::binary);
+    return in.tellg(); 
+}
+
+void Archiver::createDirectories(const PathName& path)
+{
+    vector<string> parts (StringTools::split("/", path));
+    parts.pop_back();
+    PathName directory ((string(path)[0] == '/' ? "/" : "") + StringTools::join("/", parts));
+    directory.mkdir();
+}
+
+/// Archive single file. File given by path will be moved to target location
+/// according to schema, etc.
+void Archiver::archive(const eckit::PathName& path, 
+                          const std::string& dirtySchema, 
+                          const std::string& dirtyKeywordsConfig,
+                          const std::string& dirtyOdbServerArchiveRoot)
+{
+    const std::string schema (odb::StringTool::unQuote(dirtySchema));
+    const std::string keywordsConfig (odb::StringTool::unQuote(dirtyKeywordsConfig));
+    const std::string odbServerArchiveRoot (odb::StringTool::unQuote(dirtyOdbServerArchiveRoot));
+
+    PathName targetPath (odbServerArchiveRoot + "/" + Odb2Hub::getPath(schema, path, keywordsConfig));
+
+    createDirectories(targetPath);
+
+    unsigned long long transferedFileSize (fileSize(path));
+    if (rename(string(path).c_str(), string(targetPath).c_str()))
+        throw SeriousBug(string(string("Unsuccessfull rename of ") + path + " to " + string(targetPath)).c_str());
+    else 
+    {
+        unsigned long long targetFileSize (fileSize(targetPath));
+        if (transferedFileSize != targetFileSize)
+        {
+            stringstream ss;
+            ss << "Transfered file " << path << " size is " << transferedFileSize 
+                << ", after renaming to " << targetPath << " the size is " << targetFileSize
+                <<  endl;
+            throw Exception(ss.str());
+        }
+        Log::info() << "File " << path << " successfully renamed to " << string(targetPath) << endl;
+    }
+}
+
+void Archiver::archive(eckit::MultiHandle& h,
+                       const std::vector<std::string>& keywords,
+                       const std::map<std::string,std::vector<std::string> >& request)
+{
+    std::map<std::string,std::vector<std::string> > r(Retriever::unquoteValues(request));
+
+    vector<string> sources (r["source"]);
+    if (sources.size() == 0) throw UserError("ARCHIVE missing SOURCE");
+    if (sources.size() > 1) throw UserError("ARCHIVE supports single SOURCE currently");
+
+    const string source (sources[0]);
+    const string schema (r["odbpathnameschema"][0]);
+    const string odbServerArchiveRoot (r["odbserverroots"][0]);
+    // TODO: this is a resource in dhshome/etc/config/local
+    const string odbServerKeywordsConfig (
+    "let,class=class,stream=stream,expver=expver,date=andate,time=antime,type=type,obsgroup=groupid,reportype=reportype");
+
+    PathName targetPath (FileCollector::expandTilde(odbServerArchiveRoot) + "/" + Odb2Hub::getPath(schema, source, odbServerKeywordsConfig));
+
+    Log::info() << "targetPath: " << targetPath << endl;
+
+    createDirectories(targetPath);
+    eckit::DataHandle * fh (new eckit::FileHandle(targetPath));
+
+    if (! eckit::PathName(source).exists())
+        throw UserError(string("ARCHIVE: file '") + source + "' doesn't exist." );
+
+    h += fh;
+    h += Length(fileSize(source));
+
+}
diff --git a/odb_api/src/odb_api/Archiver.h b/odb_api/src/odb_api/Archiver.h
new file mode 100755
index 0000000..cfdbe7b
--- /dev/null
+++ b/odb_api/src/odb_api/Archiver.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Archiver.h
+// Piotr Kuchta - ECMWF December 2015
+
+#ifndef odb_api_Archiver_H
+#define odb_api_Archiver_H
+
+#include <fstream>
+
+class Archiver {
+public:
+
+    static void archive(const eckit::PathName& path, 
+                        const std::string& schema, 
+                        const std::string& keywordsConfig,
+                        const std::string& odbServerArchiveRoot);
+
+    static void archive(eckit::MultiHandle&,
+                        const std::vector<std::string>&,
+                        const std::map<std::string,std::vector<std::string> >&);
+
+    static std::ifstream::pos_type fileSize(const eckit::PathName&);
+    static void createDirectories(const eckit::PathName&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/Array.h b/odb_api/src/odb_api/Array.h
new file mode 100644
index 0000000..f96ee80
--- /dev/null
+++ b/odb_api/src/odb_api/Array.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Array_H
+#define Array_H
+
+
+/// \file Array.h
+/// @author Piotr Kuchta, ECMWF August 2009
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+template <typename T>
+class Array {
+public:
+    Array() : size_(0), array_(0), owner_(true) {}
+
+    Array(size_t n)
+        : size_(n), array_(new T[n]), owner_(true)
+    {
+        ASSERT(array_);
+        bzero(array_, size_);
+    }
+
+    ~Array() { if (owner_) delete [] array_; }
+
+    size_t size() { return size_; }
+
+    void size(size_t n)
+    {
+        if (owner_) delete [] array_;
+        array_ = new T[n];
+        ASSERT(array_);
+        owner_ = true;
+        size_ = n;
+        bzero(array_, size_);
+    }
+
+    void share(void *p, size_t size)
+    {
+        if (owner_) delete [] array_;
+        array_ = reinterpret_cast<T *>(p);
+        owner_ = false;
+        size_ = size;
+    }
+
+    operator T* () { return array_; }
+
+    bool operator== (const T* p) { return array_ == p; }
+    bool operator<= (const T* p) { return array_ <= p; }
+    bool operator< (const T* p) { return array_ < p; }
+    bool operator> (const T* p) { return array_ > p; }
+    bool operator>= (const T* p) { return array_ >= p; }
+
+    template <typename P> P* cast() { return reinterpret_cast<P *>(array_); }
+
+private:
+    size_t size_;
+    T *array_;
+    bool owner_;
+};
+
+} // namespace odb
+
+#endif 
+
diff --git a/odb_api/src/odb_api/BitColumnExpression.cc b/odb_api/src/odb_api/BitColumnExpression.cc
new file mode 100755
index 0000000..4d74f5a
--- /dev/null
+++ b/odb_api/src/odb_api/BitColumnExpression.cc
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/eckit.h"
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/BitColumnExpression.h"
+#include "odb_api/SQLBit.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLTable.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+BitColumnExpression::BitColumnExpression(const std::string& name, const std::string& field, SQLTable* table)
+: ColumnExpression(name + "." + field + "@" + table->name(), table),
+  mask_(0),
+  bitShift_(0),
+  field_(field),
+  name_(name)
+{
+	Log::debug() << "BitColumnExpression::BitColumnExpression: name=" << name
+		<< ", field=" << field << ", table->name() =" << table->name()
+		<< ": name_=" << name_
+		<< std::endl;
+}
+
+BitColumnExpression::BitColumnExpression(const std::string& name, const std::string& field, const std::string& tableReference)
+: ColumnExpression(name + "." + field + tableReference, tableReference),
+  mask_(0),
+  bitShift_(0),
+  field_(field),
+  name_(name)
+{
+	Log::debug() << "BitColumnExpression::BitColumnExpression: name=" << name
+		<< ", field=" << field << ", tableReference=" << tableReference
+		<< ": name_=" << name_
+		<< std::endl;
+}
+
+BitColumnExpression::BitColumnExpression(const BitColumnExpression& o)
+: ColumnExpression(o),
+  mask_(o.mask_),
+  bitShift_(o.bitShift_),
+  field_(o.field_),
+  name_(o.name_)
+{}
+
+BitColumnExpression::~BitColumnExpression() {}
+
+const odb::sql::type::SQLType* BitColumnExpression::type() const
+{
+// Change the type to integer to be able to create a new ODA if necessary
+	return &odb::sql::type::SQLType::lookup("integer");
+}
+
+void BitColumnExpression::prepare(SQLSelect& sql)
+{
+	std::string name = name_ + "." + field_ + tableReference_;
+	if(!table_)
+		table_ = sql.findTable(name);
+	value_ = sql.column(name, table_);
+	type_  = sql.typeOf(name, table_);
+
+
+	const type::SQLBit* bit = dynamic_cast<const type::SQLBit*>(type_);
+	if(bit)
+	{
+		mask_  = bit->mask();
+		bitShift_ = bit->shift();
+	}
+	else
+	{
+		// This is for .length and .offset
+		// Not very nice, I know
+		mask_  = 0xffffffff;
+		bitShift_ = 0;
+	}
+
+}
+
+double BitColumnExpression::eval(bool& missing) const
+{
+	if(value_->second) missing = true;
+	unsigned long x = static_cast<unsigned long>(value_->first);
+	return (x & mask_) >> bitShift_;
+}
+
+void BitColumnExpression::expandStars(const std::vector<SQLTable*>& tables, expression::Expressions& e)
+{
+    using namespace eckit;
+    using namespace std;
+
+    Log::debug() << "BitColumnExpression::expandStars: " << e << endl;
+	// TODO: regex
+	if(field_ != "*")
+	{
+		e.push_back(this);
+		return;
+	}
+
+    for(std::vector<SQLTable*>::const_iterator j = tables.begin();  j != tables.end(); ++j)
+	{
+
+		SQLTable* table = (*j);
+        std::vector<std::string> names = table->bitColumnNames(name_ + tableReference_);
+
+		for(size_t i = 0; i < names.size(); i++)
+		{
+			e.push_back(new BitColumnExpression(name_, names[i], tableReference_ /*table*/));
+		}
+	}
+
+	delete this;
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/BitColumnExpression.h b/odb_api/src/odb_api/BitColumnExpression.h
new file mode 100755
index 0000000..ef222aa
--- /dev/null
+++ b/odb_api/src/odb_api/BitColumnExpression.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File BitColumnExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef odb_api_BitColumnExpression_H
+#define odb_api_BitColumnExpression_H
+
+#include "odb_api/ColumnExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class BitColumnExpression : public ColumnExpression {
+public:
+	BitColumnExpression(const std::string&, const std::string&, SQLTable*);
+	BitColumnExpression(const std::string&, const std::string&, const std::string&);
+	BitColumnExpression(const BitColumnExpression&);
+	~BitColumnExpression(); 
+
+private:
+// No copy allowed
+	BitColumnExpression& operator=(const BitColumnExpression&);
+
+protected:
+	unsigned long mask_;
+	unsigned long bitShift_;
+
+	std::string field_;
+	std::string name_;
+
+// -- Overridden methods
+	virtual void prepare(SQLSelect& sql);
+	virtual double eval(bool& missing) const;
+    virtual void expandStars(const std::vector<SQLTable*>&,expression::Expressions&);
+	virtual const odb::sql::type::SQLType* type() const;
+
+	//friend std::ostream& operator<<(std::ostream& s,const BitColumnExpression& p)
+	//	{ p.print(s); return s; }
+	friend class SQLSelectFactory;
+};
+
+} // namespace expression 
+} // namespace SQL
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Block.cc b/odb_api/src/odb_api/Block.cc
new file mode 100644
index 0000000..9e90733
--- /dev/null
+++ b/odb_api/src/odb_api/Block.cc
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Block.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+#include <ostream>
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+
+std::string Block::str() const
+{
+    stringstream ss;
+    ss << *this;
+    return ss.str();
+}
+
+ostream& operator<< (ostream& o, const Block& b)
+{
+    o << "block,file=\"" << b.fileName << "\","
+      << "start=" << b.start << ","
+      << "end=" << b.end << ","
+      << "firstRow=" << b.firstRow << ","
+      << "lastRow=" << b.lastRow 
+      ;
+    return o;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/Block.h b/odb_api/src/odb_api/Block.h
new file mode 100644
index 0000000..bdd4890
--- /dev/null
+++ b/odb_api/src/odb_api/Block.h
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, Oct 2015
+
+#ifndef Block_H
+#define Block_H
+
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/MultiHandle.h"
+#include "eckit/filesystem/PathName.h"
+
+#include "odb_api/tools/Tool.h"
+
+#include <vector>
+
+namespace odb {
+
+typedef unsigned long long ullong;
+
+class Block {
+public:
+    Block()
+    : 
+    fileName (),
+    start (),
+    end (),
+    firstRow (),
+    lastRow ()
+    {}
+
+    Block(const eckit::PathName& fileName,
+          const eckit::Offset& start,
+          const eckit::Offset& end,
+          const ullong firstRow,
+          const ullong lastRow)
+    : 
+    fileName (fileName),
+    start (start),
+    end (end),
+    firstRow (firstRow),
+    lastRow (lastRow)
+    {}
+
+    Block (const Block& other)
+    :
+    fileName (other.fileName),
+    start (other.start),
+    end (other.end),
+    firstRow (other.firstRow),
+    lastRow (other.lastRow)
+    {}
+
+    Block& operator=(const Block& other)
+    {
+        fileName = other.fileName;
+        start = other.start;
+        end = other.end;
+        firstRow = other.firstRow;
+        lastRow = other.lastRow;
+        return *this;
+    }
+
+    eckit::PathName fileName;
+    eckit::Offset start;
+    eckit::Offset end;
+    ullong firstRow;
+    ullong lastRow;
+
+    std::string str() const;
+};
+
+std::ostream& operator<<(std::ostream&, const Block&);
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/CMakeLists.txt b/odb_api/src/odb_api/CMakeLists.txt
new file mode 100644
index 0000000..d7ef236
--- /dev/null
+++ b/odb_api/src/odb_api/CMakeLists.txt
@@ -0,0 +1,478 @@
+configure_file( ODBAPIVersionSHA1.cc.in ODBAPIVersionSHA1.cc @ONLY )
+
+if ( ODB_SERVER_TIME_FORMAT_FOUR_DIGITS )
+    add_definitions ( -DODB_SERVER_TIME_FORMAT_FOUR_DIGITS )
+endif()
+
+if ( ODB_API_SCHEMA_PATH )
+    add_definitions ( -DODB_API_SCHEMA_PATH=${ODB_API_SCHEMA_PATH} )
+endif()
+
+
+list( APPEND odblib_src_files
+odbql.cc
+odbql.h
+pyodbapi.h
+ODBAPIVersionSHA1.cc
+StringTool.cc
+StringTool.h
+Array.h
+BitColumnExpression.cc
+BitColumnExpression.h
+ShiftedBitColumnExpression.cc
+ShiftedBitColumnExpression.h
+Codec.cc
+Codec.h
+CodecFactory.h
+CodecFactory.cc
+CodecOptimizer.cc
+CodecOptimizer.h
+Column.cc
+Column.h
+ColumnExpression.cc
+ColumnExpression.h
+ShiftedColumnExpression.cc
+ShiftedColumnExpression.h
+ColumnType.h
+CommandLineParser.cc
+CommandLineParser.h
+Comparator.cc
+Comparator.h
+ConstantExpression.cc
+ConstantExpression.h
+ConstantSetter.cc
+ConstantSetter.h
+DataColumn.cc
+DataColumn.h
+DataColumns.cc
+DataColumns.h
+DataField.cc
+DataField.h
+DataJoin.cc
+DataJoin.h
+DataJoinIterator.cc
+DataJoinIterator.h
+DataLink.cc
+DataLink.h
+DataLinkFiller.cc
+DataLinkFiller.h
+DataLinkIterator.cc
+DataLinkIterator.h
+DataLinks.cc
+DataLinks.h
+DataLoader.cc
+DataLoader.h
+DataPage.cc
+DataPage.h
+DataRecord.cc
+DataRecord.h
+DataRecordIterator.h
+DataRow.cc
+DataRow.h
+DataSaver.cc
+DataSaver.h
+DataSelect.cc
+DataSelect.h
+DataSelectIterator.cc
+DataSelectIterator.h
+DataSelectOutput.cc
+DataSelectOutput.h
+DataSelectSession.cc
+DataSelectSession.h
+DataSet.cc
+DataSet.h
+DataSetBuilder.cc
+DataSetBuilder.h
+DataSetFiller.cc
+DataSetFiller.h
+DataStream.cc
+DataStream.h
+DataTable.cc
+DataTable.h
+DataTableFiller.cc
+DataTableFiller.h
+DataTableIterator.cc
+DataTableIterator.h
+DataTableMappings.cc
+DataTableMappings.h
+DataTables.cc
+DataTables.h
+DataView.cc
+DataView.h
+DateTime.cc
+DateTime.h
+Decoder.cc
+Decoder.h
+Dictionary.cc
+Dictionary.h
+DispatchingWriter.cc
+DispatchingWriter.h
+DirectAccess.cc
+DirectAccess.h
+DirectAccessIterator.cc
+DirectAccessIterator.h
+Duration.cc
+Duration.h
+Endian.h
+Environment.cc
+Environment.h
+EqRegionCache.cc
+EqRegionCache.h
+Expressions.cc
+Expressions.h
+FastODA2Request.cc
+FastODA2Request.h
+FixedSizeWriterIterator.cc
+FixedSizeWriterIterator.h
+FunctionAND.cc
+FunctionAND.h
+FunctionAVG.cc
+FunctionAVG.h
+FunctionCOUNT.cc
+FunctionCOUNT.h
+FunctionDOTP.cc
+FunctionDOTP.h
+FunctionEQ.cc
+FunctionEQ.h
+FunctionRLIKE.cc
+FunctionRLIKE.h
+FunctionEQ_BOXLAT.cc
+FunctionEQ_BOXLAT.h
+FunctionEQ_BOXLON.cc
+FunctionEQ_BOXLON.h
+FunctionMATCH.h
+FunctionMATCH.cc
+SQLMATCHSubquerySession.h
+SQLMATCHSubquerySession.cc
+SQLMATCHSubquerySessionOutput.h
+SQLMATCHSubquerySessionOutput.cc
+FunctionExpression.cc
+FunctionExpression.h
+FunctionFactory.cc
+FunctionFactory.h
+MDI.cc
+MDI.h
+FunctionIN.cc
+FunctionIN.h
+FunctionIntegerExpression.cc
+FunctionIntegerExpression.h
+FunctionJOIN.cc
+FunctionJOIN.h
+FunctionJULIAN.cc
+FunctionJULIAN.h
+FunctionJULIAN_SECONDS.cc
+FunctionJULIAN_SECONDS.h
+FunctionMAX.cc
+FunctionMAX.h
+FunctionMIN.cc
+FunctionMIN.h
+FunctionFIRST.cc
+FunctionFIRST.h
+FunctionLAST.cc
+FunctionLAST.h
+FunctionNORM.cc
+FunctionNORM.h
+FunctionNOT_IN.cc
+FunctionNOT_IN.h
+FunctionNOT_NULL.cc
+FunctionNOT_NULL.h
+FunctionNULL.cc
+FunctionNULL.h
+FunctionNVL.cc
+FunctionNVL.h
+FunctionOR.cc
+FunctionOR.h
+FunctionRGG_BOXLAT.cc
+FunctionRGG_BOXLAT.h
+FunctionRGG_BOXLON.cc
+FunctionRGG_BOXLON.h
+FunctionRMS.cc
+FunctionRMS.h
+FunctionROWNUMBER.cc
+FunctionROWNUMBER.h
+FunctionSTDEV.cc
+FunctionSTDEV.h
+FunctionSUM.cc
+FunctionSUM.h
+FunctionTDIFF.cc
+FunctionTDIFF.h
+FunctionTHIN.cc
+FunctionTHIN.h
+FunctionTIMESTAMP.cc
+FunctionTIMESTAMP.h
+FunctionVAR.cc
+FunctionVAR.h
+GribCodes.cc
+GribCodes.h
+HashTable.cc
+HashTable.h
+Header.cc
+Header.h
+InMemoryDataHandle.cc
+InMemoryDataHandle.h
+IteratorProxy.h
+MD5.cc
+MD5.h
+MDSetter.cc
+MDSetter.h
+MDUpdatingIterator.cc
+MDUpdatingIterator.h
+MemoryBlock.cc
+MemoryBlock.h
+MetaData.cc
+MetaData.h
+MetaDataReader.cc
+MetaDataReader.h
+MetaDataReaderIterator.cc
+MetaDataReaderIterator.h
+NumberExpression.cc
+NumberExpression.h
+ODAColumn.cc
+ODAColumn.h
+NullColumn.cc
+NullColumn.h
+ODADatabase.cc
+ODADatabase.h
+ODAHandle.cc
+ODAHandle.h
+ODATranslator.h
+ODAUpdatingIterator.cc
+ODAUpdatingIterator.h
+ODBAPISettings.cc
+ODBAPISettings.h
+ODBAPIVersion.cc
+ODBAPIVersion.h
+ODBApplication.cc
+ODBApplication.h
+Select.cc
+Select.h
+OrderByExpressions.cc
+OrderByExpressions.h
+ParameterExpression.cc
+ParameterExpression.h
+Reader.cc
+Reader.h
+ReaderIterator.cc
+ReaderIterator.h
+RegionCache.cc
+RegionCache.h
+RggRegionCache.cc
+RggRegionCache.h
+SQLAST.cc
+SQLAST.h
+SQLBit.cc
+SQLBit.h
+SQLBitColumn.cc
+SQLBitColumn.h
+SQLBitfield.cc
+SQLBitfield.h
+SQLColumn.cc
+SQLColumn.h
+SQLDataColumn.cc
+SQLDataColumn.h
+SQLDataSet.cc
+SQLDataSet.h
+SQLDataTable.cc
+SQLDataTable.h
+SQLDataTableIterator.cc
+SQLDataTableIterator.h
+SQLDatabase.cc
+SQLDatabase.h
+SQLDistinctOutput.cc
+SQLDistinctOutput.h
+SQLDouble.cc
+SQLDouble.h
+SQLExpression.cc
+SQLExpression.h
+SQLExpressionEvaluated.cc
+SQLExpressionEvaluated.h
+SQLIndex.cc
+SQLIndex.h
+SQLInt.cc
+SQLInt.h
+SQLInteractiveSession.cc
+SQLInteractiveSession.h
+SQLNonInteractiveSession.cc
+SQLNonInteractiveSession.h
+SQLCallbackOutput.cc
+SQLCallbackOutput.h
+SQLIterator.h
+SQLIteratorOutput.cc
+SQLIteratorOutput.h
+SQLIteratorSession.cc
+SQLIteratorSession.h
+SQLODAOutput.cc
+SQLODAOutput.h
+SQLOrderOutput.cc
+SQLOrderOutput.h
+SQLOutput.cc
+SQLOutput.h
+SQLOutputConfig.cc
+SQLOutputConfig.h
+SQLParser.cc
+SQLParser.h
+odblib_lex.h
+SQLReal.cc
+SQLReal.h
+SQLSelect.cc
+SQLSelect.h
+SQLInsert.cc
+SQLInsert.h
+SQLEmbedded.h
+SQLEmbedded.cc
+SQLSelectFactory.cc
+SQLSelectFactory.h
+SQLInsertFactory.cc
+SQLInsertFactory.h
+SQLSession.cc
+SQLSession.h
+SQLSimpleOutput.cc
+SQLSimpleOutput.h
+SQLStatement.cc
+SQLStatement.h
+SQLString.cc
+SQLString.h
+SQLTable.cc
+SQLTable.h
+SQLType.cc
+SQLType.h
+SchemaAnalyzer.cc
+SchemaAnalyzer.h
+SelectIterator.cc
+SelectIterator.h
+SelectOneTable.cc
+SelectOneTable.h
+Stack.cc
+Stack.h
+StringExpression.cc
+StringExpression.h
+EmbeddedCodeExpression.cc
+EmbeddedCodeExpression.h
+EmbeddedCodeParser.cc
+EmbeddedCodeParser.h
+TODATable.cc
+TODATable.h
+TODATableIterator.cc
+TODATableIterator.h
+TReadOnlyMemoryDataHandle.cc
+TReadOnlyMemoryDataHandle.h
+TemplateParameters.cc
+TemplateParameters.h
+TextReader.cc
+TextReader.h
+TextReaderIterator.cc
+TextReaderIterator.h
+Tracer.cc
+Tracer.h
+Types.h
+UnsafeInMemoryDataHandle.h
+Writer.cc
+Writer.h
+WriterBufferingIterator.cc
+WriterBufferingIterator.h
+WriterDispatchingIterator.cc
+WriterDispatchingIterator.h
+md5_hash.h
+md5_hash.c
+odb_api.h
+odbcapi.cc
+odbcapi.h
+piconst.h
+RowsCounter.h
+RowsCounter.cc
+Block.h
+Block.cc
+Partition.h
+Partition.cc
+Partitions.h
+Partitions.cc
+Indexer.h
+Indexer.cc
+Partitioner.h
+Partitioner.cc
+FileCollector.h
+FileCollector.cc
+FileMapper.h
+FileMapper.cc
+Odb2Hub.h
+Odb2Hub.cc
+Archiver.h 
+Archiver.cc 
+Retriever.h 
+Retriever.cc
+Stager.h
+Stager.cc
+ODBTarget.cc
+ODBTarget.h
+
+
+# ECML interface
+ODBModule.h
+ODBModule.cc
+# * ECML verbs
+ecml_verbs/ArchiveHandler.h
+ecml_verbs/ArchiveHandler.cc
+ecml_verbs/RetrieveHandler.h
+ecml_verbs/RetrieveHandler.cc
+ecml_verbs/StageHandler.h
+ecml_verbs/StageHandler.cc
+ecml_verbs/CompareHandler.h
+ecml_verbs/CompareHandler.cc
+ecml_verbs/SQLHandler.h
+ecml_verbs/SQLHandler.cc
+ecml_verbs/ChunkHandler.h
+ecml_verbs/ChunkHandler.cc
+ecml_verbs/SQLTestHandler.h
+ecml_verbs/SQLTestHandler.cc
+ecml_verbs/ImportTextHandler.h
+ecml_verbs/ImportTextHandler.cc
+ecml_verbs/CreatePartitionsHandler.h
+ecml_verbs/CreatePartitionsHandler.cc
+ecml_verbs/CreateIndexHandler.h
+ecml_verbs/CreateIndexHandler.cc
+# * ECML data
+ecml_data/ResultSet.cc
+ecml_data/ResultSet.h
+ecml_data/Matrix.cc
+ecml_data/Matrix.h
+ecml_data/ResultSetStore.cc
+ecml_data/ResultSetStore.h
+ecml_data/LocalHandleFactory.h
+ecml_data/LocalHandleFactory.cc
+)
+
+# templates
+
+list( APPEND odblib_templates
+ConstantSetter.cc
+DataStream.cc
+FastODA2Request.cc
+Header.cc
+ODAUpdatingIterator.cc
+SQLIteratorOutput.cc
+TODATable.cc
+TODATableIterator.cc
+WriterDispatchingIterator.cc
+MDUpdatingIterator.cc
+MetaDataReader.cc
+ShiftedColumnExpression.cc
+)
+
+# define Odb library
+
+ecbuild_add_library( TARGET             Odb
+                     INSTALL_HEADERS    LISTED
+                     HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api
+                     COMPONENT          server
+                     SOURCES            ${odblib_src_files}
+                     GENERATED          ODBAPIVersionSHA1.cc
+                     TEMPLATES          ${odblib_templates}
+                     LINKER_LANGUAGE    CXX
+                     LIBS               ecml )
+
+ecbuild_generate_yy( YYPREFIX           odblib_
+                     YACC               sqly
+                     LEX                sqll
+                     FLEX_FLAGS         "-d"
+                     DEPENDANT          SQLParser.cc )
+
diff --git a/odb_api/src/odb_api/Codec.cc b/odb_api/src/odb_api/Codec.cc
new file mode 100644
index 0000000..bfcedd6
--- /dev/null
+++ b/odb_api/src/odb_api/Codec.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/Codec.h"
+#include "odb_api/CodecFactory.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace codec {
+
+template<> std::map<std::string, AbstractCodecFactory<DataHandle>* > AbstractCodecFactory<DataHandle>::codecFactories = std::map<std::string, AbstractCodecFactory<DataHandle>* >();
+template<> std::map<std::string, AbstractCodecFactory<FastInMemoryDataHandle>* > AbstractCodecFactory<FastInMemoryDataHandle>::codecFactories = std::map<std::string, AbstractCodecFactory<FastInMemoryDataHandle>* >();
+template<> std::map<std::string, AbstractCodecFactory<PrettyFastInMemoryDataHandle>* > AbstractCodecFactory<PrettyFastInMemoryDataHandle>::codecFactories = std::map<std::string, AbstractCodecFactory<PrettyFastInMemoryDataHandle>* >();
+
+Codec::Codec(const std::string& name)
+: name_(name),
+  hasMissing_(false),
+  missingValue_(odb::MDI::realMDI()),
+  min_(missingValue_),
+  max_(missingValue_)
+{}
+
+Codec* Codec::clone()
+{
+    Codec* c = findCodec<DataStream<SameByteOrder, DataHandle> >(name_, false);
+    c->hasMissing_ = hasMissing_;
+    c->missingValue_ = missingValue_;
+    c->min_ = min_;
+    c->max_ = max_;
+    return c;
+}
+
+Codec::~Codec() {}
+
+void Codec::missingValue(double v)
+{
+    ASSERT("Cannot change missing value after encoding of column data started" && (min_ == missingValue_) && (max_ == missingValue_));
+    min_ = max_ = missingValue_ = v;
+}
+
+HashTable& Codec::hashTable() { NOTIMP; }
+HashTable* Codec::giveHashTable() { NOTIMP; }
+void Codec::hashTable(HashTable *) { NOTIMP; }
+
+void Codec::gatherStats(double v)
+{
+    if(v == missingValue_)
+        hasMissing_ = 1;
+    else
+    {
+        if(v < min_ || min_ == missingValue_)
+            min_ = v;
+        if(v > max_ || max_ == missingValue_)
+            max_ = v;
+    }
+}
+
+void Codec::print(std::ostream& s) const
+{
+    s << name()
+    //<< ", min=" << fixed << min_
+    //<< ", max=" << max_
+        << ", range=<" << std::fixed << min_ << "," << max_ << ">";
+
+    if (hasMissing_)
+    {
+        //s << ", hasMissing=";
+
+        s << ", missingValue=" << missingValue_;
+    }
+}
+
+template<> Codec* Codec::loadCodec(DataStream<SameByteOrder,DataHandle> &f) { return AbstractCodecFactory<DataHandle>::loadCodec(f.dataHandle(), false); }
+template<> Codec* Codec::loadCodec(DataStream<OtherByteOrder,DataHandle> &f) { return AbstractCodecFactory<DataHandle>::loadCodec(f.dataHandle(), true); }
+
+template<> Codec* Codec::loadCodec(DataStream<SameByteOrder,FastInMemoryDataHandle> &f) { return AbstractCodecFactory<FastInMemoryDataHandle>::loadCodec(f.dataHandle(), false); }
+template<> Codec* Codec::loadCodec(DataStream<OtherByteOrder,FastInMemoryDataHandle> &f) { return AbstractCodecFactory<FastInMemoryDataHandle>::loadCodec(f.dataHandle(), true); }
+
+template<> Codec* Codec::loadCodec(DataStream<SameByteOrder,PrettyFastInMemoryDataHandle> &f) { return AbstractCodecFactory<PrettyFastInMemoryDataHandle>::loadCodec(f.dataHandle(), false); }
+template<> Codec* Codec::loadCodec(DataStream<OtherByteOrder,PrettyFastInMemoryDataHandle> &f) { return AbstractCodecFactory<PrettyFastInMemoryDataHandle>::loadCodec(f.dataHandle(), true); }
+
+template<>
+template<> void AbstractCodecFactory<DataHandle>::save<SameByteOrder>(Codec *codec, DataStream<SameByteOrder,DataHandle> &f) { codecFactories[codec->name()]->save(codec, f.dataHandle(), false); }
+template<> 
+template<> void AbstractCodecFactory<DataHandle>::save<OtherByteOrder>(Codec *codec, DataStream<OtherByteOrder,DataHandle> &f) { codecFactories[codec->name()]->save(codec, f.dataHandle(), true); }
+
+void Codec::loadCodecs() {
+	static CodecFactory<CodecConstant,DataHandle> codecConstantFactory("constant");
+	static CodecFactory<CodecConstantString,DataHandle> codecConstantStringFactory("constant_string");
+	static CodecFactory<CodecConstantOrMissing,DataHandle> codecConstantOrMissingFactory("constant_or_missing");
+	static CodecFactory<CodecRealConstantOrMissing,DataHandle> codecRealConstantOrMissingFactory("real_constant_or_missing");
+	static CodecFactory<CodecChars,DataHandle> codecCharsFactory("chars");
+	static CodecFactory<CodecLongReal,DataHandle> codecLongRealFactory("long_real");
+	static CodecFactory<CodecShortReal,DataHandle> codecShortRealFactory("short_real");
+	static CodecFactory<CodecShortReal2,DataHandle> codecShortReal2Factory("short_real2");
+	static CodecFactory<CodecInt32,DataHandle> codecInt32Factory("int32");
+	static CodecFactory<CodecInt16,DataHandle> codecInt16Factory("int16");
+	static CodecFactory<CodecInt8,DataHandle> codecInt8Factory("int8");
+	static CodecFactory<CodecInt16Missing,DataHandle> codecInt16MissingFactory("int16_missing");
+	static CodecFactory<CodecInt8Missing,DataHandle> codecInt8MissingFactory("int8_missing");
+	static CodecFactory<CodecInt16String,DataHandle> codecInt16StringFactory("int16_string");
+	static CodecFactory<CodecInt8String,DataHandle> codecInt8StringFactory("int8_string");
+
+	static CodecFactory<CodecConstant,FastInMemoryDataHandle> fastCodecConstantFactory("constant");
+	static CodecFactory<CodecConstantString,FastInMemoryDataHandle> fastCodecConstantStringFactory("constant_string");
+	static CodecFactory<CodecConstantOrMissing,FastInMemoryDataHandle> fastCodecConstantOrMissingFactory("constant_or_missing");
+	static CodecFactory<CodecRealConstantOrMissing,FastInMemoryDataHandle> fastCodecRealConstantOrMissingFactory("real_constant_or_missing");
+	static CodecFactory<CodecChars,FastInMemoryDataHandle> fastCodecCharsFactory("chars");
+	static CodecFactory<CodecLongReal,FastInMemoryDataHandle> fastCodecLongRealFactory("long_real");
+	static CodecFactory<CodecShortReal,FastInMemoryDataHandle> fastCodecShortRealFactory("short_real");
+	static CodecFactory<CodecShortReal2,FastInMemoryDataHandle> fastCodecShortReal2Factory("short_real2");
+	static CodecFactory<CodecInt32,FastInMemoryDataHandle> fastCodecInt32Factory("int32");
+	static CodecFactory<CodecInt16,FastInMemoryDataHandle> fastCodecInt16Factory("int16");
+	static CodecFactory<CodecInt8,FastInMemoryDataHandle> fastCodecInt8Factory("int8");
+	static CodecFactory<CodecInt16Missing,FastInMemoryDataHandle> fastCodecInt16MissingFactory("int16_missing");
+	static CodecFactory<CodecInt8Missing,FastInMemoryDataHandle> fastCodecInt8MissingFactory("int8_missing");
+	static CodecFactory<CodecInt16String,FastInMemoryDataHandle> fastCodecInt16StringFactory("int16_string");
+	static CodecFactory<CodecInt8String,FastInMemoryDataHandle> fastCodecInt8StringFactory("int8_string");
+
+	static CodecFactory<CodecConstant,PrettyFastInMemoryDataHandle> prettyFastCodecConstantFactory("constant");
+	static CodecFactory<CodecConstantString,PrettyFastInMemoryDataHandle> prettyFastCodecConstantStringFactory("constant_string");
+	static CodecFactory<CodecConstantOrMissing,PrettyFastInMemoryDataHandle> prettyFastCodecConstantOrMissingFactory("constant_or_missing");
+	static CodecFactory<CodecRealConstantOrMissing,PrettyFastInMemoryDataHandle> prettyFastCodecRealConstantOrMissingFactory("real_constant_or_missing");
+	static CodecFactory<CodecChars,PrettyFastInMemoryDataHandle> prettyFastCodecCharsFactory("chars");
+	static CodecFactory<CodecLongReal,PrettyFastInMemoryDataHandle> prettyFastCodecLongRealFactory("long_real");
+	static CodecFactory<CodecShortReal,PrettyFastInMemoryDataHandle> prettyFastCodecShortRealFactory("short_real");
+	static CodecFactory<CodecShortReal2,PrettyFastInMemoryDataHandle> prettyFastCodecShortReal2Factory("short_real2");
+	static CodecFactory<CodecInt32,PrettyFastInMemoryDataHandle> prettyFastCodecInt32Factory("int32");
+	static CodecFactory<CodecInt16,PrettyFastInMemoryDataHandle> prettyFastCodecInt16Factory("int16");
+	static CodecFactory<CodecInt8,PrettyFastInMemoryDataHandle> prettyFastCodecInt8Factory("int8");
+	static CodecFactory<CodecInt16Missing,PrettyFastInMemoryDataHandle> prettyFastCodecInt16MissingFactory("int16_missing");
+	static CodecFactory<CodecInt8Missing,PrettyFastInMemoryDataHandle> prettyFastCodecInt8MissingFactory("int8_missing");
+	static CodecFactory<CodecInt16String,PrettyFastInMemoryDataHandle> prettyFastCodecInt16StringFactory("int16_string");
+	static CodecFactory<CodecInt8String,PrettyFastInMemoryDataHandle> prettyFastCodecInt8StringFactory("int8_string");
+}
+
+} // namespace codec
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Codec.h b/odb_api/src/odb_api/Codec.h
new file mode 100644
index 0000000..cb3344e
--- /dev/null
+++ b/odb_api/src/odb_api/Codec.h
@@ -0,0 +1,822 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Codec_H
+#define Codec_H
+
+#include <strings.h> // for bzero
+#include <limits>
+
+#include "odb_api/CodecFactory.h"
+#include "odb_api/HashTable.h"
+#include "odb_api/MDI.h"
+
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class Reader;
+class SameByteOrder;
+class OtherByteOrder;
+class HashTable;
+
+namespace codec {
+
+class Codec {
+public:
+	Codec(const std::string& name);
+	virtual ~Codec();
+
+	/// Creates a clone of this codec. NOTE: the clone is not really usefull for coding/decoding, but has the same stats/missing
+	/// values as the original codec, which can be useful sometimes.
+	virtual Codec* clone();
+
+	const std::string& name() const { return name_; }
+
+	virtual unsigned char* encode(unsigned char* p, double d) = 0;
+	virtual double decode() = 0;
+	virtual void dataHandle(void *) = 0;
+
+	template <typename DATASTREAM> static Codec* findCodec(const std::string& name, bool differentByteOrder);
+	//static Codec* findCodec(const std::string& name, bool differentByteOrder);
+	
+	////template<typename BYTEORDER> static Codec* findCodec(const std::string& name);
+
+	template<typename DATASTREAM> static Codec* loadCodec(DATASTREAM &);
+
+	template<typename DATASTREAM> void save(DATASTREAM &); 
+
+	void resetStats() { min_ = max_ = missingValue_; hasMissing_ = false; }
+
+	virtual void gatherStats(double v);
+
+	void hasMissing(bool h) { hasMissing_ = h; }
+	int32_t hasMissing() const { return hasMissing_; }
+
+	void min(double m) { min_ = m; }
+	double min() const { return min_; }
+
+	void max(double m) { max_ = m; }
+	double max() const { return max_; }
+
+	void missingValue(double v); 
+	double missingValue() const { return missingValue_; } 
+
+	// Use it if you KNOW the codec encodes a std::string type column.
+	virtual HashTable& hashTable();
+
+	// Use it if you KNOW the codec encodes a std::string type column.
+	virtual HashTable* giveHashTable();
+
+	// Use it if you KNOW the codec encodes a std::string type column.
+	virtual void hashTable(HashTable *);
+
+	virtual void print(std::ostream& s) const;
+
+	friend std::ostream& operator<<(std::ostream& s, const Codec& p)
+		{ p.print(s); return s; }
+
+	static void loadCodecs();
+protected:
+
+	template<typename BYTEORDER>
+		void loadBasics(eckit::DataHandle *dh)
+	{
+		DataStream<BYTEORDER> f(dh);
+		f.readInt32(hasMissing_);
+		f.readDouble(min_);
+		f.readDouble(max_);
+		f.readDouble(missingValue_);
+	}
+
+	template<typename BYTEORDER>
+		void saveBasics(eckit::DataHandle *dh)
+	{
+		DataStream<BYTEORDER> f(dh);
+		f.writeInt32(hasMissing_);
+		f.writeDouble(min_);
+		f.writeDouble(max_);
+		f.writeDouble(missingValue_);
+	}
+
+	std::string name_;
+
+	int32_t hasMissing_;
+	double missingValue_;
+	double min_;
+	double max_;
+	
+private:
+	Codec(const Codec&);
+	Codec& operator=(const Codec&);
+};
+
+//#include "odb_api/CodecFactory.h"
+
+template<typename DATASTREAM>
+void Codec::save(DATASTREAM &f)
+{
+	f.writeString(name_);
+	AbstractCodecFactory<typename DATASTREAM::DataHandleType>::save(this, f);
+}
+
+template <typename DATASTREAM>
+Codec* Codec::findCodec(const std::string& name, bool differentByteOrder)
+{
+	Codec::loadCodecs();
+	return AbstractCodecFactory<typename DATASTREAM::DataHandleType>::getCodec(name, differentByteOrder);
+}
+
+template<typename BYTEORDER>
+class CodecConstant : public Codec {
+public:
+	CodecConstant() : Codec("constant") {}
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	virtual void print(std::ostream& s) const;
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecConstantString : public CodecConstant<BYTEORDER> {
+public:
+	CodecConstantString() : CodecConstant<BYTEORDER>() { this->name_ = "constant_string"; }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	virtual void print(std::ostream& s) const;
+
+	void load(eckit::DataHandle *dh)
+	{
+		Codec::loadBasics<BYTEORDER>(dh);
+		BYTEORDER::swap(this->min_);
+		BYTEORDER::swap(this->max_);
+	}
+
+	void save(eckit::DataHandle *dh)
+	{
+		BYTEORDER::swap(this->min_);
+		BYTEORDER::swap(this->max_);
+		Codec::saveBasics<BYTEORDER>(dh);
+	}
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecInt8 : public Codec {
+public:
+	CodecInt8() : Codec("int8") { this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI(); }
+	virtual unsigned char* encode(unsigned char* p, double d); 
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+	DataStream<BYTEORDER>& ds() { return ds_; }
+private:
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecInt8Missing : public Codec {
+public:
+	CodecInt8Missing() : Codec("int8_missing") { this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI(); }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecConstantOrMissing : public CodecInt8Missing<BYTEORDER> {
+public:
+	CodecConstantOrMissing() : CodecInt8Missing<BYTEORDER>() { this->name_ = "constant_or_missing"; }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { CodecInt8Missing<BYTEORDER>::dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	virtual void print(std::ostream& s) const;
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecRealConstantOrMissing : public CodecConstantOrMissing<BYTEORDER> {
+public:
+	CodecRealConstantOrMissing() : CodecConstantOrMissing<BYTEORDER>()
+	{
+		this->name_ = "real_constant_or_missing";
+		this->missingValue_ = this->min_ = this->max_ = odb::MDI::realMDI(); 
+	}
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+//private:
+//	DataStream<BYTEORDER>& ds() { return ds_; }
+//	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecChars : public Codec {
+public:
+	CodecChars();
+	~CodecChars();
+	virtual Codec* clone();
+
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	virtual void print(std::ostream& s) const;
+
+	virtual HashTable& hashTable();
+	virtual void hashTable(HashTable *ht);
+	virtual HashTable* giveHashTable();
+
+	void gatherStats(double v);
+	void load(eckit::DataHandle *dh);
+	void save(eckit::DataHandle *dh);
+
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+protected:
+	HashTable *hashTable_;
+};
+
+template<typename BYTEORDER>
+Codec* CodecChars<BYTEORDER>::clone()
+{
+	CodecChars* c = static_cast<CodecChars*>(this->Codec::clone());
+	*(c->hashTable_) = *hashTable_;
+	ASSERT(c->min() == this->min());
+	ASSERT(c->max() == this->max());
+	//hashTable_->dumpTable(eckit::Log::info());
+	return c;
+}
+
+template<typename BYTEORDER>
+class CodecLongReal : public Codec {
+public:
+	CodecLongReal() : Codec("long_real") {}
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+
+template<typename BYTEORDER>
+class CodecShortReal : public Codec {
+public:
+	CodecShortReal() : Codec("short_real") {}
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecShortReal2 : public Codec {
+public:
+	CodecShortReal2() : Codec("short_real2") {}
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+/*
+template<typename BYTEORDER>
+class CodecShortRealScaled : public Codec {
+public:
+	CodecShortRealScaled() : Codec("short_real_scaled") {}
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+
+	// TODO
+
+};
+*/
+
+template<typename BYTEORDER>
+class CodecInt32 : public Codec {
+public:
+	CodecInt32() : Codec("int32") { this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI(); }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecInt16 : public Codec {
+public:
+	CodecInt16() : Codec("int16") { this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI(); }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+	DataStream<BYTEORDER>& ds() { return ds_; }
+private:
+	DataStream<BYTEORDER> ds_;
+};
+
+template<typename BYTEORDER>
+class CodecInt16Missing : public Codec {
+public:
+	CodecInt16Missing() : Codec("int16_missing") { this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI(); }
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { ds_.dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void load(eckit::DataHandle *dh) { Codec::loadBasics<BYTEORDER>(dh); }
+	void save(eckit::DataHandle *dh) { Codec::saveBasics<BYTEORDER>(dh); }
+private:
+	DataStream<BYTEORDER>& ds() { return ds_; }
+	DataStream<BYTEORDER> ds_;
+};
+
+
+template<typename BYTEORDER>
+class CodecInt16String : public CodecChars<BYTEORDER> {
+public:
+	CodecInt16String() : CodecChars<BYTEORDER>(), intCodec()
+	{
+		this->name_ = "int16_string";
+		this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI();
+		intCodec.min(0);
+	}
+	~CodecInt16String() {}
+	virtual Codec* clone();
+
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	virtual void dataHandle(void *p) { intCodec.ds().dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void gatherStats(double v) { CodecChars<BYTEORDER>::gatherStats(v); }
+	void load(eckit::DataHandle *dh) { CodecChars<BYTEORDER>::load(dh); }
+	void save(eckit::DataHandle *dh) { CodecChars<BYTEORDER>::save(dh); }
+
+private:
+	DataStream<BYTEORDER>& ds() { return intCodec.ds(); }
+
+	CodecInt16<BYTEORDER> intCodec;
+};
+
+template<typename BYTEORDER>
+Codec* CodecInt16String<BYTEORDER>::clone()
+{
+	CodecInt16String* c = static_cast<CodecInt16String*>(this->CodecChars<BYTEORDER>::clone());
+	c->intCodec.min(intCodec.min());
+	c->intCodec.max(intCodec.max());
+	c->min(this->min());
+	c->max(this->max());
+	return c;
+}
+
+template<typename BYTEORDER>
+class CodecInt8String : public CodecChars<BYTEORDER> {
+public:
+	CodecInt8String() : CodecChars<BYTEORDER>(), intCodec() 
+	{
+		this->name_ = "int8_string";
+		this->missingValue_ = this->min_ = this->max_ = odb::MDI::integerMDI();
+		intCodec.min(0);
+	}
+	~CodecInt8String() {}
+	virtual Codec* clone();
+
+	virtual unsigned char* encode(unsigned char* p, double d);
+	virtual double decode();
+
+	void dataHandle(void *p) { intCodec.ds().dataHandle(static_cast<eckit::DataHandle*>(p)); }
+
+	void gatherStats(double v) { CodecChars<BYTEORDER>::gatherStats(v); }
+	void load(eckit::DataHandle *dh) { CodecChars<BYTEORDER>::load(dh); }
+	void save(eckit::DataHandle *dh) { CodecChars<BYTEORDER>::save(dh); }
+
+private:
+	DataStream<BYTEORDER>& ds() { return intCodec.ds(); }
+private:
+	CodecInt8<BYTEORDER> intCodec;
+};
+
+template<typename BYTEORDER>
+Codec* CodecInt8String<BYTEORDER>::clone()
+{
+	CodecInt8String* c = static_cast<CodecInt8String*>(this->CodecChars<BYTEORDER>::clone());
+	c->intCodec.min(intCodec.min());
+	c->intCodec.max(intCodec.max());
+	c->min(this->min());
+	c->max(this->max());
+	return c;
+}
+
+
+template<typename BYTEORDER>
+void CodecChars<BYTEORDER>::gatherStats(double v) 
+{
+	char buf[255];
+	memcpy(buf, &v, sizeof(double));
+	buf[sizeof(double)] = 0;
+	hashTable_->store(buf);
+
+	// In case the column is const, the const value will be copied and used by the optimized codec.
+	min_ = v;
+}
+
+template<typename BYTEORDER>
+CodecChars<BYTEORDER>::CodecChars() : Codec("chars"), hashTable_(new HashTable) {}
+
+template<typename BYTEORDER>
+CodecChars<BYTEORDER>::~CodecChars() { delete hashTable_; }
+
+template<typename BYTEORDER>
+HashTable& CodecChars<BYTEORDER>::hashTable() { return *hashTable_; }
+
+template<typename BYTEORDER>
+void CodecChars<BYTEORDER>::hashTable(HashTable *ht) { delete hashTable_; hashTable_ = ht; }
+
+template<typename BYTEORDER>
+HashTable* CodecChars<BYTEORDER>::giveHashTable()
+{
+	HashTable *ret = hashTable_;
+	hashTable_ = 0;
+	return ret;
+}
+
+template<typename BYTEORDER>
+void CodecChars<BYTEORDER>::load(eckit::DataHandle *dh)
+{
+	Codec::loadBasics<BYTEORDER>(dh);
+	DataStream<BYTEORDER> ds(dh);
+	hashTable_->load(ds);
+}
+
+template<typename BYTEORDER>
+void CodecChars<BYTEORDER>::save(eckit::DataHandle *dh)
+{
+	Codec::saveBasics<BYTEORDER>(dh);
+	DataStream<BYTEORDER> ds(dh);
+	hashTable_->save(ds);
+}
+
+//template<> Codec* Codec::findCodec<SameByteOrder>(const std::string& name) { findCodec(name, false); }
+//template<> Codec* Codec::findCodec<OtherByteOrder>(const std::string& name) { findCodec(name, true); }
+
+//template<typename DATASTREAM> void Codec::save(DATASTREAM &f) { f.writeString(name_); AbstractCodecFactory::save(this, f); }
+
+template<typename BYTEORDER>
+unsigned char* CodecShortReal<BYTEORDER>::encode(unsigned char* p, double d)
+{
+    const uint32_t minFloatAsInt ( 0x800000 );
+
+    float s = (d == missingValue_) ? *reinterpret_cast<const float*>( &minFloatAsInt ) : d;
+    memcpy(p, &s, sizeof(s));
+    return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecShortReal<BYTEORDER>::decode()
+{
+    float s;
+    ds().readFloat(s);
+
+    const uint32_t minFloatAsInt ( 0x800000 );
+    return s == *reinterpret_cast<const float*>( &minFloatAsInt ) ? missingValue_ : s;
+}
+
+
+template<typename BYTEORDER>
+unsigned char* CodecShortReal2<BYTEORDER>::encode(unsigned char* p, double d)
+{
+    const uint32_t maxFloatAsInt ( 0x7f7fffff );
+    float s = (d == missingValue_) ? - *reinterpret_cast<const float*>( &maxFloatAsInt ) : d;
+    memcpy(p, &s, sizeof(s));
+    return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecShortReal2<BYTEORDER>::decode()
+{
+    float s;
+    ds().readFloat(s);
+
+    const uint32_t maxFloatAsInt ( 0x7f7fffff );
+    return s == - *reinterpret_cast<const float*>( &maxFloatAsInt ) ? missingValue_ : s;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt32<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	int32_t s = d;
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecInt32<BYTEORDER>::decode()
+{
+	int32_t s;
+	ds().readInt32(s);
+	return s;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt16<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	unsigned short s = d - min_;
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecInt16<BYTEORDER>::decode()
+{
+	uint16_t s;
+	ds().readUInt16(s);
+	return s + min_;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt16Missing<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	uint16_t s = (d == missingValue_) ? 0xffff : d - min_;
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecInt16Missing<BYTEORDER>::decode()
+{
+	uint16_t s;
+	ds().readUInt16(s);
+	return s == 0xffff ? missingValue_ : (s + min_);
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt8<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	unsigned char s = d - min_;
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecInt8<BYTEORDER>::decode()
+{
+	unsigned char s;
+	ds().readUChar(s);
+	return s + min_;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt8Missing<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	unsigned char s = (d == missingValue_) ? 0xff : d - min_;
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecInt8Missing<BYTEORDER>::decode()
+{
+	unsigned char s;
+	ds().readUChar(s);
+	return s == 0xff ? missingValue_ : (s + min_);
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt8String<BYTEORDER>::encode(unsigned char* p,double d)
+{
+	char buf[255];
+	memcpy(buf, &d, sizeof(double));
+	buf[sizeof(double)] = 0;
+	return intCodec.encode(p, this->hashTable_->findIndex(buf));
+}
+
+template<typename BYTEORDER>
+double CodecInt8String<BYTEORDER>::decode()
+{
+	int i    = intCodec.decode();
+	double d;
+	char buf[255] = {0,};
+
+	ASSERT(i < this->hashTable_->nextIndex());
+	
+	const char* s = this->hashTable_->strings()[i].c_str();
+	strncpy(buf, s, sizeof(d));
+
+	memcpy(&d, buf, sizeof(d));
+	return d;
+
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecInt16String<BYTEORDER>::encode(unsigned char* p,double d)
+{
+	char buf[255];
+	memcpy(buf, &d, sizeof(double));
+	buf[sizeof(double)] = 0;
+	return intCodec.encode(p, this->hashTable_->findIndex(buf));
+}
+
+template<typename BYTEORDER>
+double CodecInt16String<BYTEORDER>::decode()
+{
+	int i = intCodec.decode();
+	double d;
+	char buf[255] = {0,};
+
+	ASSERT(i < this->hashTable_->nextIndex());
+	
+	const char* s = this->hashTable_->strings()[i].c_str();
+	strncpy(buf, s, sizeof(d));
+
+	memcpy(&d, buf, sizeof(d));
+	return d;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecLongReal<BYTEORDER>::encode(unsigned char* p,double s)
+{
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecLongReal<BYTEORDER>::decode()
+{
+	double s;
+	ds().readDouble(s);
+	return s;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecChars<BYTEORDER>::encode(unsigned char* p, double s)
+{
+	memcpy(p, &s, sizeof(s));
+	return p + sizeof(s);
+}
+
+template<typename BYTEORDER>
+double CodecChars<BYTEORDER>::decode()
+{
+	double s;
+	ds().readDouble(s);
+	return s;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecConstant<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	return p;
+}
+
+template<typename BYTEORDER>
+double CodecConstant<BYTEORDER>::decode()
+{
+	return min_;
+}
+
+template<typename BYTEORDER>
+unsigned char* CodecConstantOrMissing<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	//return CodecInt8Missing<BYTEORDER>::encode(p, d != this->missingValue_);
+	return CodecInt8Missing<BYTEORDER>::encode(p, d);
+}
+
+template<typename BYTEORDER>
+double CodecConstantOrMissing<BYTEORDER>::decode()
+{
+	double n = CodecInt8Missing<BYTEORDER>::decode();
+	//return n ? this->min_ : this->missingValue_;
+	return n;
+}
+
+template<typename BYTEORDER>
+void CodecChars<BYTEORDER>::print(std::ostream& s) const
+{
+	s << this->name()
+	<< ", #words=" << this->hashTable_->nextIndex();
+}
+
+template<typename BYTEORDER>
+void CodecConstant<BYTEORDER>::print(std::ostream& s) const
+{
+    s << this->name() << ", value=" << std::fixed << this->min_;
+}
+
+template<typename BYTEORDER>
+void CodecConstantString<BYTEORDER>::print(std::ostream& s) const
+{
+	char buf[sizeof(double) + 1];
+	bzero(buf, sizeof(buf));
+	strncpy(buf, reinterpret_cast<const char *>(&this->min_), sizeof(double));
+	s << this->name() << ", value='" << std::string(buf) << "'";
+}
+
+template<typename BYTEORDER>
+void CodecConstantOrMissing<BYTEORDER>::print(std::ostream& s) const
+{
+	s << this->name() << ", value=";
+	if (this->min_ == this->missingValue_)
+		s << "NULL";
+	else
+        s << std::fixed << this->min_;
+
+	if (this->hasMissing_)
+		s << ", missingValue=" << this->missingValue_;
+}
+
+
+
+template<typename BYTEORDER>
+unsigned char* CodecConstantString<BYTEORDER>::encode(unsigned char* p, double d)
+{
+	return p;
+}
+
+template<typename BYTEORDER>
+double CodecConstantString<BYTEORDER>::decode()
+{
+	return this->min_;
+}
+
+
+} // namespace codec
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/CodecFactory.cc b/odb_api/src/odb_api/CodecFactory.cc
new file mode 100644
index 0000000..0d51ede
--- /dev/null
+++ b/odb_api/src/odb_api/CodecFactory.cc
@@ -0,0 +1,14 @@
+#include "odb_api/CodecFactory.h"
+#include "odb_api/Codec.h"
+
+namespace odb {
+namespace codec {
+
+void CodecFactoryBase::loadCodecs()
+{
+    Codec::loadCodecs();
+
+}
+
+} // codec
+} // odb
diff --git a/odb_api/src/odb_api/CodecFactory.h b/odb_api/src/odb_api/CodecFactory.h
new file mode 100644
index 0000000..b52d6b5
--- /dev/null
+++ b/odb_api/src/odb_api/CodecFactory.h
@@ -0,0 +1,133 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odblib_CodecFactory_H
+#define odblib_CodecFactory_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/UnsafeInMemoryDataHandle.h"
+#include "odb_api/DataStream.h"
+
+
+namespace eckit {
+class DataHandle;
+}
+
+namespace odb {
+namespace codec {
+
+class Codec;
+//template<class A, class B> class DataStream;
+
+class CodecFactoryBase {
+protected:
+    static void loadCodecs();
+};
+
+
+template <typename DATAHANDLE>
+class AbstractCodecFactory : public CodecFactoryBase {
+public:
+	static Codec* getCodec(const std::string& name, bool differentByteOrder) {
+		return codecFactories[name]->create(differentByteOrder);
+	}
+
+	static Codec* loadCodec(DATAHANDLE *dh, bool differentByteOrder) {
+        loadCodecs();
+		std::string name;
+		if (differentByteOrder)
+		{
+			DataStream<OtherByteOrder,DATAHANDLE> ds(dh);
+			ds.readString(name);
+		}
+		else
+		{
+			DataStream<SameByteOrder,DATAHANDLE> ds(dh);
+			ds.readString(name);
+		}
+		AbstractCodecFactory *factory = codecFactories[name];
+		if (factory == 0)
+		{
+            std::stringstream ss;
+			ss << "Unknown codec '" << name << "'. You may need to use a newer version of ODB API.";
+			throw eckit::UserError(ss.str());
+		}
+		Codec *codec = factory->load(dh, differentByteOrder);
+		return codec;
+	}
+
+	
+	template<typename BYTEORDER>
+		static void save(Codec *codec, DataStream<BYTEORDER,DATAHANDLE> &f);
+
+	virtual Codec* create(bool sourceHasDifferentByteOrder) = 0;
+
+	virtual Codec* load(eckit::DataHandle *, bool sourceHasDifferentByteOrder) = 0;
+	virtual Codec* load(FastInMemoryDataHandle*, bool sourceHasDifferentByteOrder) { NOTIMP; return 0; } //= 0;
+
+	virtual void save(Codec *, eckit::DataHandle *, bool sourceHasDifferentByteOrder) = 0;
+	
+protected:
+	AbstractCodecFactory(const std::string& name) { codecFactories[name] = this; }
+
+private:
+	static std::map<std::string, AbstractCodecFactory<DATAHANDLE> *> codecFactories;
+};
+
+
+template <template <typename> class CODEC,typename DATAHANDLE>
+class CodecFactory : public AbstractCodecFactory<DATAHANDLE> {
+public:
+	CodecFactory (const std::string& name) : AbstractCodecFactory<DATAHANDLE>(name) { }
+
+	virtual Codec* create(bool sourceHasDifferentByteOrder)
+	{
+		if (sourceHasDifferentByteOrder)
+			return new CODEC<OtherByteOrder>; 
+		else
+			return new CODEC<SameByteOrder>;
+	}
+
+	virtual Codec* load(eckit::DataHandle *dh, bool sourceHasDifferentByteOrder)
+	{
+		if(sourceHasDifferentByteOrder)
+		{
+			CODEC<OtherByteOrder> *codec = new CODEC<OtherByteOrder>;
+			codec->load(dh);
+			return codec;
+		}
+		else
+		{
+			CODEC<SameByteOrder> *codec = new CODEC<SameByteOrder>;
+			codec->load(dh);
+			return codec;
+		}
+	}
+
+	virtual void save(Codec *codec, eckit::DataHandle *dh, bool sourceHasDifferentByteOrder)
+	{
+		if(sourceHasDifferentByteOrder)
+		{
+			CODEC<OtherByteOrder> *theCodec = static_cast<CODEC<OtherByteOrder> *>(codec);
+			theCodec->save(dh);
+		}
+		else
+		{
+			CODEC<SameByteOrder> *theCodec = static_cast<CODEC<SameByteOrder> *>(codec);
+			theCodec->save(dh);
+		}
+	}
+};
+
+}
+}
+
+
+#endif // odblib_CodecFactory_H
diff --git a/odb_api/src/odb_api/CodecOptimizer.cc b/odb_api/src/odb_api/CodecOptimizer.cc
new file mode 100644
index 0000000..9fa3102
--- /dev/null
+++ b/odb_api/src/odb_api/CodecOptimizer.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file CodecOptimizer.cc
+///
+/// @author Piotr Kuchta, Jan 2010
+
+#include "eckit/config/Resource.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/CodecOptimizer.h"
+
+//
+
+namespace odb {
+namespace codec {
+
+CodecOptimizer::CodecOptimizer()
+: defaultCodec_()
+{
+	defaultCodec_[REAL] = "short_real"; // in the future: short_real2
+	defaultCodec_[DOUBLE] = "long_real";
+	defaultCodec_[STRING] = "chars";
+	defaultCodec_[INTEGER] = "int32";
+	defaultCodec_[BITFIELD] = "int32";
+
+    typedef eckit::StringTools S;
+    std::vector<std::string> mappings (S::split(",", eckit::Resource<std::string>("$ODB_DEFAULT_CODEC", "")));
+
+	for (size_t i = 0; i < mappings.size(); ++i)
+	{
+		std::vector<std::string> a(S::split(":", mappings[i]));
+		ASSERT("Wrong format of $ODB_DEFAULT_CODEC" && a.size() == 2);
+		defaultCodec_[Column::type(S::trim(a[0]))] = S::trim(a[1]);
+	}
+}
+
+} // namespace codec 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/CodecOptimizer.h b/odb_api/src/odb_api/CodecOptimizer.h
new file mode 100644
index 0000000..366eec5
--- /dev/null
+++ b/odb_api/src/odb_api/CodecOptimizer.h
@@ -0,0 +1,142 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file CodecOptimizer.h
+///
+/// @author Piotr Kuchta, Jan 2010
+
+#ifndef CodecOptimizer_H
+#define CodecOptimizer_H
+
+#include "eckit/eckit.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/MetaData.h"
+
+namespace odb {
+namespace codec {
+
+class Codec;
+
+class CodecOptimizer 
+{
+public:
+	CodecOptimizer();
+	template <typename DATASTREAM>
+		int setOptimalCodecs(MetaData& columns);
+private:
+    std::map<ColumnType, std::string> defaultCodec_;
+};
+
+template <typename DATASTREAM>
+int CodecOptimizer::setOptimalCodecs(MetaData& columns)
+{
+	//ostream &LOG = eckit::Log::debug();
+	for (size_t i = 0; i < columns.size(); i++) {
+		Column& col = *columns[i];
+		long long n;
+		double min = col.min();
+		double max = col.max();
+		bool hasMissing = col.hasMissing();
+		double missing = col.missingValue();
+		//LOG << "CodecOptimizer::setOptimalCodecs: " << i << " " << col.name() << ", min=" << min << ", max=" << max << std::endl;
+		std::string codec(defaultCodec_[col.type()]);
+		switch(col.type())
+		{
+			case REAL:
+				if(max == min)
+					codec = col.hasMissing() ? "real_constant_or_missing" : "constant";
+				col.coder(Codec::findCodec<DATASTREAM>(codec, false));
+				col.hasMissing(hasMissing);
+				col.missingValue(missing);
+				col.min(min);
+				col.max(max);
+				//LOG << " REAL has values in range <" << col.min() << ", " << col.max()
+				//	<< ">. Codec: "  << col.coder()
+				//	<< std::endl;
+				break;
+
+			case DOUBLE:
+				if(max == min)
+					codec = col.hasMissing() ? "real_constant_or_missing" : "constant";
+				col.coder(Codec::findCodec<DATASTREAM>(codec, false));
+				col.hasMissing(hasMissing);
+				col.missingValue(missing);
+				col.min(min);
+				col.max(max);
+				//LOG << " DOUBLE has values in range <" << col.min() << ", " << col.max()
+				//	<< ">. Codec: "  << col.coder()
+				//	<< std::endl;
+				break;
+
+			case STRING:
+				{
+					n = col.coder().name() == "constant_string" ? 1 : col.coder().hashTable().nextIndex();
+					if(n == 1)
+						codec = "constant_string";
+					else if(n < 256)
+						codec = "int8_string";
+					else if(n < 65536)
+						codec = "int16_string";
+
+					Codec * newCodec = Codec::findCodec<DATASTREAM>(codec, false);
+					if (n > 1)
+						newCodec->hashTable(col.coder().giveHashTable());
+					col.coder(newCodec);
+					col.hasMissing(hasMissing);
+					col.missingValue(missing);
+					col.min(min);
+					col.max(max);
+					//LOG << " STRING has " << n << " different value(s). Codec: " << codec
+					//	<< std::endl;
+				}
+				break;
+
+			case BITFIELD:
+			case INTEGER:
+				n = max - min;
+				//LOG << " { min=" << min << ", max=" << max << ", n=" << n << "} ";
+				if(col.hasMissing())
+				{
+					if(n == 0) codec = "constant_or_missing";
+					else if(n < 0xff) codec = "int8_missing";
+					else if(n < 0xffff) codec = "int16_missing";
+				}
+				else
+				{
+					if(n == 0) codec = "constant";
+					else if(n <= 0xff) codec = "int8";
+					else if(n <= 0xffff) codec = "int16";
+				}
+				col.coder(Codec::findCodec<DATASTREAM>(codec, false));
+				col.hasMissing(hasMissing);
+				col.missingValue(missing);
+				col.min(min);
+				col.max(max);
+				//LOG << (col.type() == BITFIELD ? " BITFIELD" : " INTEGER")
+				//	<< " has " << n + 1 << " different value(s)"
+				//	<< (col.hasMissing() ? " and has missing value. " : ". ")
+				//	<< "Codec: " << col.coder() << "."
+				//	<< std::endl;
+				break;
+
+			default: eckit::Log::error() << "Unsupported type: [" << col.type() << "]" << std::endl;
+				break;
+		}
+
+		//if (odb::ODBAPISettings::debug && i == 28) eckit::Log::info() << ": AFTER " << col << " -> " << col.coder() << std::endl;
+	}
+	return 0;
+}
+
+} // namespace codec
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Column.cc b/odb_api/src/odb_api/Column.cc
new file mode 100644
index 0000000..8f3b8fc
--- /dev/null
+++ b/odb_api/src/odb_api/Column.cc
@@ -0,0 +1,131 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/eckit.h"
+#include "eckit/parser/StringTools.h"
+
+#include "odb_api/Column.h"
+
+using namespace eckit;
+
+namespace odb {
+
+Column::Column(MetaData &owner)
+: owner_(owner),
+  name_(),
+  type_(IGNORE),
+  coder_(0),
+  bitfieldDef_()
+{}
+
+Column::Column(const Column& o)
+: owner_(o.owner_),
+  name_(o.name_),
+  type_(o.type_),
+  coder_( 0),
+  bitfieldDef_(o.bitfieldDef_)
+{
+	*this = o;
+}
+
+Column::~Column()
+{
+    //cerr << "Column::~Column():@" << this  << ", name= " << name_ << std::endl << std::endl;
+	delete coder_; 
+}
+
+Column& Column::operator=(const Column& other)
+{
+	name(other.name()); 
+
+	type<DataStream<SameByteOrder, DataHandle> >(other.type(), false);
+	if (type_ == BITFIELD)
+		bitfieldDef(other.bitfieldDef());
+
+	//delete coder_;
+	//coder_ = (other.coder_)->clone();
+	//hasMissing(other.hasMissing());
+	missingValue(other.missingValue());
+	return *this;
+}
+
+const char *Column::columnTypeName(ColumnType type)
+{
+	switch(type) {
+		case IGNORE:   return "IGNORE";
+		case INTEGER:  return "INTEGER";
+		case REAL:     return "REAL";
+		case DOUBLE:   return "DOUBLE";
+		case STRING:   return "STRING";
+		case BITFIELD: return "BITFIELD";
+		default:       return "UNKNOWN_TYPE";
+	}
+}
+
+ColumnType Column::type(const std::string& t)
+{
+    std::string ut(StringTools::upper(t));
+	if (ut == "IGNORE") return IGNORE;
+	if (ut == "INTEGER") return INTEGER;
+	if (ut == "REAL") return REAL;
+	if (ut == "DOUBLE") return DOUBLE;
+	if (ut == "STRING") return STRING;
+	if (ut == "BITFIELD") return BITFIELD;
+
+	Log::error() << "Unknown type: '" << t << "'" << std::endl;
+	ASSERT(0 && "Unknown type");
+	return IGNORE;
+}
+
+bool Column::isConstant()
+{
+	// FIXME
+	return coder().name() == "constant"
+		|| coder().name() == "constant_string";
+}
+
+//Column::Column(DataHandle *dataHandle) : dataHandle_(dataHandle), name_(), type_(IGNORE/*?*/), coder_(0) {}
+
+
+/// return true if names and types are the same; do not compare codecs.
+bool Column::operator==(const Column& other) const
+{
+	if (name() == other.name() && type() == other.type())
+		return true;
+	return false;
+}
+
+void Column::print(std::ostream& s) const
+{
+	s << "name: " << name_ << ", ";
+	s << "type: " << columnTypeName(odb::ColumnType(type_));
+
+	if (type_ == BITFIELD)
+	{
+		FieldNames names = bitfieldDef_.first;
+		Sizes sizes = bitfieldDef_.second;
+		ASSERT(names.size() == sizes.size());
+		s << " [";
+		for (size_t i = 0; i < names.size(); ++i)
+			s << names[i] << ":" << sizes[i]
+				<< (i != names.size() - 1 ? ";" : "");
+		s << "] ";
+	}
+			
+	s << ", ";
+
+	s << "codec: ";
+	if (coder_) s << *coder_;
+	else s << "NONE";
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/Column.h b/odb_api/src/odb_api/Column.h
new file mode 100644
index 0000000..a9ea2c0
--- /dev/null
+++ b/odb_api/src/odb_api/Column.h
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Column_H
+#define Column_H
+
+#include "odb_api/Codec.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/Types.h"
+
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class Reader;
+class HashTable;
+class MetaData;
+
+class Column {
+
+public:
+	Column(MetaData &);
+	Column(const Column&);
+
+	static const char *columnTypeName(ColumnType type);
+
+	static ColumnType type(const std::string&);
+
+	Column& operator=(const Column&);
+
+	bool operator==(const Column&) const;
+	bool operator!=(const Column& other) const { return ! (*this == other); }
+
+	virtual ~Column();
+
+	template<typename DATASTREAM> void load(DATASTREAM &);
+	template<typename DATASTREAM> void save(DATASTREAM &);
+
+	codec::Codec& coder() const { return *coder_; }
+	void coder(codec::Codec *c) { delete coder_; coder_ = c; }
+
+	void name(const std::string name) { name_ = name; }
+	const std::string &name() const { return name_; }
+
+	template<typename DATASTREAM> void type(ColumnType t, bool differentByteOrder); 
+
+	void type(ColumnType t) { type_ = t; }
+	ColumnType type() const { return ColumnType(type_); }
+
+	bool isConstant();
+
+	/// Delegations to Codec:
+
+	void hasMissing(bool h) { coder_->hasMissing(h); }
+	int32_t hasMissing() const { return coder_->hasMissing(); }
+
+	void min(double m) { coder_->min(m); }
+	double min() const { return coder_->min(); }
+
+	void max(double m) { coder_->max(m); }
+	double max() const { return coder_->max(); }
+
+	void missingValue(double v) { coder_->missingValue(v); }
+	double missingValue() const { return coder_->missingValue(); } 
+
+	void resetStats() { coder_->resetStats(); }
+
+	void bitfieldDef(const BitfieldDef& b) { bitfieldDef_ = b; }
+	const BitfieldDef& bitfieldDef() const { return bitfieldDef_; }
+
+	virtual void print(std::ostream& s) const;
+
+#ifdef SWIGPYTHON
+	const std::string __repr__()
+	{
+		return //std::string("<") + 
+			name_ + ":" + columnTypeName(odb::ColumnType(type_))
+		 //+ ">" 
+			;
+	}
+	const std::string __str__() { return name_; }
+#endif
+
+	friend std::ostream& operator<<(std::ostream& s, const Column& p)
+		{ p.print(s); return s; }
+
+private:
+
+	MetaData& owner_;
+	std::string name_;
+	/// Note: type_ should be ColumnType, but it is saved on file so must be of a fixed size type.
+	int32_t type_;
+	codec::Codec* coder_;
+	/// bitfieldDef_ is not empty if type_ == BITFIELD.
+	BitfieldDef bitfieldDef_;
+	//std::string typeSignature_;
+
+};
+
+template<typename DATASTREAM> void Column::load(DATASTREAM &f)
+{
+	f.readString(name_);
+	f.readInt32(type_);
+	if (type_ == BITFIELD)
+	{
+		FieldNames names;
+		Sizes sizes;
+		bitfieldDef_ = make_pair(names, sizes);
+		f.readBitfieldDef(bitfieldDef_);
+	}
+
+	coder(codec::Codec::loadCodec(f));
+}
+
+template<typename DATASTREAM> void Column::save(DATASTREAM &f)
+{
+	f.writeString(name_);
+	f.writeInt32(type_);
+
+	if (type_ == BITFIELD)
+		f.writeBitfieldDef(bitfieldDef_);
+
+	coder().save(f);
+}
+
+template <typename DATASTREAM> 
+void Column::type(ColumnType t, bool differentByteOrder)
+{
+	type_ = t;
+	std::string codecName;
+	switch (type_)
+	{
+		case INTEGER:  codecName = "int32"; break;
+		case BITFIELD:
+                       // TODO: 'unsigned_int64'
+                       codecName = "int32"; break;
+		case REAL:     codecName = "long_real"; break;
+		case DOUBLE:   codecName = "long_real"; break;
+		case STRING:   codecName = "chars"; break;
+		default:
+			ASSERT(!"Type not supported");
+			break;
+	}
+	coder(codec::Codec::findCodec<DATASTREAM>(codecName, differentByteOrder));
+
+    // TODO: when we have codec unsigned_int64 it will have 0 as 
+    if (type_ == BITFIELD) missingValue(MDI::bitfieldMDI());
+}
+
+} // namespace odb {
+
+#endif
diff --git a/odb_api/src/odb_api/ColumnExpression.cc b/odb_api/src/odb_api/ColumnExpression.cc
new file mode 100755
index 0000000..64569ad
--- /dev/null
+++ b/odb_api/src/odb_api/ColumnExpression.cc
@@ -0,0 +1,197 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include <sstream>
+
+#include "eckit/eckit.h"
+#include "eckit/utils/Translator.h"
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/SQLColumn.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLTable.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+static std::pair<double,bool> zero_(0,false);
+
+ColumnExpression::ColumnExpression(const std::string& name, SQLTable* table, int begin, int end)
+: type_(0),
+  value_(&zero_),
+  columnName_(name),
+  table_(table),
+  tableReference_(),
+  beginIndex_(begin),
+  endIndex_(end),
+  nominalShift_(0)
+{}
+
+ColumnExpression::ColumnExpression(const std::string& name, const std::string& tableReference, int begin, int end)
+: type_(0),
+  value_(&zero_),
+  columnName_(name),
+  table_(0),
+  tableReference_(tableReference),
+  beginIndex_(begin),
+  endIndex_(end),
+  nominalShift_(0)
+{}
+
+ColumnExpression::ColumnExpression(const ColumnExpression& e)
+: type_(e.type_),
+  value_(e.value_),
+  columnName_(e.columnName_),
+  table_(e.table_),
+  tableReference_(e.tableReference_),
+  beginIndex_(e.beginIndex_),
+  endIndex_(e.endIndex_),
+  nominalShift_(e.nominalShift_)
+{}
+
+SQLExpression* ColumnExpression::clone() const { return new ColumnExpression(*this); }
+
+ColumnExpression::~ColumnExpression() {}
+
+double ColumnExpression::eval(bool& missing) const
+{
+	if(value_->second)
+		missing = true;
+	return value_->first;
+}
+
+void ColumnExpression::prepare(SQLSelect& sql)
+{
+	std::string fullName;
+
+	SQLTable* table = sql.findTable(columnName_, &fullName, &hasMissingValue_, &missingValue_, &isBitfield_, &bitfieldDef_);
+
+	if(!table_) table_ = table;
+ 
+	value_ = sql.column(columnName_, table_);
+	type_  = sql.typeOf(columnName_, table_);
+
+	Log::debug() << "ColumnExpression::prepare: columnName_=" << columnName_ 
+	<< ", title=" << title()
+	<< ", table=" << table_->name()
+	<< ", fullName =" << fullName
+	<< " type=" << *type_ 
+	<< " bitfieldDef.first.size =" << bitfieldDef_.first.size()
+	<< std::endl;
+
+	if (columnName_ == title() && columnName_ != fullName)
+	{
+		title(fullName);
+
+		Log::debug() << "ColumnExpression::prepare: columnName_=" << columnName_ 
+		<< ", title[PATCHED]=" << title()
+		<< std::endl;
+	}
+}
+
+void ColumnExpression::cleanup(SQLSelect& sql)
+{
+	value_ = &zero_;
+	type_  = 0;
+}
+
+
+void ColumnExpression::print(std::ostream& s) const 
+{
+	s << columnName_;
+	if (nominalShift_ != 0)
+		s << "#" << nominalShift_;
+	//if(table_) s << "@" << table_->fullName();
+}
+
+void ColumnExpression::output(SQLOutput& o) const 
+{ 
+	bool missing = false;
+	double v = eval(missing);
+	type_->output(o, v, missing); 
+}
+
+void ColumnExpression::expandStars(const std::vector<SQLTable*>& tables, expression::Expressions& e)
+{
+    std::ostream& L(Log::debug());
+	L << "ColumnExpression::expandStars: expanding '" << columnName_ << "' (" << tableReference_ << ")" << std::endl;
+
+	if(beginIndex_ != -1 && endIndex_ != -1)
+	{
+		ASSERT(beginIndex_ <= endIndex_);
+		for(int i = beginIndex_; i <= endIndex_; i++)
+			e.push_back(new ColumnExpression(columnName_ + "_" + Translator<int,std::string>()(i), this->table()));
+		return;
+	}
+
+	if(columnName_ != "*")
+	{
+        // replace ColumnExpressions referring to aliases created with AS with references to appropriate expressions 
+        // If it's not an alias then:
+		e.push_back(this);
+		return;
+	}
+
+    std::stringstream ss;
+	
+	unsigned int matched = 0;
+    for(std::vector<SQLTable*>::const_iterator j = tables.begin();  j != tables.end(); ++j)
+	{
+		SQLTable* table = (*j);
+        std::vector<std::string> names = table->columnNames();
+
+		for(size_t i = 0; i < names.size(); i++)
+		{
+			if ((tableReference_.size())
+				&& ((names[i].rfind(tableReference_) == std::string::npos)
+					|| (names[i].rfind(tableReference_) + tableReference_.size() < names[i].size())))
+			{
+				L << "ColumnExpression::expandStars: skip '" << names[i] << "'" << std::endl;
+				continue;
+			}
+			
+			ss << names[i] << ", ";
+			++matched;
+			e.push_back(new ColumnExpression(names[i], table));
+		}
+	}
+	if (! matched)
+		throw eckit::UserError(std::string("No columns matching ") + columnName_ + tableReference_ + " found.");
+
+	L << "ColumnExpression::expandStars: added " << ss.str() << std::endl;
+	delete this;
+}
+
+bool ColumnExpression::indexed() 
+{ 
+	ASSERT(table_);
+	SQLColumn* col = table_->column(columnName_);
+	return col->hasIndex();
+}
+
+SQLIndex* ColumnExpression::getIndex(double* value)
+{
+	ASSERT(table_);
+	SQLColumn* col = table_->column(columnName_);
+	return col->getIndex(value);
+}
+
+void ColumnExpression::tables(std::set<SQLTable*>& t) 
+{ 
+	ASSERT(table_);
+	t.insert(table_); 
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ColumnExpression.h b/odb_api/src/odb_api/ColumnExpression.h
new file mode 100755
index 0000000..38c1e6a
--- /dev/null
+++ b/odb_api/src/odb_api/ColumnExpression.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ColumnExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+#ifndef ColumnExpression_H
+#define ColumnExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace expression {
+
+class ColumnExpression : public SQLExpression {
+public:
+	ColumnExpression(const std::string&, SQLTable*, int begin = -1, int end = -1);
+	ColumnExpression(const std::string&, const std::string& tableReference, int begin = -1, int end = -1);
+	ColumnExpression(const ColumnExpression&);
+	~ColumnExpression(); 
+
+	SQLTable* table() { return table_; }
+	double* current() { return &(value_->first); }
+	SQLExpression* clone() const;
+
+	SQLExpression* nominalShift(int n) { nominalShift_ = n; return this; }
+
+protected:
+	const type::SQLType*   type_;
+    std::pair<double,bool>*     value_;
+	std::string                 columnName_;
+	SQLTable*              table_;
+	std::string                 tableReference_;
+	int                    beginIndex_;
+	int                    endIndex_;
+	int                    nominalShift_;
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+	virtual double eval(bool& missing) const;
+	virtual bool isConstant() const { return false; }
+	virtual void output(SQLOutput& s) const;
+
+private:
+	ColumnExpression& operator=(const ColumnExpression&);
+
+// -- Overridden methods
+	virtual const type::SQLType* type() const { return type_; }
+    virtual void expandStars(const std::vector<SQLTable*>&, Expressions&);
+	virtual void tables(std::set<SQLTable*>&);
+	virtual bool indexed();
+	virtual SQLIndex* getIndex(double*);
+
+	friend class SQLSelectFactory;
+
+	//friend std::ostream& operator<<(std::ostream& s,const ColumnExpression& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ColumnType.h b/odb_api/src/odb_api/ColumnType.h
new file mode 100644
index 0000000..f6ba43d
--- /dev/null
+++ b/odb_api/src/odb_api/ColumnType.h
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odblib_ColumnType_H
+#define odblib_ColumnType_H
+
+namespace odb {
+
+enum ColumnType
+{
+	IGNORE   = 0,
+	INTEGER  = 1,
+	REAL     = 2,
+	STRING   = 3,
+	BITFIELD = 4,
+	DOUBLE   = 5
+};
+
+} // namespace odb 
+
+#endif // odblib_ColumnType_H
diff --git a/odb_api/src/odb_api/CommandLineParser.cc b/odb_api/src/odb_api/CommandLineParser.cc
new file mode 100644
index 0000000..eeb72b3
--- /dev/null
+++ b/odb_api/src/odb_api/CommandLineParser.cc
@@ -0,0 +1,160 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// \file CommandLineParser.h
+///
+/// @author Piotr Kuchta, ECMWF, July 2009
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/utils/Translator.h"
+#include "odb_api/CommandLineParser.h"
+
+namespace odb {
+namespace tool {
+
+CommandLineParser::~CommandLineParser() {}
+
+CommandLineParser::CommandLineParser(int argc, char **argv)
+: commandLineParsed_(false),
+  argc_(argc),
+  argv_(argv),
+  registeredOptionsWithArguments_(),
+  optionsWithArguments_(),
+  optionsNoArguments_(),
+  parameters_()
+{}
+
+CommandLineParser::CommandLineParser(const CommandLineParser& other)
+{
+	CommandLineParser& self = *this;
+
+	self.registeredOptionsWithArguments_ = other.registeredOptionsWithArguments_;
+	self.parameters_ = const_cast<CommandLineParser&>(other).parameters();
+	self.optionsWithArguments_ = other.optionsWithArguments_;
+	self.optionsNoArguments_ = other.optionsNoArguments_;
+	self.commandLineParsed_ = true;
+
+	ASSERT(self.commandLineParsed_ == other.commandLineParsed_);
+}
+
+CommandLineParser& CommandLineParser::operator=(const CommandLineParser& other)
+{
+	if (this == &other) return *this;
+
+	CommandLineParser& self = *this;
+
+	self.registeredOptionsWithArguments_ = other.registeredOptionsWithArguments_;
+	self.parameters_ = const_cast<CommandLineParser&>(other).parameters();
+	self.optionsWithArguments_ = other.optionsWithArguments_;
+	self.optionsNoArguments_ = other.optionsNoArguments_;
+	self.commandLineParsed_ = true;
+
+	ASSERT(self.commandLineParsed_ == other.commandLineParsed_);
+
+	return *this;
+}
+
+int CommandLineParser::argc() { return argc_; }
+
+std::string CommandLineParser::argv(int i)
+{
+	if (i >= argc_)
+	{
+        std::stringstream ss;
+		ss << "Expected at least " << i << " command line parameters";
+		throw eckit::UserError(ss.str());
+	}
+	return argv_[i];
+}
+
+void CommandLineParser::registerOptionWithArgument(const std::string& option)
+{
+	registeredOptionsWithArguments_.insert(std::string(option));
+}
+
+const std::vector<std::string> CommandLineParser::parameters()
+{
+	if (! commandLineParsed_) parseCommandLine();
+	return parameters_;
+}
+
+bool CommandLineParser::optionIsSet(const std::string& option)
+{
+	if (! commandLineParsed_) parseCommandLine();
+
+	if (optionsNoArguments_.find(option) != optionsNoArguments_.end())
+		return true;
+
+	return optionsWithArguments_.find(option) != optionsWithArguments_.end();
+}
+
+template <typename T>
+T CommandLineParser::optionArgument(const std::string& option, T defaultValue)
+{
+	if (! commandLineParsed_) parseCommandLine();
+
+	std::map<std::string, std::string>::iterator it = optionsWithArguments_.find(option);
+	if (it == optionsWithArguments_.end())
+		return defaultValue;
+
+    eckit::Translator<std::string, T> translator;
+	return translator(it->second);
+}
+
+void CommandLineParser::parseCommandLine()
+{
+	for (int i = 0; i < argc(); ++i)
+	{
+		std::string s = argv(i);
+		if (s[0] != '-' || s.size() == 1)
+		{
+			parameters_.push_back(s);
+            eckit::Log::debug() << "CommandLineParser::parseCommandLine: parameter: " << s << std::endl;
+		}
+		else
+		{
+			if (registeredOptionsWithArguments_.find(s) != registeredOptionsWithArguments_.end())
+			{	
+				optionsWithArguments_[s] = argv(++i);
+                eckit::Log::debug() << "CommandLineParser::parseCommandLine: option with argument: "
+					<< s << " = " << optionsWithArguments_[s] << std::endl;
+			}
+			else
+			{
+				optionsNoArguments_.insert(s);
+                eckit::Log::debug() << "CommandLineParser::parseCommandLine: option with no argument: " << s << std::endl;
+			}
+		}
+	}
+	commandLineParsed_ = true;
+}
+
+void CommandLineParser::print(std::ostream& s) const
+{
+    for (std::set<std::string>::const_iterator i = optionsNoArguments_.begin(); i != optionsNoArguments_.end(); ++i)
+		s << *i << "  ";
+
+	for (std::map<std::string, std::string>::const_iterator i = optionsWithArguments_.begin(); i != optionsWithArguments_.end(); ++i)
+		s << i->first << " " << i->second << "  ";
+
+	for (size_t i = 0; i < parameters_.size(); ++i)
+		s << parameters_[i] << "  ";
+
+}
+
+// Template function's explicit instantiations.
+
+template std::string CommandLineParser::optionArgument(const std::string&, std::string);
+template int CommandLineParser::optionArgument(const std::string&, int);
+template long CommandLineParser::optionArgument(const std::string&, long);
+template double CommandLineParser::optionArgument(const std::string&, double);
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/CommandLineParser.h b/odb_api/src/odb_api/CommandLineParser.h
new file mode 100644
index 0000000..9b73a68
--- /dev/null
+++ b/odb_api/src/odb_api/CommandLineParser.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file CommandLineParser.h
+///
+/// @author Piotr Kuchta, ECMWF, July 2009
+
+#ifndef CommandLineParser_H
+#define CommandLineParser_H
+
+#include "eckit/eckit.h"
+
+namespace odb {
+namespace tool {
+
+class CommandLineParser {
+public:
+
+    CommandLineParser(int argc, char **argv);
+
+    CommandLineParser(const CommandLineParser&);
+    CommandLineParser& operator=(const CommandLineParser&);
+
+	virtual ~CommandLineParser();
+
+	void registerOptionWithArgument(const std::string&);
+
+	/// @return command line parameters (without the options starting with '-')
+	const std::vector<std::string> parameters();
+	std::string parameters(size_t i) { return parameters()[i]; }
+
+	/// @return true if argumentless option is set
+	bool optionIsSet(const std::string&);
+
+	/// @return value of the command line option passed to the tool converted 
+	///         to given type, or value of the second parameter if option not
+	///         present on command line.
+	template <typename T> T optionArgument(const std::string&, T defaultValue);
+
+	int argc();
+	std::string argv(int i);
+	char **argv() { return argv_; }
+
+private:
+    
+	void parseCommandLine();
+
+
+	bool commandLineParsed_;
+	int argc_;
+	char **argv_;
+
+    std::set<std::string> registeredOptionsWithArguments_;
+
+	std::map<std::string, std::string> optionsWithArguments_;
+    std::set<std::string> optionsNoArguments_;
+	std::vector<std::string> parameters_;
+
+	void print(std::ostream& s) const;
+	friend std::ostream& operator<<(std::ostream& s, const CommandLineParser& o) { o.print(s);  return s; }
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/Comparator.cc b/odb_api/src/odb_api/Comparator.cc
new file mode 100644
index 0000000..feef9b5
--- /dev/null
+++ b/odb_api/src/odb_api/Comparator.cc
@@ -0,0 +1,196 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/Tracer.h"
+ #include <string.h>
+
+using namespace std;
+using namespace eckit;
+
+class ValuesDifferent : public Exception {
+public:
+	ValuesDifferent(const std::string& what) : Exception(what) {}
+};
+
+
+namespace odb {
+
+Comparator::Comparator(bool checkMissingFlag)
+: nRow_(0),
+  checkMissingFlag_(checkMissingFlag),
+  NaN_isOK_(Resource<bool>("$ODB_API_NAN_IS_OK", false))
+{}
+
+
+void Comparator::compare(const PathName& p1, const PathName& p2)
+{
+	std::vector<std::string> noExcludedColumnTypes;
+	compare(p1, p2, noExcludedColumnTypes);
+}
+
+void Comparator::compare(eckit::DataHandle& l, eckit::DataHandle& r)
+{
+	std::vector<std::string> noExcludedColumnTypes;
+	odb::Reader oda1(l);
+	odb::Reader oda2(r);
+
+	odb::Reader::iterator it1(oda1.begin());
+	odb::Reader::iterator end1(oda1.end());
+	odb::Reader::iterator it2(oda2.begin());
+	odb::Reader::iterator end2(oda2.end());
+	
+	compare(it1, end1, it2, end2, "left", "right", noExcludedColumnTypes);
+}
+
+void Comparator::compare(const PathName& p1, const PathName& p2, const std::vector<std::string>& excludedColumnsTypes)
+{
+    Tracer t(Log::debug(), std::string("Comparator::compare: ") + p1 + ", " + p2);
+
+	odb::Reader oda1(p1);
+	odb::Reader oda2(p2);
+
+	odb::Reader::iterator it1(oda1.begin());
+	odb::Reader::iterator end1(oda1.end());
+	odb::Reader::iterator it2(oda2.begin());
+	odb::Reader::iterator end2(oda2.end());
+	
+	compare(it1, end1, it2, end2, p1, p2, excludedColumnsTypes);
+}
+
+void Comparator::raiseNotEqual(const Column& column, double d1, double d2) {
+    ColumnType type(column.type());
+    stringstream ss;
+    ss << "Values different in column " << column.name() << ": " 
+        << StringTool::valueAsString(d1, type) << " is not equal " << StringTool::valueAsString(d2, type) << endl;
+    throw ValuesDifferent(ss.str());
+}
+
+void Comparator::compare(int nCols, const double *data1, const double *data2, const MetaData& md1, const MetaData& md2)
+{
+    unsigned long long numberOfDifferences (0);
+    for (int i=0; i < nCols; i++)
+        try {
+            const Column& column(*md1[i]);
+            ColumnType type(column.type());
+            double d1 (data1[i]), d2 (data2[i]);
+
+            switch (type)
+            {
+                case STRING:
+                    if (strncmp(reinterpret_cast<const char*>(&d1), reinterpret_cast<const char*>(&d2), sizeof(double)))
+                        raiseNotEqual(column, d1, d2);
+                    break;
+                case INTEGER:
+                case BITFIELD:
+                case DOUBLE:
+                    if (! (same(d1, d2) || (NaN_isOK_ && (::isnan(d1) && ::isnan(d2)))))
+                        raiseNotEqual(column, d1, d2);
+                    break;
+                case REAL:
+                    if (! (same(float(d1), float(d2)) || (NaN_isOK_ && (::isnan(d1) && ::isnan(d2)))))
+                        raiseNotEqual(column, d1, d2);
+                    break;
+                case IGNORE:
+                default:
+                    ASSERT(!"Unknown type");
+                    break;
+            }
+        } catch (Exception &e) {
+            ++numberOfDifferences; 
+            Log::info() << "While comparing rows number " << nRow_ << ", columns " << i
+                << " found different." << std::endl;
+            Log::info() << " " << e.what() << std::endl;
+
+            Log::info() << " data1[" << i << "] = " << std::scientific << data1[i] << std::endl;
+            Log::info() << " data2[" << i << "] = " << std::scientific << data2[i] << std::endl;
+
+            Log::info() << " md1[" << i << "] = " << *md1[i] << std::endl;
+            Log::info() << " md2[" << i << "] = " << *md2[i] << std::endl;
+
+            //TODO: make it an option to stop when an error found
+            //throw;
+        }
+    if (numberOfDifferences)
+    {
+        stringstream ss;
+        ss << "Files differ. "; // << numberOfDifferences << " difference" << ((numberOfDifferences == 1) ? "" : "s") << " found.";
+        throw Exception(ss.str());
+    }
+}
+
+
+void Comparator::compare(const MetaData& metaData1, const MetaData& metaData2, const std::vector<std::string>& exColumnsTypes)
+{
+	ASSERT("Number of columns must be the same" && (metaData1.size() == metaData2.size()));
+
+	std::set<std::string> excludedColumnsTypes(exColumnsTypes.begin(), exColumnsTypes.end());
+
+	size_t size = metaData1.size();
+	for (size_t i = 0; i < size; i++)
+	{
+		Column &column1 = *metaData1[i];
+		Column &column2 = *metaData2[i];
+
+		try {
+			ASSERT(column1.name() == column2.name());
+
+			if (excludedColumnsTypes.find(column1.name()) == excludedColumnsTypes.end())
+			{
+				ASSERT(column1.type() == column2.type());
+
+				if (column1.type() == BITFIELD)
+					if (! (column1.bitfieldDef() == column2.bitfieldDef()))
+					{
+						Log::error() << "Comparator::compare: bitfield definitions for column "
+							<< i << " '" << column1.name() << "' differ." << std::endl;
+						ASSERT(column1.bitfieldDef() == column2.bitfieldDef());
+					}
+			}
+		
+			if (checkMissingFlag_)	
+			{
+				if (column1.missingValue() != column2.missingValue())
+				{
+					Log::warning() << column1.name() << " : " 
+						<< "column1.missingValue()=" << column1.missingValue() << ", " 
+						<< "column2.missingValue()=" << column2.missingValue() << ", " 
+						<< std::endl;
+					ASSERT(column1.missingValue() == column2.missingValue());
+				}
+			}
+			else
+            {
+				if (column1.missingValue() != column2.missingValue())
+				{
+					Log::warning() << column1.name() << " : " 
+						<< "column1.missingValue()=" << column1.missingValue() << ", " 
+						<< "column2.missingValue()=" << column2.missingValue() << ", " 
+						<< std::endl;
+				}
+			}
+
+			if (column1.hasMissing() && column2.hasMissing())
+				ASSERT(column1.missingValue() == column2.missingValue());
+		} catch (...) {
+            Log::info() << "While comparing column " << i << ": "
+				<< column1.name() << std::endl;
+			throw;
+		}
+	}
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Comparator.h b/odb_api/src/odb_api/Comparator.h
new file mode 100644
index 0000000..3a5fbf1
--- /dev/null
+++ b/odb_api/src/odb_api/Comparator.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Comparator_H
+#define Comparator_H
+
+#include <cmath>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+
+namespace eckit {
+    class PathName;
+    class DataHandle;
+}
+
+namespace odb {
+
+class MetaData;
+class Column;
+
+const double maxAbsoluteError = 1e-9;
+const double maxRelativeError = 1e-9;
+
+
+class Comparator {
+public:
+	Comparator(bool checkMissingFlag = true);
+
+	void operator()() { run(); }
+	void run(); 
+
+	template <typename T1, typename T2>
+		bool compare(T1& it1, const T1& end1, T2& it2, const T2& end2, const std::string& desc1, const std::string& desc2);
+
+	template <typename T1, typename T2>
+		bool compare(T1& it1, const T1& end1, T2& it2, const T2& end2, const std::string& desc1, const std::string& desc2,
+					const std::vector<std::string>& excludedColumnsTypes);
+
+	void compare(eckit::DataHandle&, eckit::DataHandle&);
+	void compare(const eckit::PathName&, const eckit::PathName&);
+	void compare(const eckit::PathName&, const eckit::PathName&, const std::vector<std::string>& excludedColumnsTypes);
+
+	void compare(const MetaData&, const MetaData&, const std::vector<std::string>&);
+	void compare(int nCols, const double *data1, const double *data2, const MetaData&, const MetaData&);
+
+	void checkMissingFlag(bool v) { checkMissingFlag_ = v; }
+
+	inline static double err(double A, double B)
+	{
+		double relativeError;
+
+		if(fabs(A) <= maxAbsoluteError || fabs(B) <= maxAbsoluteError)
+			relativeError = fabs(A-B);
+		else if (fabs(B) > fabs(A))
+			relativeError = fabs((A - B) / B);
+		else
+			relativeError = fabs((A - B) / A);
+		return relativeError;
+	}
+
+	inline static int same(double A,double B) { return err(A,B) < maxRelativeError; }
+
+    void raiseNotEqual(const Column&, double, double);
+
+private:
+	long nRow_;
+	bool checkMissingFlag_;
+	bool NaN_isOK_;
+};
+
+template<typename T1, typename T2>
+bool Comparator::compare(T1& it1, const T1& end1, T2& it2, const T2& end2, const std::string& desc1, const std::string& desc2)
+{
+	std::vector<std::string> noExcludedColumns;
+	return compare(it1, end1, it2, end2, desc1, desc2, noExcludedColumns);
+}
+
+template<typename T1, typename T2>
+bool Comparator::compare(T1& it1, const T1& end1, T2& it2, const T2& end2, const std::string& desc1, const std::string& desc2,
+						const std::vector<std::string>& excludedColumnsTypes)
+{
+	eckit::Log::info() << "Comparator::compare: (1) " << desc1 << " to (2) " << desc2 << std::endl;
+
+	nRow_ = 0;
+
+	compare(it1->columns(), it2->columns(), excludedColumnsTypes);
+
+	for (; it1 != end1 && it2 != end2; ++it1, ++it2)
+	{
+		++nRow_;
+
+		if (it1->isNewDataset())
+			compare(it1->columns(), it2->columns(), excludedColumnsTypes);
+		if (it2->isNewDataset())
+			compare(it1->columns(), it2->columns(), excludedColumnsTypes);
+
+		compare(it1->columns().size(), it1->data(), it2->data(), it1->columns(), it2->columns());
+	}
+
+	ASSERT("First file has more rows!"  && ! (it1 != end1));
+	ASSERT("Second file has more rows!" && ! (it2 != end2));
+	return true; // ?
+}
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/ConstantExpression.cc b/odb_api/src/odb_api/ConstantExpression.cc
new file mode 100755
index 0000000..5d79cc5
--- /dev/null
+++ b/odb_api/src/odb_api/ConstantExpression.cc
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ConstantExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+ConstantExpression::ConstantExpression(double v, bool missing, const odb::sql::type::SQLType* type)
+: value_(v), missing_(missing), type_(*type)
+{}
+
+ConstantExpression::~ConstantExpression() {}
+
+void ConstantExpression::output(SQLOutput& o) const 
+{ 
+    type_.output(o, value_, missing_);
+}
+
+const type::SQLType* ConstantExpression::type() const { return &type_; }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ConstantExpression.h b/odb_api/src/odb_api/ConstantExpression.h
new file mode 100755
index 0000000..85ebbfb
--- /dev/null
+++ b/odb_api/src/odb_api/ConstantExpression.h
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ConstantExpression.h
+// Piotr Kuchta - ECMWF Oct 11
+
+#ifndef ConstantExpression_H
+#define ConstantExpression_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLSelect;
+class SQLTable;
+class SQLIndex;
+class SQLOutput;
+
+namespace expression {
+
+class ConstantExpression : public SQLExpression {
+public:
+	ConstantExpression(double, bool, const odb::sql::type::SQLType*);
+	virtual ~ConstantExpression(); 
+
+	virtual void prepare(SQLSelect&) { NOTIMP; }
+	virtual void cleanup(SQLSelect&) { NOTIMP; }
+
+	// -- For WHERE
+	virtual double eval(bool& missing) const { missing = missing_; return value_; }
+
+	virtual bool andSplit(expression::Expressions&) { return false; }
+	virtual void tables(std::set<SQLTable*>&) {}
+
+	virtual bool isConstant() const { return true; }
+	virtual bool isNumber() const { NOTIMP; return false; }
+
+	//virtual SQLExpression* simplify(bool&);
+	//virtual void title(const std::string&);
+	//virtual std::string title() const;
+
+	virtual const odb::sql::type::SQLType* type() const;
+	// ----
+
+	virtual SQLExpression* clone() const { NOTIMP; return 0; }
+	
+	virtual bool isAggregate() const { return false; }
+	// For select expression
+
+	virtual void output(SQLOutput& s) const;
+	virtual void partialResult() { NOTIMP; }
+	virtual void expandStars(const std::vector<SQLTable*>&,expression::Expressions&) { NOTIMP; }
+
+	virtual bool isBitfield() const { return isBitfield_; }
+	BitfieldDef bitfieldDef() const { return bitfieldDef_; }
+	virtual bool hasMissingValue() const { return hasMissingValue_; }
+	double missingValue() const { return missingValue_; }
+
+	virtual bool indexed()  { return false; }
+	virtual bool useIndex() { return false; }
+	virtual SQLIndex* getIndex(double* = 0) { return 0; }
+
+protected:
+	virtual void print(std::ostream&) const { NOTIMP; }; 
+
+	bool isBitfield_;
+	BitfieldDef bitfieldDef_;
+	bool hasMissingValue_;
+	double missingValue_;
+
+private:
+	ConstantExpression(const ConstantExpression&);
+	ConstantExpression& operator=(const ConstantExpression&);
+
+	double value_;
+	bool missing_;
+	const odb::sql::type::SQLType& type_;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+
+#endif
diff --git a/odb_api/src/odb_api/ConstantSetter.cc b/odb_api/src/odb_api/ConstantSetter.cc
new file mode 100644
index 0000000..fca7e56
--- /dev/null
+++ b/odb_api/src/odb_api/ConstantSetter.cc
@@ -0,0 +1,14 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+namespace odb {
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/ConstantSetter.h b/odb_api/src/odb_api/ConstantSetter.h
new file mode 100644
index 0000000..8efef17
--- /dev/null
+++ b/odb_api/src/odb_api/ConstantSetter.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ConstantSetter_H
+#define ConstantSetter_H
+
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/ODAUpdatingIterator.h"
+
+namespace odb {
+
+template <typename T>
+class ConstantSetter
+{
+public:
+	typedef typename odb::ODAUpdatingIterator<T> iterator_class;
+	typedef typename odb::IteratorProxy<iterator_class, ConstantSetter, const double> iterator;
+
+	ConstantSetter(const T& b, const T& e, const std::vector<std::string>& columns, const std::vector<double>& values)
+	: ii_(b), end_(e), columns_(columns), values_(values)
+	{}
+
+	~ConstantSetter() {}
+
+	iterator begin() { return iterator(new iterator_class(ii_, end_, columns_, values_)); }
+	const iterator end() { return iterator(new iterator_class(end_)); }
+
+private:
+	T ii_;
+	const T& end_;
+	const std::vector<std::string> columns_;
+	const std::vector<double> values_;
+};
+
+} // namespace odb
+
+#include "odb_api/ConstantSetter.cc"
+
+#endif
+
diff --git a/odb_api/src/odb_api/DataColumn.cc b/odb_api/src/odb_api/DataColumn.cc
new file mode 100644
index 0000000..d7d2caf
--- /dev/null
+++ b/odb_api/src/odb_api/DataColumn.cc
@@ -0,0 +1,75 @@
+/// @file   DataColumn.cc
+/// @author Tomas Kral
+
+#include "odb_api/Column.h"
+#include "odb_api/DataColumn.h"
+
+using namespace std;
+
+namespace odb {
+
+DataColumn::DataColumn(const std::string& name, ColumnType type)
+  : name_(name),
+    type_(type),
+    missingValue_(defaultMissingValue(type)),
+    defaultValue_(missingValue_),
+    bitfieldDef_()
+{}
+
+DataColumn::DataColumn(const std::string& name, ColumnType type, double missingValue)
+  : name_(name),
+    type_(type),
+    missingValue_(missingValue),
+    defaultValue_(missingValue_),
+    bitfieldDef_()
+{}
+
+DataColumn::DataColumn(const std::string& name, ColumnType type, double missingValue,
+        double defaultValue)
+  : name_(name),
+    type_(type),
+    missingValue_(missingValue),
+    defaultValue_(defaultValue),
+    bitfieldDef_()
+{}
+
+DataColumn::DataColumn(const odb::Column& column)
+  : name_(column.name()),
+    type_(column.type()),
+    missingValue_(defaultMissingValue(type_)),
+    defaultValue_(missingValue_),
+    bitfieldDef_(column.bitfieldDef())
+{}
+
+double DataColumn::defaultMissingValue(ColumnType type)
+{
+    switch (type)
+    {
+        case INTEGER:  return odb::MDI::integerMDI();
+        case REAL:     return odb::MDI::realMDI();
+        case DOUBLE:   return odb::MDI::realMDI();
+        case STRING:   return *reinterpret_cast<const double*>("        ");
+        case BITFIELD: return 0;
+        default: ASSERT(!"Unexpected odb::ColumnType.");
+    };
+
+    return 0;
+}
+
+bool DataColumn::operator==(const DataColumn& other) const
+{
+    return (name_ == other.name_)
+        && (type_ == other.type_)
+        && (missingValue_ == other.missingValue_)
+        && (defaultValue_ == other.defaultValue_);
+}
+
+bool DataColumn::operator!=(const DataColumn& other) const
+{
+    return (name_ != other.name_)
+        || (type_ != other.type_)
+        || (missingValue_ != other.missingValue_)
+        || (defaultValue_ != other.defaultValue_);
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataColumn.h b/odb_api/src/odb_api/DataColumn.h
new file mode 100644
index 0000000..c93a2dd
--- /dev/null
+++ b/odb_api/src/odb_api/DataColumn.h
@@ -0,0 +1,90 @@
+/// @file   DataColumn.h
+/// @author Tomas Kral
+
+#ifndef odb_sql_DataColumn_H
+#define odb_sql_DataColumn_H
+
+#include "eckit/eckit.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/Types.h"
+
+namespace odb {
+
+class Column;
+
+/*! Represents a single column of a table.
+ *
+ *  The DataColumn class is a basic building block for defining the schema
+ *  (or structure) of a DataTable. The schema is created by adding one or
+ *  more DataColumn objects into a DataColumns collection.
+ *
+ *  Each DataColumn has a name, type, missing and default values. Columns
+ *  representing bitfields also provide a bitfield definition.
+ *
+ *  @ingroup data
+ */
+class DataColumn
+{
+public:
+    /// Creates a new column given its @e name and @e type.
+    DataColumn(const std::string& name, ColumnType type);
+
+    /// Creates a new column given its @e name, @e type and missing value.
+    DataColumn(const std::string& name, ColumnType type, double missingValue);
+
+    /// Creates a new column given its @e name, @e type, missing and default values.
+    DataColumn(const std::string& name, ColumnType type, double missingValue,
+            double defaultValue);
+
+    /// Creates a new column copying all the properties from an odb::Column.
+    DataColumn(const odb::Column& column);
+
+    /// Returns the column's name.
+    const std::string& name() const { return name_; }
+
+    /// Returns the column's data type.
+    ColumnType type() const { return type_; }
+
+    /// Returns the column's missing value.
+    double missingValue() const { return missingValue_; }
+
+    /// Sets the column's missing value.
+    void missingValue(double value) { missingValue_ = value; }
+
+    /// Returns the column's default value.
+    double defaultValue() const { return defaultValue_; }
+
+    /// Sets the column's default value.
+    void defaultValue(double value) { defaultValue_ = value; }
+
+    /// Returns the column's bitfield definition.
+    const odb::BitfieldDef& bitfieldDef() const { return bitfieldDef_; }
+
+    /// Sets the column's bitfield definition.
+    void bitfieldDef(const odb::BitfieldDef& def) { bitfieldDef_ = def; }
+
+    /// Compares two columns for equality.
+    /// Returns @c true if the name, type, missing and default values of the
+    /// two columns are equal, otherwise returns @c false.
+
+    bool operator==(const DataColumn& other) const;
+
+    /// Compares two columns for in-equality.
+    /// Returns @c true if either the name, type, missing or default values of the
+    /// two columns are not equal, otherwise returns @c false.
+
+    bool operator!=(const DataColumn& other) const;
+
+private:
+    static double defaultMissingValue(ColumnType type);
+
+    std::string name_;
+    ColumnType type_;
+    double missingValue_;
+    double defaultValue_;
+    odb::BitfieldDef bitfieldDef_;
+};
+
+} // namespace odb
+
+#endif // DATACOLUMN_H_
diff --git a/odb_api/src/odb_api/DataColumns.cc b/odb_api/src/odb_api/DataColumns.cc
new file mode 100644
index 0000000..e59684e
--- /dev/null
+++ b/odb_api/src/odb_api/DataColumns.cc
@@ -0,0 +1,112 @@
+/// @file   DataColumns.cc
+/// @author Tomas Kral
+
+#include "eckit/eckit.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/MetaData.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+DataColumns::DataColumns()
+  : std::vector<DataColumn>()
+{}
+
+DataColumns::DataColumns(const odb::MetaData& metadata)
+  : std::vector<DataColumn>()
+{
+    reserve(metadata.size());
+
+    for (size_t i = 0; i < metadata.size(); i++)
+    {
+        odb::Column& column = *metadata[i];
+        push_back(DataColumn(column));
+    }
+}
+
+DataColumns::DataColumns(const DataColumns& other)
+  : std::vector<DataColumn>(other.begin(), other.end())
+{}
+
+DataColumns::~DataColumns()
+{}
+
+DataColumns DataColumns::operator+(const DataColumns& other) const
+{
+    DataColumns columns(*this);
+    columns.insert(columns.end(), other.begin(), other.end());
+    return columns;
+}
+
+DataColumns& DataColumns::operator+=(const DataColumns& other)
+{
+    insert(end(), other.begin(), other.end());
+    return *this;
+}
+
+size_t DataColumns::indexOf(const std::string& name) const
+{
+    std::vector<size_t> indices;
+
+    for (size_t i = 0; i < size(); i++)
+        if (at(i).name() == name || at(i).name().find(name + "@") == 0)
+                indices.push_back(i);
+
+    if (indices.size() > 1)
+        throw eckit::UserError(std::string("Ambiguous column name: '") + name + "'");
+
+    if (indices.size() == 0)
+        throw eckit::UserError(std::string("Column '") + name + "' not found.");
+
+    return indices[0];
+}
+
+void DataColumns::add(const std::string& name, const std::string& typeName)
+{
+    ColumnType type;
+
+    if      (typeName == "INTEGER")  type = INTEGER;
+    else if (typeName == "REAL")     type = REAL;
+    else if (typeName == "DOUBLE")   type = DOUBLE;
+    else if (typeName == "STRING")   type = STRING;
+    else if (typeName == "BITFIELD") type = BITFIELD;
+    else
+        throw eckit::UserError("Unsupported column type: " + typeName);
+
+    push_back(DataColumn(name, type));
+}
+
+DataColumns& DataColumns::operator=(const odb::MetaData& metadata)
+{
+    clear();
+    reserve(metadata.size());
+
+    for (size_t i = 0; i < metadata.size(); i++)
+    {
+        odb::Column& column = *metadata[i];
+        push_back(DataColumn(column));
+    }
+
+    return *this;
+}
+
+bool DataColumns::operator==(const DataColumns& other) const
+{
+    if (this == &other)
+        return true;
+
+    if (size() != other.size())
+        return false;
+
+    for (size_t i = 0; i < size(); ++i)
+    {
+        if (!(at(i) == other.at(i)))
+            return false;
+    }
+
+    return true;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataColumns.h b/odb_api/src/odb_api/DataColumns.h
new file mode 100644
index 0000000..244c56e
--- /dev/null
+++ b/odb_api/src/odb_api/DataColumns.h
@@ -0,0 +1,82 @@
+/// @file   DataColumns.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATACOLUMNS_H_
+#define ODBLIB_DATACOLUMNS_H_
+
+#include "odb_api/DataColumn.h"
+
+namespace odb { class MetaData; }
+
+namespace odb {
+
+/*! Represents a collection of table columns.
+ *
+ *  The DataColumns class defines a schema of a DataTable. You can access
+ *  the DataColumns through the @ref DataTable::columns "columns()"
+ *  method of a DataTable class.
+ *
+ *  @ingroup data
+ */
+class DataColumns : private std::vector<DataColumn>
+{
+public:
+    using std::vector<DataColumn>::push_back;
+    using std::vector<DataColumn>::size;
+    using std::vector<DataColumn>::empty;
+    using std::vector<DataColumn>::begin;
+    using std::vector<DataColumn>::end;
+    using std::vector<DataColumn>::clear;
+    using std::vector<DataColumn>::operator[];
+    using std::vector<DataColumn>::operator=;
+
+    /// Iterates through columns in the collection.
+    typedef std::vector<DataColumn>::iterator iterator;
+
+    /// Iterates through columns in the collection (const version).
+    typedef std::vector<DataColumn>::const_iterator const_iterator;
+
+    /// Creates a new empty collection of columns.
+    DataColumns();
+
+    /// Creates and populates a new collection of columns.
+    DataColumns(const odb::MetaData& metadata);
+
+    /// Creates and populates a new collection of columns.
+    DataColumns(const DataColumns& other);
+
+    /// Destroys the collection.
+    ~DataColumns();
+
+    /// Returns column of the given name.
+    DataColumn& operator[](const std::string& name)
+    { return (*this)[indexOf(name)]; }
+
+    /// Returns column of the given name (const overload).
+    const DataColumn& operator[](const std::string& name) const
+    { return (*this)[indexOf(name)]; }
+
+    /// Returns joined collection of the current and the @p other collection.
+    DataColumns operator+(const DataColumns& other) const;
+
+    /// Adds columns from the @p other collection to the current one.
+    DataColumns& operator+=(const DataColumns& other);
+
+    /// Compares two column collections.
+    /// Returns @c true if the two column collections contains the same columns.
+    bool operator==(const DataColumns& other) const;
+
+    /// Returns index of the column given its name.
+    size_t indexOf(const std::string& name) const;
+
+    /// Adds a new column of the given name and type into the collection.
+    void add(const std::string& name, const std::string& type);
+
+    /// Assigns columns from ODB metadata.
+    /// This method is provided for inter-operability with ODB library.
+    DataColumns& operator=(const odb::MetaData& metadata);
+};
+
+} // namespace odb
+
+#endif // ODBLIB_DATACOLUMNS_H_
diff --git a/odb_api/src/odb_api/DataField.cc b/odb_api/src/odb_api/DataField.cc
new file mode 100644
index 0000000..f38ee9b
--- /dev/null
+++ b/odb_api/src/odb_api/DataField.cc
@@ -0,0 +1,39 @@
+/// @file   DataField.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataField.h"
+
+using namespace std;
+
+namespace odb {
+
+DataField::DataField(DataRow& row, const DataColumn& column, size_t index)
+  : row_(&row),
+    column_(&column),
+    index_(index)
+{}
+
+std::ostream& operator<<(std::ostream& stream, const DataField& field)
+{
+    stream << field.name() << ": ";
+
+    switch (field.type())
+    {
+        case INTEGER: stream << field.as<long>();
+        case REAL:    stream << field.as<float>();
+        case DOUBLE:  stream << field.as<double>();
+        case STRING:  stream << field.as<std::string>();
+        case BITFIELD:
+            stream << hex << setiosflags(ios_base::showbase);
+            stream << field.as<unsigned int>();
+            stream << dec << resetiosflags(ios_base::showbase);
+            break;
+            
+        default: ASSERT(!"Unknown DataType!");
+    }
+
+    return stream;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataField.h b/odb_api/src/odb_api/DataField.h
new file mode 100644
index 0000000..afae0f5
--- /dev/null
+++ b/odb_api/src/odb_api/DataField.h
@@ -0,0 +1,71 @@
+/// @file   DataField.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATAFIELD_H_
+#define ODBLIB_DATAFIELD_H_
+
+#include "eckit/eckit.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/DataColumn.h"
+#include "odb_api/DataRow.h"
+
+namespace odb {
+
+class DataRecord;
+
+/** @brief Represents a single field of a data table.
+ *
+ *  @see DataRecord
+ *  @ingroup data
+ */
+class DataField
+{
+public:
+    /// Returns the field's name.
+    const std::string& name() const
+    { return column_->name(); }
+
+    /// Returns the field's data type.
+    ColumnType type() const
+    { return column_->type(); }
+
+    /// Returns the field's value casted to the given type.
+    template <typename T>
+    T as() const { return row_->get<T>(index_); }
+
+    /// Returns the field's missing value.
+    double missingValue() const
+    { return column_->missingValue(); }
+
+    /// Returns the field's default value.
+    double defaultValue() const
+    { return column_->defaultValue(); }
+
+    /// Returns @c true if the field's value is @c NULL (i.e. missing value),
+    /// otherwise returns @c false.
+    bool isNull() const
+    { return (*row_)[index_] == column_->missingValue(); }
+
+    /// Sets the field's value to the @em value.
+    template <typename T>
+    DataField& operator=(const T& value)
+    { row_->DataRow::set<T>(index_, value); return *this; }
+
+private:
+    /// Creates new DataField instance.
+    DataField(DataRow& row, const DataColumn& column, size_t index);
+
+    DataField& operator=(const DataField&);
+
+    DataRow* const row_;
+    const DataColumn* const column_;
+    size_t index_;
+
+    friend class DataRecord;
+};
+
+std::ostream& operator<<(std::ostream& stream, const DataField& field);
+
+} // namespace odb
+
+#endif // ODBLIB_DATAFIELD_H_
diff --git a/odb_api/src/odb_api/DataJoin.cc b/odb_api/src/odb_api/DataJoin.cc
new file mode 100644
index 0000000..068be23
--- /dev/null
+++ b/odb_api/src/odb_api/DataJoin.cc
@@ -0,0 +1,40 @@
+/// @file   DataJoin.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataJoin.h"
+#include "odb_api/DataLink.h"
+
+using namespace std;
+
+namespace odb {
+
+DataJoin::DataJoin(const DataTable& left, const DataTable& right,
+        const std::string& primaryKey, const std::string& foreignKey, Type type)
+  : leftTable_(left),
+    rightTable_(right),
+    joinedColumns_(left.columns() + right.columns()),
+    primaryKey_(primaryKey),
+    foreignKey_(foreignKey),
+    type_(type)
+{}
+
+DataJoin::DataJoin(const DataLink& link)
+  : leftTable_(link.parent()),
+    rightTable_(link.child()),
+    joinedColumns_(leftTable_.columns() + rightTable_.columns()),
+    primaryKey_(link.primaryKey()),
+    foreignKey_(link.foreignKey()),
+    type_(LINKED)
+{}
+
+DataJoin::iterator DataJoin::begin() const
+{
+    return iterator(new internal::DataJoinIterator(*this, true));
+}
+
+DataJoin::iterator DataJoin::end() const
+{
+    return iterator(new internal::DataJoinIterator(*this, false));
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataJoin.h b/odb_api/src/odb_api/DataJoin.h
new file mode 100644
index 0000000..d3465ea
--- /dev/null
+++ b/odb_api/src/odb_api/DataJoin.h
@@ -0,0 +1,82 @@
+/// @file   DataJoin.h
+/// @author Tomas Kral
+
+#ifndef DATAJOIN_H_
+#define DATAJOIN_H_
+
+#include "odb_api/DataJoinIterator.h"
+#include "odb_api/SharedIterator.h"
+
+namespace odb {
+
+class DataTable;
+class DataLink;
+
+/*! Represents two joined tables.
+ *
+ *  The DataJoin class provides a view of two joined tables. The two tables
+ *  are joined on a matching key. The DataJoin provides an input iterator,
+ *  which is a foreward iterator giving read-only access to the records of the 
+ *  two joined tables.
+ *
+ *  @ingroup data
+ */
+class DataJoin
+{
+public:
+    enum Type { INNER = 0, LINKED = 1 };
+
+    /// Iterator providing read-only access to the result of joined tables.
+    typedef SharedIterator<internal::DataJoinIterator> iterator;
+
+    /// Creates join of two tables given the primary and foreign key.
+    /// If the optional parameter @e linked is @c true (default), DataJoin
+    /// will assume that the two tables are linked and thus will optimize a
+    /// foreign key lookups.
+
+    DataJoin(const DataTable& left, const DataTable& right,
+             const std::string& primaryKey, const std::string& foreignKey,
+             Type type = LINKED);
+
+    /// Creates join of linked tables.
+    explicit DataJoin(const DataLink& link);
+
+    /// Returns columns of the joined tables.
+    const DataColumns& columns() const
+    { return joinedColumns_; }
+
+    /// Returns reference to the left table.
+    const DataTable& leftTable() const { return leftTable_; }
+
+    /// Returns reference to the right table.
+    const DataTable& rightTable() const { return rightTable_; }
+
+    /// Returns primary key of the left table.
+    const std::string& primaryKey() const { return primaryKey_; }
+
+    /// Returns foreign key of the right table.
+    const std::string& foreignKey() const { return foreignKey_; }
+
+    /// Returns iterator pointing to the first join result.
+    iterator begin() const;
+
+    /// Returns iterator pointing past the last join result.
+    iterator end() const;
+
+private:
+    DataJoin(const DataJoin&);
+    DataJoin& operator=(const DataJoin&);
+
+    const DataTable& leftTable_;
+    const DataTable& rightTable_;
+    const DataColumns joinedColumns_;
+    std::string primaryKey_;
+    std::string foreignKey_;
+    Type type_;
+
+    friend class internal::DataJoinIterator;
+};
+
+} // namespace odb
+
+#endif // DATAJOIN_H_
diff --git a/odb_api/src/odb_api/DataJoinIterator.cc b/odb_api/src/odb_api/DataJoinIterator.cc
new file mode 100644
index 0000000..d10682d
--- /dev/null
+++ b/odb_api/src/odb_api/DataJoinIterator.cc
@@ -0,0 +1,188 @@
+/// @file   DataJoinIterator.cc
+/// @author Tomas Kral
+
+#include "eckit/eckit.h"
+#include "odb_api/DataJoin.h"
+#include "odb_api/DataJoinIterator.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace internal {
+
+DataJoinIterator::DataJoinIterator(const DataJoin& join, bool begin)
+  : join_(join),
+    columnsSize_(join.columns().size()),
+    rowsSize_(10),
+    cacheSize_(rowsSize_ * (columnsSize_ + 1)), // + 1 extra column for row metadata
+    cache_(begin ? new double [cacheSize_] : 0),
+    cacheIt_(cache_),
+    cacheEnd_(cache_ + cacheSize_),
+    rowProxy_(cacheIt_),
+    leftIt_(begin ? join.leftTable().begin() : join.leftTable().end()),
+    leftEnd_(join.leftTable().end()),
+    leftSize_(join.leftTable().columns().size()),
+    rightIt_(begin ? join.rightTable().begin() : join.rightTable().end()),
+    rightEnd_(join.rightTable().end()),
+    rightSize_(join.rightTable().columns().size()),
+    primaryKeyIndex_(join.leftTable().columnIndex(join.primaryKey())),
+    foreignKeyIndex_(join.rightTable().columnIndex(join.foreignKey())),
+    type_(join.type_),
+    done_(begin ? false : true)
+{
+    if (begin)
+    {
+        ASSERT(cache_);
+        initialize();
+        populate();
+    }
+}
+
+DataJoinIterator::~DataJoinIterator()
+{
+    delete [] cache_;
+}
+
+void DataJoinIterator::increment()
+{
+    if (!done_)
+    {
+        cacheIt_ += columnsSize_ + 1;
+
+        if (cacheIt_ == cacheEnd_)
+        {
+            if (leftIt_ == leftEnd_ && rightIt_ == rightEnd_)
+            {
+                done_ = true;
+                return;
+            }
+
+            populate();
+            cacheIt_ = cache_;
+        }
+
+        rowProxy_ = cacheIt_;
+    }
+}
+
+void DataJoinIterator::populate()
+{
+    switch (type_) {
+
+    case DataJoin::LINKED:
+    {
+        double primaryKey = (*leftIt_)[primaryKeyIndex_];
+        copy(leftIt_->data(), leftIt_->data() + leftSize_, cache_ + 1);
+
+        double* cacheIt = cache_;
+        for (; cacheIt != cacheEnd_ && rightIt_ != rightEnd_;
+                cacheIt += columnsSize_ + 1, ++rightIt_)
+        {
+            double foreignKey = (*rightIt_)[foreignKeyIndex_];
+
+            if (foreignKey != primaryKey)
+            {
+                ++leftIt_; 
+                copy(leftIt_->data(), leftIt_->data() + leftSize_, cacheIt + 1);
+                primaryKey = (*leftIt_)[primaryKeyIndex_];
+
+                if (foreignKey != primaryKey)
+                {
+                    std::stringstream message("DataJoinIterator: ");
+                    message << "Mismatch of primary and foreign keys in linked tables.";
+                    message << std::endl;
+                    throw eckit::UserError(message.str());
+                }
+            }
+            else if (cacheIt > cache_)
+            {
+                double* previous = cacheIt - columnsSize_ - 1;
+                copy(previous + 1, previous + 1 + leftSize_, cacheIt + 1);
+            }
+
+            copy(rightIt_->data(), rightIt_->data() + rightSize_,
+                    cacheIt + 1 + leftSize_);
+        }
+
+        // If reached the end of the right table, increment left table iterator
+        // to point to the end.
+        
+        if (rightIt_ == rightEnd_)
+            ++leftIt_;
+
+        cacheEnd_ = cacheIt;
+    }
+    break;
+
+    case DataJoin::INNER:
+    {
+        double primaryKey = (*leftIt_)[primaryKeyIndex_];
+        bool primaryKeyChanged = true;
+
+        double* cacheIt = cache_;
+
+        // Loop until the cache is full or we reached end of the left table.
+        while (cacheIt != cacheEnd_ && leftIt_ != leftEnd_)
+        {
+            // Find matching foreign key in the right table.
+            for (; rightIt_ != rightEnd_ &&
+                    (*rightIt_)[foreignKeyIndex_] != primaryKey; ++rightIt_);
+
+            if (rightIt_ != rightEnd_)
+            {
+                // Found matching foreign key in the right table.
+                
+                if (primaryKeyChanged)
+                {
+                    // Copy new row from the left table.
+                    copy(leftIt_->data(), leftIt_->data() + leftSize_, cacheIt + 1);
+                    primaryKeyChanged = false;
+                }
+                else
+                {
+                    // The same row is already in the cache so copy it from
+                    // the previous cache row.
+                    DataRowProxy prevRow = cacheIt - columnsSize_ - 1;
+                    copy(prevRow.data(), prevRow.data() + leftSize_, cacheIt + 1);
+                }
+
+                // Copy content of the right table row.
+                copy(rightIt_->data(), rightIt_->data() + rightSize_,
+                        cacheIt + 1 + leftSize_);
+
+                ++rightIt_;
+                cacheIt += columnsSize_ + 1;
+            }
+            else if (++leftIt_ != leftEnd_)
+            {
+                double key = (*leftIt_)[primaryKeyIndex_];
+                ASSERT(key != primaryKey); // TODO: throw exception rather than assert
+                primaryKey = key;
+                primaryKeyChanged = true;
+                rightIt_ = join_.rightTable().begin();
+            }
+        }
+        
+        cacheEnd_ = cacheIt;
+    }
+    break;
+
+    default:
+        ASSERT(!"DataJoinIterator: Unexpected join type.");
+    }
+}
+
+void DataJoinIterator::initialize()
+{
+    for (double* data = cache_; data != cache_ + cacheSize_;
+            data += columnsSize_ + 1)
+    {
+        DataRowProxy row(data);
+        row.size(columnsSize_);
+        row.flag(0);
+    }
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataJoinIterator.h b/odb_api/src/odb_api/DataJoinIterator.h
new file mode 100644
index 0000000..396481e
--- /dev/null
+++ b/odb_api/src/odb_api/DataJoinIterator.h
@@ -0,0 +1,71 @@
+/// @file   DataJoinIterator.h
+/// @author Tomas Kral
+
+#ifndef DATAJOINITERATOR_H_
+#define DATAJOINITERATOR_H_
+
+#include "odb_api/DataRow.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/IteratorFacade.h"
+
+namespace odb {
+
+class DataJoin;
+
+namespace internal {
+
+/*! @internal
+ *  @brief Input iterator providing read-only access to joined tables.
+ *  @see DataJoin
+ *  @ingroup data
+ */
+class DataJoinIterator
+  : public InputIteratorFacade<DataJoinIterator, const DataRow>
+{
+public:
+    ~DataJoinIterator();
+
+private:
+    DataJoinIterator(const DataJoin& join, bool begin);
+    DataJoinIterator(const DataJoinIterator&);
+    DataJoinIterator& operator=(const DataJoinIterator&);
+
+    const DataRow& dereference() const
+    { return reinterpret_cast<const DataRow&>(rowProxy_); }
+
+    void increment();
+
+    bool equal(const DataJoinIterator& other) const
+    { return done_; }
+
+    void initialize();
+    void populate();
+
+private:
+    const DataJoin& join_;
+    size_t columnsSize_;
+    size_t rowsSize_;
+    size_t cacheSize_;
+    double* const cache_;
+    double* cacheIt_;
+    double* cacheEnd_;
+    DataRowProxy rowProxy_;
+    DataTable::const_iterator leftIt_;
+    DataTable::const_iterator leftEnd_;
+    size_t leftSize_;
+    DataTable::const_iterator rightIt_;
+    DataTable::const_iterator rightEnd_;
+    size_t rightSize_;
+    size_t primaryKeyIndex_;
+    size_t foreignKeyIndex_;
+    int type_;
+    bool done_;
+
+    friend class odb::DataJoin;
+    friend class odb::IteratorFacadeAccess;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATAJOINITERATOR_H_
diff --git a/odb_api/src/odb_api/DataLink.cc b/odb_api/src/odb_api/DataLink.cc
new file mode 100644
index 0000000..5e0bf63
--- /dev/null
+++ b/odb_api/src/odb_api/DataLink.cc
@@ -0,0 +1,104 @@
+/// @file   DataLink.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataLink.h"
+
+using namespace std;
+
+namespace odb {
+
+DataLink::DataLink(DataTable& parent, DataTable& child)
+  : owner_(0),
+    parentTable_(parent),
+    childTable_(child),
+    entries_(),
+    primaryKey_(""),
+    foreignKey_(""),
+    offsetName_(""),
+    lengthName_(""),
+    dirty_(false)
+{}
+
+DataLink::DataLink(DataTable& parent, DataTable& child,
+        const std::string& primaryKey, const std::string& foreignKey)
+  : owner_(0),
+    parentTable_(parent),
+    childTable_(child),
+    entries_(),
+    primaryKey_(primaryKey),
+    foreignKey_(foreignKey),
+    offsetName_(""),
+    lengthName_(""),
+    dirty_(false)
+{
+    build(primaryKey, foreignKey);
+}
+
+DataLink::~DataLink()
+{}
+
+DataLink::iterator DataLink::begin()
+{
+    return DataLink::iterator(*this, true);
+}
+
+DataLink::const_iterator DataLink::begin() const
+{
+    return DataLink::const_iterator(*this, true);
+}
+
+DataLink::iterator DataLink::end()
+{
+    return DataLink::iterator(*this, false);
+}
+
+DataLink::const_iterator DataLink::end() const
+{
+    return DataLink::const_iterator(*this, false);
+}
+
+DataTable::iterator DataLink::insert(DataLink::iterator& range,
+        const DataTable::iterator& position, const DataRow& row)
+{
+    ptrdiff_t n = (position - range.begin_);
+    DataTable::iterator it(childTable_.insert(position, row));
+    range.begin_ = it - n;
+
+    // NOTE: If implementing direct access operator[] in the future,
+    // don't forget to recompute offsets when they get dirty after
+    // inserting new rows.
+
+    ++(*range.entry_).length;
+    dirty_ = true;
+
+    return it;
+}
+
+void DataLink::build(const std::string& primaryKey, const std::string& foreignKey)
+{
+    entries_.reserve(parentTable_.size());
+
+    primaryKey_ = primaryKey;
+    foreignKey_ = foreignKey;
+
+    size_t primaryIndex = parentTable_.columnIndex(primaryKey);
+    size_t foreignIndex = childTable_.columnIndex(foreignKey);
+
+    size_t offset = 0;
+    size_t length = 0;
+
+    for (DataTable::const_iterator parentRow = parentTable_.begin(),
+            childRow = childTable_.begin(); parentRow != parentTable_.end()
+            && childRow != childTable_.end(); ++parentRow)
+    {
+        const double key = (*parentRow)[primaryIndex];
+
+        for (length = 0; childRow != childTable_.end()
+                && (*childRow)[foreignIndex] == key; ++childRow, ++length);
+
+        entries_.push_back(Entry(offset, length));
+        offset += length;
+    }
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataLink.h b/odb_api/src/odb_api/DataLink.h
new file mode 100644
index 0000000..44ede4e
--- /dev/null
+++ b/odb_api/src/odb_api/DataLink.h
@@ -0,0 +1,161 @@
+/// @file   DataLink.h
+/// @author Tomas Kral
+
+#ifndef DATALINK_H_
+#define DATALINK_H_
+
+#include "odb_api/DataLinkIterator.h"
+
+namespace odb {
+
+class DataSet;
+class DataLinks;
+class DataTable;
+
+namespace internal { class DataLinkFillerIterator; }
+
+/*! Represents a link between two tables.
+ *
+ *  The DataLink class is used to navigate through the rows of related
+ *  DataTable objects. The relationship between the two tables is defined by
+ *  matching pairs of the primary and foreign keys. The type of both columns
+ *  must be identical.
+ *
+ *  DataLink represents a special case of one-to-many relationship where all
+ *  foreign keys in the child table appear in the same order as primary keys
+ *  in the parent table and there is at least one matching foreign key per
+ *  each primary key of the parent table.
+ *
+ *  The @ref DataLink::iterator "iterator" of the DataLink class provides
+ *  sequential access to the parent rows and at the same time allows to
+ *  navigate through all related rows in the child table.
+ *
+ *  The following code example demonstrates how to first create a link between
+ *  two tables related via "parent_id" and "child_id" columns, and how to use
+ *  the @ref DataLink::iterator "iterator" of the DataLink class to navigate
+ *  through the related rows.
+ *
+ *  @code
+ *  DataLink link(parentTable, childTable, "parent_id", "child_id");
+ *
+ *  for (DataLink::iterator range = link.begin();
+ *          range != link.end(); ++range)
+ *  {
+ *      for (DataTable::iterator childRow = range->begin();
+ *              childRow != range->end(); ++childRow)
+ *      {
+ *          DataRecord record(*childRow, childTable.columns());
+ *          cout << record << std::endl;
+ *      }
+ *  }
+ *  @endcode
+ *  
+ *  @see DataTable, DataJoin
+ *
+ *  @ingroup data
+ */
+class DataLink
+{
+    typedef internal::DataLinkEntry Entry;
+    typedef std::vector<Entry> Entries;
+public:
+    /// Iterates through DataLink entries.
+    typedef internal::DataLinkIterator<DataRow> iterator;
+
+    /// Iterates through DataLink entries (const version).
+    typedef internal::DataLinkIterator<const DataRow> const_iterator;
+
+    /// Creates and populates a new link given the @em parent and @em child
+    /// table.
+    DataLink(DataTable& parent, DataTable& child, const std::string& primaryKey,
+            const std::string& foreignKey);
+
+    /// Destroys link object.
+   ~DataLink();
+
+    /// Returns pointer to the owner of the link.
+    DataSet* dataset() { return owner_; }
+
+    /// Returns reference to the parent table.
+    DataTable& parent() { return parentTable_; }
+
+    /// Returns const reference to the parent table.
+    const DataTable& parent() const { return parentTable_; }
+
+    /// Returns reference to the child table.
+    DataTable& child() { return childTable_; }
+
+    /// Returns const reference to the child table.
+    const DataTable& child() const { return childTable_; }
+
+    /// Returns primary key of the parent table.
+    const std::string& primaryKey() const { return primaryKey_; }
+
+    /// Returns foreign key of the child table.
+    const std::string& foreignKey() const { return foreignKey_; }
+
+    /// Returns iterator to the first link entry.
+    iterator begin();
+
+    /// Returns iterator to the first link entry (const version).
+    const_iterator begin() const;
+
+    /// Returns iterator past the last link entry.
+    iterator end();
+
+    /// Returns iterator past the last link entry (const version).
+    const_iterator end() const;
+
+    /// Returns the number of link entries.
+    size_t size() const { return entries_.size(); }
+
+    /// Clears the link by removing all entries.
+    /// @note Calling this method invalidates all exisisting @ref
+    /// DataLink::iterator iterators.
+    void clear() { entries_.clear(); }
+
+    /// Returns @c true if there are no link entries.
+    bool empty() const { return entries_.empty(); }
+
+    /// Inserts the @em row at the given @em position in the child table.
+    DataTable::iterator insert(DataLink::iterator& range,
+            const DataTable::iterator& position, const DataRow& row);
+
+    void offsetName(const std::string name) { offsetName_ = name; }
+    const std::string& offsetName() const { return offsetName_; }
+
+    const std::string& lengthName() const { return lengthName_; }
+    void lengthName(const std::string name) { lengthName_ = name; }
+
+private:
+    /// Creates new link between the @em parent and the @em child table.
+    DataLink(DataTable& parent, DataTable& child);
+
+    void dataset(DataSet* dataset) { owner_ = dataset; }
+    void build(const std::string&, const std::string&);
+    void push_back(const Entry entry) { entries_.push_back(entry); }
+
+private:
+    DataLink(const DataLink&);
+    DataLink& operator=(const DataLink&);
+
+    DataSet* owner_;
+    DataTable& parentTable_;
+    DataTable& childTable_;
+    Entries entries_;
+    std::string primaryKey_;
+    std::string foreignKey_;
+    std::string offsetName_;
+    std::string lengthName_;
+    bool dirty_;
+
+    friend class DataSetBuilder;
+    friend class DataLinks;
+    friend class internal::DataLinkFillerIterator;
+    template <typename R> friend class internal::DataLinkIteratorTraits;
+    template <typename R, typename T> friend class internal::DataLinkIterator;
+};
+
+} // namespace odb
+
+#endif // DATALINK_H_
diff --git a/odb_api/src/odb_api/DataLinkFiller.cc b/odb_api/src/odb_api/DataLinkFiller.cc
new file mode 100644
index 0000000..4bfa1a0
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinkFiller.cc
@@ -0,0 +1,60 @@
+/// @file   DataLinkFiller.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataLinkFiller.h"
+#include "odb_api/DataLink.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+DataLinkFiller::DataLinkFiller(DataLink& link)
+  : link_(link),
+    offsetColumnIndex_(0),
+    lenColumnIndex_(0)
+{}
+
+DataLinkFiller::iterator DataLinkFiller::begin()
+{
+    return DataLinkFiller::iterator(*this);
+}
+
+DataLinkFillerIterator::DataLinkFillerIterator(const DataLinkFiller& filler)
+  : link_(&filler.link_),
+    offsetColumnIndex_(filler.offsetColumnIndex_),
+    lenColumnIndex_(filler.lenColumnIndex_),
+    rowsToSkip_(0)
+{}
+
+DataLinkFillerIterator& DataLinkFillerIterator::operator=(const double* data)
+{
+    if (!rowsToSkip_)
+    {
+        fillEntry(data);
+        updateRowsToSkip(data);
+    }
+
+    return *this;
+}
+
+DataLinkFillerIterator& DataLinkFillerIterator::operator++()
+{
+    --rowsToSkip_;
+    return *this;
+}
+
+void DataLinkFillerIterator::fillEntry(const double* data)
+{
+    DataLink::Entry entry(data[offsetColumnIndex_], data[lenColumnIndex_]);
+    link_->push_back(entry);
+}
+
+void DataLinkFillerIterator::updateRowsToSkip(const double* data)
+{
+    rowsToSkip_ = data[lenColumnIndex_];
+    ASSERT(rowsToSkip_ > 0);
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataLinkFiller.h b/odb_api/src/odb_api/DataLinkFiller.h
new file mode 100644
index 0000000..ebe5bfe
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinkFiller.h
@@ -0,0 +1,69 @@
+/// @file   DataLinkFiller.h
+/// @author Tomas Kral
+
+#ifndef DATALINKFILLER_H_
+#define DATALINKFILLER_H_
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataLink;
+
+namespace internal {
+
+class DataLinkFillerIterator;
+
+/*! Fills DataLink with entries from data source.
+ *
+ *  The DataLinkFiller class populates a DataLink with entries from a data
+ *  source (i.e. ODB file).
+ *
+ *  @ingroup data
+ */
+class DataLinkFiller
+{
+public:
+    typedef DataLinkFillerIterator iterator;
+    explicit DataLinkFiller(DataLink& link);
+
+    void offsetColumnIndex(size_t n) { offsetColumnIndex_ = n; }
+    void lenColumnIndex(size_t n) { lenColumnIndex_ = n; }
+
+    iterator begin();
+
+private:
+    DataLinkFiller(const DataLinkFiller&);
+    DataLinkFiller& operator=(const DataLinkFiller&);
+
+    DataLink& link_;
+    size_t offsetColumnIndex_;
+    size_t lenColumnIndex_;
+
+    friend class DataLinkFillerIterator;
+};
+
+class DataLinkFillerIterator
+  : public std::iterator<std::output_iterator_tag, DataLinkFillerIterator>
+{
+public:
+    explicit DataLinkFillerIterator(const DataLinkFiller& filler);
+    DataLinkFillerIterator& operator*() { return *this; }
+    DataLinkFillerIterator& operator=(const double* data);
+    DataLinkFillerIterator& operator++();
+
+private:
+    void fillEntry(const double* data);
+    void updateRowsToSkip(const double* data);
+
+private:
+    DataLink* link_;
+    size_t offsetColumnIndex_;
+    size_t lenColumnIndex_;
+    size_t rowsToSkip_;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATALINKFILLER_H_
diff --git a/odb_api/src/odb_api/DataLinkIterator.cc b/odb_api/src/odb_api/DataLinkIterator.cc
new file mode 100644
index 0000000..03a1233
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinkIterator.cc
@@ -0,0 +1,40 @@
+/// @file   DataLinkIterator.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataLink.h"
+#include "odb_api/DataLinkIterator.h"
+
+namespace odb {
+namespace internal {
+
+template <typename Row, typename Traits>
+DataLinkIterator<Row, Traits>::DataLinkIterator()
+  : entry_(),
+    begin_()
+{}
+
+template <typename Row, typename Traits>
+DataLinkIterator<Row, Traits>::DataLinkIterator(link_type& link, bool begin)
+  : entry_(begin ? link.entries_.begin() : link.entries_.end()),
+    begin_(begin ? link.childTable_.begin() : link.childTable_.end())
+{}
+
+template <typename Row, typename Traits>
+typename DataLinkIterator<Row, Traits>::table_iterator_type
+DataLinkIterator<Row, Traits>::end() const
+{
+    table_iterator_type end(begin_);
+    std::advance(end, entry_->length);
+    return end;
+}
+
+// Explicit template instantiations.
+
+template struct DataLinkIteratorTraits<DataRow>;
+template struct DataLinkIteratorTraits<const DataRow>;
+
+template class DataLinkIterator<DataRow>;
+template class DataLinkIterator<const DataRow>;
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataLinkIterator.h b/odb_api/src/odb_api/DataLinkIterator.h
new file mode 100644
index 0000000..d721ee7
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinkIterator.h
@@ -0,0 +1,125 @@
+/// @file   DataLinkIterator.h
+/// @author Tomas Kral
+
+#ifndef DATALINKITERATOR_H_
+#define DATALINKITERATOR_H_
+
+#include "eckit/eckit.h"
+#include "odb_api/DataRow.h"
+#include "odb_api/DataTable.h"
+
+namespace odb {
+
+class DataLink;
+
+namespace internal {
+
+struct DataLinkEntry
+{
+    DataLinkEntry(size_t offset, size_t length)
+      : offset(offset), length(length) {}
+
+    size_t offset;
+    size_t length;
+};
+
+/// Provides member types required by DataLinkIterator class.
+template <typename T>
+struct DataLinkIteratorTraits
+{
+    typedef typename T::link_type link_type;
+    typedef typename T::table_iterator_type table_iterator_type;
+    typedef typename T::entry_iterator_type entry_iterator_type;
+};
+
+/// Template spacialization of DataLinkIteratorTraits for the DataRow class.
+template <>
+struct DataLinkIteratorTraits<DataRow>
+{
+    typedef DataLink link_type;
+    typedef DataTable::iterator table_iterator_type;
+    typedef std::vector<DataLinkEntry>::iterator entry_iterator_type;
+};
+
+/// Template spacialization of DataLinkIteratorTraits for the @c const DataRow class.
+template <>
+struct DataLinkIteratorTraits<const DataRow>
+{
+    typedef const DataLink link_type;
+    typedef DataTable::const_iterator table_iterator_type;
+    typedef std::vector<DataLinkEntry>::const_iterator entry_iterator_type;
+};
+
+/*! @internal
+ *
+ *  @brief Iterates on DataLink entries.
+ *
+ *  The DataLinkIterator represents a link between a single row of a parent
+ *  table and a set of related rows from a child table. The respective child
+ *  rows can be accessed through DataRange by dereferencing the iterator
+ *  object.
+ *
+ *  @tparam Row    represents a row type (DataRow or const DataRow)
+ *  @tparam Traits specialization of the DataLinkIteratorTraits class for the
+ *                 given row type
+ *
+ *  @ingroup data
+ */
+template <typename Row, typename Traits = DataLinkIteratorTraits<Row> >
+class DataLinkIterator
+  : public BidirectionalIteratorFacade<DataLinkIterator<Row, Traits>,
+    DataLinkIterator<Row, Traits> >
+{
+    typedef typename Traits::link_type link_type;
+    typedef typename Traits::table_iterator_type table_iterator_type;
+    typedef typename Traits::entry_iterator_type entry_iterator_type;
+public:
+    DataLinkIterator();
+
+    template <typename R, typename T>
+    DataLinkIterator(const DataLinkIterator<R, T>& other);
+
+    /// Returns iterator to the beginning of the range.
+    table_iterator_type begin() const { return begin_; }
+
+    /// Returns iterator past the end of the range.
+    table_iterator_type end() const;
+
+    /// Returns the number of rows in the range.
+    size_t size() const { return entry_->length; }
+
+private:
+    DataLinkIterator(link_type& link, bool begin);
+
+    DataLinkIterator& dereference() const
+    { return const_cast<DataLinkIterator&>(*this); }
+
+    void increment()
+    { std::advance(begin_, entry_->length); ++entry_; }
+
+    void decrement()
+    { std::advance(begin_, (--entry_)->length); }
+
+    template <typename R, typename T>
+    bool equal(const DataLinkIterator<R, T>& other) const
+    { return (entry_ == other.entry_); }
+
+    entry_iterator_type entry_;
+    table_iterator_type begin_;
+
+    friend class odb::DataLink;
+    friend class odb::IteratorFacadeAccess;
+    template <typename R, typename T> friend class DataLinkIterator;
+};
+
+template <typename Row, typename Traits>
+template <typename R, typename T>
+DataLinkIterator<Row, Traits>::DataLinkIterator(const DataLinkIterator<R, T>& other)
+  : entry_(other.entry_),
+    begin_(other.begin_)
+{}
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATALINKITERATOR_H_
diff --git a/odb_api/src/odb_api/DataLinks.cc b/odb_api/src/odb_api/DataLinks.cc
new file mode 100644
index 0000000..9f09fab
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinks.cc
@@ -0,0 +1,39 @@
+/// @file   DataLinks.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataLink.h"
+#include "odb_api/DataLinks.h"
+
+namespace odb {
+
+DataLinks::DataLinks(DataSet& owner)
+  : owner_(owner),
+    links_()
+{}
+
+void DataLinks::insert(DataLink* link)
+{
+    ASSERT(link != 0 && "Null DataLink pointers are not allowed.");
+    ASSERT(!links_.count(link) && "Duplicate DataLinks are not allowed.");
+    ASSERT(!link->dataset() && "DataLink cannot be owned by other DataSet.");
+
+    link->dataset(&owner_);
+    links_.insert(link);
+}
+
+DataLinks::iterator DataLinks::find(const DataTable* parent,
+        const DataTable* child)
+{
+    for (iterator it = links_.begin(), end = links_.end(); it != end; ++it)
+    {
+        DataLink& link = (**it);
+
+        if (&link.parent() == parent && &link.child() == child)
+            return it;
+    }
+
+    return links_.end();
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataLinks.h b/odb_api/src/odb_api/DataLinks.h
new file mode 100644
index 0000000..dcacceb
--- /dev/null
+++ b/odb_api/src/odb_api/DataLinks.h
@@ -0,0 +1,73 @@
+/// @file   DataLinks.h
+/// @author Tomas Kral
+
+#ifndef DATALINKS_H_
+#define DATALINKS_H_
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataLink;
+class DataSet;
+class DataTable;
+
+/*! Represents a collection of dataset links.
+ *
+ *  The DataLinks collection can be accessed trough the @ref DataSet::links
+ *  "links()" method of the DataSet class.
+ *
+ *  @ingroup data
+ */
+class DataLinks
+{
+public:
+    /// Iterates over links in dataset.
+    typedef std::set<DataLink*>::iterator iterator;
+
+    /// Iterates over links in dataset (const version).
+    typedef std::set<DataLink*>::const_iterator const_iterator;
+
+    /// Adds a link to the dataset.
+    /// The DataSet takes ownership of the DataLink.
+    void insert(DataLink* link);
+
+    /// Finds a link between the @p parent and the @p child table.
+    iterator find(const DataTable* const parent,
+            const DataTable* const child);
+
+    /// Returns the number of links in dataset.
+    size_t size() const { return links_.size(); }
+
+    /// Returns true if there are no links in the dataset.
+    bool empty() const { return links_.empty(); }
+
+    /// Returns iterator to the first link in the dataset.
+    iterator begin() { return links_.begin(); }
+
+    /// Returns iterator past the last link in the dataset.
+    iterator end() { return links_.end(); }
+
+    /// Returns iterator to the first link in the dataset (const overload).
+    const_iterator begin() const { return links_.begin(); }
+
+    /// Returns iterator past the last link in the dataset (const overload).
+    const_iterator end() const { return links_.end(); }
+
+private:
+    /// Creates a news collection of links.
+    explicit DataLinks(DataSet& owner);
+
+private:
+    DataLinks(const DataLinks&);
+    DataLinks& operator=(const DataLinks&);
+
+    DataSet& owner_;
+    std::set<DataLink*> links_;
+
+    friend class DataSet;
+};
+
+} // namespace odb
+
+#endif // DATALINKS_H_
diff --git a/odb_api/src/odb_api/DataLoader.cc b/odb_api/src/odb_api/DataLoader.cc
new file mode 100644
index 0000000..d0b483d
--- /dev/null
+++ b/odb_api/src/odb_api/DataLoader.cc
@@ -0,0 +1,190 @@
+/// @file   DataLoader.cc
+/// @author Tomas Kral
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/io/FileHandle.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataLoader.h"
+#include "odb_api/DataSetBuilder.h"
+#include "odb_api/DataSetFiller.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/TextReaderIterator.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+DataLoader::DataLoader(const std::string& path)
+  : query_(""),
+    path_(path),
+    handle_(0),
+    deleteHandle_(true),
+    stream_(0),
+    deleteStream_(true),
+    mappings_(),
+    open_(true)
+{
+    open(path);
+}
+
+DataLoader::DataLoader(DataHandle& handle)
+  : query_(""),
+    path_(""),
+    handle_(&handle),
+    deleteHandle_(false),
+    stream_(0),
+    deleteStream_(true),
+    mappings_(),
+    open_(true)
+{}
+
+DataLoader::DataLoader(std::istream& stream)
+  : query_(""),
+    path_(""),
+    handle_(0),
+    deleteHandle_(true),
+    stream_(&stream),
+    deleteStream_(false),
+    mappings_(),
+    open_(true)
+{}
+
+DataLoader::~DataLoader()
+{
+    close();
+}
+
+void DataLoader::open(const std::string& path)
+{
+    std::string ext = path.substr(path.size()-4);
+
+    if (ext == ".csv" || ext == ".txt")
+    {
+        stream_ = new std::ifstream(path.c_str());
+        ASSERT(stream_);
+    }
+    else {
+        handle_ = new FileHandle(path);
+        ASSERT(handle_);
+        handle_->openForRead();
+    }
+
+    open_ = true;
+}
+
+void DataLoader::select(const std::string& query)
+{
+    query_ = query;
+
+    if (handle_)
+        handle_->rewind();
+    else if (stream_)
+        stream_->seekg(0);
+}
+
+void DataLoader::fill(DataSet& dataset)
+{
+    ASSERT(open_);
+
+    if (handle_)
+        fillFromBinary(dataset);
+    else if (stream_)
+        fillFromText(dataset);
+    else
+        throw eckit::UserError("DataLoader: Unrecognized data source: " + path_);
+}
+
+void DataLoader::fill(DataSet& dataset, const std::string& tableName)
+{
+    ASSERT(!query_.empty());
+    fillFromQuery(dataset, tableName);
+}
+
+void DataLoader::fillFromText(DataSet& dataset)
+{
+    using namespace internal;
+
+    ASSERT(stream_);
+    ASSERT(query_.empty());
+
+    TextReader reader(*stream_, ","); // FIXME: delimiter is hardcoded now
+    TextReader::iterator source = reader.begin();
+    const MetaData& metadata = source->columns();
+
+    DataSetBuilder builder(metadata, mappings_, true);
+    builder.build(dataset);
+
+    DataSetFiller filler(dataset, metadata);
+    DataSetFiller::iterator target = filler.begin();
+
+    for (; source != reader.end(); ++source, ++target)
+        *target = source->data();
+}
+
+void DataLoader::fillFromBinary(DataSet& dataset)
+{
+    using namespace internal;
+
+    ASSERT(handle_);
+    ASSERT(query_.empty());
+
+    Reader reader(*handle_);
+    Reader::iterator source = reader.begin();
+    const MetaData& metadata (source->columns());
+
+    DataSetBuilder builder(metadata, mappings_, true);
+    builder.build(dataset);
+
+    DataSetFiller filler(dataset, metadata);
+    DataSetFiller::iterator target = filler.begin();
+
+    for (; source != reader.end(); ++source, ++target)
+        *target = source->data();
+}
+
+void DataLoader::fillFromQuery(DataSet& dataset, const std::string& name) const
+{
+    Select select(query_, *handle_);
+    Select::iterator source = select.begin();
+
+    DataColumns columns = source->columns();
+    DataTable* table = 0;
+
+    if (dataset.tables().count(name))
+    {
+        table = *dataset.tables().find(name);
+        ASSERT(table);
+        // TODO: Maybe compare not only the number of columns but also
+        // the column name and data type.
+        ASSERT(table->columns().size() == columns.size());
+    }
+    else
+    {
+        table = new DataTable(name, columns);
+        ASSERT(table);
+        dataset.tables().insert(table);
+    }
+
+    for (; source != select.end(); ++source)
+        table->push_back(source->data());
+}
+
+void DataLoader::close()
+{
+    if (deleteHandle_) delete handle_;
+    if (deleteStream_) delete stream_;
+
+    deleteHandle_ = true;
+    deleteStream_ = true;
+    query_.clear();
+    open_ = false;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataLoader.h b/odb_api/src/odb_api/DataLoader.h
new file mode 100644
index 0000000..8844eec
--- /dev/null
+++ b/odb_api/src/odb_api/DataLoader.h
@@ -0,0 +1,90 @@
+/// @file   DataLoader.h
+/// @author Tomas Kral
+
+#ifndef DATALOADER_H_
+#define DATALOADER_H_
+
+#include "odb_api/DataTableMappings.h"
+
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class DataSet;
+
+class Select;
+
+/*! Loads data from a data source into a dataset.
+ *  
+ *  The DataLoader class is responsible for populating a DataSet with data
+ *  from a data source. The DataLoader first inspects a schema of a data
+ *  source and if necessary creates respective DataTable and DataLink
+ *  objects. Then the loader fills all tables and links in a DataSet with
+ *  data from the data source.
+ *
+ *  The following code snippet demonstrates how to use DataLoader to fill
+ *  a DataSet with data from ODB file.
+ *
+ *  @code
+ *  DataLoader loader("airep.odb");
+ *  DataSet dataset("airep");
+ *  loader.fill(dataset);
+ *  @endcode
+ *
+ *  @ingroup data
+ */
+class DataLoader
+{
+public:
+    /// Creates a new loader given the path to the source.
+    DataLoader(const std::string& path);
+
+    /// Creates a new loader given the handle to the source.
+    DataLoader(eckit::DataHandle& handle);
+
+    /// Creates a new loader given the input stream to the source.
+    DataLoader(std::istream& stream);
+
+    /// Destroys loader object.
+   ~DataLoader();
+
+    void mappings(const DataTableMappings& mappings)
+    { mappings_ = mappings; }
+
+    /// Opens a new data source for loading.
+    void open(const std::string& path);
+
+    /// Selects columns that will be used to populate the dataset.
+    void select(const std::string& query);
+
+    /// Populates @em dataset from the data source.
+    void fill(DataSet& dataset);
+
+    /// Populates @em dataset from the data source.
+    void fill(DataSet& dataset, const std::string& tableName);
+
+    /// Closes the data source.
+    void close();
+
+private:
+    void fillFromText(DataSet& dataset);
+    void fillFromBinary(DataSet& dataset);
+    void fillFromQuery(DataSet& dataset, const std::string& name) const;
+
+private:
+    DataLoader(const DataLoader&);
+    DataLoader& operator=(const DataLoader&);
+
+    std::string query_;
+    std::string path_;
+    eckit::DataHandle* handle_;
+    bool deleteHandle_;
+    std::istream* stream_;
+    bool deleteStream_;
+    DataTableMappings mappings_;
+    bool open_;
+};
+
+} // namespace odb
+
+#endif // DATALOADER_H_
diff --git a/odb_api/src/odb_api/DataPage.cc b/odb_api/src/odb_api/DataPage.cc
new file mode 100644
index 0000000..c636c08
--- /dev/null
+++ b/odb_api/src/odb_api/DataPage.cc
@@ -0,0 +1,236 @@
+/// @file   DataPage.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataPage.h"
+#include "odb_api/DataTable.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+DataPage::DataPage(DataTable& table)
+  : table_(table),
+    width_(table.columns().size() + 1), // + 1 extra column for row flags
+    height_(optimizeHeight(table)),
+    fillMark_(optimizeFillMark(table)),
+    size_(0),
+    rowsAligned_(true),
+    buffer_(new double [(width_ + 1) * height_]), // + 1 extra column for row pointers
+    data_(buffer_ + height_),
+    rows_(reinterpret_cast<DataRowProxy*>(buffer_)),
+    begin_(rows_),
+    previous_(0),
+    next_(0),
+    rank_(0)
+{
+    ASSERT(buffer_);
+    alignRows(height_);
+}
+
+DataPage::~DataPage()
+{
+    delete [] buffer_;
+}
+
+size_t DataPage::resize(size_t n, bool init)
+{
+    // Do not resize the page beyond its fill mark.
+    if (n > fillMark_)
+        n = fillMark_;
+
+    if (n < size_)
+    {
+        drop(begin() + n, end());
+    }
+    else if (n > size_)
+    {
+        DataRowProxy* row = end();
+
+        ASSERT(!row->used());
+
+        if (init)
+        {
+            row->initialize(table_.columns());
+            row->flag(DataRow::USED | DataRow::INITIALIZED);
+        }
+        else
+        {
+            row->used(true);
+        }
+
+        for (DataRowProxy* r = row + 1; r != rows_ + n; ++r)
+        {
+            ASSERT(!r->used());
+
+            if (init)
+            {
+                if (r->modified() || !r->initialized())
+                    copy(row->data(), row->data() + width_ - 1, r->data());
+                
+                r->flag(DataRow::USED | DataRow::INITIALIZED);
+            }
+            else
+            {
+                r->used(true);
+            }
+        }
+    }
+
+    return (size_ = n);
+}
+
+void DataPage::clear()
+{
+    drop(begin(), end());
+
+    if (!rowsAligned_)
+    {
+        alignRows(size_);
+        rowsAligned_ = true;
+    }
+
+    size_ = 0;
+}
+
+bool DataPage::push_back(const DataRow& row)
+{
+    if (size_ >= fillMark_)
+        return false;
+
+    ++size_;
+
+    copy(row.begin(), row.end(), back().begin());
+    back().flag(DataRow::USED);
+
+    return true;
+}
+
+bool DataPage::push_back(const double* const data)
+{
+    if (size_ >= fillMark_)
+        return false;
+
+    ++size_;
+
+    copy(data, data + width_ - 1, back().data());
+    back().flag(DataRow::USED);
+
+    return true;
+}
+
+bool DataPage::insert(DataRowProxy* pos, const DataRow& row)
+{
+    ASSERT(row.size() == width_ - 1);
+    ASSERT(begin() <= pos && pos <= end() && "DataPage::insert(): Invalid position.");
+
+    if (full())
+        return false;
+
+    DataRowProxy last = *end();
+    last.used(true);
+
+    copy(row.begin(), row.end(), last.begin());
+    copy_backward(pos, end(), end() + 1);
+    *pos = last;
+
+    if (pos != end())
+        rowsAligned_ = false;
+
+    ++size_;
+
+    return true;
+}
+
+DataPage* DataPage::split()
+{
+    DataPage* page = new DataPage(table_);
+
+    DataRowProxy* first = begin() + size_ / 2;
+    for (DataRowProxy* row = first; row != end(); ++row)
+    {
+        page->push_back(row->data());
+    }
+
+    size_ /= 2;
+
+    return page;
+}
+
+void DataPage::drop(DataRowProxy* begin, DataRowProxy* end)
+{
+    for (DataRowProxy* row = begin; row != end; ++row)
+    {
+        ASSERT(row->used());
+        row->used(false);
+    }
+}
+
+void DataPage::alignRows(size_t n)
+{
+    for (size_t i = 0; i < n; ++i)
+    {
+        rows_[i] = &data_[i * width_];
+        rows_[i].flag(0);
+    }
+}
+
+void DataPage::initializeRows(size_t n)
+{
+    if (n == 0) return;
+
+    rows_->initialize(table_.columns());
+    rows_->flag(DataRow::INITIALIZED);
+
+    double* prev = data_;
+    double* next = data_ + width_;
+    double* last = data_ + width_ * n;
+
+    for (; next != last; prev = next, next += width_)
+    {
+        std::copy(prev, prev + width_, next);
+    }
+}
+
+size_t DataPage::optimizeHeight(const DataTable& table)
+{
+    const DataTableProperties& properties = table.properties();
+    const DataColumns& columns = table.columns();
+
+    size_t height = 0;
+
+    if (properties.blockSizeInNumberOfRows())
+        height = properties.blockSizeInNumberOfRows();
+    else
+    {
+        size_t sizeInKb = properties.blockSizeInKb();
+        height = sizeInKb * 1000 / sizeof(double) / (columns.size() + 1);
+        height = max(height, (size_t) MIN_BLOCK_HEIGHT);
+    }
+
+    return height;
+}
+
+size_t DataPage::optimizeFillMark(const DataTable& table)
+{
+    const DataTableProperties& properties = table.properties();
+    size_t fillMark = 0;
+
+    if (properties.blockFillMarkInNumberOfRows())
+    {
+        fillMark = properties.blockFillMarkInNumberOfRows();
+        ASSERT(properties.blockSizeInNumberOfRows() > fillMark);
+    }
+    else
+    {
+        double ratio = properties.blockFillMark();
+        ASSERT(ratio > 0.0 && ratio <= 1.0);
+        ASSERT(properties.blockSizeInKb() != 0.0);
+        fillMark = optimizeHeight(table) * ratio;
+    }
+
+    return fillMark;
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataPage.h b/odb_api/src/odb_api/DataPage.h
new file mode 100644
index 0000000..f719e97
--- /dev/null
+++ b/odb_api/src/odb_api/DataPage.h
@@ -0,0 +1,168 @@
+/// @file   DataPage.h
+/// @author Tomas Kral
+
+#ifndef DATAPAGE_H_
+#define DATAPAGE_H_
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataRow.h"
+
+namespace odb {
+
+class DataTable;
+
+namespace internal {
+
+/** @internal
+ *
+ *  @brief Represents a page of table data.
+ *
+ *  @ingroup data
+ */
+class DataPage
+{
+    enum { MIN_BLOCK_HEIGHT = 1000 };
+public:
+    /// Creates a new page.
+    explicit DataPage(DataTable& table);
+
+    /// Frees the page from memory.
+    ~DataPage();
+
+    /// Returns reference to the given row of the page.
+    DataRowProxy& operator[](size_t n) const { return begin_[n]; }
+
+    /// Returns reference to the given row of the page.
+    DataRowProxy& at(size_t n) const { ASSERT(n < size_); return begin_[n]; }
+
+    /// Returns pointer to the first row in the page.
+    DataRowProxy* begin() const { return begin_; }
+
+    /// Returns pointer past the last row in the page.
+    DataRowProxy* end() const { return begin_ + size_; }
+
+    /// Returns reference to the firtst row in the page.
+    DataRowProxy& front() { return *begin_; }
+
+    /// Returns reference to the firtst row in the page.
+    const DataRowProxy& front() const { return *begin_; }
+
+    /// Returns reference to the last row in the page.
+    DataRowProxy& back() { return *(begin_ + size_ - 1); }
+
+    /// Returns reference to the last row in the page.
+    const DataRowProxy& back() const { return *(begin_ + size_ - 1); }
+
+    /// Returns the number of rows stored in the page.
+    size_t size() const { return size_; }
+
+    /// Returns the maximum number of rows that can be stored in the page.
+    size_t capacity() const { return height_; }
+
+    /// Attempts to resize the page to the given number of rows.
+    /// Returns the actual number of rows to which the page was resized. If the
+    /// optional parameter @e initialize is @c true, the elements of the newly
+    /// added rows will be initialized to their respective column missing
+    /// values. Note that this method does not change the @ref DataPage::capacity
+    /// "capacity" of the page but the number of the actively used rows.
+
+    size_t resize(size_t n, bool initialize = false);
+
+    /// Clears the page by dropping the content of all its rows.
+    void clear();
+
+    /// Returns @c true if the page is empty.
+    bool empty() const { return size_ == 0; }
+
+    /// Returns @c true if the page is full.
+    bool full() const { return size_ == capacity(); }
+
+    /// Attempts to add elements of the row at the end of the page.
+    ///
+    /// This method attempts to copy elements of the supplied @e row at the
+    /// end of the data page. Returns @c true if the @e row was successfully
+    /// added to the page, otherwise, if the page is already full or the
+    /// filling mark has been reached, returns @c false.
+
+    bool push_back(const DataRow& row);
+
+    /// Attempts to add elements of the data array at the end of the page.
+    ///
+    /// This method attempts to copy elements of the supplied @e data array at the
+    /// end of the data page. Returns @c true if the @e data were successfully
+    /// added to the page, otherwise, if the page is already full or the
+    /// filling mark has been reached, returns @c false. Note that no bound
+    /// checking is performed on the supllied @e data array and it is user's
+    /// responsibility to provide an array of the proper size.
+    //
+    /// This method is typically used when populating the page from a data
+    /// source which does not provide a DataRow interface.
+
+    bool push_back(const double* const data);
+
+    /// Attempts to insert the row at the given position.
+    /// This method attempts to copy elements of the supplied @e row before the
+    /// given @e position. Returns @c true if the @e row could be inserted into
+    /// the page, otherwise, if the page is already full, returns @c false.
+    /// Note that inserting a row into a page may invalidate all previously
+    /// obtained pointers and references.
+
+    bool insert(DataRowProxy* position, const DataRow& row);
+
+    /// Splits the page and moves half of its rows to a new page.
+    /// Returns pointer to a new page.
+    DataPage* split();
+
+    /// Returns link to the next page.
+    DataPage* next() const { return next_; }
+
+    /// Sets the link to the next page.
+    void next(DataPage* next) { next_ = next; }
+
+    /// Returns link to the previous page.
+    DataPage* previous() const { return previous_; }
+
+    /// Sets the link to the previous page.
+    void previous(DataPage* previous) { previous_ = previous; }
+
+    /// Returns the rank of the page.
+    int rank() const { return rank_; }
+
+    /// Sets the rank of the page.
+    void rank(int rank) { rank_ = rank; }
+
+private:
+    DataPage(const DataPage&);
+    DataPage& operator=(const DataPage&);
+
+    void drop(DataRowProxy* begin, DataRowProxy* end);
+
+    // Aligns first n rows with the data buffer.
+    void alignRows(size_t n);
+
+    /// Initializes first n rows of the page.
+    void initializeRows(size_t n);
+
+    static size_t optimizeHeight(const DataTable& table);
+    static size_t optimizeFillMark(const DataTable& table);
+
+private:
+    DataTable& table_;
+    size_t width_;
+    size_t height_;
+    size_t fillMark_;
+    size_t size_;
+    bool rowsAligned_;
+    double* const buffer_;
+    double* const data_;
+    DataRowProxy* const rows_;
+    DataRowProxy* begin_;
+    DataPage* previous_;
+    DataPage* next_;
+    int rank_;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATAPAGE_H_
diff --git a/odb_api/src/odb_api/DataRecord.cc b/odb_api/src/odb_api/DataRecord.cc
new file mode 100644
index 0000000..b61f0f8
--- /dev/null
+++ b/odb_api/src/odb_api/DataRecord.cc
@@ -0,0 +1,32 @@
+/// @file   DataRecord.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataRecord.h"
+
+namespace odb {
+
+DataRecord::DataRecord(DataRow& row, const DataColumns& columns)
+  : row_(row),
+    columns_(columns)
+{}
+
+DataField DataRecord::operator[](const std::string& name)
+{
+    size_t n = columns_.indexOf(name);
+    return DataField(row_, columns_[n], n);
+}
+
+std::ostream& operator<<(std::ostream& stream, const DataRecord& record)
+{
+    stream << "(";
+
+    for (size_t i = 0; i < record.size(); i++)
+    {
+        const DataField field = record[i];
+        stream << field << ", ";
+    }
+
+    return stream << "\b\b)";
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataRecord.h b/odb_api/src/odb_api/DataRecord.h
new file mode 100644
index 0000000..9725532
--- /dev/null
+++ b/odb_api/src/odb_api/DataRecord.h
@@ -0,0 +1,112 @@
+/// @file   DataRecord.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATARECORD_H_
+#define ODBLIB_DATARECORD_H_
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataField.h"
+#include "odb_api/DataRecordIterator.h"
+
+namespace odb {
+
+class DataRow;
+
+/** Represents a single table record.
+ *
+ *  Unlike DataRow, which only provides low-level access to the DataTable
+ *  values, the DataRecord class provides a higher-level interface which
+ *  combines both the DataRow access methods and the DataColumn meta-data. Each
+ *  DataRecord consists of a collection of DataField objects (one field per
+ *  column) which can be used to access and manipulate both values and
+ *  meta-data of a single table cell.
+ *
+ *  @code
+ *  const DataRow& row = table[0];
+ *  const DataRecord record(row, table.columns());
+ *
+ *  for (DataRecord::const_iterator it = record.begin();
+ *          it != record.end(); ++it)
+ *  {
+ *      const DataField& field = *it;
+ *
+ *      cout << "name=" << field.name() << std::endl;
+ *      cout << "type=" << field.type() << std::endl;
+ *      cout << "value=" << field.as<double>() << std::endl;
+ *  }
+ *  @endcode
+ *
+ *  @see DataField, DataRow, DataColumn
+ *  @ingroup data
+ */
+class DataRecord
+{
+    typedef internal::DataRecordIterator<DataRecord, DataField>
+            DataRecordIterator;
+
+    typedef internal::DataRecordIterator<const DataRecord, const DataField>
+            ConstDataRecordIterator;
+public:
+    /// Foreward iterator on record's fields.
+    typedef DataRecordIterator iterator;
+
+    /// Foreward iterator on record's fields (const version).
+    typedef ConstDataRecordIterator const_iterator;
+
+    /// Creates new DataRecord instance.
+    DataRecord(DataRow& row, const DataColumns& columns);
+
+    /// Returns the given field.
+    DataField operator[](unsigned int n)
+    { return DataField(row_, columns_[n], n); }
+
+    /// Returns the given field (const overload).
+    const DataField operator[](unsigned int n) const
+    { return const_cast<DataRecord&>(*this)[n]; }
+
+    /// Returns the field given its name.
+    DataField operator[](const std::string& name);
+
+    /// Returns the field given its name (const overload).
+    const DataField operator[](const std::string& name) const
+    { return const_cast<DataRecord&>(*this)[name]; }
+
+    /// Returns the given field.
+    DataField at(unsigned int n)
+    { ASSERT(n < size()); return (*this)[n]; }
+
+    /// Returns the given field (const overload).
+    const DataField at(unsigned int n) const
+    { return const_cast<DataRecord&>(*this).at(n); }
+
+    /// Returns number of fields in the record.
+    unsigned int size() const { return columns_.size(); }
+
+    /// Returns iterator to the first field.
+    iterator begin() { return iterator(*this, true); }
+
+    /// Returns iterator to the first field (const version).
+    const_iterator begin() const { return const_iterator(*this, true); };
+
+    /// Returs iterator past the last field.
+    iterator end() { return iterator(*this, false); }
+
+    /// Returs iterator past the last field (const version.)
+    const_iterator end() const { return const_iterator(*this, false); }
+
+private:
+    DataRecord(const DataRecord&);
+    DataRecord& operator=(const DataRecord&);
+
+    DataRow& row_;
+    const DataColumns& columns_;
+
+    template <typename, typename> friend class internal::DataRecordIterator;
+};
+
+std::ostream& operator<<(std::ostream& stream, const DataRecord& record);
+
+} // namespace odb
+
+#endif // ODBLIB_DATARECORD_H_
diff --git a/odb_api/src/odb_api/DataRecordIterator.h b/odb_api/src/odb_api/DataRecordIterator.h
new file mode 100644
index 0000000..20b783d
--- /dev/null
+++ b/odb_api/src/odb_api/DataRecordIterator.h
@@ -0,0 +1,68 @@
+/// @file   DataRecordIterator.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATARECORDITERATOR_H_
+#define ODBLIB_DATARECORDITERATOR_H_
+
+#include <stddef.h>
+#include "odb_api/IteratorFacade.h"
+
+namespace odb {
+
+class DataRecord;
+
+namespace internal {
+
+/*! @internal
+ *  @brief Foreward iterator on record's fields.
+ *  @ingroup data
+ */
+template <typename R, typename F>
+class DataRecordIterator
+  : public ForwardIteratorFacade<DataRecordIterator<R, F>, F, F>
+{
+public:
+    DataRecordIterator() {}
+
+    template <typename T, typename U>
+    DataRecordIterator(const DataRecordIterator<T, U>& other)
+      : record_(other.record_),
+        index_(other.index_)
+    {}
+
+    template <typename T, typename U>
+    DataRecordIterator& operator=(const DataRecordIterator<T, U>& other)
+    {
+        record_ = other.record_;
+        index_ = other.index_;
+    }
+
+private:
+    DataRecordIterator(R& record, bool begin)
+      : record_(&record),
+        index_(begin ? 0 : record.size())
+    {}
+
+    F dereference() const { return (*record_)[index_]; }
+    void increment() { ++index_; }
+
+    template <typename T, typename U>
+    bool equal(const DataRecordIterator<T, U>& other) const
+    { return index_ == other.index_; }
+
+    template <typename T, typename U>
+    ptrdiff_t distance(const DataRecordIterator<T, U>& other) const
+    { return (other.index_ - index_); }
+
+    R* record_;
+    unsigned int index_;
+
+    friend class odb::DataRecord;
+    friend class odb::IteratorFacadeAccess;
+    template <typename T, typename U> friend class DataRecordIterator;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // ODBLIB_DATARECORDITERATOR_H_
diff --git a/odb_api/src/odb_api/DataRow.cc b/odb_api/src/odb_api/DataRow.cc
new file mode 100644
index 0000000..fbc310a
--- /dev/null
+++ b/odb_api/src/odb_api/DataRow.cc
@@ -0,0 +1,98 @@
+/// @file   DataRow.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataRow.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+void DataRowBase::initialize(const DataColumns& columns)
+{
+    double* d = data();
+
+    for (size_t i = 0; i < columns.size(); ++i, ++d)
+    {
+        *d = columns[i].missingValue();
+    }
+}
+
+} // namespace internal
+
+DataRow::DataRow(size_t n)
+  : internal::DataRowBase(new double [n+1])
+{
+    ASSERT(data_);
+    flag(STANDALONE);
+    size(n);
+}
+
+DataRow::DataRow(size_t n, double value)
+  : internal::DataRowBase(new double [n+1])
+{
+    ASSERT(data_);
+    size(n);
+    fill(begin(), end(), value);
+    flag(STANDALONE | INITIALIZED);
+}
+
+DataRow::DataRow(const DataColumns& columns, bool init)
+  : internal::DataRowBase(new double [columns.size()+1])
+{
+    ASSERT(data_);
+
+    size(columns.size());
+    flag(STANDALONE);
+
+    if (init)
+    {
+        initialized(true);
+        initialize(columns);
+    }
+}
+
+DataRow::~DataRow()
+{
+    if (standalone())
+        delete [] data_;
+}
+
+DataRow::DataRow(const DataRow& other)
+  : internal::DataRowBase(new double [other.size() + 1])
+{
+    ASSERT(data_);
+    size(other.size());
+    copy(other.begin(), other.end(), begin());
+    flag(STANDALONE | INITIALIZED);
+}
+
+// Template specializations.
+
+template <>
+std::string DataRow::get(size_t index) const
+{
+    const char *s = reinterpret_cast<const char*>(&at(index));
+
+    size_t n = 0;
+    for (; n < sizeof(double) && s[n]; ++n); 
+
+    return std::string(s, n);
+}
+
+template <>
+void DataRow::set(size_t index, const std::string& value)
+{
+    modified(true);
+
+    const char* v = value.c_str();
+    char* d = reinterpret_cast<char*>(&at(index));
+
+    size_t j = 0;
+    for (; j < sizeof(double) && v[j]; ++j) d[j] = v[j];
+    for (; j < sizeof(double); ++j) d[j] = ' ';
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataRow.h b/odb_api/src/odb_api/DataRow.h
new file mode 100644
index 0000000..4af5edd
--- /dev/null
+++ b/odb_api/src/odb_api/DataRow.h
@@ -0,0 +1,224 @@
+/// @file   DataRow.h
+/// @author Tomas Kral
+
+#ifndef DATAROW_H_
+#define DATAROW_H_
+
+#include <stdint.h>
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataTable;
+class DataColumns;
+
+namespace internal {
+
+class DataPage;
+class DataSelectIterator;
+template <typename R, typename T> class DataTableIterator;
+
+/*! @internal
+ *  Represents a base class implementing common functionalities
+ *  of both DataRow and DataRowProxy classes.
+ *
+ *  @ingroup data
+ */
+class DataRowBase
+{
+public:
+    /// Bit flags indicating the status of the row.
+    enum Flag
+    {
+        INITIALIZED = 0x01,
+        USED = 0x02,
+        MODIFIED = 0x04,
+        STANDALONE = 0x08,
+    };
+
+protected:
+    union Meta
+    {
+        struct { uint32_t size, flag; } meta;
+        uint64_t data;
+    };
+
+public:
+    /// Returns reference to the given element.
+    double& operator[](size_t n) { modified(true); return data()[n]; }
+
+    /// Returns reference to the given element (const overload).
+    const double& operator[](size_t n) const { return data()[n]; }
+
+    /// Returns iterator pointing to the first row element.
+    double* begin() { modified(true); return data_ + 1; }
+
+    /// Returns iterator pointing to the first row element (const overload).
+    const double* begin() const { return data_ + 1; }
+
+    /// Returns iterator pointing past the last row element.
+    double* end() { return data_ + 1 + size(); }
+
+    /// Returns iterator pointing past the last row element (const overload).
+    const double* end() const { return data_ + 1 + size(); }
+
+    /// Returns pointer to the row's data.
+    const double* const data() const { return data_ + 1; }
+
+    /// Returns the number of row's elements.
+    const uint32_t& size() const { return meta().meta.size; }
+
+    /// Returns true if the row has been initialized.
+    bool initialized() const { return flag() & INITIALIZED; }
+
+    /// Returns true if the row has been modified.
+    bool modified() const { return flag() & MODIFIED; }
+
+    /// Returns true if the row is not attached to any table.
+    bool standalone() const { return flag() & STANDALONE; }
+
+    /// Returns true if the row is being actively used by the table.
+    bool used() const { return flag() & USED; }
+
+protected:
+    DataRowBase() : data_(0) {}
+    DataRowBase(double* const data) : data_(data) {}
+
+    double* const data() { return data_ + 1; }
+    void initialize(const DataColumns& columns);
+    Meta& meta() const { return reinterpret_cast<Meta&>(data_[0]); }
+    uint32_t& flag() const { return meta().meta.flag; }
+    void flag(uint32_t flag) { meta().meta.flag = flag; }
+    void size(uint32_t n) { meta().meta.size = n; }
+    void initialized(bool b) { b ? flag() |= INITIALIZED : flag() &= (~INITIALIZED); }
+    void used(bool b) { b ? flag() |= USED : flag() &= (~USED); }
+    void modified(bool b) { b ? flag() |= MODIFIED : flag() &= (~MODIFIED); }
+    void standalone(bool b) { b ? flag() |= STANDALONE : flag() &= (~STANDALONE); }
+
+    double* data_;
+
+    friend class odb::internal::DataSelectIterator;
+};
+
+} // namespace internal
+
+/*! Represents a row of data.
+ *
+ *  The DataRow class represents an actual data contained in a DataTable. It is
+ *  used to access and modify values in a DataTable. The DataRow can be either
+ *  a single standalone row not attached to any table, or it can be a part of a
+ *  table. To access rows of a DataTable one can use an @ref DataTable::iterator
+ *  "iterator" of the DataTable class or a direct access operator.
+ *
+ *  @see DataRecord, DataField
+ *  @ingroup data
+ */
+class DataRow : public internal::DataRowBase
+{
+public:
+    /// Creates a new standalone row.
+    /// This constructor creates a new row with the given number of elements.
+    /// Note that the row elements are left un-initialized.
+
+    explicit DataRow(size_t n);
+
+    /// Creates a new standalone row.
+    /// This constructor creates a new row with the given number of elements.
+    /// The row elements are initialized to the given default @e value.
+
+    DataRow(size_t n, double value);
+
+    /// Creates a new standalone row.
+    /// This constructor creates a new row with the number of elements equal
+    /// to the number of @e columns in the supplied DataColumns collection.
+    /// If the optional parameter @e initialize is true, the row elements
+    /// will be initialized to the default values of their respective columns.
+
+    explicit DataRow(const DataColumns& columns, bool initialize = false);
+
+    /// Creates a new standalone row.
+    /// This constructor creates a new row which is a copy of the @e other row.
+
+    DataRow(const DataRow& other);
+
+    /// Frees memory of standalone rows.
+    ~DataRow();
+
+    /// Gets a value of the given element casted to the specific type.
+    template <typename T>
+    T get(size_t index) const
+    {
+        return static_cast<T>(at(index));
+    }
+
+    /// Sets a value of the given element of the row.
+    template <typename T>
+    void set(size_t index, const T& value)
+    {
+        modified(true);
+        at(index) = static_cast<double>(value);
+    }
+
+private:
+    DataRow();
+    DataRow& operator=(const DataRow&);
+
+    /// Creates a new row from the pre-allocated @e data buffer.
+    DataRow(double* const data) : DataRowBase(data) {}
+
+    /// Returns reference to the given element.
+    double& at(size_t index) const { return data_[index+1]; }
+};
+
+// Declaration of template specializations.
+
+template <>
+std::string DataRow::get<std::string>(size_t index) const;
+
+template <>
+void DataRow::set<std::string>(size_t index, const std::string& value);
+
+namespace internal {
+
+/*! @internal
+ *  Represents a proxy for a data row.
+ *
+ *  The only difference with DataRow class is the construction, destruction
+ *  and copy implementation. The DataRowProxy is used internaly by DataPage
+ *  to reference to a pre-allocated memory. The DataRowProxy never owns the
+ *  actual data.
+ *
+ *  @ingroup data
+ */
+class DataRowProxy : public DataRowBase
+{
+public:
+    using DataRowBase::size;
+    using DataRowBase::flag;
+    using DataRowBase::data;
+
+    DataRowProxy()
+      : DataRowBase() {}
+
+    DataRowProxy(double* const data)
+      : DataRowBase(data) {}
+
+    DataRowProxy& operator=(double* const data)
+    { data_ = data; return *this; }
+
+    operator DataRow&()
+    { return reinterpret_cast<DataRow&>(*this); } 
+
+    operator const DataRow&() const
+    { return reinterpret_cast<const DataRow&>(*this); } 
+
+private:
+    friend class DataPage;
+    friend class DataTable;
+    template <typename R, typename T> friend class DataTableIterator;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATAROW_H_
diff --git a/odb_api/src/odb_api/DataSaver.cc b/odb_api/src/odb_api/DataSaver.cc
new file mode 100644
index 0000000..1c86766
--- /dev/null
+++ b/odb_api/src/odb_api/DataSaver.cc
@@ -0,0 +1,67 @@
+/// @file   DataSaver.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataSaver.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataView.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+DataSaver::DataSaver(const std::string& path)
+  : writer_(path),
+    target_(writer_.begin())
+{}
+
+DataSaver::~DataSaver()
+{
+    close();
+}
+
+void DataSaver::save(const DataSet& dataset, const DataTable& master)
+{
+    ASSERT(&dataset == master.dataset()
+           && "Master table must be owned by the dataset!");
+    save(dataset, master.name());
+}
+
+void DataSaver::save(const DataSet& dataset, const std::string& master)
+{
+    DataView view(dataset, master, true);
+    odb::MetaData metadata(0);
+
+    for (size_t i = 0; i < view.columns().size(); ++i)
+    {
+        typedef odb::DataStream<odb::SameByteOrder, DataHandle> DataStream;
+
+        DataColumn& column = view.columns()[i];
+        odb::Column* c = new odb::Column(metadata);
+
+        ASSERT(c);
+
+        c->name(column.name());
+        c->type<DataStream>(column.type(), false);
+        c->missingValue(column.missingValue());
+        c->bitfieldDef(column.bitfieldDef());
+
+        metadata.push_back(c);
+    }
+
+    target_->columns(metadata);
+    target_->writeHeader();
+    size_t size = view.columns().size();
+
+    for (DataView::iterator source = view.begin();
+            source != view.end(); ++source, ++target_)
+        copy(*source, *source + size, target_->data()); 
+}
+
+void DataSaver::close()
+{
+    target_->close();
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSaver.h b/odb_api/src/odb_api/DataSaver.h
new file mode 100644
index 0000000..d90529f
--- /dev/null
+++ b/odb_api/src/odb_api/DataSaver.h
@@ -0,0 +1,57 @@
+/// @file   DataSaver.h
+/// @author Tomas Kral
+
+#ifndef DATASAVER_H_
+#define DATASAVER_H_
+
+#include "eckit/eckit.h"
+#include "odb_api/Writer.h"
+
+namespace odb {
+
+class DataSet;
+class DataTable;
+
+/*! Saves data from a dataset into a target file.
+ *  
+ *  @ingroup data
+ */
+class DataSaver
+{
+public:
+    /// Creates a new saver and opens the target file.
+    DataSaver(const std::string& path);
+
+    /// Closes the target file and destroys the saver object.
+   ~DataSaver();
+
+    /*! Saves the @em dataset's tables in the target file.
+     *
+     *  @param dataset reference to a dataset of interest
+     *  @param master  reference to a master table to be saved in the target
+     *                 file with all its descendants
+     */
+    void save(const DataSet& dataset, const DataTable& master);
+
+    /*! Saves the @em dataset's tables in the target file.
+     *
+     *  @param dataset reference to a dataset of interest
+     *  @param master  name of the master table to be saved in the target file
+     *                 with all its descendants
+     */
+    void save(const DataSet& dataset, const std::string& master);
+
+    /// Closes the target file.
+    void close();
+
+private:
+    DataSaver(const DataSaver&);
+    DataSaver& operator=(const DataSaver&);
+
+    Writer<> writer_;
+    Writer<>::iterator target_;
+};
+
+} // namespace odb
+
+#endif // DATASAVER_H_
diff --git a/odb_api/src/odb_api/DataSelect.cc b/odb_api/src/odb_api/DataSelect.cc
new file mode 100644
index 0000000..82a3243
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelect.cc
@@ -0,0 +1,125 @@
+/// @file   DataSelect.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataSelect.h"
+#include "odb_api/DataSelectIterator.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLDataSet.h"
+#include "odb_api/SQLDataTable.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+using namespace odb;
+using namespace eckit;
+
+namespace odb {
+
+
+DataSelect::DataSelect(const std::string& statement, const DataSet& dataset, ecml::ExecutionContext* context)
+  : statement_(statement),
+    dataset_(&dataset),
+    table_(0),
+    begin_(new internal::DataSelectIterator(*this, true, context)),
+    context_(context)
+{
+    populateColumns();
+}
+
+DataSelect::DataSelect(const std::string& statement, const DataTable& table, ecml::ExecutionContext* context)
+  : statement_(statement),
+    dataset_(0),
+    table_(&table),
+    begin_(new internal::DataSelectIterator(*this, true, context)),
+    context_(context)
+{
+    populateColumns();
+}
+
+sql::SQLDatabase* DataSelect::database() const
+{
+    sql::SQLDataSet* db = new sql::SQLDataSet(table_ ? table_->name() : dataset_->name());
+
+    ASSERT(db);
+
+    if (dataset_)
+    {
+        for (DataTables::const_iterator it = dataset_->tables().begin();
+                it != dataset_->tables().end(); ++it)
+        {
+            SQLDataTable* table = new SQLDataTable(*db, **it);
+            ASSERT(table);
+            db->addTable(table);
+        }
+    }
+    else if (table_)
+    {
+        SQLDataTable* table = new SQLDataTable(*db, *table_);
+        ASSERT(table);
+        db->addTable(table);
+    }
+
+    return db;
+}
+
+DataSelect::~DataSelect()
+{}
+
+DataSelect::iterator DataSelect::begin()
+{
+    internal::DataSelectIterator* it = begin_.get()
+        ? begin_.release()
+        : new internal::DataSelectIterator(*this, true, context_);
+
+    ASSERT(it);
+
+    it->prepare();
+
+    return iterator(it);
+}
+
+DataSelect::iterator DataSelect::end() const
+{
+    return iterator(new internal::DataSelectIterator(*this, false, context_));
+}
+
+void DataSelect::populateColumns()
+{
+    ASSERT(begin_.get());
+
+    const Expressions& results = begin_->results();
+
+    for (size_t i = 0; i < results.size(); i++)
+    {
+        const sql::expression::SQLExpression* exp = results[i];
+        const sql::type::SQLType* sqlType = exp->type();
+        const int kind = sqlType->getKind();
+
+        ColumnType dataType;
+
+        switch (kind)
+        {
+            using namespace odb::sql::type;
+
+            case SQLType::realType:    dataType = REAL;     break;
+            case SQLType::doubleType:  dataType = DOUBLE;   break;
+            case SQLType::integerType: dataType = INTEGER;  break;
+            case SQLType::stringType:  dataType = STRING;   break;
+            case SQLType::bitmapType:  dataType = BITFIELD; break;
+            case SQLType::blobType: NOTIMP; break;
+            default:
+                Log::error() << "Unknown type: " << *sqlType << ", kind: " << kind << std::endl;
+                ASSERT(!"UnknownType");
+                break;
+        }
+
+        DataColumn c(exp->title(), dataType);
+        // TODO: c.hasMissing(exp->hasMissingValue());
+        c.missingValue(exp->missingValue());
+        c.bitfieldDef(exp->bitfieldDef());
+        columns_.push_back(c);
+    }
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSelect.h b/odb_api/src/odb_api/DataSelect.h
new file mode 100644
index 0000000..d014e6e
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelect.h
@@ -0,0 +1,95 @@
+/// @file   DataSelect.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATASELECT_H_
+#define ODBLIB_DATASELECT_H_
+
+#include "eckit/eckit.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/DataColumns.h"
+#include "odb_api/SharedIterator.h"
+
+namespace odb { namespace sql { class SQLDatabase; } }
+
+namespace odb {
+
+class DataSet;
+class DataTable;
+
+namespace internal { class DataSelectIterator; }
+
+/*! Provides means of executing SQL select statements on in-memory data tables.
+ *
+ * The DataSelect class is typically used in situations when you need to
+ * execute one or more SQL select statements on a single DataTable. This table
+ * can be either a standalone table or a table which is part of a DataSet.
+ *
+ * The primary advantage of using DataSelect over other SQL querying methods
+ * (e.g.  odb::Select) is that it operates purely on in-memory data and thus
+ * eliminates any overhead that might be incurred by reading data direcly from
+ * disk devices.  This becomes increasingly relevant if you want to perform
+ * more than one SQL query on the same table during the execution of your
+ * program. However, the downside of this method is that it requires to hold
+ * the entire table in memory so one needs to choose the right tradeoff.
+ *
+ * Results of the SQL query can be obtained using the @ref DataSelect::iterator
+ * "iterator" of the DataSelect class. This iterator provides a sequential,
+ * read-only access to the individual @ref DataRow "rows" of the SQL query's
+ * result.
+ *
+ * To see an example how to use DataSelect to populate a DataTable one can
+ * refer to @ref UsingDataSelectToPopulateADataTable.
+ * 
+ * @see DataTable, DataSet
+ * 
+ * @ingroup data
+ */
+class DataSelect
+{
+public:
+    /// Represents an iterator on the results of the SQL query.
+    typedef SharedIterator<internal::DataSelectIterator> iterator;
+
+    /// Creates new query on the given @em dataset.
+    DataSelect(const std::string& statement, const DataSet& dataset, ecml::ExecutionContext*);
+
+    /// Creates new query on the given @em table.
+    DataSelect(const std::string& statement, const DataTable& table, ecml::ExecutionContext*);
+
+    /// Frees the query from memory.
+    ~DataSelect();
+
+    /// Returns columns of the query's result.
+    const DataColumns& columns() const { return columns_; }
+
+    /// Returns the query's statement.
+    const std::string& statement() const { return statement_; }
+
+    /// Returns iterator pointing to the first result of the query.
+    iterator begin();
+
+    /// Returns iterator pointing past the last result of the query.
+    iterator end() const;
+
+private:
+    DataSelect(const DataSelect&);
+    DataSelect& operator=(const DataSelect&);
+
+    odb::sql::SQLDatabase* database() const;
+    void populateColumns();
+
+    const std::string statement_;
+    const DataSet* const dataset_;
+    const DataTable* const table_;
+    std::auto_ptr<internal::DataSelectIterator> begin_;
+    DataColumns columns_;
+    ecml::ExecutionContext* context_;
+
+    friend class internal::DataSelectIterator;
+};
+
+} // namespace odb
+
+#endif // ODBLIB_DATASELECT_H_
diff --git a/odb_api/src/odb_api/DataSelectIterator.cc b/odb_api/src/odb_api/DataSelectIterator.cc
new file mode 100644
index 0000000..9e9de45
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectIterator.cc
@@ -0,0 +1,98 @@
+/// @file   DataSelectIterator.h
+/// @author Tomas Kral
+
+#include "odb_api/DataSelect.h"
+#include "odb_api/DataSelectIterator.h"
+#include "odb_api/DataSelectSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelect.h"
+
+using namespace odb;
+
+namespace odb {
+namespace internal {
+
+DataSelectIterator::DataSelectIterator(const DataSelect& query, bool begin, ecml::ExecutionContext* context)
+  : context_(context),
+    query_(query),
+    session_(begin ? createSession() : 0),
+    select_(begin ? dynamic_cast<sql::SQLSelect*>(session_->statement()) : 0),
+    row_(begin ? select_->results().size() : 0),
+
+    aggregateResult_(false),
+    noMore_(begin ? false : true)
+{
+    if (begin)
+    {
+        ASSERT(session_);
+        ASSERT(select_);
+    }
+}
+
+void DataSelectIterator::prepare()
+{
+    ASSERT(select_);
+    select_->pushFirstFrame();
+    increment();
+}
+
+const Expressions& DataSelectIterator::results() const
+{
+    ASSERT(select_);
+    return select_->results();
+}
+
+DataSelectIterator::~DataSelectIterator()
+{
+    delete session_;
+    delete select_;
+}
+
+DataSelectSession* DataSelectIterator::createSession()
+{
+    DataSelectSession* session = new DataSelectSession(*this);
+    sql::SQLDatabase* db = query_.database();
+
+    ASSERT(db);
+    ASSERT(session);
+
+    sql::SQLParser parser;
+    parser.parseString(*session, query_.statement(), *db, sql::SQLOutputConfig::defaultConfig());
+
+    sql::SQLStatement* statement = session->statement();
+    sql::SQLSelect* select = dynamic_cast<sql::SQLSelect*>(statement);
+    select->prepareExecute();
+
+    return session;
+}
+
+bool DataSelectIterator::equal(const DataSelectIterator&) const
+{
+    return noMore_;
+}
+
+void DataSelectIterator::increment()
+{
+    if (noMore_ || aggregateResult_)
+    {
+        noMore_ = true;
+        return;
+    }
+
+    bool ok = select_->processOneRow(context_);
+
+    if (!ok)
+    {
+        if (select_->isAggregate())
+        {
+            aggregateResult_ = true;
+        }
+        else
+            noMore_ = true;
+
+        select_->postExecute(context_);
+    }
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSelectIterator.h b/odb_api/src/odb_api/DataSelectIterator.h
new file mode 100644
index 0000000..e4c2d1d
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectIterator.h
@@ -0,0 +1,70 @@
+/// @file   DataSelectIterator.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_DATASELECTITERATOR_H_
+#define ODBLIB_DATASELECTITERATOR_H_
+
+#include "odb_api/DataRow.h"
+#include "odb_api/IteratorFacade.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace odb
+{
+    namespace sql
+    {
+        class SQLSession;
+        class SQLSelect;
+
+        namespace expression { class Expressions; }
+    }
+}
+
+namespace odb {
+
+class DataSelect;
+
+namespace internal {
+
+class DataSelectSession;
+
+/*! @internal
+ *  @brief Input iterator providing a read-only access to SQL query results.
+ *  @ingroup data
+ */
+class DataSelectIterator
+  : public InputIteratorFacade<DataSelectIterator, const DataRow>
+{
+public:
+   ~DataSelectIterator();
+    double* const data() { return row_.data(); }
+private:
+    DataSelectIterator(const DataSelect& select, bool begin, ecml::ExecutionContext*);
+    // DataSelectIterator(const DataSelect& select);
+
+    DataSelectIterator(const DataSelectIterator&);
+    DataSelectIterator& operator=(const DataSelectIterator&);
+
+    DataSelectSession* createSession();
+    void prepare();
+    const odb::sql::expression::Expressions& results() const;
+
+    const DataRow& dereference() const { return row_; }
+    void increment();
+    bool equal(const DataSelectIterator&) const;
+
+    ecml::ExecutionContext* context_;
+    const DataSelect& query_;
+    DataSelectSession* session_;
+    odb::sql::SQLSelect* select_;
+    DataRow row_;
+    bool aggregateResult_;
+    bool noMore_;
+
+    friend class odb::DataSelect;
+    friend class odb::IteratorFacadeAccess;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // ODBLIB_DATASELECTITERATOR_H_
diff --git a/odb_api/src/odb_api/DataSelectOutput.cc b/odb_api/src/odb_api/DataSelectOutput.cc
new file mode 100644
index 0000000..729d1be
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectOutput.cc
@@ -0,0 +1,36 @@
+/// @file   DataSelectOutput.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataSelectIterator.h"
+#include "odb_api/DataSelectOutput.h"
+#include "odb_api/Expressions.h"
+
+using namespace odb;
+
+namespace odb {
+namespace internal {
+
+DataSelectOutput::DataSelectOutput(DataSelectIterator& it)
+  : it_(it),
+    count_(0)
+{}
+
+DataSelectOutput::~DataSelectOutput()
+{}
+
+bool DataSelectOutput::output(const Expressions& results, ecml::ExecutionContext* context)
+{
+    double* const data = it_.data();
+    size_t size = results.size();
+    bool missing = false;
+
+    for (size_t i = 0; i < size; i++)
+        data[i] = results[i]->eval(missing /*= false */);
+
+    ++count_;
+
+    return true;
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSelectOutput.h b/odb_api/src/odb_api/DataSelectOutput.h
new file mode 100644
index 0000000..9612ff6
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectOutput.h
@@ -0,0 +1,50 @@
+/// @file   DataSelectOutput.h
+/// @author Tomas Kral
+
+#ifndef DataSelectOutput_H
+#define DataSelectOutput_H
+
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace internal {
+
+class DataSelectIterator;
+
+class DataSelectOutput
+  : public odb::sql::SQLOutput
+{
+public:
+	DataSelectOutput(DataSelectIterator& it);
+   ~DataSelectOutput();
+
+protected:
+	virtual void print(std::ostream&) const {}
+
+private:
+	DataSelectOutput(const DataSelectOutput&);
+	DataSelectOutput& operator=(const DataSelectOutput&);
+
+    DataSelectIterator& it_;
+    unsigned long long count_;
+
+	virtual void size(int) {}
+	virtual void reset() { count_ = 0; }
+	virtual void flush(ecml::ExecutionContext*) {}
+	virtual bool output(const odb::sql::expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(odb::sql::SQLSelect&) {}
+	virtual void cleanup(odb::sql::SQLSelect&) {}
+	virtual unsigned long long count() { return count_; }
+
+	virtual void outputReal(double, bool) {}
+	virtual void outputDouble(double, bool) {}
+	virtual void outputInt(double, bool) {}
+	virtual void outputUnsignedInt(double, bool) {}
+	virtual void outputString(double, bool) {}
+	virtual void outputBitfield(double, bool) {}
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DataSelectOutput_H
diff --git a/odb_api/src/odb_api/DataSelectSession.cc b/odb_api/src/odb_api/DataSelectSession.cc
new file mode 100644
index 0000000..a820386
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectSession.cc
@@ -0,0 +1,47 @@
+/// @file   DataSelectSession.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataSelectOutput.h"
+#include "odb_api/DataSelectSession.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace internal {
+
+DataSelectSession::DataSelectSession(DataSelectIterator& it)
+ : SQLSession(odb::sql::SQLOutputConfig::defaultConfig(), ","),
+   it_(it), 
+   sql_(0)
+{
+    loadDefaultSchema();
+}
+
+DataSelectSession::~DataSelectSession()
+{}
+
+void DataSelectSession::statement(odb::sql::SQLStatement* sql)
+{
+    ASSERT(sql);    
+    sql_ = sql;
+    gotSelectAST(false);
+}
+
+odb::sql::SQLOutput* DataSelectSession::defaultOutput()
+{
+    return new DataSelectOutput(it_);
+}
+
+odb::sql::SQLStatement* DataSelectSession::statement()
+{
+    typedef odb::sql::SQLStatement* P;
+    if (gotSelectAST())
+    {
+        gotSelectAST(false);
+        sql_ = P(selectFactory().create(*this, selectAST()));
+    }
+    return sql_;
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSelectSession.h b/odb_api/src/odb_api/DataSelectSession.h
new file mode 100644
index 0000000..6df137b
--- /dev/null
+++ b/odb_api/src/odb_api/DataSelectSession.h
@@ -0,0 +1,36 @@
+/// @file   DataSelectSession.h
+/// @author Tomas Kral
+
+#ifndef DATASELECTSESSION_H_
+#define DATASELECTSESSION_H_
+
+#include "odb_api/SQLSession.h"
+
+namespace odb {
+namespace internal {
+
+class DataSelectIterator;
+
+class DataSelectSession : public odb::sql::SQLSession
+{
+public:
+    DataSelectSession(DataSelectIterator& it);
+   ~DataSelectSession();
+
+    odb::sql::SQLStatement* statement();
+
+private:
+    DataSelectSession(const DataSelectSession&);
+    DataSelectSession& operator=(const DataSelectSession&);
+
+    void statement(odb::sql::SQLStatement* sql);
+    odb::sql::SQLOutput* defaultOutput();
+
+    DataSelectIterator& it_;
+    odb::sql::SQLStatement* sql_;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATASELECTSESSION_H_
diff --git a/odb_api/src/odb_api/DataSet.cc b/odb_api/src/odb_api/DataSet.cc
new file mode 100644
index 0000000..2d4b94c
--- /dev/null
+++ b/odb_api/src/odb_api/DataSet.cc
@@ -0,0 +1,32 @@
+/// @file   DataSet.cc
+/// @author Tomas Kral
+
+#include "eckit/eckit.h"
+#include "odb_api/DataLink.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+
+using namespace std;
+
+namespace odb {
+
+DataSet::DataSet(const std::string& name)
+  : name_(name),
+    tables_(*this),
+    links_(*this)
+{}
+
+DataSet::~DataSet()
+{
+    for (DataTables::iterator it = tables_.begin();
+            it != tables_.end(); ++it)
+        delete (*it);
+}
+
+void DataSet::clear()
+{
+    for_each(tables_.begin(), tables_.end(), mem_fun(&DataTable::clear));
+    for_each(links_.begin(), links_.end(), mem_fun(&DataLink::clear));
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSet.h b/odb_api/src/odb_api/DataSet.h
new file mode 100644
index 0000000..3b1c0d9
--- /dev/null
+++ b/odb_api/src/odb_api/DataSet.h
@@ -0,0 +1,62 @@
+/// @file   DataSet.h
+/// @author Tomas Kral
+
+#ifndef DATASET_H_
+#define DATASET_H_
+
+#include "odb_api/DataLinks.h"
+#include "odb_api/DataTables.h"
+
+namespace odb {
+
+class DataTable;
+class DataLink;
+
+/*! Represents an in-memory representation of a database.
+ *
+ *  A DataSet is an in-memory data container that looks like a database. A
+ *  DataSet consists of a collection of tables that can be related to each
+ *  other via links. Whereas the tables hold the actual data, the
+ *  links allow to navigate through the table hierarchy.
+ *
+ *  @ingroup data
+ */
+class DataSet
+{
+public:
+    /// Creates a new dataset with the given @p name.
+    explicit DataSet(const std::string& name = "unknown");
+
+    /// Destroys dataset object.
+    virtual ~DataSet();
+
+    /// Returns the name of the dataset.
+    const std::string& name() const { return name_; }
+
+    /// Returns reference to the collection of tables.
+    DataTables& tables() { return tables_; }
+
+    /// Returns const reference to the collection of tables.
+    const DataTables& tables() const { return tables_; }
+
+    /// Returns reference to the collection of links.
+    DataLinks& links() { return links_; }
+
+    /// Returns const reference to the collection of links.
+    const DataLinks& links() const { return links_; }
+
+    /// Clears the dataset by removing all rows in all tables.
+    void clear();
+
+private:
+    DataSet(const DataSet&);
+    DataSet& operator=(const DataSet&);
+
+    std::string name_;
+    DataTables tables_;
+    DataLinks links_;
+};
+
+} // namespace odb
+
+#endif // DATASET_H_
diff --git a/odb_api/src/odb_api/DataSetBuilder.cc b/odb_api/src/odb_api/DataSetBuilder.cc
new file mode 100644
index 0000000..67e4807
--- /dev/null
+++ b/odb_api/src/odb_api/DataSetBuilder.cc
@@ -0,0 +1,241 @@
+/// @file   DataSetBuilder.cc
+/// @author Tomas Kral
+
+#include "eckit/eckit.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataLink.h"
+#include "odb_api/DataSetBuilder.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/MetaData.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+namespace {
+
+enum TokenType
+{
+    TABLE_COLUMN = 0,
+    LINK_OFFSET_COLUMN = 1,
+    LINK_LEN_COLUMN = 2
+};
+
+struct Token
+{
+    TokenType type;
+    std::string columnName;
+    odb::ColumnType columnType;
+    std::string tableName;
+    std::string parentTableName;
+    std::string childTableName;
+};
+
+typedef std::vector<Token> TokenVector;
+TokenVector tokenize(const odb::MetaData& metaData);
+
+TokenVector tokenize(const odb::MetaData& metaData)
+{
+    TokenVector tokens;
+
+    for (size_t i = 0; i < metaData.size(); i++)
+    {
+        size_t pos;
+        Token token;
+        odb::Column& column = *metaData[i];
+
+        token.columnName = column.name();
+        token.columnType = column.type();
+
+        if ((pos = token.columnName.find(".offset@")) != std::string::npos)
+        {
+            token.type = LINK_OFFSET_COLUMN;
+            token.childTableName = token.columnName.substr(0, pos);
+            token.parentTableName = token.columnName.substr(pos + 8);
+        }
+        else if ((pos = token.columnName.find(".len@")) != std::string::npos)
+        {
+            token.type = LINK_LEN_COLUMN;
+            token.childTableName = token.columnName.substr(0, pos);
+            token.parentTableName = token.columnName.substr(pos + 5);
+        }
+        else
+        {
+            token.type = TABLE_COLUMN;
+            pos = token.columnName.find("@");
+            token.tableName = token.columnName.substr(pos + 1);
+
+            if (token.tableName.empty())
+                throw eckit::UserError("Column " + token.columnName
+                       + "does not belong to any table.");
+        }
+
+        tokens.push_back(token);
+    }
+
+    return tokens;
+}
+
+ColumnType convertColumnType(odb::ColumnType type)
+{
+    switch (type)
+    {
+        case odb::INTEGER:  return INTEGER;
+        case odb::REAL:     return REAL;
+        case odb::DOUBLE:   return DOUBLE;
+        case odb::STRING:   return STRING;
+        case odb::BITFIELD: return BITFIELD;
+        default: ASSERT(!"Unexpected odb::ColumnType.");
+    };
+
+    return ColumnType(0); // never reached
+}
+
+} // namespace
+
+DataSetBuilder::DataSetBuilder(const odb::MetaData& metadata, bool buildLinks)
+  : metadata_(metadata),
+    mapping_(),
+    buildLinks_(buildLinks)
+{}
+
+DataSetBuilder::DataSetBuilder(const odb::MetaData& metadata,
+        const DataTableMappings& mapping, bool buildLinks)
+  : metadata_(metadata),
+    mapping_(mapping),
+    buildLinks_(buildLinks)
+{}
+
+void DataSetBuilder::build(DataSet& dataset) const
+{
+    buildTables(dataset);
+
+    if (buildLinks_)
+        buildLinks(dataset);
+}
+
+void DataSetBuilder::buildTables(DataSet& dataset) const
+{
+    typedef std::map<std::string, DataColumns> ColumnsMap;
+
+    ColumnsMap columnsMap;
+    TokenVector tokens = tokenize(metadata_);
+
+    for (size_t i = 0; i < tokens.size(); i++)
+    {
+        Token& token = tokens[i];
+
+        if (token.type == TABLE_COLUMN)
+        {
+            std::string name = token.tableName;
+
+            DataTableMappings::const_iterator it = mapping_.find(name);
+            if (it != mapping_.end())
+                name = it->second;
+
+            DataColumns& columns = columnsMap[name];
+            columns.push_back(DataColumn(*metadata_[i]));
+        }
+    }
+
+    for (ColumnsMap::iterator it = columnsMap.begin();
+            it != columnsMap.end(); ++it)
+    {
+        DataTable* table = new DataTable(it->first, it->second);
+        dataset.tables().insert(table);
+    }
+}
+
+void DataSetBuilder::buildLinks(DataSet& dataset) const
+{
+    TokenVector tokens = tokenize(metadata_);
+
+    Token previousToken;
+    for (size_t i = 0; i < tokens.size(); i++)
+    {
+        Token& token = tokens[i];
+
+        switch (token.type)
+        {
+        case (TABLE_COLUMN):
+            break;
+        case (LINK_OFFSET_COLUMN):
+            // Nothing to be done here. We assume that offset columns
+            // are always followed by len columns.
+            break;
+
+        case (LINK_LEN_COLUMN):
+        {
+            ASSERT(previousToken.type == LINK_OFFSET_COLUMN);
+            ASSERT(previousToken.parentTableName == token.parentTableName);
+            ASSERT(previousToken.childTableName == token.childTableName);
+
+            std::string parentName = token.parentTableName;
+            std::string childName = token.childTableName;
+
+            // Look for table mappings.
+            DataTableMappings::const_iterator it = mapping_.find(parentName);
+            if (it != mapping_.end())
+                parentName = it->second;
+
+            it = mapping_.find(childName);
+            if (it != mapping_.end())
+                childName = it->second;
+
+            // Ignore links between two aligned tables mapped into a
+            // single one.
+            if (parentName == childName)
+            {
+                Log::info() << "Ignoring link between aligned tables "
+                    << token.parentTableName << " and " 
+                    << token.childTableName << std::endl;
+                break;
+            }
+
+            DataTables::iterator t;
+            // Find parent table. If there is none issue a warning and skip
+            // building the link.
+            t = dataset.tables().find(parentName);
+            if (t == dataset.tables().end())
+            {
+                Log::warning() << "No parent table found for the link "
+                    << token.columnName << std::endl;
+                break;
+            }
+            DataTable* parent = *t;
+
+            // Find child table. If there is none issue a warning and skip
+            // building the link.
+            t = dataset.tables().find(childName);
+            if (t == dataset.tables().end())
+            {
+                Log::warning() << "No child table found for the link "
+                    << token.columnName << std::endl;
+                break;
+            }
+            DataTable* child = *t;
+
+            // Make sure that in case of multiple aligned tables
+            // which are joined to a signle table using table mappings
+            // we create only one link.
+            DataLinks::const_iterator l;
+            l = dataset.links().find(parent, child);
+            if (l == dataset.links().end())
+            {
+                DataLink* link = new DataLink(*parent, *child);
+                link->offsetName(previousToken.columnName);
+                link->lengthName(token.columnName);
+                dataset.links().insert(link);
+            }
+
+        }   break;
+        }
+
+        previousToken = token;
+    }
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSetBuilder.h b/odb_api/src/odb_api/DataSetBuilder.h
new file mode 100644
index 0000000..40c2468
--- /dev/null
+++ b/odb_api/src/odb_api/DataSetBuilder.h
@@ -0,0 +1,54 @@
+/// @file   DataSetBuilder.h
+/// @author Tomas Kral
+
+#ifndef DATASETBUILDER_H_
+#define DATASETBUILDER_H_
+
+#include "odb_api/DataTableMappings.h"
+#include "odb_api/MetaData.h"
+
+namespace odb {
+
+class DataSet;
+class DataTable;
+class DataLink;
+
+/*! Builds a dataset from a source metadata.
+ *
+ *  The DataSetBuilder class is responsible for building DataTable and
+ *  DataLink objects of a DataSet given the metadata of a data source.
+ *
+ *  @ingroup data
+ */
+class DataSetBuilder
+{
+public:
+    /// Creates a new dataset builder.
+    DataSetBuilder(const odb::MetaData& metadata, bool buildLinks);
+
+    /// Creates a new dataset builder providing the table mappings.
+    DataSetBuilder(const odb::MetaData& metadata,
+            const DataTableMappings& mapping, bool buildLinks);
+
+    /// Builds dataset tables and links.
+    void build(DataSet& dataset) const;
+
+private:
+    /// Builds dataset tables.
+    void buildTables(DataSet& dataset) const;
+
+    /// Builds dataset links.
+    void buildLinks(DataSet& dataset) const;
+
+private:
+    DataSetBuilder(const DataSetBuilder&);
+    DataSetBuilder& operator=(const DataSetBuilder&);
+
+    odb::MetaData metadata_;
+    DataTableMappings mapping_;
+    bool buildLinks_;
+};
+
+} // namespace odb
+
+#endif // DATASETBUILDER_H_
diff --git a/odb_api/src/odb_api/DataSetFiller.cc b/odb_api/src/odb_api/DataSetFiller.cc
new file mode 100644
index 0000000..9896b4a
--- /dev/null
+++ b/odb_api/src/odb_api/DataSetFiller.cc
@@ -0,0 +1,139 @@
+/// @file   DataSetFiller.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataLink.h"
+#include "odb_api/DataSetFiller.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataTables.h"
+#include "odb_api/MetaData.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+DataSetFiller::DataSetFiller(DataSet& dataset, const odb::MetaData& metadata)
+  : dataset_(dataset),
+    tableFillers_(),
+    linkFillers_()
+{
+    buildFillers(metadata);
+}
+
+DataSetFiller::~DataSetFiller()
+{
+    for (DataTableFillers::iterator it = tableFillers_.begin();
+            it != tableFillers_.end(); ++it)
+    {
+        DataTableFiller* filler = *it;
+        delete filler;
+    }
+
+    for (DataLinkFillers::iterator it = linkFillers_.begin();
+            it != linkFillers_.end(); ++it)
+    {
+        DataLinkFiller* filler = *it;
+        delete filler;
+    }
+}
+
+void DataSetFiller::buildFillers(const odb::MetaData& metadata)
+{
+    for (DataTables::iterator it = dataset_.tables().begin();
+            it != dataset_.tables().end(); ++it)
+    {
+        DataTable& table = (**it);
+
+        // Add source columns for the filler.
+        DataTableFiller* filler = new DataTableFiller(table);
+
+        for (size_t i = 0; i < table.columns().size(); i++)
+        {
+            const DataColumn& column = table.columns()[i];
+            filler->addColumn(metadata.columnIndex(column.name()));
+        }
+
+        // Add link columns for the filler (needed to figure how many rows
+        // belong to parent table and how many to child table).
+        for (DataLinks::iterator it = dataset_.links().begin(),
+                end = dataset_.links().end(); it != end; ++it)
+        {
+            DataLink& link = (**it);
+
+            if (link.parent().name() == table.name())
+            {
+                size_t linkLenIndex = metadata.columnIndex(link.lengthName());
+                filler->addLink(linkLenIndex);
+            }
+        }
+
+        tableFillers_.push_back(filler); 
+    }
+
+    for (DataLinks::iterator it = dataset_.links().begin(),
+            end = dataset_.links().end(); it != end; ++it)
+    {
+        DataLink& link = (**it);
+
+        size_t offsetColumnIndex = metadata.columnIndex(link.offsetName());
+        size_t lenColumnIndex = metadata.columnIndex(link.lengthName());
+
+        DataLinkFiller* filler = new DataLinkFiller(link);
+
+        filler->offsetColumnIndex(offsetColumnIndex);
+        filler->lenColumnIndex(lenColumnIndex);
+
+        linkFillers_.push_back(filler);
+    }
+}
+
+DataSetFiller::iterator DataSetFiller::begin()
+{
+    return DataSetFiller::iterator(*this);
+}
+
+DataSetFillerIterator::DataSetFillerIterator(DataSetFiller& owner)
+  : owner_(&owner),
+    tableFillers_(),
+    linkFillers_()
+{
+    tableFillers_.reserve(owner.tableFillers_.size());
+    for (DataTableFillers::iterator it = owner.tableFillers_.begin(),
+            end = owner.tableFillers_.end(); it != end; ++it)
+        tableFillers_.push_back((*it)->begin());
+
+    linkFillers_.reserve(owner.linkFillers_.size());
+    for (DataLinkFillers::iterator it = owner.linkFillers_.begin(),
+            end = owner.linkFillers_.end(); it != end; ++it)
+        linkFillers_.push_back((*it)->begin());
+}
+
+DataSetFillerIterator& DataSetFillerIterator::operator=(const double* data)
+{
+    for (DataTableFillerIterators::iterator it = tableFillers_.begin(),
+            end = tableFillers_.end(); it != end; ++it)
+        (**it) = data;
+
+    for (DataLinkFillerIterators::iterator it = linkFillers_.begin(),
+            end = linkFillers_.end(); it != end; ++it)
+        (**it) = data;
+
+    return *this;
+}
+
+DataSetFillerIterator& DataSetFillerIterator::operator++()
+{
+    for (DataTableFillerIterators::iterator it = tableFillers_.begin(),
+            end = tableFillers_.end(); it != end; ++it)
+        ++(*it);
+
+    for (DataLinkFillerIterators::iterator it = linkFillers_.begin(),
+            end = linkFillers_.end(); it != end; ++it)
+        ++(*it);
+
+    return *this;
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataSetFiller.h b/odb_api/src/odb_api/DataSetFiller.h
new file mode 100644
index 0000000..fe68dfd
--- /dev/null
+++ b/odb_api/src/odb_api/DataSetFiller.h
@@ -0,0 +1,73 @@
+/// @file   DataSetFiller.h
+/// @author Tomas Kral
+
+#ifndef DATASETFILLER_H_
+#define DATASETFILLER_H_
+
+#include "odb_api/DataLinkFiller.h"
+#include "odb_api/DataTableFiller.h"
+
+namespace odb { class MetaData; }
+
+namespace odb {
+
+class DataSet;
+
+namespace internal {
+
+class DataSetFillerIterator;
+
+/*! Fills DataSet with data from a source.
+ *
+ *  @see DataTableFiller, DataLinkFiller
+ *  @ingroup data
+ */
+class DataSetFiller
+{
+    typedef std::vector<DataTableFiller*> DataTableFillers;
+    typedef std::vector<DataLinkFiller*> DataLinkFillers;
+public:
+    typedef DataSetFillerIterator iterator;
+
+    DataSetFiller(DataSet& dataset, const odb::MetaData& metadata);
+    ~DataSetFiller();
+
+    iterator begin();
+
+private:
+    void buildFillers(const odb::MetaData& metadata);
+
+private:
+    DataSetFiller(const DataSetFiller&);
+    DataSetFiller& operator=(const DataSetFiller&);
+
+    DataSet& dataset_;
+    DataTableFillers tableFillers_;
+    DataLinkFillers linkFillers_;
+
+    friend class DataSetFillerIterator;
+};
+
+class DataSetFillerIterator
+  : public std::iterator<std::output_iterator_tag, DataSetFillerIterator>
+{
+    typedef std::vector<DataTableFiller*> DataTableFillers;
+    typedef std::vector<DataLinkFiller*> DataLinkFillers;
+    typedef std::vector<DataTableFiller::iterator> DataTableFillerIterators;
+    typedef std::vector<DataLinkFiller::iterator> DataLinkFillerIterators;
+public:
+    explicit DataSetFillerIterator(DataSetFiller& filler);
+    DataSetFillerIterator& operator*() { return *this; }
+    DataSetFillerIterator& operator=(const double* data);
+    DataSetFillerIterator& operator++();
+
+private:
+    DataSetFiller* owner_;
+    DataTableFillerIterators tableFillers_;
+    DataLinkFillerIterators linkFillers_;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATASETFILLER_H_
diff --git a/odb_api/src/odb_api/DataStream.cc b/odb_api/src/odb_api/DataStream.cc
new file mode 100644
index 0000000..e2c9a67
--- /dev/null
+++ b/odb_api/src/odb_api/DataStream.cc
@@ -0,0 +1,316 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DataStream.cc
+///
+/// @author Piotr Kuchta, March 2009
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/log/CodeLocation.h"
+#include "odb_api/MemoryBlock.h"
+
+namespace odb {
+
+inline void checkRead(int code, const char *msg, const eckit::CodeLocation& loc)
+{
+    if(code != 0)
+    {
+        throw eckit::ReadError("DataStream");
+    }
+}
+
+#define CHECK_READ(a)     ::odb::checkRead(!(a),#a,Here())
+
+template <typename T, typename D>
+DataStream<T,D>::DataStream() : f() {}
+
+template <typename T, typename D>
+DataStream<T,D>::DataStream(D *dh) : f(dh) {}
+
+template <typename T, typename D>
+DataStream<T,D>::DataStream(D &dh) : f(&dh) {}
+
+template <typename T, typename D>
+void DataStream<T,D>::close() { f->close(); }
+
+template <typename T, typename D>
+long DataStream<T,D>::read(void* p, long l) { return f->read(p, l);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readInt32(int32_t& i)
+{
+	CHECK_READ(f->read(&i, sizeof(int32_t)) == sizeof(int32_t));
+	T::swap(i);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readInt64(int64_t& i)
+{
+	CHECK_READ(f->read(&i, sizeof(int64_t)) == sizeof(int64_t));
+	T::swap(i);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeInt32(int32_t i)
+{
+	T::swap(i);
+	ASSERT(f->write(&i, sizeof(int32_t)) == sizeof(int32_t));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeInt64(int64_t i)
+{
+	T::swap(i);
+	ASSERT(f->write(&i, sizeof(int64_t)) == sizeof(int64_t));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readInt16(int16_t& i)
+{
+	CHECK_READ(f->read(&i, sizeof(int16_t)) == sizeof(int16_t));
+	T::swap(i);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeInt16(int16_t i)
+{
+	T::swap(i);
+	ASSERT(f->write(&i, sizeof(int16_t)) == sizeof(int16_t));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readUInt16(uint16_t& i)
+{
+	CHECK_READ(f->read(&i, sizeof(uint16_t)) == sizeof(uint16_t));
+	T::swap(i);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeUInt16(uint16_t i)
+{
+	T::swap(i);
+	ASSERT(f->write(&i, sizeof(uint16_t)) == sizeof(uint16_t));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readBuffer(MemoryBlock &buffer)
+{
+	int32_t size;
+	readInt32(size);
+	buffer.size(size);
+	CHECK_READ(f->read(buffer, size) == size);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeBuffer(const MemoryBlock &buffer)
+{
+	writeInt32(buffer.size());
+	eckit::Length writtenBytes = f->write(buffer, buffer.size());
+	ASSERT(long (writtenBytes) == long (buffer.size()));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readString(std::string &s)
+{
+	int32_t len;
+	readInt32(len);
+
+#ifdef _HPUX_SOURCE
+	char buff[8 * 1024 * 1024];
+	ASSERT(len < sizeof(buff));
+#else
+	char buff[len];
+#endif
+	CHECK_READ(f->read(&buff, len) == len);
+	
+	std::string r(buff, len);
+
+	s = r;
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeString(const std::string &s)
+{
+	int32_t len = s.size();
+	writeInt32(len);
+	ASSERT(f->write(s.c_str(), len) == len);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readChar(char &c)
+{
+	CHECK_READ(f->read(&c, sizeof(char)) == sizeof(char));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeChar(char c)
+{
+	size_t n = f->write(&c, sizeof(char));
+	ASSERT(n == sizeof(char));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readUChar(unsigned char &c)
+{
+	size_t n = f->read(&c, sizeof(unsigned char));
+	ASSERT(n == sizeof(unsigned char));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeUChar(const unsigned char c)
+{
+	ASSERT(f->write(&c, sizeof(unsigned char)) == sizeof(unsigned char));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readBytes(char *buff, size_t &len)
+{
+	CHECK_READ(f->read(buff, len) == static_cast<long>(len));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeBytes(const char *buff, size_t len)
+{
+	ASSERT(f->write(buff, len) == static_cast<long>(len));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readDouble(double &d)
+{
+	CHECK_READ(f->read(&d, sizeof(double)) == sizeof(double));
+	T::swap(d);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeDouble(double d)
+{
+	T::swap(d);
+	ASSERT(f->write(&d, sizeof(double)) == sizeof(double));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readFloat(float &d)
+{
+	CHECK_READ(f->read(&d, sizeof(float)) == sizeof(float));
+	T::swap(d);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeFloat(float d)
+{
+	T::swap(d);
+	ASSERT(f->write(&d, sizeof(float)) == sizeof(float));
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readProperties(Properties &p)
+{
+	int32_t size = 0;
+	readInt32(size);
+
+	for (int i = 0; i < size; i++)
+	{
+		std::string key, value;
+		readString(key);
+		readString(value);
+		p[key] = value;
+	}
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeProperties(const Properties &p)
+{
+	int32_t size = p.size();
+	writeInt32(size);
+
+	for(Properties::const_iterator i = p.begin(); i != p.end(); ++i)
+	{
+		writeString(i->first);
+		writeString(i->second);
+	}
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readFlags(Flags &v)
+{
+	int32_t size = 0;
+	readInt32(size);
+
+	v.clear();
+	v.resize(size);
+
+	for (int32_t i = 0; i < size; ++i)
+		readDouble(v[i]);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeFlags(const Flags &v)
+{
+	int32_t size = v.size();
+	writeInt32(size);
+
+	for (int32_t i = 0; i < size; ++i)
+		writeDouble(v[i]);
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::readBitfieldDef(BitfieldDef &v)
+{
+	FieldNames& names = v.first;
+	Sizes& sizes = v.second;
+	
+	names.clear();
+	sizes.clear();
+
+	int32_t namesSize = 0;
+	readInt32(namesSize);
+
+	for (int32_t i = 0; i < namesSize; i++)
+	{
+		std::string s;
+		readString(s);
+		names.push_back(s);
+	}
+
+	int32_t sizesSize = 0;
+	readInt32(sizesSize);
+
+	ASSERT(namesSize == sizesSize);
+
+	for (int32_t i = 0; i < sizesSize; i++)
+	{
+		int32_t n;
+		readInt32(n);
+		sizes.push_back(n);
+	}
+}
+
+template <typename T, typename D>
+void DataStream<T,D>::writeBitfieldDef(const BitfieldDef &v)
+{
+	const FieldNames& names = v.first;
+	const Sizes& sizes = v.second;
+
+	writeInt32(names.size());
+	for (size_t i = 0; i < names.size(); i++)
+		writeString(names[i]);
+
+	writeInt32(sizes.size());
+	for (size_t i = 0; i < sizes.size(); i++)
+		writeInt32(sizes[i]);
+}
+
+#undef CHECK_READ
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/DataStream.h b/odb_api/src/odb_api/DataStream.h
new file mode 100644
index 0000000..d74508f
--- /dev/null
+++ b/odb_api/src/odb_api/DataStream.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DataStream.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef DataStream_H
+#define DataStream_H
+
+#include <stdint.h>
+
+#include "eckit/eckit.h"
+#include "odb_api/Types.h"
+
+namespace eckit { class DataHandle; }
+class MemoryBlock;
+
+namespace odb {
+
+typedef std::map<std::string, std::string> Properties;
+typedef std::vector<double> Flags;
+
+class SameByteOrder {
+public:
+	template<typename T> static void swap(T &) {}
+};
+
+class OtherByteOrder {
+public:
+	template<typename T> static void swap(T &o)
+	{
+		char *p = reinterpret_cast<char *>(&o);
+		size_t size = sizeof(T);
+		for (size_t i=0; i < size / 2; ++i)
+			std::swap(p[i], p[size - i - 1]);
+	}
+};
+
+template <typename T = SameByteOrder, typename D = eckit::DataHandle>
+class DataStream {
+public:
+	typedef T ByteOrderType;
+	typedef D DataHandleType;
+
+	DataStream (D &);
+	DataStream (D *);
+	DataStream ();
+
+	void close();
+
+	long read(void* p, long l); 
+	D *dataHandle() { return f; }
+	void dataHandle(D *dh) { f = dh; }
+
+	void readInt64(int64_t &);
+	void writeInt64(int64_t);
+
+	void readInt32(int32_t &);
+	void writeInt32(int32_t);
+
+	void readInt16(int16_t &);
+	void writeInt16(int16_t);
+
+	void readUInt16(uint16_t &);
+	void writeUInt16(uint16_t);
+
+	void readString(std::string &);
+	void writeString(const std::string &);
+
+	void readBuffer(MemoryBlock &);
+	void writeBuffer(const MemoryBlock &);
+
+	void readChar(char &);
+	void writeChar(char);
+
+	void readUChar(unsigned char &);
+	void writeUChar(unsigned char);
+
+	void readBytes(char *, size_t &);
+	void writeBytes(const char *, size_t);
+
+	void readDouble(double &);
+	void writeDouble(double);
+
+	void readFloat(float &);
+	void writeFloat(float);
+
+	void readProperties(Properties &);
+	void writeProperties(const Properties &);
+
+	void readFlags(Flags &);
+	void writeFlags(const Flags &);
+
+	void readBitfieldDef(BitfieldDef &);
+	void writeBitfieldDef(const BitfieldDef &);
+private:
+	D * f;
+
+// No copy allowed.
+    DataStream(const DataStream&);
+    DataStream& operator=(const DataStream&);
+};
+
+} // namespace odb {
+
+#include "odb_api/DataStream.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/DataTable.cc b/odb_api/src/odb_api/DataTable.cc
new file mode 100644
index 0000000..daf5ee1
--- /dev/null
+++ b/odb_api/src/odb_api/DataTable.cc
@@ -0,0 +1,278 @@
+/// @file   DataTable.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataPage.h"
+#include "odb_api/DataTable.h"
+
+using namespace std;
+
+namespace odb {
+
+using namespace internal;
+
+DataTableProperties::DataTableProperties()
+  : blockSizeInKb_(DEFAULT_BLOCK_SIZE_IN_KB),
+    blockSizeInNumberOfRows_(DEFAULT_BLOCK_SIZE_IN_NUMBER_OF_ROWS),
+    blockFillMark_(DEFAULT_BLOCK_FILL_MARK),
+    blockFillMarkInNumberOfRows_(DEFAULT_BLOCK_FILL_MARK_IN_NUMBER_OF_ROWS)
+{}
+
+DataTable::DataTable(const std::string& name, const DataColumns& columns,
+        const DataTableProperties& properties)
+  : owner_(0),
+    name_(name),
+    columns_(columns),
+    properties_(properties),
+    frontPage_(0),
+    backPage_(0),
+    endPage_(0)
+{
+    frontPage_ = backPage_ = endPage_ = new DataPage(*this);
+    ASSERT(frontPage_);
+    frontPage_->rank(1);
+}
+
+DataTable::~DataTable()
+{
+    DataPage* page = frontPage_;
+
+    while (page)
+    {
+        DataPage* next = page->next();
+        delete page;
+        page = next;
+    }
+}
+
+size_t DataTable::size() const
+{
+    size_t size = 0;
+    const DataPage* page = frontPage_;
+
+    while (page)
+    {
+        size += page->size();
+
+        if (page == backPage_)
+            break;
+
+        page = page->next();
+    }
+
+    return size;
+}
+
+bool DataTable::empty() const
+{
+    const DataPage* page = frontPage_;
+
+    while (page)
+    {
+        if (!page->empty())
+            return false;
+
+        if (page == backPage_)
+            break;
+
+        page = page->next();
+    }
+
+    return true;
+}
+
+void DataTable::clear()
+{
+    DataPage* page = frontPage_;
+
+    while (page)
+    {
+        page->clear();
+
+        if (page == backPage_)
+            break;
+
+        page = page->next();
+    }
+
+    backPage_ = frontPage_;
+}
+
+DataRow& DataTable::operator[](size_t n)
+{
+    DataPage* page = frontPage_;
+
+    while (page)
+    {
+        if (n < page->size())
+            break;
+
+        n -= page->size();
+        page = page->next();
+    }
+
+    ASSERT(page && "Row index out of bounds!");
+
+    return static_cast<DataRow&>(page->at(n));
+}
+
+size_t DataTable::capacity() const
+{
+    const DataPage* page = frontPage_;
+    size_t c = 0;
+
+    while (page)
+    {
+        c += page->capacity();
+        page = page->next();
+    }
+
+    return c;
+}
+
+void DataTable::reserve(size_t n)
+{
+    size_t c = capacity();
+
+    while (c < n)
+    {
+        DataPage* page = extend();
+        c += page->capacity();
+    }
+}
+
+void DataTable::resize(size_t n, bool initialize)
+{
+    size_t size_ = size();
+
+    if (n > size_)
+    {
+        size_t more = n - size_ + backPage_->size();
+        more -= backPage_->resize(more, initialize);
+
+        while (more)
+        {
+            if (backPage_ == endPage_)
+                backPage_ = extend();
+            else
+                backPage_ = backPage_->next();
+
+            more -= backPage_->resize(more, initialize);
+        }
+    }
+    else if (n < size_)
+    {
+        while ((size_ - backPage_->size()) >= n)
+        {
+            size_ -= backPage_->size();
+            backPage_->resize(0);
+            backPage_ = backPage_->previous();
+            ASSERT(backPage_);
+        }
+
+        size_t s = n - (size_ - backPage_->size());
+        ASSERT(s == backPage_->resize(s));
+    }
+
+    size_ = n;
+}
+
+void DataTable::push_back(const DataRow& r)
+{
+    // TODO: tidy this
+    const DataRowProxy& row = reinterpret_cast<const DataRowProxy&>(r);
+
+    if (!backPage_->push_back(row))
+    {
+        if (backPage_ != endPage_)
+            backPage_ = backPage_->next(); 
+        else
+            backPage_ = extend();
+
+        ASSERT(backPage_->push_back(row));
+    }
+}
+
+void DataTable::push_back(const double* const data)
+{
+    if (!backPage_->push_back(data))
+    {
+        if (backPage_ != endPage_)
+            backPage_ = backPage_->next(); 
+        else
+            backPage_ = extend();
+
+        ASSERT(backPage_->push_back(data));
+    }
+}
+
+DataTable::iterator DataTable::insert(iterator it, const DataRow& row)
+{
+    DataPage* first = it.block_;
+    DataRowProxy* position = it.row_;
+
+    if (first->insert(position, row))
+        return it;
+
+    DataPage* second = first->split();
+
+    // Update links to the previous and the next page.
+    second->previous(first);
+    second->next(first->next());
+    first->next(second);
+
+    // Update ranks of the pages.
+    DataPage* p = second;
+    while (p)
+    {
+        p->rank(p->previous()->rank() + 1);
+        p = p->next();
+    }
+    
+    if (position <= first->end())
+    {
+        ASSERT(first->insert(position, row));
+        return it;
+    }
+    else
+    {
+        position = second->begin() + (position - first->end());
+        ASSERT(second->insert(position, row));
+        return iterator(second, position);
+    }
+}
+
+DataRow& DataTable::back()
+{
+    ASSERT(backPage_->size() > 0 && "No rows in the DataTable");
+    return static_cast<DataRow&>(backPage_->back());
+}
+
+const DataRow& DataTable::back() const
+{
+    ASSERT(backPage_->size() > 0 && "No rows in the DataTable");
+    return static_cast<const DataRow&>(backPage_->back());
+}
+
+DataPage* DataTable::extend()
+{
+    DataPage* page = new DataPage(*this);
+
+    ASSERT(page);
+
+    endPage_->next(page);
+    page->rank(endPage_->rank() + 1);
+    page->previous(endPage_);
+    endPage_ = page;
+
+    return page;
+}
+
+std::ostream& operator<<(std::ostream& os, DataTable& table)
+{
+    os << "DataTable(name=\"" << table.name() << "\", ";
+    os << "size=" << table.size() << ")" << std::endl;
+
+    return os;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataTable.h b/odb_api/src/odb_api/DataTable.h
new file mode 100644
index 0000000..2f1289f
--- /dev/null
+++ b/odb_api/src/odb_api/DataTable.h
@@ -0,0 +1,363 @@
+/// @file   DataTable.h
+/// @author Tomas Kral
+
+#ifndef odb_sql_DataTable_H
+#define odb_sql_DataTable_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataRow.h"
+#include "odb_api/DataPage.h"
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataTableIterator.h"
+
+namespace odb {
+
+class DataSet;
+class DataTables;
+
+namespace internal
+{
+    class DataPage;
+}
+
+/*! Represents properties of a table.
+ *
+ *  The DataTableProperties class represents a set of properties that can be
+ *  used to fine tune some of the internal memory management options available
+ *  in the DataTable class.
+ *
+ *  The @e blockSizeInKb property can be used to define the size of memory
+ *  blocks (in kB) that are used to hold the actual table data. Theoretically,
+ *  the best performance should be achieved by setting this value to the size
+ *  of the processor cache. The default value is 1024 Kb. Note that the actual
+ *  size of the blocks can differ from the one hinted by the @e blockSizeInKb
+ *  option since the actual size is also constrained by factors such as number
+ *  of columns in the table.
+ *
+ *  The @e blockFillMark property defines how much space of the individual
+ *  block will be used when populating the table with data leaving the
+ *  remaining space for efficient insert operations. The accapted values are in
+ *  the range 0 to 1. The default value is 1 which means that the whole size of
+ *  the block is used when filling the table with data.
+ *
+ *  @ingroup data
+ */
+class DataTableProperties
+{
+    enum
+    {
+        DEFAULT_BLOCK_SIZE_IN_KB = 1024,
+        DEFAULT_BLOCK_SIZE_IN_NUMBER_OF_ROWS = 0,
+        DEFAULT_BLOCK_FILL_MARK = 1,
+        DEFAULT_BLOCK_FILL_MARK_IN_NUMBER_OF_ROWS = 0
+    };
+
+public:
+    /// Creates default properties.
+    DataTableProperties();
+
+    /// Returns the size of table blocks in Kb.
+    size_t blockSizeInKb() const { return blockSizeInKb_; }
+
+    /// Sets the size of table blocks in Kb.
+    void blockSizeInKb(size_t kb) { blockSizeInKb_ = kb; }
+
+    /// Returns the size of table blocks in number of rows.
+    size_t blockSizeInNumberOfRows() const
+    { return blockSizeInNumberOfRows_; }
+
+    /// Sets the size of table blocks in number of rows.
+    void blockSizeInNumberOfRows(size_t n)
+    { blockSizeInNumberOfRows_ = n; }
+
+    /// Returns the block relative fill mark.
+    double blockFillMark() const { return blockFillMark_; }
+
+    /// Sets the block relative fill mark.
+    void blockFillMark(double ratio) { blockFillMark_ = ratio; }
+
+    /// Returns the block relative fill mark.
+    size_t blockFillMarkInNumberOfRows() const
+    { return blockFillMarkInNumberOfRows_; }
+
+    /// Sets the block relative fill mark.
+    void blockFillMarkInNumberOfRows(size_t n)
+    { blockFillMarkInNumberOfRows_ = n; }
+
+private:
+    size_t blockSizeInKb_;
+    size_t blockSizeInNumberOfRows_;
+    double blockFillMark_;
+    size_t blockFillMarkInNumberOfRows_;
+};
+
+/*! @brief Represents an in-memory table of data.
+ *
+ *  A DataTable represents a memory resident table of data. DataTable consists
+ *  of DataRow objects, which represent the actual data contained in a table,
+ *  and DataColumn objects which describe the schema, or structure of a table.
+ *  DataTable can be used independently or as a member of a DataSet, in which
+ *  case, it can be accessed through the @ref DataSet::tables "tables()" method
+ *  of a DataSet class. 
+ *
+ *  @ingroup data
+ */
+class DataTable
+{
+public:
+    typedef internal::DataTableIterator<DataRow> DataTableIterator;
+    typedef internal::DataTableIterator<const DataRow> ConstDataTableIterator;
+
+    /// Helper class to insert values into the table.
+    class CommaInserter
+    {
+    public:
+        template <typename T>
+        CommaInserter(DataTable& table, const T& value)
+          : table_(table), 
+            row_(0),
+            size_(table.columns().size()),
+            index_(0)
+        {
+            table_.resize(table_.size() + 1);
+            row_ = &table_.back();
+            row_->DataRow::set<T>(index_++, value);
+        }
+
+        ~CommaInserter()
+        {
+            ASSERT((index_ == size_)
+                && "DataTable::operator<< received invalid number of values");
+        }
+
+        CommaInserter& operator,(const char* value)
+        {
+            if (index_ == size_)
+            {
+                table_.resize(table_.size() + 1);
+                row_ = &table_.back();
+                index_ = 0;
+            }
+
+            row_->DataRow::set<std::string>(index_++, std::string(value));
+
+            return *this;
+        }
+
+        template <typename T>
+        CommaInserter& operator,(const T& value)
+        {
+            if (index_ == size_)
+            {
+                table_.resize(table_.size() + 1);
+                row_ = &table_.back();
+                index_ = 0;
+            }
+
+            row_->DataRow::set<T>(index_++, value);
+
+            return *this;
+        }
+
+    private:
+        DataTable& table_;
+        DataRow* row_;
+        const size_t size_;
+        size_t index_;
+    };
+
+public:
+    /// Value type of the table.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef DataRow value_type;
+
+    /// Reference type of the table.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef DataRow& reference;
+
+    /// Constant reference type of the table.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef const DataRow& const_reference;
+
+    /// Pointer type of the table.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef DataRow* pointer;
+
+    /// Constant pointer type of the table.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef const DataRow* const_pointer;
+
+    /// Random-access iterator on table rows.
+    /// Provided for compatibility with STL algorithms.
+
+    typedef DataTableIterator iterator;
+
+    /// Random-access iterator on table rows (const version).
+    /// Provided for compatibility with STL algorithms.
+
+    typedef ConstDataTableIterator const_iterator;
+
+    /// Creates a new table given its @e name and @e columns.
+    DataTable(const std::string& name, const DataColumns& columns,
+            const DataTableProperties& properties = DataTableProperties());
+
+    /// Frees the table from memory.
+    ~DataTable();
+
+    /// Returns the name of the table.
+    const std::string& name() const { return name_; }
+
+    /// Returns pointer to the dataset by which the table is owned.
+    /// If the table is not part of a dataset, returns null pointer.
+    DataSet* const dataset() const { return owner_; }
+
+    /// Returns reference to the collection of table columns.
+    const DataColumns& columns() const { return columns_; }
+
+    /// Returns index of the given column name.
+    size_t columnIndex(const std::string& name) const
+    { return columns_.indexOf(name); }
+
+    /// Returns @c true if the table is empty.
+    bool empty() const;
+
+    /// Returns the number of rows in the table.
+    size_t size() const;
+
+    /// Returns the allocated storage in terms of number of rows.
+    size_t capacity() const;
+
+    /// Pre-allocates memory for the given number of rows.
+    void reserve(size_t n);
+
+    /// Resizes the table to the given number of rows.
+    ///
+    /// If @e n is smaller than the current table size, the content of the table
+    /// is reduced to its first @e n rows, the rest being dropped. If @e n is
+    /// greater than the current number of rows, the table content is expanded
+    /// by adding new rows at the end of the table. If the optional parameter
+    /// @e initialize is set to @c true when expanding the table, the elements
+    /// of new rows will be initialized to default values of their respective
+    /// columns.
+
+    void resize(size_t n, bool initialize = false);
+
+    /// Returns reference to the given row of the table.
+    DataRow& operator[](size_t n);
+
+    /// Returns reference to the given row of the table (const overload).
+    const DataRow& operator[](size_t n) const
+    { return const_cast<const DataTable&>(*this)[n]; }
+
+    /// Appends content of the given row at the end of the table.
+    /// This method copies the content of the supplied @e row at
+    /// the end of the table and increases the table size by one.
+
+    void push_back(const DataRow& row);
+
+    /// Appends content of the given data array at the end of the table.
+    ///
+    /// This method copies the content of the supplied @e data array
+    /// at the end of the table and increases the table size by one. Note that no
+    /// bound checking is performed on the @e data array and it is caller's
+    /// responsibility to provide an array of the proper size.
+
+    void push_back(const double* const data);
+
+    /// Inserts the row at the given position.
+    ///
+    /// This method copies the content of the supplied @e row before the given
+    /// @e position and returns iterator pointing to the newly inserted @e row.
+    /// Note that inserting a row into a data table effectively increases its
+    /// size and may cause memory reallocations thus invalidating all previously
+    /// obtained iterators, pointers and references.
+
+    iterator insert(iterator position, const DataRow& row);
+
+    /// Clears the table by dropping all the rows.
+    /// This method only clears the data. The schema of the table stays
+    /// unchanged. Note that this method does not release any allocated memory
+    /// resoures that have been previously acquired by the table.
+
+    void clear();
+
+    /// Returns iterator pointing to the first row of the table.
+    iterator begin()
+    { return iterator(frontPage_, frontPage_->begin()); }
+
+    /// Returns iterator pointing to the first row of the table (const overload).
+    const_iterator begin() const
+    { return const_iterator(frontPage_, frontPage_->begin()); }
+
+    /// Returns iterator pointing past the last row of the table.
+    iterator end()
+    { return iterator(backPage_, backPage_->end()); }
+
+    /// Returns iterator pointing past the last row of the table (const overload).
+    const_iterator end() const
+    { return iterator(backPage_, backPage_->end()); }
+
+    /// Returns reference to the last row of the table.
+    DataRow& back();
+
+    /// Returns reference to the last row of the table (const overload).
+    const DataRow& back() const;
+
+    /// Returns the table properties.
+    /// This method returns reference to the instance of DataTableProperties
+    /// class. Note that table properties can only be set at the construction
+    /// of the DataTable and remain unchanged during its life time.
+
+    const DataTableProperties& properties() const { return properties_; }
+
+    /// Inserts values into the table.
+    /// This method provides a convenient way to populate a DataTable
+    /// using stream insert operator. Note that the number of supplied values
+    /// must be a multiple of the number of columns of the table.
+    /// @code
+    /// table << 11, 273.0, "temperature",
+    ///          12, 0.074, "humidity";
+    /// @endcode
+
+    template <typename T>
+    CommaInserter operator<<(const T& value)
+    {
+        return CommaInserter(*this, value);
+    }
+
+    /// Inserts table description into the output stream.
+    friend std::ostream& operator<<(std::ostream& os, DataTable& table);
+
+private:
+    /// Delegates ownership to the given dataset.
+    void dataset(DataSet* dataset) { owner_ = dataset; }
+
+    /// Extends the table by an additional block.
+    /// This method allocates a new table block appending it at the end of the
+    /// list of blocks and returns pointer to this newly created block.
+
+    internal::DataPage* extend();
+
+private:
+    DataTable(const DataTable&);
+    DataTable& operator=(const DataTable&);
+
+    DataSet* owner_;
+    std::string name_;
+    DataColumns columns_;
+    DataTableProperties properties_;
+    internal::DataPage* frontPage_;
+    internal::DataPage* backPage_;
+    internal::DataPage* endPage_;
+
+    friend class DataTables;
+};
+
+} // namespace odb
+
+#endif // odb_sql_DataTable_H
diff --git a/odb_api/src/odb_api/DataTableFiller.cc b/odb_api/src/odb_api/DataTableFiller.cc
new file mode 100644
index 0000000..6d72133
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableFiller.cc
@@ -0,0 +1,119 @@
+/// @file   DataTableFiller.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DataTableFiller.h"
+#include "odb_api/DataTable.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+DataTableFiller::DataTableFiller(DataTable& table)
+  : targetTable_(table),
+    childFiller_(0),
+    sourceColumnIndexes_(0),
+    linkLenIndexes_(0),
+    rowBuffer_(0),
+    rowBufferSize_(0)
+{}
+
+DataTableFiller::~DataTableFiller()
+{
+    if (rowBuffer_)
+        delete [] rowBuffer_;
+}
+
+void DataTableFiller::addColumn(size_t sourceColumnIndex)
+{
+    sourceColumnIndexes_.push_back(sourceColumnIndex);
+}
+
+void DataTableFiller::addLink(size_t linkLenIndex)
+{
+    linkLenIndexes_.push_back(linkLenIndex);
+}
+
+DataTableFiller::iterator DataTableFiller::begin()
+{
+    if (!rowBuffer_ || sourceColumnIndexes_.size() > rowBufferSize_)
+    {
+        if (rowBuffer_)
+            delete [] rowBuffer_;
+
+        rowBufferSize_ = sourceColumnIndexes_.size();
+        rowBuffer_ = new double [rowBufferSize_];
+    }
+
+    return DataTableFiller::iterator(*this);
+}
+
+DataTableFillerIterator::DataTableFillerIterator(DataTableFiller& owner)
+  : owner_(&owner),
+    targetTable_(&owner.targetTable_),
+    sourceColumnIndexes_(owner.sourceColumnIndexes_),
+    linkLenIndexes_(owner.linkLenIndexes_),
+    rowBuffer_(owner.rowBuffer_),
+    rowsToSkip_(0),
+    sourceColumnsAligned_(true)
+{
+    ASSERT(sourceColumnIndexes_.size() > 0);
+
+    for (size_t i = 0; i < sourceColumnIndexes_.size() - 1; i++)
+    {
+        if (sourceColumnIndexes_[i+1] != (sourceColumnIndexes_[i] + 1))
+        {
+            sourceColumnsAligned_ = false;
+            break;
+        }
+    }
+}
+
+DataTableFillerIterator::~DataTableFillerIterator()
+{}
+
+DataTableFillerIterator& DataTableFillerIterator::operator=(const double* data)
+{
+    if (!rowsToSkip_)
+    {
+        fillRow(data);
+        updateRowsToSkip(data);
+    }
+
+    return *this;
+}
+
+DataTableFillerIterator& DataTableFillerIterator::operator++()
+{
+    rowsToSkip_ = rowsToSkip_ ? --rowsToSkip_ : 0u;
+    return *this;
+}
+
+void DataTableFillerIterator::updateRowsToSkip(const double* data)
+{
+    // NOTE: This algorithm only accounts for one level deep table
+    // hierarchies.
+    rowsToSkip_ = 0;
+    for (size_t i = 0; i < linkLenIndexes_.size(); i++)
+        rowsToSkip_ = ::max(rowsToSkip_, (size_t)data[linkLenIndexes_[i]]);
+}
+
+void DataTableFillerIterator::fillRow(const double* const data)
+{
+    if (sourceColumnsAligned_)
+    {
+        size_t offset = sourceColumnIndexes_[0];
+        targetTable_->push_back(data + offset);
+    }
+    else
+    {
+        for (size_t i = 0; i < sourceColumnIndexes_.size(); i++)
+            rowBuffer_[i] = data[sourceColumnIndexes_[i]];
+
+        targetTable_->push_back(rowBuffer_);
+    }
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataTableFiller.h b/odb_api/src/odb_api/DataTableFiller.h
new file mode 100644
index 0000000..3f7333a
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableFiller.h
@@ -0,0 +1,79 @@
+/// @file   DataTableFiller.h
+/// @author Tomas Kral
+
+#ifndef DATATABLEFILLER_H_
+#define DATATABLEFILLER_H_
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataTable;
+
+namespace internal {
+
+class DataTableFillerIterator;
+
+/*! Fills DataTable with rows from data source.
+ *
+ *  The DataTableFiller class is responsible for filling up a DataTable with
+ *  rows from data source (i.e. ODB file).
+ *
+ *  @ingroup data
+ */
+class DataTableFiller
+{
+public:
+    typedef DataTableFillerIterator iterator;
+
+    explicit DataTableFiller(DataTable& table);
+    ~DataTableFiller();
+
+    iterator begin();
+
+    void addColumn(size_t sourceColumnIndex);
+    void addLink(size_t linkLenIndex);
+
+private:
+    DataTableFiller(const DataTableFiller&);
+    DataTableFiller& operator=(const DataTableFiller&);
+
+    DataTable& targetTable_;
+    DataTableFiller* childFiller_;
+    std::vector<size_t> sourceColumnIndexes_;
+    std::vector<size_t> linkLenIndexes_;
+    double* rowBuffer_;
+    size_t rowBufferSize_;
+
+    friend class DataTableFillerIterator;
+};
+
+class DataTableFillerIterator
+  : public std::iterator<std::output_iterator_tag, DataTableFillerIterator>
+{
+public:
+    explicit DataTableFillerIterator(DataTableFiller& owner);
+    ~DataTableFillerIterator();
+
+    DataTableFillerIterator& operator*() { return *this; }
+    DataTableFillerIterator& operator=(const double* data);
+    DataTableFillerIterator& operator++();
+
+private:
+    void updateRowsToSkip(const double* data);
+    void fillRow(const double* const data);
+
+private:
+    DataTableFiller* owner_;
+    DataTable* targetTable_;
+    std::vector<size_t> sourceColumnIndexes_;
+    std::vector<size_t> linkLenIndexes_;
+    double* rowBuffer_;
+    size_t rowsToSkip_;
+    bool sourceColumnsAligned_;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATATABLEFILLER_H_
diff --git a/odb_api/src/odb_api/DataTableIterator.cc b/odb_api/src/odb_api/DataTableIterator.cc
new file mode 100644
index 0000000..8568306
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableIterator.cc
@@ -0,0 +1,160 @@
+/// @file   DataTableIterator.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataPage.h"
+#include "odb_api/DataTableIterator.h"
+
+using namespace std;
+
+namespace odb {
+namespace internal {
+
+template <typename Row, typename Traits>
+void DataTableIterator<Row, Traits>::increment()
+{
+    ++row_;
+
+    while (block_)
+    {
+        if (row_ != block_->end())
+            return;
+
+        block_ = block_->next();
+
+        if (block_)
+            row_ = block_->begin();
+    }
+}
+
+template <typename Row, typename Traits>
+void DataTableIterator<Row, Traits>::decrement()
+{
+    while (block_)
+    {
+        if (row_ != block_->begin())
+            break;
+
+        block_ = block_->previous();
+
+        if (block_)
+            row_ = block_->end();
+    }
+
+    --row_;
+}
+
+template <typename Row, typename Traits>
+void DataTableIterator<Row, Traits>::advance(ptrdiff_t n)
+{
+    if (n > 0)
+    {
+        ptrdiff_t d = min(n, block_->end() - row_);
+
+        if (d < n)
+        {
+            while (d < n)
+            {
+                if (!block_->next() || !block_->next()->size()) // Cannot advance past the last elemnt.
+                    return;
+
+                block_ = block_->next();
+
+                n -= d;
+                d = min(n, (ptrdiff_t) block_->size());
+            }
+
+            row_ = block_->begin();
+        }
+
+        row_ += n;
+
+        if (row_ == block_->end() && block_->next() && block_->next()->size())
+        {
+            block_ = block_->next();
+            row_ = block_->begin();
+        }
+
+    }
+    else if (n < 0)
+    {
+        ptrdiff_t d = max(n, block_->begin() - row_);
+
+        if (d > n)
+        {
+            while (d > n)
+            {
+                block_ = block_->previous();
+
+                ASSERT(block_);
+
+                n -= d;
+                d = max(n, -(ptrdiff_t) block_->size());
+            }
+
+            row_ = block_->end();
+        }
+
+        row_ += d;
+    }
+}
+
+template <typename Row, typename Traits>
+template <typename R, typename T>
+ptrdiff_t DataTableIterator<Row, Traits>::distance(const DataTableIterator<R, T>& other) const
+{
+    ptrdiff_t d = 0;
+
+    if (block_ == other.block_)
+    {
+        d = (row_ - other.row_);
+    }
+    else if (block_->rank() < other.block_->rank())
+    {
+        d = (row_ - block_->end());
+        block_type* block = block_->next();
+
+        while (block && block != other.block_)
+        {
+            d -= block_->size();
+            block = block_->next();
+        }
+
+        d -= (other.row_ - block->begin());
+    }
+    else if (block_->rank() > other.block_->rank())
+    {
+        d = (row_ - block_->begin());
+        block_type* block = block_->previous();
+
+        while (block && block != other.block_)
+        {
+            d += block_->size();
+            block = block_->previous();
+        }
+
+        d += block->end() - other.row_;
+    }
+
+    return d;
+}
+
+
+// Explicit template instantiations.
+
+template struct DataTableIteratorTraits<DataRow>;
+template struct DataTableIteratorTraits<const DataRow>;
+
+template class DataTableIterator<DataRow>;
+template class DataTableIterator<const DataRow>;
+
+template ptrdiff_t
+DataTableIterator<DataRow>::distance(const DataTableIterator<DataRow>&) const;
+template ptrdiff_t
+DataTableIterator<DataRow>::distance(const DataTableIterator<const DataRow>&) const;
+template ptrdiff_t
+DataTableIterator<const DataRow>::distance(const DataTableIterator<DataRow>&) const;
+template ptrdiff_t
+DataTableIterator<const DataRow>::distance(const DataTableIterator<const DataRow>&) const;
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataTableIterator.h b/odb_api/src/odb_api/DataTableIterator.h
new file mode 100644
index 0000000..e3b2665
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableIterator.h
@@ -0,0 +1,92 @@
+/// @file   DataTableIterator.h
+/// @author Tomas Kral
+
+#ifndef DATATABLEITERATOR_H_
+#define DATATABLEITERATOR_H_
+
+#include <stddef.h>
+
+#include "odb_api/DataPage.h"
+#include "odb_api/IteratorFacade.h"
+
+namespace odb {
+
+class DataTable;
+class DataRow;
+
+namespace internal {
+
+template <typename T>
+struct DataTableIteratorTraits
+{
+    typedef typename T::block_type block_type;
+    typedef typename T::row_proxy_type row_proxy_type;
+};
+
+template <>
+struct DataTableIteratorTraits<DataRow>
+{
+    typedef DataPage block_type;
+    typedef DataRowProxy row_proxy_type;
+};
+
+template <>
+struct DataTableIteratorTraits<const DataRow>
+{
+    typedef const DataPage block_type;
+    typedef const DataRowProxy row_proxy_type;
+};
+
+/*! An iterator on data table rows.
+ *
+ *  The DataTableIterator template implements a random-access iterator on the
+ *  rows of a DataTable.
+ *
+ *  @ingroup data
+ *  @internal
+ */
+template <typename Row, typename Traits = DataTableIteratorTraits<Row> >
+class DataTableIterator
+  : public RandomIteratorFacade<DataTableIterator<Row, Traits>, Row>
+{
+    typedef typename Traits::block_type block_type;
+    typedef typename Traits::row_proxy_type row_proxy_type;
+public:
+    DataTableIterator()
+     : block_(0), row_(0) {}
+
+    template <typename R, typename T>
+    DataTableIterator(const DataTableIterator<R, T>& other)
+      : block_(other.block_), row_(other.row_) {}
+
+private:
+    DataTableIterator(block_type* block, row_proxy_type* row)
+      : block_(block), row_(row) {}
+
+    Row& dereference() const
+    { return reinterpret_cast<Row&>(*row_); }
+
+    void increment();
+    void decrement();
+    void advance(ptrdiff_t n);
+
+    template <typename R, typename T>
+    ptrdiff_t distance(const DataTableIterator<R, T>& other) const;
+
+    template <typename R, typename T>
+    bool equal(const DataTableIterator<R, T>& other) const
+    { return (row_ == other.row_); }
+
+    block_type* block_;
+    row_proxy_type* row_;
+
+    friend class DataPage;
+    friend class odb::DataTable;
+    friend class odb::IteratorFacadeAccess;
+    template <typename R, typename T> friend class DataTableIterator;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATATABLEITERATOR_H_
diff --git a/odb_api/src/odb_api/DataTableMappings.cc b/odb_api/src/odb_api/DataTableMappings.cc
new file mode 100644
index 0000000..6efd430
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableMappings.cc
@@ -0,0 +1,19 @@
+/// @file   DataTableMappings.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataTableMappings.h"
+
+using namespace std;
+
+namespace odb {
+
+DataTableMappings::DataTableMappings()
+  : Map()
+{}
+
+void DataTableMappings::add(const std::string& source, const std::string& target)
+{
+    insert(pair<std::string, std::string>(source, target));
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataTableMappings.h b/odb_api/src/odb_api/DataTableMappings.h
new file mode 100644
index 0000000..f9205a7
--- /dev/null
+++ b/odb_api/src/odb_api/DataTableMappings.h
@@ -0,0 +1,30 @@
+/// @file   DataTableMappings.h
+/// @author Tomas Kral
+
+#ifndef DATATABLEMAPPINGS_H_
+#define DATATABLEMAPPINGS_H_
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataTableMappings : private std::map<std::string, std::string>
+{
+    typedef std::map<std::string, std::string> Map;
+public:
+    using Map::iterator;
+    using Map::const_iterator;
+    using Map::begin;
+    using Map::end;
+    using Map::find;
+
+    /// Creates a new table mapping.
+    DataTableMappings();
+
+    /// Maps the data source table to the given dataset table.
+    void add(const std::string& source, const std::string& target);
+};
+
+} // namespace odb
+
+#endif // DATATABLEMAPPINGS_H_
diff --git a/odb_api/src/odb_api/DataTables.cc b/odb_api/src/odb_api/DataTables.cc
new file mode 100644
index 0000000..a6ae08b
--- /dev/null
+++ b/odb_api/src/odb_api/DataTables.cc
@@ -0,0 +1,54 @@
+/// @file   DataTables.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataTable.h"
+#include "odb_api/DataTables.h"
+
+using namespace std;
+
+namespace odb {
+
+DataTables::DataTables(DataSet& owner)
+  : owner_(owner)
+{}
+
+void DataTables::insert(DataTable* table)
+{
+    ASSERT(!table->name().empty() && "DataTable with empty name not allowed.");
+    ASSERT(!tableMap_.count(table->name()) && "Duplicate DataTable names not allowed.");
+    ASSERT(!table->dataset() && "DataTable owned by other DataSet.");
+
+    table->dataset(&owner_);
+    tableMap_[table->name()] = table;
+    tableSet_.insert(table);
+}
+
+DataTables::iterator DataTables::find(const std::string& name)
+{
+    std::set<DataTable*>::iterator it = tableSet_.begin();
+
+    for (; it != tableSet_.end(); ++it)
+    {
+        if ((*it)->name() == name)
+            return it;
+    }
+
+    return it;
+}
+
+DataTable* const DataTables::operator[](const std::string& name)
+{
+    std::map<std::string, DataTable*>::iterator it = tableMap_.find(name);
+
+    if (it != tableMap_.end())
+        return it->second;
+
+    return 0;
+}
+
+const DataTable* const DataTables::operator[](const std::string& name) const
+{
+    return const_cast<const DataTables&>(*this)[name];
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataTables.h b/odb_api/src/odb_api/DataTables.h
new file mode 100644
index 0000000..74dc55a
--- /dev/null
+++ b/odb_api/src/odb_api/DataTables.h
@@ -0,0 +1,95 @@
+/// @file   DataTables.h
+/// @author Tomas Kral
+
+#ifndef DATATABLES_H_
+#define DATATABLES_H_
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class DataSet;
+class DataTable;
+
+/*! Represets a collection of dataset tables.
+ *
+ *  The DataTables collection can be accessed trough the
+ *  @ref DataSet::tables "tables()" method of the DataSet class.
+ *
+ *  To iterate through the collection of DataTable objects in a DataSet one
+ *  can use DataTables::iterator class.
+ *
+ *  @code
+ *  for (DataTables::iterator it = dataset.tables().begin();
+ *          it != dataset.tables().end(); ++it)
+ *  {
+ *      DataTable* table = *it;
+ *      cout << table->name() << std::endl;
+ *  }
+ *  @endcode
+ *
+ *  @ingroup data
+ */
+class DataTables
+{
+public:
+    /// Iterates through the collection.
+    typedef std::set<DataTable*>::iterator iterator;
+
+    /// Iterates through the collection (const version)
+    typedef std::set<DataTable*>::const_iterator const_iterator;
+
+    /// Adds a table to the collection.
+    void insert(DataTable* table);
+
+    /// Searches the collection for the table with given name.
+    iterator find(const std::string& name);
+
+    /// Returns pointer to table with the given name.
+    DataTable* const operator[](const std::string& name);
+
+    /// Returns pointer to table with the given name (const overload).
+    const DataTable* const operator[](const std::string& name) const;
+
+    /// Returns the number of tables in the collection.
+    size_t size() const { return tableSet_.size(); }
+
+    /// Returns true if there are no tables in the collection.
+    bool empty() const { return tableSet_.empty(); }
+
+    /// Returns 1 if a table with the given @p name is found, otherwise returns 0.
+    /// This method searches for a table with the given @p name. Since the DataSet
+    /// cannot contain two duplicates of the same table, the method returns 1 if
+    /// the table was found, and 0 otherwise.
+    size_t count(const std::string& name) { return tableMap_.count(name); }
+
+    /// Returns iterator pointing to the beginning of the collection.
+    iterator begin() { return tableSet_.begin(); }
+
+    /// Returns iterator pointing to the beginning of the collection (const version).
+    const_iterator begin() const { return tableSet_.begin(); }
+
+    /// Returns iterator pointing past the end of the collection.
+    iterator end() { return tableSet_.end(); }
+
+    /// Returns iterator pointing past the end of the collection (const version).
+    const_iterator end() const { return tableSet_.end(); }
+
+private:
+    /// Creates a new collection.
+    explicit DataTables(DataSet& owner);
+
+private:
+    DataTables(const DataTables&);
+    DataTables& operator=(const DataTables&);
+
+    DataSet& owner_;
+    std::map<std::string, DataTable*> tableMap_;
+    std::set<DataTable*> tableSet_;
+
+    friend class DataSet;
+};
+
+} // namespace odb
+
+#endif // DATATABLES_H_
diff --git a/odb_api/src/odb_api/DataView.cc b/odb_api/src/odb_api/DataView.cc
new file mode 100644
index 0000000..d6aee87
--- /dev/null
+++ b/odb_api/src/odb_api/DataView.cc
@@ -0,0 +1,249 @@
+/// @file   DataView.cc
+/// @author Tomas Kral
+
+#include "odb_api/DataLinks.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataView.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+DataViewFiller::DataViewFiller(DataView& view, const DataLink& link)
+  : view_(&view),
+    fillLinks_(view.viewLinks_),
+    linkIt_(link.begin()),
+    linkEnd_(link.end()),
+    parentIt_(link.parent().begin()),
+    childIt_((*linkIt_).begin()),
+    childEnd_((*linkIt_).end()),
+    parentOffset_(0),
+    parentSize_(link.parent().columns().size()),
+    childOffset_(0),
+    childSize_(link.child().columns().size()),
+    linkOffset_(0),
+    linkSize_(2),
+    offset_(0),
+    fillParent_(true)
+{
+    ASSERT(link.size() == link.parent().size());
+
+    parentOffset_ = columnIndex(link.parent().columns()[0].name());
+    childOffset_ = columnIndex(link.child().columns()[0].name());
+
+    if (fillLinks_)
+    {
+        std::string parentName = link.parent().name();
+        std::string childName = link.child().name();
+        linkOffset_ = columnIndex(childName + ".offset@" + parentName);
+    }
+}
+
+void DataViewFiller::fill(double* const data)
+{
+    if (fillParent_)
+    {
+        const DataRow& row = *parentIt_;
+        copy(row.data(), row.data() + parentSize_, data + parentOffset_);
+
+        if (fillLinks_)
+        {
+            data[linkOffset_] = offset_;
+            data[linkOffset_ + 1] = linkIt_->size();
+
+            offset_ += linkIt_->size();
+        }
+
+        ++parentIt_;
+        ++linkIt_;
+
+        fillParent_ = false;
+    }
+
+    copy((*childIt_).data(), (*childIt_).data() + childSize_,
+            data + childOffset_);
+    ++childIt_;
+
+    if ((childIt_ == childEnd_) && (linkIt_ != linkEnd_))
+    {
+        childIt_ = (*linkIt_).begin();
+        childEnd_ = (*linkIt_).end();
+        fillParent_ = true;
+    }
+}
+
+bool DataViewFiller::done() const
+{
+    return (linkIt_ == linkEnd_) && (childIt_ == childEnd_);
+}
+
+size_t DataViewFiller::columnIndex(const std::string& name) const
+{
+    std::vector<size_t> indices;
+
+    for (size_t i = 0; i < view_->columns().size(); i++)
+        if (view_->columns().at(i).name() == name
+                || view_->columns().at(i).name().find(name + "@") == 0)
+            indices.push_back(i);
+
+    if (indices.size() > 1)
+        throw eckit::UserError(std::string("Ambiguous column name: '") + name + "'");
+
+    if (indices.size() == 0)
+        throw eckit::UserError(std::string("Column '") + name + "' not found.");
+
+    return indices[0];
+}
+
+DataView::DataView(const DataSet& dataset, const std::string& masterName,
+        bool viewLinks)
+  : columns_(),
+    fillers_(),
+    rowBuffer_(0),
+    rowBufferSize_(0),
+    viewLinks_(viewLinks)
+{
+    typedef std::vector<const DataLink*> Links;
+
+    Links links;
+
+    // Find all links belonging to the master table.
+    for (DataLinks::const_iterator it = dataset.links().begin();
+            it != dataset.links().end(); ++it)
+    {
+        const DataLink* link = *it;
+
+        if (link->parent().name() == masterName)
+            links.push_back(link);
+    }
+
+    // Compute the required size of the row buffer.
+    for (Links::const_iterator it = links.begin();
+            it != links.end(); ++it)
+    {
+        const DataLink& link = **it;
+
+        rowBufferSize_ += link.parent().columns().size();
+        rowBufferSize_ += link.child().columns().size();
+
+        if (viewLinks_)
+            rowBufferSize_ += 2; // offset and length column
+    }
+
+    // Allocate row buffer.
+    rowBuffer_ = new double [rowBufferSize_];
+
+    // Insert columns from all tables aligned with the master table.
+    for (Links::const_iterator it = links.begin();
+            it != links.end(); ++it)
+    {
+        const DataLink& link = **it;
+        const DataTable& parent = link.parent();
+
+        columns_.insert(columns_.end(), parent.columns().begin(),
+                parent.columns().end());
+    }
+
+    // Insert columns from all links.
+    if (viewLinks_)
+    {
+        for (Links::const_iterator it = links.begin();
+                it != links.end(); ++it)
+        {
+        const DataLink& link = **it;
+        const DataTable& parent = link.parent();
+        const DataTable& child = link.child();
+
+            columns_.push_back(DataColumn(child.name() + ".offset@"
+                        + parent.name(), INTEGER));    
+            columns_.push_back(DataColumn(child.name() + ".len@"
+                        + parent.name(), INTEGER));    
+        }
+    }
+
+    // Insert columns from all child tables.
+    for (Links::const_iterator it = links.begin();
+            it != links.end(); ++it)
+    {
+        const DataLink& link = **it;
+        const DataTable& child = link.child();
+
+        columns_.insert(columns_.end(), child.columns().begin(),
+                child.columns().end());
+    }
+
+    // Initialize all fillers.
+    for (Links::const_iterator it = links.begin();
+            it != links.end(); ++it)
+    {
+        const DataLink& link = **it;
+        DataViewFiller filler(*this, link);
+        fillers_.push_back(filler);
+    }
+}
+
+DataView::~DataView()
+{
+    if (rowBuffer_)
+        delete [] rowBuffer_;
+}
+
+DataView::iterator DataView::begin()
+{
+    return DataView::iterator(*this, false);
+}
+
+DataView::iterator DataView::end()
+{
+    return DataView::iterator(*this, true);
+}
+
+namespace internal {
+
+DataViewIterator::DataViewIterator(DataView& owner, bool end)
+  : owner_(&owner),
+    fillers_(end ? 0 : &owner.fillers_),
+    rowBuffer_(end ? 0 : owner.rowBuffer_),
+    rowBufferSize_(end ? 0 : owner.rowBufferSize_),
+    done_(end ? true : false)
+{
+    if (!done_)
+    {
+        for (DataViewFillers::iterator it = fillers_->begin(),
+                end = fillers_->end(); it != end; ++it)
+        {
+            DataViewFiller& filler = *it;
+            filler.fill(rowBuffer_);
+        }
+    }
+}
+
+bool DataViewIterator::operator!=(const DataViewIterator& other)
+{
+    ASSERT(owner_ == other.owner_);
+
+    if (done_)
+        return false;
+    else
+        done_ = (*fillers_)[0].done();
+    return true;
+}
+
+DataViewIterator& DataViewIterator::operator++()
+{
+    if (!done_)
+    {
+        for (DataViewFillers::iterator it = fillers_->begin(),
+                end = fillers_->end(); it != end; ++it)
+        {
+            DataViewFiller& filler = *it;
+            filler.fill(rowBuffer_);
+        }
+    }
+
+    return *this;
+}
+
+} // namespace internal
+} // namespace odb
diff --git a/odb_api/src/odb_api/DataView.h b/odb_api/src/odb_api/DataView.h
new file mode 100644
index 0000000..f048b7b
--- /dev/null
+++ b/odb_api/src/odb_api/DataView.h
@@ -0,0 +1,144 @@
+/// @file   DataView.h
+/// @author Tomas Kral
+
+#ifndef DATAVIEW_H_
+#define DATAVIEW_H_
+
+#include "odb_api/DataLink.h"
+
+namespace odb {
+
+class DataSet;
+class DataView;
+
+namespace internal { class DataViewIterator; }
+
+/*! Helper class populating a DataView.
+ *
+ *  The DataViewFiller is responsible for filling up those columns of a
+ *  DataView which correspond to the given parent and child tables of a
+ *  DataLink that was provided in the constructor.
+ *
+ *  @ingroup data
+ */
+class DataViewFiller
+{
+public:
+    DataViewFiller(DataView& view, const DataLink& link);
+
+    void fill(double* const data);
+    bool done() const;
+
+private:
+    size_t columnIndex(const std::string& name) const;
+
+private:
+    DataView* view_;
+    bool fillLinks_;
+    DataLink::const_iterator linkIt_;
+    DataLink::const_iterator linkEnd_;
+    DataTable::const_iterator parentIt_;
+    DataTable::const_iterator childIt_;
+    DataTable::const_iterator childEnd_;
+    size_t parentOffset_;
+    size_t parentSize_;
+    size_t childOffset_;
+    size_t childSize_;
+    size_t linkOffset_;
+    size_t linkSize_;
+    size_t offset_;
+    bool fillParent_;
+};
+
+/*! Provides a flat view of a dataset.
+ *
+ *  The DataView is typically used when a flat view of a dataset is needed. It
+ *  provides a read-only @ref DataView::iterator "iterator" which gives a
+ *  sequential access to the flattened representation of the content of all
+ *  related tables in a dataset.
+ *
+ *  Note that presently the DataView works only with one-level deep table
+ *  hierarchies and assumes that all tables participating in the parent-child
+ *  relationship are aligned.
+ *
+ *  @ingroup data
+ */
+class DataView
+{
+    typedef std::vector<DataViewFiller> DataViewFillers;
+public:
+    /// Iterates through rows of a flat view.
+    typedef internal::DataViewIterator iterator;
+
+    /// Represents collection of DataColumn objects.
+    typedef std::vector<DataColumn> Columns;
+
+    /// Creates a new flat view for the given @p dataset.
+    /// @param dataset a dataset containing tables to view in the flat format
+    /// @param masterName name of the master table whose descendants are to be
+    ///                   included in the view
+    /// @param viewLinks if @c true, link columns will be included in the view
+    DataView(const DataSet& dataset, const std::string& masterName,
+            bool viewLinks = true);
+
+    /// Destroys a flat view object.
+    ~DataView();
+
+    /// Returns reference to the collection of columns.
+    Columns& columns() { return columns_; }
+
+    /// Returns const reference to the collection of columns.
+    const Columns& columns() const { return columns_; }
+
+    /// Returns iterator pointing to the beginning of the flat view.
+    iterator begin();
+
+    /// Returns iterator pointing past the end of the flat view.
+    iterator end();
+
+private:
+    DataView(const DataView&);
+    DataView& operator=(const DataView&);
+
+    Columns columns_;
+    DataViewFillers fillers_;
+    double* rowBuffer_;
+    size_t rowBufferSize_;
+    bool viewLinks_;
+
+    friend class internal::DataViewIterator;
+    friend class DataViewFiller;
+};
+
+namespace internal {
+
+/*! @internal
+ *  @brief Input iterator providing read-only access to flattened dataset view.
+ *  @see DataView
+ *  @ingroup data
+ */
+class DataViewIterator :
+    public std::iterator<std::input_iterator_tag, const double*>
+{
+    typedef std::vector<DataViewFiller> DataViewFillers;
+public:
+    bool operator!=(const DataViewIterator& other);
+    const double* operator*() const { return rowBuffer_; }
+    DataViewIterator& operator++();
+
+private:
+    DataViewIterator(DataView& owner, bool end);
+
+    DataView* owner_;
+    DataViewFillers* fillers_;
+    double* rowBuffer_;
+    size_t rowBufferSize_;
+    bool done_;
+
+    friend class odb::DataView;
+};
+
+} // namespace internal
+} // namespace odb
+
+#endif // DATAVIEW_H_
diff --git a/odb_api/src/odb_api/DateTime.cc b/odb_api/src/odb_api/DateTime.cc
new file mode 100755
index 0000000..10e411c
--- /dev/null
+++ b/odb_api/src/odb_api/DateTime.cc
@@ -0,0 +1,551 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+#include <limits>
+#include <stdexcept>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/DateTime.h"
+#include "odb_api/Duration.h"
+
+using std::string;
+using std::istringstream;
+using std::ostringstream;
+using std::invalid_argument;
+using std::ostream;
+using std::istream;
+using std::setfill;
+using std::setw;
+using std::numeric_limits;
+using utils::Duration;
+
+namespace utils {
+
+// -----------------------------------------------------------------------------
+
+DateTime::DateTime() 
+: year_(0), 
+  month_(0), 
+  day_(0),
+  hour_(0), 
+  minute_(0), 
+  second_(0) 
+{
+}
+
+// -----------------------------------------------------------------------------
+
+DateTime::DateTime(const std::string & str) {
+  this->set(str);
+}
+
+// -----------------------------------------------------------------------------
+// sets the date given YYYY,MM,DD,hh,mm,ss
+DateTime::DateTime(const int &YYYY,const int &MM,const int &DD,
+                   const int &hh,const int &mm,const int &ss)
+: year_(YYYY),
+  month_(MM),
+  day_(DD),
+  hour_(hh),
+  minute_(mm),
+  second_(ss)
+{
+}
+
+// -----------------------------------------------------------------------------
+
+void DateTime::set(const std::string & str) {
+  try {
+    istringstream datestream(str);
+
+    year_ = eatChars(datestream,4);
+
+    bool dashes = (datestream.peek() == '-');
+    if (dashes) datestream.get();
+
+    month_ = eatChars(datestream,2);
+    if (dashes) {
+      char c=datestream.get();
+      if (c != '-') throw str;
+    }
+
+    day_ = eatChars(datestream,2);
+
+    char c=datestream.get();
+    if (c != 'T') throw str;
+
+    hour_ = eatChars(datestream,2);
+
+    bool colons = (datestream.peek() == ':');
+    if (colons) datestream.get();
+
+    minute_ = eatChars(datestream,2);
+    if (colons) {
+      char c=datestream.get();
+      if (c != ':') throw str;
+    }
+
+    second_ = eatChars(datestream,2);
+
+    c=datestream.get();
+    if (c != 'Z') throw str;
+
+    datestream.peek();
+    if (!datestream.eof()) throw str;
+
+    if (!valid()) throw str;
+
+  }
+  catch(...) {
+    std::string message="Badly formatted date: ";
+    message.append(str);
+    throw invalid_argument(message);
+  }
+}
+
+// -----------------------------------------------------------------------------
+
+ostream& operator<<(std::ostream& output, const DateTime& t) {
+    output << t.toString();
+    return output;
+}
+
+// -----------------------------------------------------------------------------
+
+istream& operator>>(std::istream& input, DateTime& t) {
+    std::string time;
+    input >> time;
+    t.set(time);
+    return input;
+}
+
+// -----------------------------------------------------------------------------
+
+int DateTime::eatChars (std::istream & is, int nchars) {
+  // consume nchars characters from the stream and interpret as an integer
+  std::string str;
+  for (int i=0; i<nchars; ++i) {
+    str.append(1, (char) is.get());
+  }
+
+  istringstream mys(str);
+  int ret;
+  mys >> ret;
+  if (mys.fail()) throw(str);
+  return ret;
+}
+
+// -----------------------------------------------------------------------------
+
+bool DateTime::isLeapYear (int year) {
+  return ( ( year%4 == 0 && ( year%100 != 0 ) ) || year%400 ) == 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool DateTime::valid() {
+  bool good;
+  good = year_   >= 0 && year_   <= 9999 && month_  >= 0 && month_  <= 12 &&
+         hour_   >= 0 && hour_   <= 23   && minute_ >= 0 && minute_ <= 59 &&
+         second_ >= 0 && second_ <= 59   && day_    >= 0;
+
+  if (good) {
+    if (month_ == 9 || month_ == 4 || month_ == 6 || month_ == 11) {
+        good = (day_ <= 30);
+    } else if (month_ != 2) {
+        good = (day_ <= 31);
+    } else if (isLeapYear(year_)) {
+        good = (day_ <= 29); // leap year
+    } else {
+        good = (day_ <= 28);
+    }
+  }
+
+  return good;
+}
+
+// -----------------------------------------------------------------------------
+
+void DateTime::set(const int & year, const int & month, const int & day,
+               const int & hour, const int & minute, const int & second) {
+
+  year_ = year;
+  month_ = month;
+  day_ = day;
+  hour_ = hour;
+  minute_ = minute;
+  second_ = second;
+
+  if (!valid()) {
+    std::string message = "Invalid DateTime: ";
+    message.append(this->toString());
+    year_ = 0;
+    month_ = 0;
+    day_ = 0;
+    hour_ = 0;
+    minute_ = 0;
+    second_ = 0;
+    throw invalid_argument(message);
+  }
+}
+
+// -----------------------------------------------------------------------------
+
+std::string DateTime::toString() const {
+  ostringstream os;
+  os << setfill('0');
+  os << setw(4) << year_;
+  os.put('-');
+  os << setw(2) << month_;
+  os.put('-');
+  os << setw(2) << day_;
+  os.put('T');
+  os << setw(2) << hour_;
+  os.put(':');
+  os << setw(2) << minute_;
+  os.put(':');
+  os << setw(2) << second_;
+  os.put('Z');
+  return os.str();
+}
+
+// -----------------------------------------------------------------------------
+
+void DateTime::get(int & year, int & month, int & day,
+               int & hour, int & minute, int & second) const {
+  year = year_;
+  month = month_;
+  day = day_;
+  hour = hour_;
+  minute = minute_;
+  second = second_;
+}
+//==============================================================================
+unsigned long long DateTime::dateToJulian() const
+//==============================================================================
+{
+  int m1 = 0, m2 = 0, a = 0, b = 0, c = 0;
+
+//
+//  Compute the Julian Day number applying the following formula
+//
+//  julian_day = ( 1461 * ( y + 4800 + ( m - 14 ) / 12 ) ) / 4 +
+//               ( 367 * ( m - 2 - 12 * ( ( m - 14 ) / 12 ) ) ) / 12 -
+//                   ( 3 * ( ( y + 4900 + ( m - 14 ) / 12 ) / 100 ) ) / 4 +
+//                     d - 32075
+//
+
+  m1 = (month_ - 14)/12;
+  a  = (1461 * (year_ + 4800 + m1))/4;
+
+  b  = (367 * (month_ - 2 - (12 * m1)))/12;
+
+  m2 = (year_ + 4900 + m1)/100;
+
+  c  = (3 * (m2))/4;
+
+  return a + b - c + day_ - 32075;
+
+}
+//==============================================================================
+void DateTime::julianToDate(const unsigned long long &julian)
+//==============================================================================
+{
+  unsigned long long l     = 0; 
+  unsigned long long n     = 0; 
+  unsigned long long i     = 0; 
+  unsigned long long j     = 0; 
+  unsigned long long jdate = 0; 
+  unsigned long long day   = 0; 
+  unsigned long long month = 0; 
+  unsigned long long year  = 0; 
+
+  const unsigned long long JULIAN_MIN = 0LL;
+
+// Modified Julian date
+
+  jdate = julian;
+
+//  if (jdate >= JULIAN_MIN) { // always true : unsigned >= 0
+
+    l = jdate + 68569;
+    n = ( 4 * l ) / 146097;
+    l = l - ( 146097 * n + 3 ) / 4;
+    i = ( 4000 * ( l + 1 ) ) / 1461001;
+    l = l - ( 1461 * i ) / 4 + 31;
+    j = ( 80 * l ) / 2447;
+    day = l - ( 2447 * j ) / 80;
+    l = j / 11;
+    month = j + 2 - ( 12 * l );
+    year = 100 * ( n - 49 ) + i + l;
+    //if (numeric_limits<int>::min() <= year <= numeric_limits<int>::max() ) {
+
+    day_ = (int) day;
+    month_ = (int) month;
+    year_ = (int) year;
+    //} 
+
+// }
+
+}
+//==============================================================================
+int DateTime::hmsToSeconds() const
+//==============================================================================
+{
+ return SEC_HOUR * hour_ + SEC_MIN * minute_ + second_;
+}
+//==============================================================================
+void DateTime::secondToHms(const int & seconds)
+//==============================================================================
+{
+
+  int local_sec=0;
+
+  if (seconds >= 0 && seconds <= SEC_DAY) {
+    local_sec = seconds;
+    hour_ = local_sec / SEC_HOUR;
+    local_sec %= SEC_HOUR;
+    minute_ = local_sec / MIN_HOUR;
+    local_sec %= MIN_HOUR;
+    second_ = local_sec;
+  }
+}
+//==============================================================================
+void DateTime::addDays(const int & days)
+//==============================================================================
+{
+  unsigned long long julian = 0;
+
+  julian = dateToJulian();
+
+  julian += days;
+
+ //   if (numeric_limits<long>::min() <= julian <= numeric_limits<long>::max() ) {
+    julianToDate(julian);
+ // }
+  
+}
+//==============================================================================
+void DateTime::addHours(const int &hours)
+//==============================================================================
+{
+ DateTime fulldate(year_,month_,day_, hour_, minute_, second_);
+
+ unsigned long long julian = dateToJulian();
+ int seconds = hmsToSeconds();
+
+ {
+   int days = 0;
+   int new_hours = 0;
+   int new_seconds = 0;
+
+   new_hours = hours;
+   days = new_hours / HOUR_DAY;
+
+   julian += days;
+
+   new_hours %= HOUR_DAY;
+
+   new_seconds = new_hours * SEC_HOUR;
+   seconds += new_seconds;
+
+   if (seconds < 0) {
+     julian--;
+     seconds += SEC_DAY;
+   }
+
+   if (seconds >= SEC_DAY) {
+     julian++;
+     seconds -= SEC_DAY;
+   }
+
+ }
+ julianToDate(julian);
+ secondToHms(seconds);
+}
+
+//==============================================================================
+void DateTime::addMinutes(const int &minutes)
+//==============================================================================
+{
+ unsigned long long julian = dateToJulian();
+ int seconds = hmsToSeconds();
+ {
+  int days = 0;
+  int new_minutes = 0;
+  int new_seconds = 0;
+
+  new_minutes = minutes;
+  days = new_minutes / MIN_DAY;
+  julian += days;
+  new_minutes %= MIN_DAY;
+  new_seconds = new_minutes * SEC_MIN;
+  seconds += new_seconds;
+
+  if (seconds < 0) {
+    julian--;
+    seconds += SEC_DAY;
+  }
+
+  if (seconds >= SEC_DAY) {
+    julian++;
+    seconds -= SEC_DAY;
+  }
+ }
+
+ julianToDate(julian);
+ secondToHms(seconds);
+}
+//==============================================================================
+void DateTime::addSeconds(const int &seconds)
+//==============================================================================
+{
+ unsigned long long julian = dateToJulian();
+
+ int full_seconds = hmsToSeconds();
+ {
+  int days=0;
+  int new_seconds = 0;
+  
+  new_seconds = seconds;
+  days = new_seconds / SEC_DAY;
+ 
+  julian += days;
+  new_seconds %= SEC_DAY;
+
+  full_seconds += (int) new_seconds;
+
+  if (full_seconds < 0) {
+    julian--;
+    full_seconds += SEC_DAY;
+  } else if (full_seconds >= SEC_DAY) {
+    julian++;
+    full_seconds -= SEC_DAY;
+  }
+ }
+ julianToDate(julian);
+ secondToHms(full_seconds);
+}
+
+//==============================================================================
+int DateTime::daysDateMinusDate(const DateTime &d1) const // return the number of days
+//==============================================================================
+{
+  unsigned long long julian1 = dateToJulian();
+  unsigned long long julian2 = d1.dateToJulian();
+
+  return (julian1 - julian2);
+}
+
+//==============================================================================
+int DateTime::hoursDateMinusDate(const DateTime &d1) const
+//==============================================================================
+// return the number of hours
+{
+  int days = daysDateMinusDate(d1);
+
+  int s1 = hmsToSeconds();
+  int s2 = d1.hmsToSeconds();
+
+  return (days * HOUR_DAY + (s1-s2)/SEC_HOUR);
+}
+
+//==============================================================================
+int DateTime::minutesDateMinusDate(const DateTime &d1) const
+//==============================================================================
+{
+  int days = daysDateMinusDate(d1);
+
+  int s1 = hmsToSeconds();
+  int s2 = d1.hmsToSeconds();
+
+  return (days*MIN_DAY + (s1-s2) / SEC_MIN);
+}
+
+//==============================================================================
+int DateTime::secondsDateMinusDate(const DateTime &d1) const
+//==============================================================================
+{
+  int days = daysDateMinusDate(d1);
+
+  int s1 = hmsToSeconds();
+  int s2 = d1.hmsToSeconds();
+
+  return (days*SEC_DAY + (s1-s2));
+}
+
+
+// -----------------------------------------------------------------------------
+
+DateTime& DateTime::operator+=(const Duration & s) {
+
+
+  ASSERT(s.toSeconds()>=numeric_limits<int>::min() &&
+         s.toSeconds()<=numeric_limits<int>::max());
+
+	int  secs = s.toSeconds();
+
+	addSeconds(secs);
+	return *this;
+}
+
+// -----------------------------------------------------------------------------
+
+DateTime& DateTime::operator-=(const Duration & s) {
+  Duration negs = s;
+  negs.negate();
+  return this->operator+=(negs);
+}
+
+// -----------------------------------------------------------------------------
+
+const DateTime DateTime::operator+(const Duration & s) const {
+    DateTime result = *this; // Make a copy of myself.
+    result += s;         // Use += to add s to the copy.
+    return result;
+  }
+
+// -----------------------------------------------------------------------------
+
+const DateTime DateTime::operator-(const Duration & s) const {
+    DateTime result = *this; // Make a copy of myself.
+    result -= s;         // Use -= to subtract s from the copy
+    return result;
+  }
+
+// -----------------------------------------------------------------------------
+
+const Duration DateTime::operator-(const DateTime& other) const {
+  return Duration((int) secondsDateMinusDate(other));
+}
+
+// -----------------------------------------------------------------------------
+
+bool DateTime::operator==(const DateTime& other) const {
+  return (year_ == other.year_) && (month_ == other.month_) &&
+         (day_ == other.day_) && (hour_ == other.hour_) &&
+         (minute_ == other.minute_) && (second_ == other.second_);
+}
+
+// -----------------------------------------------------------------------------
+
+bool DateTime::operator!=(const DateTime& other) const {return !(*this == other);}
+
+bool DateTime::operator<(const DateTime& other) const {return (*this - other) < 0;}
+
+bool DateTime::operator<=(const DateTime& other) const {return (*this - other) <= 0;}
+
+bool DateTime::operator>(const DateTime& other) const {return (*this - other) > 0;}
+
+bool DateTime::operator>=(const DateTime& other) const {return (*this - other) >= 0;}
+
+} //namespace
diff --git a/odb_api/src/odb_api/DateTime.h b/odb_api/src/odb_api/DateTime.h
new file mode 100755
index 0000000..2347b73
--- /dev/null
+++ b/odb_api/src/odb_api/DateTime.h
@@ -0,0 +1,126 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TIME_HEADER_H
+#define TIME_HEADER_H
+
+#include "eckit/eckit.h"
+
+// Forward declarations
+namespace utils { 
+  class Duration;
+}
+
+namespace utils {
+
+const int SEC_MIN     =    60;
+const int SEC_HOUR    =  3600;
+const int SEC_DAY     = 86400;
+const int HOUR_DAY    =    24;
+const int MIN_HOUR    =    60;
+const int MIN_DAY     =  1440;
+
+//! This class represents time, and provides methods to manipulate time
+/*! 
+ * DateTime is represented internally as UTC quantized to the nearest second.
+ */
+
+class DateTime {
+
+//! << and >> can be used to output and set the time as ISO 8601 strings
+  friend std::ostream& operator<<(std::ostream&, const DateTime&);
+  friend std::istream& operator>>(std::istream&, DateTime&);
+
+public:
+
+// -- Constructors
+  DateTime(); // sets the date to 0000-00-00T00:00:00Z
+
+  DateTime(const std::string &); // sets the date given an ISO 8601 std::string
+
+  DateTime(const int&,const int &,const int &,const int &,const int &,const int &); // sets the date given YYYY,MM,DD,hh,mm,ss
+
+// -- Destructor
+  //  ~DateTime()  -- not required. This is a simple class.
+
+// -- Methods
+
+  //! Set the time from an ISO 8601 format std::string: ${date}T${time}Z
+  //! where date is YYYYMMDD or YYYY-MM-DD and time is hhmmss or hh:mm:ss
+  void set(const std::string &);
+
+  //! Set the date and time from integers YYYY,MM,DD,hh,mm,ss
+  void set(const int &, const int &, const int &,
+           const int &, const int &, const int &);
+
+  //! Convert the time to ISO 8601 format: YYYY-MM-DDThh:mm:ssZ
+  std::string toString() const;
+
+  //! Get the date and time as integers
+  void get(int &, int &, int &, int &, int &, int &) const;
+
+  // Functions from eckit
+  void addDays(const int &);
+  void addHours(const int &);
+  void addMinutes(const int &);
+  void addSeconds(const int &);
+  unsigned long long dateToJulian() const;
+  void julianToDate(const unsigned long long &);
+  int hmsToSeconds() const;
+  void secondToHms(const int &);
+
+  int daysDateMinusDate(const DateTime &) const; // return the number of days
+  int hoursDateMinusDate(const DateTime &) const; // return the number of hours
+  int minutesDateMinusDate(const DateTime&) const;
+  int secondsDateMinusDate(const DateTime&) const;
+
+  // Operators to add/subtract a Duration to/from a DateTime
+  DateTime& operator+=(const Duration & );
+  DateTime& operator-=(const Duration & );
+  const DateTime operator+(const Duration & ) const;
+  const DateTime operator-(const Duration & ) const;
+
+  // Difference in seconds between two DateTimes
+  const Duration operator-(const DateTime&) const;
+
+  // Comparison operators
+  bool operator==(const DateTime&) const;
+  bool operator!=(const DateTime&) const;
+  bool operator<(const DateTime&) const;
+  bool operator<=(const DateTime&) const;
+  bool operator>(const DateTime&) const;
+  bool operator>=(const DateTime&) const;
+
+private:
+
+// -- Copy allowed
+// DateTime(const DateTime&); -- default shallow copy is OK
+// DateTime& operator=(const DateTime&); -- default assignment is OK
+
+  int eatChars (std::istream &, int);
+  bool isLeapYear (int);
+  bool valid();
+
+// -- Members
+
+  int year_;
+  int month_;
+  int day_;
+  int hour_;
+  int minute_;
+  int second_;
+
+// -- Overridden methods
+
+};
+
+} //namespace utils
+
+#endif
diff --git a/odb_api/src/odb_api/Decoder.cc b/odb_api/src/odb_api/Decoder.cc
new file mode 100755
index 0000000..c8b0844
--- /dev/null
+++ b/odb_api/src/odb_api/Decoder.cc
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include <bitset>
+#include "odb_api/Decoder.h"
+
+namespace odb {
+
+std::string Decoder::printBinary(W n) {
+    std::stringstream ss;
+    printBinary(ss, n);
+    return ss.str();
+}
+
+std::string Decoder::printHexadecimal(W n) {
+    std::stringstream ss;
+    printHexadecimal(ss, n);
+    return ss.str();
+}
+
+void Decoder::printHexadecimal(std::ostream& ss, W n)
+{
+    ss << std::hex << n << std::dec;
+}
+
+Decoder::W Decoder::makeMask(W size)
+{
+	W mask = 0;
+	while(size--) { mask <<= 1; mask |= 1; }
+	return mask;
+}
+
+void Decoder::printBinary(std::ostream& ss, W n)
+{
+	unsigned char *s = reinterpret_cast<unsigned char *>(&n);
+
+	bool oneSeen = false;
+
+	int endianTest = 1;
+	if (*reinterpret_cast<char *>(&endianTest))
+	for (int i = sizeof(W) - 1; i >= 0; --i)
+	{
+		unsigned char c = s[i];
+		for(unsigned char mask = 1 << 7; mask; mask >>= 1)
+		{
+			if (c & mask)
+			{
+				ss << '1';
+				oneSeen = true;
+			}
+			else
+			{
+				if (oneSeen) ss << '0';
+			}
+		}
+	}
+	else
+	for (int i = 0; i < sizeof(W); ++i)
+	{
+		unsigned char c = s[i];
+		for(unsigned char mask = 1 << 7; mask; mask >>= 1)
+		{
+			if (c & mask)
+			{
+				ss << '1';
+				oneSeen = true;
+			}
+			else
+			{
+				if (oneSeen) ss << '0';
+			}
+		}
+	}
+
+	if (! oneSeen)
+		ss << '0';
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Decoder.h b/odb_api/src/odb_api/Decoder.h
new file mode 100755
index 0000000..1d03c4d
--- /dev/null
+++ b/odb_api/src/odb_api/Decoder.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Decoder.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef Decoder_H
+#define Decoder_H
+
+
+namespace odb {
+
+class Decoder {
+public:
+	typedef long long W;
+
+	static W makeMask(W);
+	static void printBinary(std::ostream&, W);
+	static std::string printBinary(W);
+
+    static void printHexadecimal(std::ostream&, W);
+	static std::string printHexadecimal(W);
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Dictionary.cc b/odb_api/src/odb_api/Dictionary.cc
new file mode 100755
index 0000000..8d4e5be
--- /dev/null
+++ b/odb_api/src/odb_api/Dictionary.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/Dictionary.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+// TODO: a special type for Dictionary?
+const type::SQLType* Dictionary::type() const { return &type::SQLType::lookup("real"); }
+
+Dictionary& Dictionary::operator=(const Dictionary& e)
+{
+	Map::operator=(e);
+	return *this;
+}
+
+SQLExpression * Dictionary::clone() const
+{
+	Dictionary *r = new Dictionary;
+	for (Dictionary::const_iterator it = begin(); it != end(); ++it)
+		(*r)[it->first] = it->second->clone();
+
+	return r;
+}
+
+void Dictionary::release()
+{
+	for (Dictionary::const_iterator it = begin(); it != end(); ++it)
+		delete it->second;
+}
+
+void Dictionary::print(std::ostream& o) const
+{
+	o << "{";
+	for (Dictionary::const_iterator it = begin(); it != end(); ++it)
+	{
+		o << it->first << " : ";
+		it->second->print(o);
+		o << ", ";
+	}
+	o << "}";
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Dictionary.h b/odb_api/src/odb_api/Dictionary.h
new file mode 100755
index 0000000..e10d8e6
--- /dev/null
+++ b/odb_api/src/odb_api/Dictionary.h
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file Dictionary.h
+/// Piotr Kuchta - ECMWF Dec 11
+
+#ifndef Dictionary_H
+#define Dictionary_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+typedef std::map<std::string,odb::sql::expression::SQLExpression*> Map;
+
+class Dictionary : public SQLExpression, public Map
+{
+public:
+	Dictionary() : Map() {}
+	Dictionary(const Dictionary& e)
+	: SQLExpression(), Map(e)
+	{}
+
+	Dictionary& operator=(const Dictionary&);
+
+	virtual void release();
+
+	virtual void print(std::ostream& s) const;
+
+	friend std::ostream& operator<<(std::ostream& o, const Dictionary& e)
+		{ e.print(o); return o; }
+
+//////////////////////////////////////////////////////////////////////////////////////
+	
+	virtual const odb::sql::type::SQLType* type() const;
+
+	virtual void prepare(SQLSelect&)  { NOTIMP; }
+	virtual void cleanup(SQLSelect&)  { NOTIMP; }
+
+	// -- For WHERE
+	virtual double eval(bool& missing) const  { NOTIMP; }
+
+	virtual bool isConstant() const  { NOTIMP; }
+	virtual bool isNumber() const { return false; }
+	virtual bool isVector() const { return false; }
+	//virtual const Vector& std::vector() const { return *this; }
+	virtual bool isDictionary() const { return true; }
+	virtual Dictionary& dictionary() { return *this; }
+
+	virtual SQLExpression* simplify(bool&) { NOTIMP; }
+
+	virtual SQLExpression* clone() const;
+	
+	virtual bool isAggregate() const { return false; }
+	// For select expression
+
+	virtual void output(SQLOutput&) const { return NOTIMP; }
+	virtual void partialResult() {}
+	virtual void expandStars(const std::vector<SQLTable*>&,expression::Dictionary&) { NOTIMP; }
+//////////////////////////////////////////////////////////////////////////////////////
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/DirectAccess.cc b/odb_api/src/odb_api/DirectAccess.cc
new file mode 100644
index 0000000..df9315e
--- /dev/null
+++ b/odb_api/src/odb_api/DirectAccess.cc
@@ -0,0 +1,254 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODA.cc
+///
+/// @author Baudouin Raoult, Dec 2013
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "eckit/eckit.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/PartHandle.h"
+#include "eckit/io/SharedHandle.h"
+#include "eckit/log/Bytes.h"
+#include "eckit/log/BigNum.h"
+
+#include "eckit/log/Timer.h"
+#include "odb_api/DirectAccess.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+DirectAccessBlock::~DirectAccessBlock()
+{
+    delete handle_;
+    delete[] data_;
+}
+
+void DirectAccessBlock::unload() {
+    delete handle_; handle_ = 0;
+    delete[] data_; data_ = 0;
+    unloads_ ++;
+}
+
+
+DirectAccess::DirectAccess(DataHandle &dh, const std::string& statement, size_t maxBlocksSize)
+    : HandleHolder(dh),
+      statement_(statement),
+      current_(new DirectAccessIterator(*this)),
+      row_(current_),
+      maxBlocksSize_(maxBlocksSize),
+      usedBlocksSize_(0),
+      seq_(0)
+{
+    initBlocks();
+}
+
+DirectAccess::DirectAccess(DataHandle *dh, const std::string& statement, size_t maxBlocksSize)
+    : HandleHolder(dh),
+      statement_(statement),
+      current_(new DirectAccessIterator(*this)),
+      row_(current_),
+      maxBlocksSize_(maxBlocksSize),
+      usedBlocksSize_(0),
+      seq_(0)
+{
+    initBlocks();
+}
+
+DirectAccess::DirectAccess(const std::string& path, const string &statement, size_t maxBlocksSize)
+    : HandleHolder(new FileHandle(path)),
+      statement_(statement),
+      path_(path),
+      current_(new DirectAccessIterator(*this)),
+      row_(current_),
+      maxBlocksSize_(maxBlocksSize),
+      usedBlocksSize_(0),
+      seq_(0)
+{
+    handle().openForRead();
+    initBlocks();
+}
+
+void DirectAccess::initBlocks()
+{
+    eckit::Timer timer("DirectAccessIterator::initBlocks");
+    IteratorProxy<MetaDataReaderIterator, DirectAccessIterator, const double> it(new MetaDataReaderIterator(handle(), true));
+    IteratorProxy<MetaDataReaderIterator, DirectAccessIterator, const double> end(0);
+
+    unsigned long long n = 0;
+    size_t c = 0;
+    for (; it != end; ++it)
+    {
+        const MetaData &md = it->columns();
+        n += md.rowsNumber();
+        blocks_.push_back(DirectAccessBlock(c++,
+                                            md.rowsNumber(),
+                                            it.iter_->blockStartOffset(),
+                                            it.iter_->blockEndOffset() - it.iter_->blockStartOffset()
+                                            ));
+    }
+
+    std::cout << "Rows " << BigNum(n) << std::endl;
+    eckit::Timer t("DirectAccessIterator::initBlocks (index)");
+
+    ASSERT(size_t(n) == n);
+    index_.reserve(n);
+
+    for(std::deque<DirectAccessBlock>::iterator j = blocks_.begin(); j != blocks_.end(); ++j) {
+        DirectAccessBlock& b = *j;
+        size_t n = 0;
+        for(size_t i = 0; i < b.rows(); ++i) {
+            index_.push_back(std::make_pair(&b, n));
+            n++;
+        }
+    }
+
+}
+
+
+template<class Source>
+void DirectAccess::readPart(DirectAccessBlock& b, Source& in)
+{
+    typename Source::iterator it = in.begin();
+    typename Source::iterator end = in.end();
+
+    const MetaData& md = it->columns();
+
+    //std::cout << "SIZE " << md.size() << std::endl;
+    //std::cout <<  md << std::endl;
+
+    size_t width = md.size();
+    size_t height = b.rows();
+
+
+    b.size(width * height);
+    b.width(width);
+
+    usedBlocksSize_  += b.size();
+    b.data(new double[b.size()]);
+    b.metaData(md.clone());
+
+    //eckit::Timer t("Read part");
+    size_t n = 0;
+    size_t off = 0;
+    for(; it != end; ++it) {
+        const double* d = it->data();
+        std::copy(d, d+width, b.data() + off);
+        n++;
+        off += width;
+/*
+        if(n <5) {
+            std::cout << "++++";
+            for(size_t p = 0; p < width; p++) std::cout << " - " << d[p]; std::cout << std::endl;
+        }*/
+    }
+    ASSERT(n == height); // This will happen if there is a 'where' clause
+}
+
+DirectAccess::row* DirectAccess::operator[](size_t n)
+{
+
+    ASSERT(n < index_.size());
+    std::pair<DirectAccessBlock*, size_t>& e = index_[n];
+    DirectAccessBlock* b = e.first;
+    if(!b->handle()) {
+
+
+        while(usedBlocksSize_ >= maxBlocksSize_) {
+            // Unload blocks
+
+
+            unsigned long long t = 0;
+            std::deque<DirectAccessBlock>::iterator k;
+
+            for(std::deque<DirectAccessBlock>::iterator j = blocks_.begin();
+                j != blocks_.end(); ++j) {
+                DirectAccessBlock& u = *j;
+                if(u.handle()) {
+                    if(t == 0 || u.last() < t) {
+                        k = j;
+                        t = u.last();
+                    }
+                }
+            }
+
+            ASSERT(t);
+            usedBlocksSize_ -= (*k).size();
+            (*k).unload();
+            //std::cout << "UNLOAD " << (*k).n() << " maxBlocksSize " << maxBlocksSize_ << std::endl;
+
+        }
+
+        /*
+        std::cout << "LOADING block " << b->n() << " at offset " << eckit::Bytes(b->offset()) << ", length "
+                  <<  eckit::Bytes(b->length()) << std::endl;
+        std::cout << "INDEX is " << n << " offset in block is " << e.second << std::endl;
+        */
+        b->handle(new PartHandle(new SharedHandle(handle()), b->offset(), b->length()));
+
+
+    }
+
+    if(!b->data())
+    {
+        if(statement_.length()) {
+            Select in(statement_, *b->handle());
+            readPart(*b, in);
+        }
+        else
+        {
+            Reader in(*b->handle());
+            readPart(*b, in);
+        }
+
+    }
+
+    b->last(++seq_) ;
+    //std::cout << " ==== n = " << n << " idx " << e.second << std::endl;
+
+/*
+    {
+        const double* d = b->data() + e.second;
+        std::cout << "****";
+        for(size_t p = 0; p < 10; p++) std::cout << " - " << d[p]; std::cout << std::endl;
+    }*/
+
+    idx_   = e.second * b->width();
+    block_ = b;
+    return &(*current_);
+
+}
+
+
+DirectAccess::~DirectAccess()
+{
+    /*
+
+    std::cout << "BLOCKS : " << blocks_.size() << std::endl;
+    for(std::deque<DirectAccessBlock>::iterator j = blocks_.begin(); j != blocks_.end(); ++j) {
+        DirectAccessBlock& b = *j;
+        if(b.loads())
+            std::cout << "BLOCK " << b.n() << " loads: " << b.loads() << " unloads: " << b.unloads() << std::endl;
+    }
+    */
+
+}
+
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/DirectAccess.h b/odb_api/src/odb_api/DirectAccess.h
new file mode 100644
index 0000000..8a32ac6
--- /dev/null
+++ b/odb_api/src/odb_api/DirectAccess.h
@@ -0,0 +1,178 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DirectAccess.h
+///
+/// @author Baudouin Raoult, Dec 2013
+
+
+#ifndef DirectAccess_H
+#define DirectAccess_H
+
+#ifdef SWIGPYTHON
+#include <Python.h>
+#endif
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/HandleHolder.h"
+
+#include "odb_api/DirectAccessIterator.h"
+#include "odb_api/IteratorProxy.h"
+
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class MetaData;
+
+
+class DirectAccessBlock {
+    size_t n_;
+    size_t rows_;
+    eckit::Offset offset_;
+    eckit::Length length_;
+    eckit::DataHandle* handle_;
+    double* data_;
+    MetaData* metaData_;
+    unsigned long long last_;
+    unsigned long long unloads_;
+    unsigned long long loads_;
+    size_t size_;
+    size_t width_;
+
+public:
+    DirectAccessBlock(size_t n, size_t rows, const eckit::Offset& offset, const eckit::Length& length):
+        n_(n), rows_(rows), offset_(offset),
+        length_(length), handle_(0), data_(0), metaData_(0), loads_(0), unloads_(0), size_(0), width_(0) {}
+
+    ~DirectAccessBlock();
+
+    size_t n() const { return n_; }
+    size_t rows() const { return rows_; }
+    eckit::Offset offset() const { return offset_; }
+    eckit::Length length() const { return length_; }
+    eckit::DataHandle* handle() const { return handle_; }
+
+    void handle(eckit::DataHandle *h) {
+        ASSERT(h); ASSERT(!handle_);
+        handle_ = h;
+        loads_++;
+    }
+
+    void unload();
+
+    double* data() { return data_; }
+
+    void data(double *h) {
+        ASSERT(h); ASSERT(!data_);
+        data_ = h;
+    }
+
+    MetaData* metaData() { return metaData_; }
+
+    void metaData(MetaData *h) {
+        ASSERT(h); ASSERT(!metaData_);
+        metaData_ = h;
+    }
+
+    unsigned long long last() const { return last_; }
+
+    void last(unsigned long long h) {
+        last_ = h;
+    }
+
+    size_t size() const { return size_; }
+
+    void size(size_t h) {
+        size_ = h;
+    }
+
+    size_t width() const { return width_; }
+
+    void width(size_t h) {
+        width_ = h;
+    }
+
+    unsigned long long loads() const { return loads_; }
+    unsigned long long unloads() const { return unloads_; }
+
+};
+
+
+class DirectAccess : public eckit::HandleHolder
+{
+public:
+
+    typedef IteratorProxy<DirectAccessIterator, DirectAccess, double> iterator;
+    typedef iterator::Row row;
+
+    DirectAccess(eckit::DataHandle &, const std::string &statement = "", size_t maxBlocksSize = 1024*1024*64);
+    DirectAccess(eckit::DataHandle *, const std::string &statement = "", size_t maxBlocksSize = 1024*1024*64);
+    DirectAccess(const std::string& path, const std::string &statement = "", size_t maxBlocksSize = 1024*1024*64);
+
+    virtual ~DirectAccess();
+
+
+    row* operator[](size_t);
+
+    eckit::DataHandle* dataHandle() { return &handle(); }
+    // For C API
+    DirectAccessIterator* createReadIterator(const eckit::PathName&);
+    DirectAccessIterator* createReadIterator();
+
+#ifdef SWIGPYTHON
+    iterator __iter__() { return iterator(createReadIterator()); }
+#endif
+
+    size_t size() const { return index_.size(); }
+    size_t count() const { return blocks_.size(); }
+
+private:
+    // No copy allowed
+    DirectAccess(const DirectAccess&);
+    DirectAccess& operator=(const DirectAccess&);
+
+    void initBlocks();
+
+    std::deque<DirectAccessBlock> blocks_;
+    std::vector<std::pair<DirectAccessBlock*,size_t> > index_;
+
+
+    template<class Source>
+    void readPart(DirectAccessBlock& b, Source& s);
+
+    //const eckit::PathName path_;
+    const std::string path_;
+
+    iterator current_;
+    row row_;
+
+    DirectAccessBlock* block_;
+    size_t idx_;
+
+    size_t maxBlocksSize_;
+    size_t usedBlocksSize_;
+
+    std::string statement_;
+
+    unsigned long long seq_;
+
+
+
+    friend class DirectAccessIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/DirectAccessIterator.cc b/odb_api/src/odb_api/DirectAccessIterator.cc
new file mode 100644
index 0000000..6640ffc
--- /dev/null
+++ b/odb_api/src/odb_api/DirectAccessIterator.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DirectAccessIterator.cc
+///
+/// @author Baudouin Raoult, Dec 2013
+
+#include "odb_api/DirectAccess.h"
+#include "odb_api/DirectAccessIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+
+DirectAccessIterator::DirectAccessIterator(DirectAccess& owner):
+    owner_(owner),
+    refCount_(0)
+{
+}
+
+DirectAccessIterator::~DirectAccessIterator()
+{
+}
+
+MetaData& DirectAccessIterator::columns() {
+    return *owner_.block_->metaData();
+}
+
+double &DirectAccessIterator::data(size_t n) {
+    size_t i = owner_.idx_;
+    return *(owner_.block_->data() + i + n);
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/DirectAccessIterator.h b/odb_api/src/odb_api/DirectAccessIterator.h
new file mode 100644
index 0000000..084bfe9
--- /dev/null
+++ b/odb_api/src/odb_api/DirectAccessIterator.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DirectAccessIterator.h
+///
+/// @author Baudouin Raoult, Dec 2013
+
+
+#ifndef DirectAccessIterator_H
+#define DirectAccessIterator_H
+
+#include "eckit/eckit.h"
+
+namespace odb {
+
+class MetaData;
+
+class DirectAccess;
+
+class DirectAccessIterator
+{
+public:
+    DirectAccessIterator (DirectAccess &owner);
+    ~DirectAccessIterator ();
+
+    MetaData& columns();
+    double &data(size_t n);
+
+    size_t refCount_;
+
+private:
+
+    DirectAccess& owner_;
+
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/DispatchingWriter.cc b/odb_api/src/odb_api/DispatchingWriter.cc
new file mode 100644
index 0000000..b7f4874
--- /dev/null
+++ b/odb_api/src/odb_api/DispatchingWriter.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DispatchingWriter.cc
+///
+/// @author Piotr Kuchta, June 2009
+
+#include "eckit/config/Resource.h"
+#include "odb_api/DispatchingWriter.h"
+
+using namespace eckit;
+
+namespace odb {
+
+DispatchingWriter::DispatchingWriter(const std::string& outputFileTemplate, int maxOpenFiles, bool append)
+: outputFileTemplate_(outputFileTemplate),
+  maxOpenFiles_(maxOpenFiles ? maxOpenFiles : Resource<long>("$ODBAPI_MAX_OPEN_FILES;-maxOpenFiles;maxOpenFiles", 250)),
+  append_(append)
+{}
+
+DispatchingWriter::~DispatchingWriter() {}
+
+DispatchingWriter::iterator_class* DispatchingWriter::writer()
+{
+        NOTIMP;
+        // TODO:
+        return 0;
+}
+
+DispatchingWriter::iterator DispatchingWriter::begin()
+{
+	return iterator(new iterator_class(*this, maxOpenFiles_, append_));
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/DispatchingWriter.h b/odb_api/src/odb_api/DispatchingWriter.h
new file mode 100644
index 0000000..bfd1ea2
--- /dev/null
+++ b/odb_api/src/odb_api/DispatchingWriter.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file DispatchingWriter.h
+///
+/// @author Piotr Kuchta, June 2009
+
+#ifndef odb_api_DispatchingWriter_H
+#define odb_api_DispatchingWriter_H
+
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/WriterDispatchingIterator.h"
+
+namespace eckit { class PathName; }
+
+namespace odb {
+
+class DispatchingWriter
+{
+public:
+	typedef WriterDispatchingIterator<WriterBufferingIterator, DispatchingWriter> iterator_class;
+	typedef IteratorProxy<iterator_class, DispatchingWriter>  iterator;
+
+	DispatchingWriter(const std::string &outputFileTemplate, int maxOpenFiles = 0, bool append = false);
+	virtual ~DispatchingWriter();
+
+	const std::string outputFileTemplate() { return outputFileTemplate_; }
+
+	iterator begin();
+
+protected:
+	iterator_class* writer();
+
+private:
+// No copy allowed
+    DispatchingWriter(const DispatchingWriter&);
+    DispatchingWriter& operator=(const DispatchingWriter&);
+
+	const std::string outputFileTemplate_;
+	int maxOpenFiles_; 
+	bool append_;
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Duration.cc b/odb_api/src/odb_api/Duration.cc
new file mode 100644
index 0000000..b9a23ce
--- /dev/null
+++ b/odb_api/src/odb_api/Duration.cc
@@ -0,0 +1,227 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <locale>         // std::locale, std::isdigit
+#include <stdexcept>
+
+#include "eckit/eckit.h"
+#include "odb_api/Duration.h"
+
+using std::string;
+using std::istringstream;
+using std::ostringstream;
+using std::istream;
+using std::ostream;
+using std::locale;
+using std::invalid_argument;
+
+namespace utils {
+
+Duration::Duration() : seconds_(0) {}
+
+Duration::Duration(const int64_t s) : seconds_(s) {}
+
+Duration::Duration(const std::string & s) { this->set(s); }
+
+ostream& operator<<(std::ostream& output, const Duration& d) {
+    output << d.toString();
+    return output;
+}
+
+istream& operator>>(std::istream& input, Duration& d) {
+    std::string duration;
+    input >> duration;
+    d.set(duration);
+    return input;
+}
+
+void Duration::set(const std::string & str) {
+  this ->seconds_ = 0;
+
+  try {
+    istringstream is(str);
+
+    char c = is.get();
+    if (!is.good()) throw str;
+
+    // strip off any initial '-' and/or 'P'
+
+    bool negative = false;
+    if (c == '-') {
+      negative = true;
+      c = is.get();
+      if (!is.good()) throw str;
+    }
+
+    if (c != 'P') throw str;
+
+    // We now expect either 'T', or an integer followed by 'D'
+
+    c = is.get();
+    if (!is.good()) throw str;
+
+    if (c != 'T') {
+      is.unget();
+      std::string days = eatDigits(is);    
+      if (days.length()==0) throw str;
+      c = is.get();
+      if (!is.good() || c != 'D') throw str;
+      istringstream mys(days);
+      int ndays;
+      mys >> ndays;
+      if (mys.fail()) throw str;
+      this ->seconds_ = 86400*ndays;
+    } else {
+      is.unget();
+    }
+
+    // Now, we can have the end of the std::string, or a 'T'
+
+    c = is.get();
+    if (!is.eof()) {
+      if (c != 'T') throw str;  
+
+      // Now we can have an integer followed by 'H', 'M' or 'S'
+      
+      bool hoursdone = false;
+      bool minsdone = false;
+      bool secsdone = false;
+
+      for (int i=0; i<3; i++) {
+        c = is.peek();
+        if (is.eof()) break;
+        std::string num = eatDigits(is);
+        c = is.get();
+        if (!is.good()) throw str;
+
+        if (c == 'H' && !hoursdone && !minsdone && !secsdone) {
+          istringstream mys(num);
+          int hours;
+          mys >> hours;
+          if (mys.fail()) throw str;
+          this ->seconds_ += 3600*hours;
+          hoursdone = true;
+        }
+        else if (c == 'M' && !minsdone && !secsdone) {
+          istringstream mys(num);
+          int mins;
+          mys >> mins;
+          if (mys.fail()) throw str;
+          this ->seconds_ += 60*mins;
+          minsdone = true;
+        }
+        else if (c == 'S' && !secsdone) {
+          istringstream mys(num);
+          int secs;
+          mys >> secs;
+          if (mys.fail()) throw str;
+          this ->seconds_ += secs;
+          secsdone = true;
+        }
+        else throw str;
+      }
+    }
+
+    c = is.peek();
+    if (!is.eof()) throw str;
+    
+    if (negative) this->negate();
+
+  }
+  catch(...) {
+    std::string message="Badly formed duration string: ";
+    message.append(str);
+    throw invalid_argument(message);
+  }
+}
+
+std::string Duration::eatDigits(std::istream & is) {
+  std::string str;
+  char c;
+  locale loc;
+  while (isdigit(c = is.get(),loc)) {
+    if (!is.good()) throw;
+    str.push_back(c);
+  }
+  is.unget();
+  return str;
+}
+
+int64_t Duration::toSeconds () const {return seconds_;}
+
+std::string Duration::toString () const {
+  ostringstream os;
+  int64_t remainder = seconds_;
+
+  if (remainder < 0) {
+    os << "-P";
+    remainder = -remainder;
+  } else {
+    os << "P";
+  }
+
+  int days = remainder/86400;
+  if (days != 0 ) {
+    os << days << "D";
+    remainder = remainder - 86400*days; 
+  }
+
+  if (remainder != 0) {
+    os << "T";
+  }
+
+  int hours = remainder/3600;
+  if (hours != 0) {
+    os << hours << "H";
+    remainder = remainder - 3600*hours; 
+  }
+
+  int minutes = remainder/60;
+  if (minutes != 0) {
+    os << minutes << "M";
+    remainder = remainder - 60*minutes;
+  }
+
+  if (remainder != 0 || seconds_ == 0) {
+    os << remainder << "S";
+  }
+
+  return os.str();
+}
+
+    bool Duration::operator==(const Duration& other) const {
+      return this->seconds_ == other.seconds_;
+    }
+
+    bool Duration::operator!=(const Duration& other) const {
+      return this->seconds_ != other.seconds_;
+    }
+
+    bool Duration::operator<(const Duration& other) const {
+      return this->seconds_ < other.seconds_;
+    }
+
+    bool Duration::operator<=(const Duration& other) const {
+      return this->seconds_ <= other.seconds_;
+    }
+
+    bool Duration::operator>(const Duration& other) const {
+      return this->seconds_ > other.seconds_;
+    }
+
+    bool Duration::operator>=(const Duration& other) const {
+      return this->seconds_ >= other.seconds_;
+    }
+
+    int Duration::operator%(const Duration& other) const {
+      return this->seconds_ % other.seconds_;
+    }
+
+} //namespace
diff --git a/odb_api/src/odb_api/Duration.h b/odb_api/src/odb_api/Duration.h
new file mode 100644
index 0000000..514c18b
--- /dev/null
+++ b/odb_api/src/odb_api/Duration.h
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef DURATION_HEADER_H
+#define DURATION_HEADER_H
+
+// Forward declarations
+
+namespace utils {
+
+  //! This class represents time durations.
+  /*! 
+   *  Internally, a Duration is a 64-bit signed integer.
+   *  A Duration can be positive, negative or zero.
+   *
+   *  Durations can be compared or used to modify a DateTime.
+   *  The differece between two DateTimes is a Duration.
+   *
+   *  Durations can be input or output (including via the stream operators
+   *  <tt>>></tt> and  <tt><<</tt>) as retricted ISO 8601
+   *  duration strings:
+   *  - [-]P[dD][T[hH][mM][sS]]
+   *
+   *  Here, square brackets indicate optional content, and "d", "h", "m" and
+   *  "s" represent  non-negative integers giving the number of days, hours
+   *  minutes and seconds in the Duration. At least one of these quantities
+   *  must be present. If "T" appears, then at least one of H, M or S must
+   *  be present. Negative durations (which are not part of ISO 8601) are
+   *  represented by an initial "-".
+   *
+   *  For example, the duration "1 day 12 hours and 5 minutes" could be
+   *  represented as any of:
+   *  - P1DT12H5M
+   *  - P1DT12H300S
+   *  - PT36H300S
+   *  - PT2165M
+   *  - PT129900S
+   *
+   *  On output, the first format will be used.
+   */
+
+  class Duration {
+
+//! << and >> can be used to output and set Durations as ISO 8601 strings
+  friend std::ostream& operator<<(std::ostream&, const Duration&);
+  friend std::istream& operator>>(std::istream&, Duration&);
+
+  public:
+    Duration();
+    Duration(const int64_t);
+    Duration(const std::string &);
+
+    //! Set the duration from an ISO 8601 duration: [-]P[dD]T[hH][mM][sS]
+    void set(const std::string &);
+
+    //! Convert a duration to an integer number of seconds.
+    int64_t toSeconds () const;
+
+    //! Convert a Duration to an ISO 8601 duration: [-]P[dD]T[hH][mM][sS]
+    std::string toString() const;
+
+    //! Comparison operators
+    bool operator==(const Duration& other) const;
+    bool operator!=(const Duration& other) const;
+    bool operator<(const Duration& other) const;
+    bool operator<=(const Duration& other) const;
+    bool operator>(const Duration& other) const;
+    bool operator>=(const Duration& other) const;
+
+    //! Negate a duration
+    void negate() {seconds_ = -seconds_;}
+
+    //! Arithmetic operators
+    int operator%(const Duration& other) const;
+
+  private:
+  // -- Copy allowed
+  //  Example(const Example&); -- default shallow copy is OK
+  //  Example& operator=(const Example&); -- default shallow copy is OK
+  
+  std::string eatDigits(std::istream &);
+
+
+    int64_t seconds_;  // 32-bit int overflows at ~68 years
+
+  };
+
+} //namespace utils
+#endif
diff --git a/odb_api/src/odb_api/EmbeddedCodeExpression.cc b/odb_api/src/odb_api/EmbeddedCodeExpression.cc
new file mode 100755
index 0000000..b2ee6d9
--- /dev/null
+++ b/odb_api/src/odb_api/EmbeddedCodeExpression.cc
@@ -0,0 +1,85 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLTable.h"
+#include "odb_api/EmbeddedCodeExpression.h"
+#include "odb_api/StringTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+const odb::sql::type::SQLType* EmbeddedCodeExpression::type() const { return &odb::sql::type::SQLType::lookup("string"); }
+
+EmbeddedCodeExpression::EmbeddedCodeExpression(const std::string& text)
+: text_(text)
+{}
+
+EmbeddedCodeExpression::EmbeddedCodeExpression(const EmbeddedCodeExpression& o)
+: text_(o.text_)
+{}
+
+void EmbeddedCodeExpression::expandStars(const std::vector<SQLTable*>& tables, expression::Expressions& e)
+{
+    std::ostream& L(Log::debug());
+
+	if (! StringTool::isColumnRegex(text_))
+	{
+        NOTIMP;
+		//e.push_back(this);
+		return;
+	}
+
+	unsigned int matched (0);
+	for(std::vector<SQLTable*>::const_iterator j (tables.begin());  j != tables.end(); ++j)
+	{
+		SQLTable* table (*j);
+		std::vector<std::string> names (table->columnNames());
+		for(size_t i (0); i < names.size(); ++i)
+		{
+			const std::string& name (names[i]);
+			if (! StringTool::matchEx(text_, name))
+			{
+				L << "EmbeddedCodeExpression::expandStars: skip '" << name << "'" << std::endl;
+				continue;
+			}
+			
+			L << "EmbeddedCodeExpression::expandStars: adding '" << name << "'" << std::endl;
+			++matched;
+			e.push_back(new ColumnExpression(name, table));
+		}
+	}
+	if (! matched)
+		throw eckit::UserError(std::string("No columns matching regex '") + text_ + "' found.");
+	delete this;
+}
+
+SQLExpression* EmbeddedCodeExpression::clone() const { return new EmbeddedCodeExpression(text_); }
+
+EmbeddedCodeExpression::~EmbeddedCodeExpression() {}
+
+double EmbeddedCodeExpression::eval(bool& missing) const { NOTIMP; return 0 /*whatever*/; }
+
+void EmbeddedCodeExpression::prepare(SQLSelect& sql) {}
+
+void EmbeddedCodeExpression::cleanup(SQLSelect& sql) {}
+
+void EmbeddedCodeExpression::output(std::ostream& s) const { s << text_; }
+
+void EmbeddedCodeExpression::print(std::ostream& s) const { s << "'" << text_ << "'"; }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb 
diff --git a/odb_api/src/odb_api/EmbeddedCodeExpression.h b/odb_api/src/odb_api/EmbeddedCodeExpression.h
new file mode 100755
index 0000000..3719c1d
--- /dev/null
+++ b/odb_api/src/odb_api/EmbeddedCodeExpression.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File EmbeddedCodeExpression.h
+// Piotr Kuchta - ECMWF Septembet 2015
+
+#ifndef EmbeddedCodeExpression_H
+#define EmbeddedCodeExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class EmbeddedCodeExpression : public SQLExpression {
+public:
+	EmbeddedCodeExpression(const std::string&);
+	EmbeddedCodeExpression(const EmbeddedCodeExpression&);
+	~EmbeddedCodeExpression(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	EmbeddedCodeExpression& operator=(const EmbeddedCodeExpression&);
+
+	std::string text_;
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void expandStars(const std::vector<SQLTable*>&, expression::Expressions&);
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+
+	const type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	virtual bool isConstant() const { return false; }
+	virtual bool isNumber() const { return false; }
+	virtual void output(std::ostream& s) const;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/EmbeddedCodeParser.cc b/odb_api/src/odb_api/EmbeddedCodeParser.cc
new file mode 100644
index 0000000..4bcb3fa
--- /dev/null
+++ b/odb_api/src/odb_api/EmbeddedCodeParser.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "odb_api/EmbeddedCodeParser.h"
+#include "odb_api/Reader.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/Tracer.h"
+#include "odb_api/SQLTable.h"
+#include <string.h>
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+std::vector<odb::sql::SQLTable*> EmbeddedCodeParser::getFromTables(const string& text, const string& database, odb::sql::SQLSession& session, ecml::ExecutionContext* context)
+{
+    ASSERT(context);
+
+    ecml::Values values (context->execute(text));
+
+    Log::debug() << "EmbeddedCodeParser: values: " << values->str() <<  std::endl;
+
+    std::vector<odb::sql::SQLTable*> tables;
+
+    eckit::MultiHandle* mh (new eckit::MultiHandle);
+
+    for (ecml::Request e (values); e; e = e->rest())
+    {
+        string descriptor (e->value()->text());
+
+        Log::debug() << "EmbeddedCodeParser: descriptor: " << descriptor << std::endl;
+
+        if (e->value()->rest()) 
+            throw UserError("element on the list returned by embedded code is not a single value");
+
+        (*mh) += ecml::DataHandleFactory::openForRead(descriptor);
+    }
+    mh->openForRead();
+    odb::sql::SQLTable* t (session.openDataHandle(*mh));
+    tables.push_back(t);
+    return tables;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/EmbeddedCodeParser.h b/odb_api/src/odb_api/EmbeddedCodeParser.h
new file mode 100644
index 0000000..f581264
--- /dev/null
+++ b/odb_api/src/odb_api/EmbeddedCodeParser.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef EmbeddedCodeParser_H
+#define EmbeddedCodeParser_H
+
+#include <vector>
+#include <string>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+
+#include "odb_api/SQLSession.h"
+
+#include "SQLTable.h"
+
+namespace ecml { class ExecutionContext; }
+
+namespace odb {
+
+namespace sql { class SQLTable; }
+
+class EmbeddedCodeParser {
+public:
+    static std::vector<odb::sql::SQLTable*> getFromTables(const std::string&, const std::string&, odb::sql::SQLSession&, ecml::ExecutionContext* context);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/Endian.h b/odb_api/src/odb_api/Endian.h
new file mode 100755
index 0000000..15c5df5
--- /dev/null
+++ b/odb_api/src/odb_api/Endian.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Endian.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef Endian_H
+#define Endian_H
+
+#include "odb_api_config.h"
+
+namespace odb {
+
+template<class T> struct Swap {
+	
+	static const int half = sizeof(T) >> 1;
+	static const int last = sizeof(T) - 1;
+	T operator()(T v)
+	{
+		unsigned char *p = (unsigned char*)&v;
+		for(int i = 0; i < half ; i++) std::swap(p[i],p[last-i]);
+		return v;
+	}
+};
+
+// xlc needs this,
+// others will complain about double initialization
+#if defined( __xlC__ )
+template<class T> const int Swap<T>::half = sizeof(T) >> 1;
+template<class T> const int Swap<T>::last = sizeof(T) - 1;
+#endif
+
+class Endian {
+public:
+
+#ifdef EC_LITTLE_ENDIAN
+	template<class T> static T transform(T x)  { return Swap<T>()(x); }
+#else
+	template<class T> static T transform(T x)  { return x; }
+#endif
+
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Environment.cc b/odb_api/src/odb_api/Environment.cc
new file mode 100755
index 0000000..6c8f39b
--- /dev/null
+++ b/odb_api/src/odb_api/Environment.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Environment.h"
+#include "odb_api/SQLTable.h"
+
+namespace odb {
+namespace sql {
+
+Environment::Environment(const SortedTables::iterator ti)
+: tablesIterator_(ti), table_(0), cursor_(0)
+{}
+
+SelectOneTable*& Environment::table() { return table_; }
+
+SQLTableIterator*& Environment::cursor() { return cursor_; }
+
+Environment::~Environment() { delete cursor_; }
+
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/Environment.h b/odb_api/src/odb_api/Environment.h
new file mode 100755
index 0000000..6b4f420
--- /dev/null
+++ b/odb_api/src/odb_api/Environment.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Environment_H
+#define Environment_H
+
+#include "odb_api/SelectOneTable.h"
+
+namespace odb {
+namespace sql {
+
+// Forward declarations
+
+class SQLTableIterator;
+
+struct Environment {
+	Environment (const SortedTables::iterator);
+	~Environment ();
+
+	SelectOneTable*& table();
+	SQLTableIterator*& cursor();
+
+	void print(std::ostream& s) const;
+
+	const SortedTables::iterator tablesIterator_;
+private:
+	SelectOneTable* table_;
+	SQLTableIterator* cursor_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/EqRegionCache.cc b/odb_api/src/odb_api/EqRegionCache.cc
new file mode 100755
index 0000000..8dce1f6
--- /dev/null
+++ b/odb_api/src/odb_api/EqRegionCache.cc
@@ -0,0 +1,952 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/log/Log.h"
+#include "odb_api/EqRegionCache.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+#define regions(i,j,k)   regs  [ ((k)-1)*dim*2     + ((j)-1)*dim     + (i) - 1 ]
+#define regions_1(i,j,k) regs_1[ ((k)-1)*(dim-1)*2 + ((j)-1)*(dim-1) + (i) - 1 ]
+#define region(i,j) reg[((j)-1)*dim + (i) - 1]
+
+#define swap(T, vi, vj) { register T temp = vi; vi = vj; vj = temp; }
+
+extern double mfmod(double x,double y);
+
+//==============================================================================
+EqRegionCache::EqRegionCache()
+//==============================================================================
+: RegionCache()
+{
+}
+
+//==============================================================================
+EqRegionCache::~EqRegionCache() {}
+//==============================================================================
+
+//==============================================================================
+int EqRegionCache::gcd(int a, int &b)
+//==============================================================================
+{
+  int m = ABS(a);
+  int n = ABS(b);
+  if ( m > n ) swap(int, m, n);
+  return (m == 0) ? n : gcd(n%m, m);
+}
+
+
+//==============================================================================
+void EqRegionCache::bot_cap_region(int &dim, double &a_cap, double reg[])
+//==============================================================================
+{
+    //
+    // An array of two points representing the bottom cap of radius a_cap as a region.
+    //
+  if ( dim == 1 ) {
+    region(1,1)=piconst::two_pi-a_cap;
+    region(1,2)=piconst::two_pi;
+  }
+  else if ( dim == 2 ) {
+      // sphere_region_1 = sphere_region(dim-1);
+      // region = [[sphere_region_1(:,1); pi-a_cap],[sphere_region_1(:,2); pi]];
+    region(1,1)=0;
+    region(1,2)=piconst::two_pi;
+    region(2,1)=piconst::pi-a_cap;
+    region(2,2)=piconst::pi;
+  }
+  else {
+    Log::info() << "bot_cap_region: dim > 2 not supported";
+  }
+}
+
+//==============================================================================
+double EqRegionCache::circle_offset(int &n_top, int &n_bot)
+//==============================================================================
+{
+    //
+    //CIRCLE_OFFSET Try to maximize minimum distance of center points for S^2 collars
+    //
+    // Given n_top and n_bot, calculate an offset.
+    //
+    // The values n_top and n_bot represent the numbers of
+    // equally spaced points on two overlapping circles.
+    // The offset is given in multiples of whole rotations, and
+    // consists of three parts;
+    // 1) Half the difference between a twist of one sector on each of bottom and top.
+    // This brings the centre points into alignment.
+    // 2) A rotation which will maximize the minimum angle between
+    // points on the two circles.
+    //
+  double co =
+    (double)(1/(double)(n_bot) - 1/(double)(n_top))/2 +
+    (double)(gcd(n_top,n_bot))/(2*(double)(n_top)*(double)(n_bot));
+  return co;
+}
+
+//==============================================================================
+void EqRegionCache::cap_colats(int &dim, int &n, int &n_collars, double &c_polar,
+       const int n_regions[/* n_collars+2 */],
+       double c_caps[/* n_collars+2 */])
+//==============================================================================
+{
+    //
+    //CAP_COLATS Colatitudes of spherical caps enclosing cumulative sum of regions
+    //
+    // Given dim, N, c_polar and n_regions, determine c_caps,
+    // an increasing list of colatitudes of spherical caps which enclose the same area
+    // as that given by the cumulative sum of regions.
+    // The number of elements is n_collars+2.
+    // c_caps[1] is c_polar.
+    // c_caps[n_collars+1] is Pi-c_polar.
+    // c_caps[n_collars+2] is Pi.
+    //
+
+  int subtotal_n_regions, collar_n;
+  double ideal_region_area = area_of_ideal_region(dim,n);
+  c_caps[0] = c_polar;
+  subtotal_n_regions = 1;
+  double area;
+  for (collar_n = 1; collar_n <= n_collars; collar_n++) {
+    subtotal_n_regions += n_regions[collar_n];
+    area=subtotal_n_regions*ideal_region_area;
+    c_caps[collar_n] = sradius_of_cap(dim,area);
+  }
+  c_caps[n_collars+1] = piconst::pi;
+}
+
+//==============================================================================
+void EqRegionCache::round_to_naturals(int &n, int &n_collars,
+          const double r_regions[/* n_collars+2 */],
+          int n_regions[/* n_collars+2 */])
+//==============================================================================
+{
+    //
+    //ROUND_TO_NATURALS Round off a given list of numbers of regions
+    //
+    // Given N and r_regions, determine n_regions,
+    // a list of the natural number of regions in each collar and the polar caps.
+    // This list is as close as possible to r_regions, using rounding.
+    // The number of elements is n_collars+2.
+    // n_regions[1] is 1.
+    // n_regions[n_collars+2] is 1.
+    // The sum of n_regions is N.
+    //
+  int j;
+  double discrepancy = 0;
+  for (j=0; j<n_collars+2; j++) {
+    n_regions[j] = NINT(r_regions[j]+discrepancy);
+    discrepancy += r_regions[j]-n_regions[j];
+  }
+}
+
+//==============================================================================
+double EqRegionCache::area_of_cap(int &dim, double &s_cap)
+//==============================================================================
+{
+    //AREA_OF_CAP Area of spherical cap
+    //
+    //Syntax
+    // area = area_of_cap(dim, s_cap);
+    //
+    //Description
+    // AREA = AREA_OF_CAP(dim, S_CAP) sets AREA to be the area of an S^dim spherical
+    // cap of spherical radius S_CAP.
+    //
+    // The argument dim must be a positive integer.
+    // The argument S_CAP must be a real number or an array of real numbers.
+    // The result AREA will be an array of the same size as S_CAP.
+    //
+  double area;
+  if ( dim == 1 ) {
+    area = 2 * s_cap;
+  }
+  else if ( dim == 2 ) {
+    area = piconst::four_pi * pow(sin(s_cap/2),2);
+  }
+  else {
+    Log::info() << "area_of_cap: dim > 2 not supported";
+  }
+  return area;
+}
+
+//==============================================================================
+double EqRegionCache::area_of_collar(int &dim, double a_top, double a_bot)
+//==============================================================================
+{
+     //
+     //AREA_OF_COLLAR Area of spherical collar
+     //
+     //Syntax
+     // area = area_of_collar(dim, a_top, a_bot);
+     //
+     //Description
+     // AREA = AREA_OF_COLLAR(dim, A_TOP, A_BOT) sets AREA to be the area of
+     // an S^dim spherical collar specified by A_TOP, A_BOT, where
+     // A_TOP is top (smaller) spherical radius,
+     // A_BOT is bottom (larger) spherical radius.
+     //
+     // The argument dim must be a positive integer.
+     // The arguments A_TOP and A_BOT must be real numbers or arrays of real numbers,
+     // with the same array size.
+     // The result AREA will be an array of the same size as A_TOP.
+     //
+  return area_of_cap(dim, a_bot) - area_of_cap(dim, a_top);
+}
+
+//==============================================================================
+void EqRegionCache::ideal_region_list(int &dim, int &n, double &c_polar,
+          int &n_collars, double r_regions[/* n_collars+2 */])
+//==============================================================================
+{
+    //
+    //IDEAL_REGION_LIST The ideal real number of regions in each zone
+    //
+    // List the ideal real number of regions in each collar, plus the polar caps.
+    //
+    // Given dim, N, c_polar and n_collars, determine r_regions,
+    // a list of the ideal real number of regions in each collar,
+    // plus the polar caps.
+    // The number of elements is n_collars+2.
+    // r_regions[1] is 1.
+    // r_regions[n_collars+2] is 1.
+    // The sum of r_regions is N.
+    //
+  r_regions[0] = 1;
+  if ( n_collars > 0 ) {
+      //
+      // Based on n_collars and c_polar, determine a_fitting,
+      // the collar angle such that n_collars collars fit between the polar caps.
+      //
+    int collar_n;
+    double a_fitting = (piconst::pi-2*c_polar)/n_collars;
+    double ideal_region_area = area_of_ideal_region(dim,n);
+    for (collar_n=1; collar_n<=n_collars; collar_n++) {
+      double ideal_collar_area = area_of_collar(dim, c_polar+(collar_n-1)*a_fitting,
+                        c_polar+collar_n*a_fitting);
+      r_regions[collar_n] = ideal_collar_area / ideal_region_area;
+    }
+  } /* if ( n_collars > 0 ) */
+  r_regions[n_collars+1] = 1;
+}
+
+//==============================================================================
+double EqRegionCache::area_of_ideal_region(int &dim, int &n)
+//==============================================================================
+{
+    //
+    // AREA = AREA_OF_IDEAL_REGION(dim,N) sets AREA to be the area of one of N equal
+    // area regions on S^dim, that is 1/N times AREA_OF_SPHERE(dim).
+    //
+    // The argument dim must be a positive integer.
+    // The argument N must be a positive integer or an array of positive integers.
+    // The result AREA will be an array of the same size as N.
+    //
+  double area = area_of_sphere(dim)/n;
+  return area;
+}
+
+
+//==============================================================================
+int EqRegionCache::num_collars(int &n, double &c_polar, double a_ideal)
+//==============================================================================
+{
+    //
+    //NUM_COLLARS The number of collars between the polar caps
+    //
+    // Given N, an ideal angle, and c_polar,
+    // determine n_collars, the number of collars between the polar caps.
+    //
+  int num_c;
+    // n_collars = zeros(size(N));
+    // enough = (N > 2) & (a_ideal > 0);
+    // n_collars(enough) = max(1,round((pi-2*c_polar(enough))./a_ideal(enough)));
+  bool enough = ((n > 2) && (a_ideal > 0)) ? true : false;
+  if ( enough ) {
+    num_c = NINT((piconst::pi-2*c_polar)/a_ideal);
+    if (num_c < 1) num_c = 1;
+  }
+  else {
+    num_c = 0;
+  }
+  return num_c;
+}
+
+//==============================================================================
+double EqRegionCache::ideal_collar_angle(int &dim, int &n)
+//==============================================================================
+{
+    //
+    //IDEAL_COLLAR_ANGLE The ideal angle for spherical collars of an EQ partition
+    //
+    //Syntax
+    // angle = ideal_collar_angle(dim,N);
+    //
+    //Description
+    // ANGLE = IDEAL_COLLAR_ANGLE(dim,N) sets ANGLE to the ideal angle for the
+    // spherical collars of an EQ partition of the unit sphere S^dim into N regions.
+    //
+    // The argument dim must be a positive integer.
+    // The argument N must be a positive integer or an array of positive integers.
+    // The result ANGLE will be an array of the same size as N.
+    //
+  return pow(area_of_ideal_region(dim,n),(1/(double)(dim)));
+}
+
+//==============================================================================
+double EqRegionCache::my_gamma(double &x)
+//==============================================================================
+{
+  const double p0  =  0.999999999999999990e0;
+  const double p1  = -0.422784335098466784e0;
+  const double p2  = -0.233093736421782878e0;
+  const double p3  =  0.191091101387638410e0;
+  const double p4  = -0.024552490005641278e0;
+  const double p5  = -0.017645244547851414e0;
+  const double p6  =  0.008023273027855346e0;
+  const double p7  = -0.000804329819255744e0;
+  const double p8  = -0.000360837876648255e0;
+  const double p9  =  0.000145596568617526e0;
+  const double p10 = -0.000017545539395205e0;
+  const double p11 = -0.000002591225267689e0;
+  const double p12 =  0.000001337767384067e0;
+  const double p13 = -0.000000199542863674e0;
+  int n = NINT(x - 2);
+  double w = x - (n + 2);
+  double y = ((((((((((((p13 * w + p12) * w + p11) * w + p10) *
+              w + p9) * w + p8) * w + p7) * w + p6) * w + p5) *
+         w + p4) * w + p3) * w + p2) * w + p1) * w + p0;
+  int k;
+  if (n > 0) {
+    w = x - 1;
+    for (k = 2; k <= n; k++) w *= (x - k);
+  }
+  else {
+    int nn = -n - 1;
+    w = 1;
+    for (k=0; k <= nn; k++) y *= (x + k);
+  }
+  return w / y;
+}
+
+
+//==============================================================================
+double EqRegionCache::area_of_sphere(int &dim)
+//==============================================================================
+{
+    //
+    // AREA = AREA_OF_SPHERE(dim) sets AREA to be the area of the sphere S^dim
+    //
+  double power = (double)(dim+1)/(double)2;
+  return 2*pow(piconst::pi,power)/my_gamma(power);
+}
+
+
+
+//==============================================================================
+double EqRegionCache::sradius_of_cap(int &dim, double & area)
+//==============================================================================
+{
+    //
+    // S_CAP = SRADIUS_OF_CAP(dim, AREA) returns the spherical radius of
+    // an S^dim spherical cap of area AREA.
+    //
+  double radius = 0;
+  if ( dim == 1 ) {
+    radius = area/2;
+  }
+  else if ( dim == 2 ) {
+    radius = 2*asin(sqrt(area/piconst::pi)/2);
+  }
+  else {
+    Log::info() << "sradius_of_cap: dim > 2 not supported";
+  }
+  return radius;
+}
+
+//==============================================================================
+double EqRegionCache::polar_colat(int & dim, int & n)
+//==============================================================================
+{
+   //
+   // Given dim and N, determine the colatitude of the North polar spherical cap.
+   //
+  double colat;
+  if ( n == 1 ) colat=piconst::pi;
+  else if ( n == 2 ) colat=piconst::half_pi;
+  else if ( n > 2 ) {
+    double area = area_of_ideal_region(dim,n);
+    colat=sradius_of_cap(dim,area);
+  }
+  return colat;
+}
+
+//==============================================================================
+void EqRegionCache::top_cap_region(int &dim, double & a_cap,double reg[])
+//==============================================================================
+{
+    //
+    // An array of two points representing the top cap of radius a_cap as a region.
+    //
+  if ( dim == 1 ) {
+    region(1,1)=0;
+    region(1,2)=a_cap;
+  }
+  else if ( dim == 2 ) {
+      // sphere_region_1 = sphere_region(dim-1);
+      // region = [[sphere_region_1(:,1); 0], [sphere_region_1(:,2); a_cap]];
+    region(1,1)=0;
+    region(1,2)=piconst::two_pi;
+    region(2,1)=0;
+    region(2,2)=a_cap;
+  }
+  else {
+    Log::info() << "top_cap_region: dim > 2 not supported";
+  }
+
+}
+
+//==============================================================================
+void EqRegionCache::sphere_region(int &dim, double reg[])
+//==============================================================================
+{
+  //    
+  //   An array of two points representing S^dim as a region.
+  //    
+  if ( dim == 1 ) {
+    region(1,1)=0;
+    region(1,2)=piconst::two_pi;
+  }
+  else if ( dim == 2 ) {
+  // 
+  //  sphere_region_1 = sphere_region(dim-1);
+  //  region = [[sphere_region_1(:,1); 0],[sphere_region_1(:,2); pi]] ;
+  //
+    region(1,1)=0;
+    region(1,2)=piconst::two_pi;
+    region(2,1)=0;
+    region(2,2)=piconst::pi;
+  }
+  else {
+    Log::info() << "sphere_region: dim > 2 not supported";
+  }
+}
+
+//==============================================================================
+void EqRegionCache::eq_caps(int &dim, int &n, 
+	double s_cap[/* n */],
+	int n_regions[/* n */],
+	int *N_collars)
+//==============================================================================
+{
+  int j;
+  double c_polar;
+  int n_collars = *N_collars;
+
+  if ( n == 1 ) {
+      //
+      // We have only one region, which must be the whole sphere.
+      //
+    s_cap[0]=piconst::pi;
+    n_regions[0]=1;
+    *N_collars=0;
+    return;
+  }
+
+  if ( dim == 1 ) {
+      //
+      // We have a circle. Return the angles of N equal sectors.
+      //
+      // sector = 1:N;
+      //
+      // Make dim==1 consistent with dim>1 by
+      // returning the longitude of a sector enclosing the
+      // cumulative sum of arc lengths given by summing n_regions.
+      //
+      // s_cap = sector*two_pi/N;
+      // n_regions = ones(size(sector));
+    for (j=0; j<n; j++) {
+      s_cap[j]=(j+1)*piconst::two_pi/(double)n;
+      n_regions[j]=1;
+    }
+    *N_collars=0;
+    return;
+  }
+
+  if ( dim == 2 ) {
+      //
+      // Given dim and N, determine c_polar
+      // the colatitude of the North polar spherical cap.
+      //
+
+    c_polar = polar_colat(dim,n);
+
+      //
+      // Given dim and N, determine the ideal angle for spherical collars.
+      // Based on N, this ideal angle, and c_polar,
+      // determine n_collars, the number of collars between the polar caps.
+      //
+
+    n_collars = *N_collars = num_collars(n,c_polar,ideal_collar_angle(dim,n));
+
+      //
+      // Given dim, N, c_polar and n_collars, determine r_regions,
+      // a list of the ideal real number of regions in each collar,
+      // plus the polar caps.
+      // The number of elements is n_collars+2.
+      // r_regions[1] is 1.
+      // r_regions[n_collars+2] is 1.
+      // The sum of r_regions is N.
+      //
+      // r_regions = ideal_region_list(dim,N,c_polar,n_collars)
+
+    {
+      double *r_regions = NULL;
+      r_regions = new double [n_collars+2];
+      ideal_region_list(dim,n,c_polar,n_collars,r_regions);
+      
+	//
+	// Given N and r_regions, determine n_regions,
+	// a list of the natural number of regions in each collar and
+	// the polar caps.
+	// This list is as close as possible to r_regions.
+	// The number of elements is n_collars+2.
+	// n_regions[1] is 1.
+	// n_regions[n_collars+2] is 1.
+	// The sum of n_regions is N.
+	//
+	// n_regions = round_to_naturals(N,r_regions)
+      
+      round_to_naturals(n,n_collars,r_regions,n_regions);
+      delete [] r_regions;
+    }
+    
+      //
+      // Given dim, N, c_polar and n_regions, determine s_cap,
+      // an increasing list of colatitudes of spherical caps which enclose the same area
+      // as that given by the cumulative sum of regions.
+      // The number of elements is n_collars+2.
+      // s_cap[1] is c_polar.
+      // s_cap[n_collars+1] is Pi-c_polar.
+      // s_cap[n_collars+2] is Pi
+      // 
+      // s_cap = cap_colats(dim,N,c_polar,n_regions)
+
+    cap_colats(dim,n,n_collars,c_polar,n_regions,s_cap);
+  }
+}
+
+
+//==============================================================================
+void EqRegionCache::eq_regions(int dim, int n, double regs[])
+//==============================================================================
+{
+  //
+  // 
+  //    REGIONS = EQ_REGIONS(dim,N) uses the recursive zonal equal area sphere
+  //    partitioning algorithm to partition S^dim (the unit sphere in dim+1
+  //    dimensional space) into N regions of equal area and small diameter.
+  //   
+  //    The arguments dim and N must be positive integers.
+  //   
+  //    The result REGIONS is a (dim by 2 by N) array, representing the regions
+  //    of S^dim. Each element represents a pair of vertex points in spherical polar
+  //    coordinates.
+  //   
+  //    Each region is defined as a product of intervals in spherical polar
+  //    coordinates. The pair of vertex points regions(:,1,n) and regions(:,2,n) give
+  //    the lower and upper limits of each interval.
+     
+  double *regs_1 = NULL;
+  double *s_cap = NULL, r_top[2], r_bot[2], c_top, c_bot, offset;
+  int *n_regions = NULL, collar_n, n_collars, n_in_collar, region_1_n, region_n;
+  int i, j, k;
+
+  s_cap = new double[n+2];
+  n_regions = new int[n+2];
+
+  if (n == 1) {
+    //
+    // 
+    //  We have only one region, which must be the whole sphere.
+    // 
+    //  regions(:,:,1)=sphere_region(dim)
+    //
+    sphere_region(dim,&regions(1,1,1));
+  } else {
+    //
+    //  
+    //  Start the partition of the sphere into N regions by partitioning
+    //  to caps defined in the current dimension.
+    //  
+    //  [s_cap, n_regions] = eq_caps(dim,N)
+    //
+
+
+    eq_caps(dim,n,s_cap,n_regions,&n_collars);
+
+    //
+    //   
+    //  s_cap is an increasing list of colatitudes of the caps.
+    // 
+    //
+    if ( dim == 1 ) {
+      //
+      //   
+      //    We have a circle and s_cap is an increasing list of angles of sectors.
+      //   
+      //   
+      //    Return a list of pairs of sector angles.
+      //   
+      //
+      for (k=1; k<=n; k++)
+	for (j=1; j<=2; j++)
+	  for (i=1; i<=dim; i++)
+	    regions(i,j,k) = 0;
+
+      for (k=2; k<=n; k++)
+	regions(1,1,k) = s_cap[k-2];
+
+      for (k=1; k<=n; k++) 
+	regions(1,2,k) = s_cap[k-1];
+      
+    } else {
+
+      //   
+      //    We have a number of zones: two polar caps and a number of collars.
+      //    n_regions is the list of the number of regions in each zone.
+      //   
+      //    n_collars = size(n_regions,2)-2
+      //   
+      //    Start with the top cap
+      //   
+      //    regions(:,:,1) = top_cap_region(dim,s_cap(1))
+      //
+
+      top_cap_region(dim,s_cap[0],&regions(1,1,1));
+      region_n = 1;
+      
+      //
+      //    
+      //    Determine the dim-regions for each collar
+      //   
+      //
+      if ( dim == 2 ) offset=0;
+      for (collar_n=1; collar_n<=n_collars; collar_n++) {
+	int size_regions_1_3;
+	//
+	// c_top is the colatitude of the top of the current collar.
+	// 
+        c_top = s_cap[collar_n-1];
+	
+	//
+	// c_bot is the colatitude of the bottom of the current collar.
+	// 
+        c_bot = s_cap[collar_n];
+	
+	//
+	// n_in_collar is the number of regions in the current collar.
+	//
+	n_in_collar = n_regions[collar_n];
+	
+	// 
+	// The top and bottom of the collar are small (dim-1)-spheres,
+	// which must be partitioned into n_in_collar regions.
+	// Use eq_regions recursively to partition the unit (dim-1)-sphere.
+	// regions_1 is the resulting list of (dim-1)-region pairs.
+	//
+	// regions_1 = eq_regions(dim-1,n_in_collar)
+	// 
+        size_regions_1_3 = n_in_collar;
+	
+	regs_1 = new double[(dim-1)*2*(n-1)];
+	eq_regions(dim-1, n_in_collar, regs_1);
+	//
+	//  Given regions_1, determine the dim-regions for the collar.
+	//  Each element of regions_1 is a (dim-1)-region pair for the (dim-1)-sphere.
+	//
+	if ( dim == 2 ) {
+	  //
+	  //  The (dim-1)-sphere is a circle
+	  //  Offset each sector angle by an amount which accumulates over
+	  //  each collar.
+	  //
+	  for (region_1_n=1; region_1_n<=n_in_collar; region_1_n++) {
+	    //
+	    //    Top of 2-region
+	    //    The first angle is the longitude of the top of
+	    //    the current sector of regions_1, and
+	    //    the second angle is the top colatitude of the collar
+	    //   
+	    //    r_top = [mod(regions_1(1,1,region_1_n)+two_pi*offset,two_pi); c_top]
+	    //
+	    
+	    r_top[0]=mfmod(regions_1(1,1,region_1_n)+piconst::two_pi*offset,piconst::two_pi);
+	    r_top[1]=c_top;
+	    //
+	    //   Bottom of 2-region
+	    //   The first angle is the longitude of the bottom of
+	    //   the current sector of regions_1, and
+	    //   the second angle is the bottom colatitude of the collar
+	    //  
+	    //   r_bot = [mod(regions_1(1,2,region_1_n)+two_pi*offset,two_pi); c_bot]
+	    //
+	    
+	    r_bot[0]=mfmod(regions_1(1,2,region_1_n)+piconst::two_pi*offset,piconst::two_pi);
+	    r_bot[1]=c_bot;
+	    if ( r_bot[0] <= r_top[0] )
+	      r_bot[0] += piconst::two_pi;
+	    
+	    region_n++;
+	    /* regions(:,:,region_n) = [r_top,r_bot] */
+	    regions(1,1,region_n) = r_top[0];
+	    regions(1,2,region_n) = r_bot[0];
+	    regions(2,1,region_n) = r_top[1];
+	    regions(2,2,region_n) = r_bot[1];
+	  }
+	  //
+	  //  Given the number of sectors in the current collar and
+	  //  in the next collar, calculate the next offset.
+	  //  Accumulate the offset, and force it to be a number between 0 and 1.
+	  //  
+	  
+	  offset += circle_offset(n_in_collar,n_regions[1+collar_n]);
+	  offset -= floor(offset);
+	} else {
+	  for (region_1_n=1; region_1_n <= size_regions_1_3; region_1_n++) {
+	    region_n++;
+	    
+	    //
+	    //    Dim-region;
+	    //    The first angles are those of the current (dim-1) region of regions_1.
+	    //
+	    
+	    for (j=1; j<=2; j++)
+	      for (i=1; i<=dim-1; i++)
+	        regions(i,j,region_n) = regions_1(i,j,region_1_n);
+	    
+	    //
+	    //    The last angles are the top and bottom colatitudes of the collar.
+	    //   
+	    //    regions(dim,:,region_n) = [c_top,c_bot]
+	    //
+	    
+	    regions(dim,1,region_n) = c_top;
+	    regions(dim,2,region_n) = c_bot;
+	  }
+	}
+
+        delete [] regs_1;
+
+      }
+      //
+      //  End with the bottom cap.
+      // 
+      //  regions(:,:,N) = bot_cap_region(dim,s_cap(1))
+      //
+      bot_cap_region(dim,s_cap[0],&regions(1,1,n));
+    }
+  }
+  delete [] s_cap;
+  delete [] n_regions;
+}
+
+//==============================================================================
+double EqRegionCache::eq_area(const double &rn)
+//==============================================================================
+{
+ int n = rn;
+  double eq_area = (sphere_area/n);
+  return eq_area;
+}
+
+//==============================================================================
+double EqRegionCache::eq_n(const double &resol) 
+//==============================================================================
+{
+  double dres = ( resol <  min_resol ? min_resol : resol ) * piconst::pi_over_180;
+  double eq_area = dres * dres ; /* Actually: (resol*pi/180)^2 * R^2 (approx.) */
+  int n = NINT(sphere_area/eq_area); /* Note: R^2's would have been divided away */
+  return n;
+}
+
+//==============================================================================
+double EqRegionCache::get_resol(const double & nval) 
+//==============================================================================
+{
+  return eq_n(nval);
+}
+//==============================================================================
+double EqRegionCache::eq_resol(const double &rn) 
+//==============================================================================
+{
+  double eq_area_value = eq_area(rn);
+  double resol = sqrt(eq_area_value) * piconst::recip_pi_over_180; /* In degrees ~ at Equator for small resol's */
+  if (resol < min_resol) resol = min_resol;
+  return resol;
+}
+
+//==============================================================================
+void EqRegionCache::create_cache(const double & resol, const int & n) 
+//==============================================================================
+{
+// nothing done for this resolution
+  double *regs = NULL;
+  int jb, nb = 0;
+  int nbelm = dim*2*resol;
+
+  regs = new double [nbelm];
+  eq_regions(dim,resol,&regions(1,1,1));
+
+  {
+   //   After eq_regions() function call, latitudes & longitudes are in radians
+   //   and between [ 0 .. pi ] and [ 0 .. two_pi ], respectively.
+   //
+   //   Pay special attention to cap regions (j=1 & j=n), where starting and
+   //   ending longitude has the same constant value; make them -180 and +180, respectively .
+
+    int j;
+    for (j=1; j<=n; j++) {
+      // In radians 
+      double startlon = regions(1,1,j);
+      double startlat = regions(2,1,j);
+      double endlon   = regions(1,2,j);
+      double endlat   = regions(2,2,j);
+      // shift latitudes to start from -half_pi, not 0 
+      startlat -= piconst::half_pi;
+      endlat -= piconst::half_pi;
+
+      if (j == 1 || j == n) { // cap regions 
+        endlon = startlon;
+      }
+
+      // Replace values in regions, still in radians 
+      regions(1,1,j) = startlon;
+      regions(2,1,j) = startlat;
+      regions(1,2,j) = endlon;
+      regions(2,2,j) = endlat;
+    }
+  }
+
+    //
+    //  Derive how many (nb) latitude bands we have i.e. keep track how often 
+    //  the consecutive (startlat,endlat)-pairs change their value.
+    //
+
+    int j = 1;
+    int ncnt;
+    double three_sixty = 360;
+    double last_startlat = regions(2,1,j);
+    double last_endlat   = regions(2,2,j);
+
+    nb = 1; /* Southern polar cap */
+    for (j=2; j<n; j++) { // All but caps 
+      double startlat = regions(2,1,j);
+      double endlat   = regions(2,2,j);
+      if (last_startlat == startlat && last_endlat == endlat) {
+        // No change i.e. we are still in the same latitude band 
+        continue;
+      } else {
+        // New latitude band //
+        ++nb;
+        last_startlat = startlat;
+        last_endlat   = endlat;
+      }
+    } // for (j=2; j<n; j++) 
+    if (n > 1) { // Northern polar cap 
+      ++nb;
+    }
+
+    // 
+    // We have now in total "nb" latitude bands.
+    // Lets store their starting and ending latitudes for much faster lookup.
+    //
+
+    double *latband = new double[nb + 1];
+    int *loncnt = new int [nb];
+    double *stlon = new double [nb];
+    double *deltalon = new double [nb];
+
+    jb = -1;
+
+    j = 1;
+    last_startlat = regions(2,1,j);
+    last_endlat   = regions(2,2,j);
+    loncnt[++jb] = 1;
+    latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole 
+
+    for (j=2; j<n; j++) { // All but caps 
+      double startlat = regions(2,1,j);
+      double endlat   = regions(2,2,j);
+      if (last_startlat == startlat && last_endlat == endlat) {
+        // Increase longitude count by one for this latitude band 
+        loncnt[jb]++;
+      } else {
+        // For previous latitude band ... 
+        ncnt = loncnt[jb];
+        deltalon[jb] = three_sixty/ncnt;
+        stlon[jb] = -deltalon[jb]/2; // Starting longitude at 0 GMT minus half longitudinal delta 
+        // New latitude band 
+        last_startlat = startlat;
+        last_endlat   = endlat;
+        loncnt[++jb] = 1;
+        latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole 
+      }
+    } // for (j=2; j<n; j++) 
+
+    if (n > 1) { // Northern polar cap (mapped to be the South pole, in fact) 
+      ncnt = loncnt[jb];
+      deltalon[jb] = three_sixty/ncnt;
+      stlon[jb] = -deltalon[jb]/2; // Starting longitude at 0 GMT minus half longitudinal delta 
+      j = n;
+      last_startlat = regions(2,1,j);
+      last_endlat   = regions(2,2,j);
+      loncnt[++jb] = 1;
+      latband[jb] = -R2D(last_startlat); // Reverse latitudes from S->N to N->S pole 
+    }
+
+    {
+      ncnt = loncnt[jb];
+      deltalon[jb] = three_sixty/ncnt;
+      stlon[jb] = -deltalon[jb]/2; // Starting longitude at 0 GMT minus half longitudinal delta 
+      j = n+1;
+      latband[++jb] = -R2D(last_endlat); // Reverse latitudes from S->N to N->S pole 
+    }
+
+      // Release space allocated by regs 
+
+   delete [] regs;
+
+   // Calculate mid latitudes for each band by a simple averaging process 
+
+   double *midlat = new double [nb];
+   for (jb=0; jb<nb; jb++) {
+     midlat[jb] = (latband[jb] + latband[jb+1])/2;
+   }
+   // Store in cache 
+   RegionCacheKind kind = eq_cache_kind;
+   RegionCache::put_cache(kind, resol, nb, 
+                     latband, midlat,
+                     stlon, deltalon,
+                     loncnt);
+
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/EqRegionCache.h b/odb_api/src/odb_api/EqRegionCache.h
new file mode 100755
index 0000000..8a012d3
--- /dev/null
+++ b/odb_api/src/odb_api/EqRegionCache.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file EqRegionCache.h
+/// ECMWF July 2010
+///
+/// A PARTITION OF THE UNIT SPHERE INTO REGIONS OF EQUAL AREA AND SMALL DIAMETER
+/// Algorithm by Paul Leopardi, School of Mathematics, University of South Wales
+///
+
+#ifndef EqRegionCache_H
+#define EqRegionCache_H
+
+#include "odb_api/RegionCache.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class EqRegionCache : public RegionCache {
+public:
+
+	EqRegionCache();
+
+	~EqRegionCache();
+
+private:
+// No copy allowed
+    EqRegionCache(const EqRegionCache&);
+    EqRegionCache& operator=(const EqRegionCache&);
+
+    virtual double get_resol(const double & val);
+    virtual void create_cache(const double &, const int &);
+
+    int gcd(int, int&);
+    void eq_caps(int &, int &, double [], int [], int*);
+    void bot_cap_region(int &, double &, double []);
+    double circle_offset(int &, int &);
+    void cap_colats(int &, int &, int &, double &,const int [], double []);
+    void round_to_naturals(int &, int &, const double [], int []);
+    double area_of_cap(int &, double &);
+    double area_of_collar(int &, double, double);
+    void ideal_region_list(int &, int &, double &,int &, double []);
+    double area_of_ideal_region(int &, int &);
+    int num_collars(int &, double &, double);
+    double ideal_collar_angle(int &, int &);
+    double sradius_of_cap(int &, double&);
+    double my_gamma(double &);
+    double area_of_sphere(int &);
+    double polar_colat(int &, int &);
+    double eq_area(const double &);
+    double eq_resol(const double&); 
+    double eq_n(const double&); 
+    void eq_regions(int, int, double []);
+    void sphere_region(int &, double []);
+    void top_cap_region(int &, double & ,double []);
+
+    // -- Friends
+    //friend std::ostream& operator<<(std::ostream& s,const EqRegionCache& p)
+    //	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Expressions.cc b/odb_api/src/odb_api/Expressions.cc
new file mode 100755
index 0000000..bfa1bd4
--- /dev/null
+++ b/odb_api/src/odb_api/Expressions.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+const type::SQLType* Expressions::type() const { return &type::SQLType::lookup("real"); }
+
+Expressions& Expressions::operator=(const Expressions& e)
+{
+	ExpressionsVector::operator=(e);
+	return *this;
+}
+
+//Expressions * Expressions::clone() const
+SQLExpression * Expressions::clone() const
+{
+	Expressions *r = new Expressions(this->size());
+	for (size_t i = 0; i < this->size(); ++i)
+		(*r)[i] = (*this)[i]->clone();
+
+	return r;
+}
+
+void Expressions::release()
+{
+	for (size_t i = 0; i < this->size(); ++i)
+		delete at(i);
+}
+
+void Expressions::print(std::ostream& o) const
+{
+	o << "[";
+	for (size_t i = 0; i < size(); ++i)
+	{
+		at(i)->print(o);
+		o << ",";
+	}
+	o << "]";
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Expressions.h b/odb_api/src/odb_api/Expressions.h
new file mode 100755
index 0000000..32234f4
--- /dev/null
+++ b/odb_api/src/odb_api/Expressions.h
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file Expressions.h
+/// Piotr Kuchta - ECMWF Nov 11
+
+#ifndef odb_api_Expressions_H
+#define odb_api_Expressions_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+typedef std::vector<odb::sql::expression::SQLExpression*> ExpressionsVector;
+
+class Expressions : public SQLExpression, public ExpressionsVector
+{
+public:
+	Expressions() : ExpressionsVector() {}
+	Expressions(size_t i) : ExpressionsVector(i, 0) {}
+	Expressions(size_t i, SQLExpression* e) : ExpressionsVector(i, e) {}
+
+	Expressions(const Expressions& e)
+	: SQLExpression(), ExpressionsVector(e)
+	{}
+
+	Expressions& operator=(const Expressions&);
+
+	virtual void release();
+
+	virtual void print(std::ostream& s) const;
+
+	friend std::ostream& operator<<(std::ostream& o, const Expressions& e)
+		{ e.print(o); return o; }
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+	virtual void prepare(SQLSelect&)  {}
+	virtual void cleanup(SQLSelect&)  {}
+
+	const type::SQLType* type() const;
+
+	// -- For WHERE
+	virtual double eval(bool& missing) const  { NOTIMP; }
+
+	virtual bool isConstant() const  { NOTIMP; }
+	virtual bool isNumber() const { return false; }
+	virtual bool isVector() const { return true; }
+	virtual Expressions& vector() { return *this; }
+
+	virtual SQLExpression* simplify(bool&) { return this; }
+
+	virtual SQLExpression* clone() const;
+	
+	virtual bool isAggregate() const { return false; }
+	// For select expression
+
+	virtual void output(SQLOutput&) const { return NOTIMP; }
+	virtual void partialResult() {}
+	virtual void expandStars(const std::vector<SQLTable*>&,expression::Expressions&) { NOTIMP; }
+//////////////////////////////////////////////////////////////////////////////////////
+};
+
+typedef std::vector<Expressions> VectorOfExpressions;
+//class VectorOfExpressions : public vector<Expressions> { public: ~VectorOfExpressions() { for (size_t i=0; i < size(); ++i) ; } };
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FastODA2Request.cc b/odb_api/src/odb_api/FastODA2Request.cc
new file mode 100644
index 0000000..70dec21
--- /dev/null
+++ b/odb_api/src/odb_api/FastODA2Request.cc
@@ -0,0 +1,279 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/parser/Tokenizer.h"
+#include "odb_api/GribCodes.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/ODAHandle.h"
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+
+namespace odb {
+
+template <typename T>
+FastODA2Request<T>::FastODA2Request()
+: firstMD_(0),
+  rowsNumber_(0),
+  mergeSimilarBlocks_(true)
+{}
+
+template <typename T>
+void FastODA2Request<T>::parseConfig(const std::string& s)
+{
+    ecml::Cell* r (ecml::RequestParser::parse(s, true /*debug*/));
+    r = r->value();
+
+    for (ecml::Request elt(r->rest()); elt; elt = elt->rest())
+	{
+        std::string key (elt->text());
+        ASSERT(elt->tag() == "");
+        std::string value (elt->value()->str());
+
+		eckit::Log::debug() << "parseConfig: " << key << "=" << value << std::endl;
+
+		addColumn(key, value);
+	}
+}
+
+template <typename T>
+void FastODA2Request<T>::addColumn(const std::string& keyword, const std::string& columnName)
+{
+	keywords_.push_back(keyword);
+	columnNames_.push_back(columnName);
+}
+
+template <typename T>
+bool FastODA2Request<T>::scanFile(const eckit::PathName& fileName)
+{
+	eckit::OffsetList offsets;
+	eckit::LengthList lengths;
+	std::vector<ODAHandle*> handles;
+
+	bool r = scanFile(fileName, offsets, lengths, handles);
+
+	for (size_t i = 0; i < handles.size(); ++i)
+		delete handles[i];
+	handles.clear();
+
+	return r;
+}
+
+template <typename T>
+bool FastODA2Request<T>::scanFile(const eckit::PathName& fileName, eckit::OffsetList& offsets, eckit::LengthList& lengths, std::vector<ODAHandle*>& handles)
+{
+    using eckit::Log;
+    ostream& L (Log::debug());
+
+	L << "Iterating over headers of '" << fileName << "'" <<  std::endl;
+	
+    inputFile_ = fileName;
+
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+	MDR mdReader(fileName);
+	MDR::iterator it = mdReader.begin(), end = mdReader.end();
+
+    std::auto_ptr<MetaData> currentMD(it->columns().clone());
+	rowsNumber_ = currentMD->rowsNumber();
+
+    values_ = std::vector<std::set<std::string> >(currentMD->size(), std::set<std::string>());
+	unsigned long int mds = 0;	
+	for ( ; it != end; ++it)
+	{
+		ASSERT(it->isNewDataset());
+		const MetaData &md (it->columns());
+		++mds;
+
+		eckit::Offset startOffset = (**it).blockStartOffset(), endOffset = (**it).blockEndOffset();
+		eckit::Length blockSize = endOffset - startOffset;
+
+		if (!offsets.size() || !mergeSimilarBlocks_ || !currentMD->equalsIncludingConstants(md, columnNames_))
+		{
+			L << "FastODA2Request@" << this << "::scanFile: new handle for <" << startOffset << "," << endOffset << ">" << std::endl;
+
+			ODAHandle* odaHandle = new ODAHandle(startOffset, endOffset);
+			if (! collectValues(md, *odaHandle))
+			{
+				L << "FastODA2Request@" << this << "::scanFile: collectValues returned false" << std::endl;
+				return false;
+			}
+			currentMD.reset(md.clone());
+			ASSERT(currentMD->equalsIncludingConstants(md, columnNames_));
+
+			offsets.push_back(startOffset);
+			lengths.push_back(blockSize);
+			handles.push_back(odaHandle);
+		}
+		else
+		{
+			L << "FastODA2Request@" << this << "::scanFile: append <" << startOffset << "," << endOffset << "> to existing handle" << std::endl;
+
+			ODAHandle& lastHandle = *(handles.back());
+			lastHandle.end(lastHandle.end() + blockSize);
+			lengths.back() += blockSize;
+		}
+		rowsNumber_ += md.rowsNumber();
+	}
+	Log::debug() << "FastODA2Request@" << this << "::scanFile => offsets=" << offsets << std::endl;
+	Log::debug() << "FastODA2Request@" << this << "::scanFile => lengths=" << lengths << std::endl;
+	Log::debug() << "FastODA2Request@" << this << "::scanFile => rowsNumber_=" << rowsNumber_ << std::endl;
+	return true;
+}
+
+template <typename T>
+bool FastODA2Request<T>::collectValues(const MetaData& md, ODAHandle& odaHandle)
+{
+    using eckit::Offset;
+    ostream& L (eckit::Log::debug());
+    
+	std::vector<std::string> currentValues;
+	for (size_t i = 0; i < columnNames_.size(); ++i)
+	{
+		const std::string& columnName (columnNames_[i]);
+
+		Column* column = md.hasColumn(columnName) ? md.columnByName(columnName) : 0;
+		std::string v = ! column ? columnNotFound(columnName)
+				: ! column->isConstant() ? columnIsNotConstant(*column)
+				: column->type() == odb::STRING ? StringTool::double_as_string(column->min())
+				: column->type() == odb::INTEGER ? StringTool::int_as_double2string(column->min())
+				: eckit::Translator<double, std::string>()(column->min());
+		values_[i].insert(v);
+		currentValues.push_back(v);
+		double dv = !column ? odb::MDI::realMDI() : column->min();
+
+		L << "FastODA2Request@" << this << "::collectValues: columnName: " << columnName << ": " << v << "(" << dv << ")" << endl;
+
+		odaHandle.addValue(columnNames_[i], dv);
+		doubleValues_[keywords_[i]].insert(dv);
+	}
+
+	if (columnNames_.size())
+	{
+		if (valuesSeen_.find(currentValues) == valuesSeen_.end())
+            valuesSeen_[currentValues] = std::make_pair<Offset,Offset>(odaHandle.start(), odaHandle.end());
+		else {
+			std::pair<Offset,Offset> p = valuesSeen_[currentValues];
+			std::vector<std::string> vs = columnNames_;
+			for (size_t i = 0; i < vs.size(); ++i)
+				vs[i] += std::string("=") + currentValues[i];
+            std::stringstream s;
+			s << "Values " << vs << " found in blocks <" << p.first << "," << p.second << ">"
+				<< " and <" << odaHandle.start() << "," << odaHandle.end() << ">";
+			if (! duplicateCombination(s.str()))
+				return false;
+		}
+	}
+	return true;
+}
+
+template <typename T>
+std::string FastODA2Request<T>::genRequest() const
+{
+    std::stringstream request;
+    ostream& L (eckit::Log::debug());
+
+	for (size_t i = 0; i < columnNames_.size(); ++i)
+	{
+		const std::string& key = keywords_[i];
+		std::string k = eckit::StringTools::upper(key);
+		std::string valuesList;
+        const std::set<std::string>& vs = values_[i];
+        for (std::set<std::string>::const_iterator vi = vs.begin(); vi != vs.end(); ++vi)
+			valuesList += std::string(vi != vs.begin() ? "/" : "") + patchValue(k, *vi);
+		if (i > 0)
+			request << ",\n";
+		request << key << " = " << valuesList;
+	}
+
+    L << "FastODA2Request@" << this << "::genRequest() => " << std::endl << request.str() << std::endl;
+	
+	return request.str();
+}
+
+template <typename T>
+std::string FastODA2Request<T>::patchValue(const std::string& k, const std::string& value) const
+{
+    using eckit::Log;
+    using eckit::StringTools;
+    ostream& L (Log::debug());
+    
+	std::string v = StringTools::trim(value);
+	L << "FastODA2Request@" << this << "::patchValue: v = '" << v  << "', key = " << k << std::endl;
+	if (k == "TIME")
+		v = StringTool::patchTimeForMars(v);
+	else if (k == "CLASS" || k == "TYPE" || k == "STREAM" || k == "OBSGROUP")
+	{
+		L << "FastODA2Request@" << this << "::genRequest: checking if '" << v << "' is numeric" << std::endl;
+		if (StringTool::check(v, isdigit))
+		{
+			L << "FastODA2Request@" << this << "::genRequest: replacing " << v << " with ";
+			v = GribCodes::alphanumeric(StringTools::lower(k), v);
+			L << v << std::endl;
+		}
+		v = StringTools::upper(v);
+	}
+	return v;
+}
+
+template <typename T>
+const std::set<std::string>& FastODA2Request<T>::getValues(const std::string& keyword)
+{
+	for (size_t i = 0; i < keywords_.size(); ++i)
+		if (keywords_[i] == keyword)
+			return values_[i];
+	throw eckit::UserError(std::string("Keyword '") + keyword + "' not found");
+	// This is to keep the compiler happy:
+	return values_[-1];
+}
+
+template <typename T>
+std::map<std::string, std::vector<double> > FastODA2Request<T>::getValues()
+{
+	std::map<std::string, std::vector<double> > r;
+
+	for (std::map<std::string, std::set<double> >::const_iterator it = doubleValues_.begin(); it != doubleValues_.end(); ++it)
+	{
+		const std::string &k = it->first;
+		const std::set<double>& values = it->second;
+
+		std::vector<double>& vs = r[k] = std::vector<double>();
+
+		for (std::set<double>::const_iterator vi = values.begin(); vi != values.end(); ++vi)
+			vs.push_back(*vi);
+	}
+	return r;
+}
+
+template <typename T>
+std::map<std::string, double> FastODA2Request<T>::getUniqueValues()
+{
+	std::map<std::string, double> r;
+	for (size_t i (0); i < keywords_.size(); ++i)
+	{
+		std::string kw (keywords_[i]);
+		if ( doubleValues_[kw].size() != 1)
+		{
+            std::stringstream s;
+            std::set<double>& values (doubleValues_[kw]);
+			s << "Data should contain only one '" << kw << "' value, found: " << values.size() << ". ";
+            for (std::set<double>::iterator it (values.begin()); it != values.end(); ++it)
+                s << *it << ", ";
+			throw eckit::UserError(s.str());
+		}
+		r[kw] = *doubleValues_[kw].begin();
+	}
+	return r;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FastODA2Request.h b/odb_api/src/odb_api/FastODA2Request.h
new file mode 100644
index 0000000..873b596
--- /dev/null
+++ b/odb_api/src/odb_api/FastODA2Request.h
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef FastODA2Request_H
+#define FastODA2Request_H
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "odb_api/MetaData.h"
+
+namespace odb {
+
+class ODAHandle;
+
+template <typename T>
+class FastODA2Request {
+
+public:
+	FastODA2Request();
+
+	void parseConfig(const std::string& s);
+	void addColumn(const std::string& keyword, const std::string& columnName);
+
+	bool scanFile(const eckit::PathName&);
+	bool scanFile(const eckit::PathName&, eckit::OffsetList&, eckit::LengthList&, std::vector<ODAHandle*>&);
+
+	std::string genRequest() const;
+
+    const std::set<std::string>& getValues(const std::string& keyword);
+	std::map<std::string, double> getUniqueValues();
+	std::map<std::string, std::vector<double> > getValues();
+
+	void mergeSimilarBlocks(bool m) { mergeSimilarBlocks_ = m; }
+	
+	unsigned long long rowsNumber() { return rowsNumber_; }
+
+protected:
+	bool collectValues(const MetaData&, ODAHandle&);
+
+	std::string columnIsNotConstant(const Column& column) { return T::columnIsNotConstant(column); }
+	std::string columnNotFound(const std::string& columnName) { return T::columnNotFound(columnName); }
+	bool duplicateCombination(const std::string& errorMessage) { return T::duplicateCombination(errorMessage); }
+
+	std::string patchValue(const std::string& k, const std::string& value) const;
+
+private:
+	MetaData firstMD_;
+	eckit::PathName inputFile_;
+	std::vector<std::string> keywords_;
+	std::vector<std::string> columnNames_;
+    std::vector<std::set<std::string> > values_;
+	std::map<std::string, std::set<double> > doubleValues_;
+
+    std::map<std::vector<std::string>, std::pair<eckit::Offset, eckit::Offset> > valuesSeen_;
+	unsigned long long rowsNumber_;
+	bool mergeSimilarBlocks_;
+};
+
+
+struct ODA2RequestServerTraits {
+	static std::string columnIsNotConstant(const Column& column)
+	{
+        std::stringstream ss;
+		ss << "Column '" << column.name() << "' is not constant"
+			<< " (min=" << column.min() << ", max=" << column.max() << ")";
+		throw eckit::UserError(ss.str());
+	}
+
+	static std::string columnNotFound(const std::string& columnName)
+	{ throw eckit::UserError(std::string("Column '") + columnName + "' not found."); }
+
+	static bool duplicateCombination(const std::string& errorMessage)
+	{ throw eckit::UserError(errorMessage); return false; }
+};
+
+struct ODA2RequestClientTraits {
+	static std::string columnIsNotConstant(const Column& column) { return std::string("MULTIPLE"); }
+	static std::string columnNotFound(const std::string& columnName) { return std::string("MISSING"); }
+	static bool duplicateCombination(const std::string& errorMessage)
+	{ eckit::Log::error() << errorMessage << std::endl; return false; }
+};
+
+} // namespace odb
+
+#include "odb_api/FastODA2Request.cc"
+
+#endif
+
diff --git a/odb_api/src/odb_api/FileCollector.cc b/odb_api/src/odb_api/FileCollector.cc
new file mode 100755
index 0000000..c39390d
--- /dev/null
+++ b/odb_api/src/odb_api/FileCollector.cc
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <ctype.h>
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Timer.h"
+#include "eckit/config/Resource.h"
+
+#include "odb_api/FileCollector.h"
+#include "odb_api/FileMapper.h"
+
+using namespace eckit;
+using namespace std;
+
+FileCollector::FileCollector(const FileMapper& fm, MultiHandle& mh)
+: mapper_(fm),
+  multiHandle_(mh),
+  allFound_()
+{}
+
+string FileCollector::expandTilde(const string& s) 
+{
+    string dhsHome (::getenv("DHSHOME") ? ::getenv("DHSHOME") 
+                    : ::getenv("TEST_DHSHOME") ? ::getenv("TEST_DHSHOME")
+                    : "");
+
+    if (! dhsHome.size())
+        throw UserError("expandTilde: DHSHOME or TEST_DHSHOME must be set");
+
+    if (s.size() && s[0] == '~')
+        return dhsHome + "/" + s.substr(1);
+
+    return s;
+}
+
+FileCollector::~FileCollector() {}
+
+string FileCollector::prestageScriptPath(const std::map<std::string,std::vector<std::string> >& r) const
+{
+    string prestageScript;
+    if (r.find("odbServerPrestageScript") != r.end())
+    {
+        vector<string> odbServerPrestageScripts (r.at("odbServerPrestageScript"));
+        if (odbServerPrestageScripts.size() > 1)
+            throw UserError("odbServerPrestageScript must be one value at most");
+        if (odbServerPrestageScripts.size() == 1)
+            prestageScript = odbServerPrestageScripts[0];
+    }
+    if (prestageScript.size() == 0)
+        prestageScript = string(Resource<string>("$ODB_SERVER_PRESTAGE_SCRIPT", ""));
+    if (prestageScript.size() == 0)
+        prestageScript = string(Resource<string>("odbServerPrestageScript", ""));
+
+    return expandTilde(prestageScript);
+}
+
+void FileCollector::prestage(const std::map<std::string,std::vector<std::string> >& r, const string& fileName)
+{
+    static string prestageScript (prestageScriptPath(r));
+    if (prestageScript.size() == 0)
+        return;
+
+    Timer timer("Prestage " + fileName);
+    stringstream cmd;
+    cmd << prestageScript << " " << fileName;
+    Log::debug() << "Calling prestage script: '" << cmd.str() << "'" << endl;
+    int rc (::system(cmd.str().c_str()));
+    if (rc != 0) {
+        stringstream ss;
+        ss << "prestage command '" << cmd.str() << "' failed (return code: " << rc << ")";
+        Log::error() << ss.str() << endl;
+        throw SeriousBug(ss.str());
+    }
+}
+
+std::vector<std::string> FileCollector::foundFiles() const { return allFound_; }
+
+std::vector<eckit::PathName> FileCollector::foundFilesAsPathNames() const 
+{ 
+    std::vector<eckit::PathName> r;
+    for (size_t i(0); i < allFound_.size(); ++i)
+        r.push_back(allFound_[i]);
+    return r;
+}
+
+void FileCollector::collectFile(const std::map<std::string,std::vector<std::string> >& r, const map<string,string>& values)
+{
+    const string relativePath (mapper_.encodeRelative(values));
+
+    vector<string> foundFiles, files (mapper_.encode(values));
+    ASSERT(files.size() > 0);
+    for (size_t i (0); i < files.size(); ++i)
+    {
+        const string& p (expandTilde(files[i]));
+        prestage(r, p);
+        if (PathName(p).exists())
+        {
+            Log::info() << "FileCollector::collectFile: FOUND " << p << endl;
+            foundFiles.push_back(p);
+        } else {
+            Log::info() << "FileCollector::collectFile: NOT found " << p << endl;
+        }
+    }
+
+    if (foundFiles.size() > 1)
+    {
+        stringstream ss;
+        ss << "File " << relativePath << " found in more than one root directory: " << foundFiles;
+        throw UserError(ss.str());
+    }
+
+    if (foundFiles.size() == 1)
+    {
+        allFound_.push_back(foundFiles[0]);
+        multiHandle_ += PathName(foundFiles[0]).fileHandle();
+    }
+}
+
+void FileCollector::findFiles(const vector<string>& keywords, const map<string, vector<string> >& request)
+{
+    product(0, keywords, request, map<string,string>());
+}
+
+void FileCollector::product(size_t k, 
+                            const vector<string>& keywords, 
+                            const map<string, vector<string> >& request, 
+                            const map<string,string>& combination)
+{
+	if (k == keywords.size())
+		return collectFile(request, combination);
+
+	const string& keyword (keywords[k]);
+	const vector<string>& values (request.find(keyword)->second);
+	for (size_t i (0); i < values.size(); ++i)
+	{
+		map<string,string> newCombination(combination);
+		newCombination[keyword] = values[i];
+		product(k + 1, keywords, request, newCombination);
+	}
+}
+
diff --git a/odb_api/src/odb_api/FileCollector.h b/odb_api/src/odb_api/FileCollector.h
new file mode 100755
index 0000000..e91a249
--- /dev/null
+++ b/odb_api/src/odb_api/FileCollector.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FileCollector.h
+/// Piotr Kuchta - ECMWF Feb 12
+
+#ifndef FileCollector_H
+#define FileCollector_H
+
+#include "eckit/memory/NonCopyable.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+class FileMapper;
+
+class FileCollector : private eckit::NonCopyable {
+public:
+    
+	FileCollector(const FileMapper&, eckit::MultiHandle&);
+	virtual ~FileCollector();
+
+    void findFiles(const std::vector<std::string>&, const std::map<std::string,std::vector<std::string> >&);
+
+    void prestage(const std::map<std::string,std::vector<std::string> >&, const std::string& fileName);
+
+    std::vector<std::string> foundFiles() const;
+    std::vector<eckit::PathName> foundFilesAsPathNames() const;
+
+    static std::string expandTilde(const std::string& s);
+
+protected:
+	virtual void collectFile(const std::map<std::string,std::vector<std::string> >&, const std::map<std::string,std::string>& values);
+
+private:
+	void product(size_t k, const std::vector<std::string>& keywords, const std::map<std::string,std::vector<std::string> >&, const std::map<std::string,std::string>&);
+    std::string prestageScriptPath(const std::map<std::string, std::vector<std::string> >&r) const;
+
+    const FileMapper& mapper_;
+	eckit::MultiHandle& multiHandle_;
+    std::vector<std::string> allFound_;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/FileMapper.cc b/odb_api/src/odb_api/FileMapper.cc
new file mode 100755
index 0000000..58ae980
--- /dev/null
+++ b/odb_api/src/odb_api/FileMapper.cc
@@ -0,0 +1,188 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <ctype.h>
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "eckit/types/Types.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/filesystem/PathName.h"
+
+#include "odb_api/FileMapper.h"
+#include "odb_api/FileCollector.h"
+
+using namespace eckit;
+using namespace std;
+
+typedef StringTools S;
+
+static Mutex local_mutex;
+
+FileMapper::FileMapper(const string& pathNameSchema)
+{
+	parsePathNameSchema(pathNameSchema);
+}
+
+FileMapper::~FileMapper() {}
+
+// Order of keywords: /tmp/p4/mars/client/dev/grib_api/src/hypercube.c
+
+void FileMapper::checkRoots() const
+{
+    AutoLock<Mutex> lock(local_mutex);
+    ASSERT("FileMapper: roots_ not set" && roots_.size());
+
+    bool atLeastOneRootExists (false);
+
+    for (size_t i (0); i < roots_.size(); ++i)
+    {
+        const string& p (roots_[i]);
+        bool exists (PathName(roots_[i]).exists());
+
+        Log::info() << "checkRoots: " << i << ": " << roots_[i] 
+            << " " << exists << std::endl;
+        
+        if (exists)
+            atLeastOneRootExists = true;
+    }
+    if (! atLeastOneRootExists)
+    {
+        stringstream msg;
+        msg << "No directory specified in odbServerRoots exists, checked: " << roots_[0]; 
+        for (size_t i(1); i < roots_.size(); ++i)
+            msg << ":" << roots_[i];
+
+        throw UserError(msg.str());
+    }
+}
+
+void FileMapper::parsePathNameSchema(const std::string& pathNameSchema)
+{
+    AutoLock<Mutex> lock(local_mutex);
+
+    Log::debug() << "pathNameSchema: " << pathNameSchema << std::endl;
+
+    placeholders_.clear();
+    separators_.clear();
+    const string& s (pathNameSchema);
+    for (size_t i (0); i < s.size(); )
+    {
+        size_t begin (s.find('{', i));
+        if (begin == std::string::npos)
+        {
+            separators_.push_back(s.substr(i));
+            break;
+        }
+        size_t end (s.find('}', begin));
+        ASSERT(end != std::string::npos);
+
+        separators_.push_back(s.substr(i, begin - i));
+        placeholders_.push_back(s.substr(begin + 1, end - begin - 1));
+
+        i = end + 1;
+    }
+
+    // TODO: keywords_ needs to be sorted as in //depot/mars/client/dev/grib_api/src/hypercube.c
+    //keywords_ = placeholders_;
+
+    Log::debug() << "pathNameSchema: separators_=" << separators_ << std::endl;
+    Log::debug() << "pathNameSchema: placeholders_ =" << placeholders_ << std::endl;
+}
+
+std::vector<std::string> FileMapper::keywords() const { return placeholders_; }
+
+void FileMapper::addRoot(const std::string& p)
+{
+	roots_.push_back(FileCollector::expandTilde(p));
+}
+
+void FileMapper::addRoots(const std::vector<std::string>& roots)
+{
+    for (size_t i (0); i < roots.size(); ++i)
+        addRoot(roots[i]);
+}
+
+string FileMapper::patchTime(const string& s) const
+{
+    ASSERT("Format of time" && s.size() != 3 && !(s.size() > 6));
+    string r (s);
+
+#ifdef ODB_SERVER_TIME_FORMAT_FOUR_DIGITS
+    if (s.size() == 1) r = string("0") + s + "00";
+    //                '60000' => '0600'
+    if (s.size() == 5) r = string("0") + s.substr(0,3);
+    //                '120000' => '1200'
+    if (s.size() == 6) r = s.substr(0,4);
+
+    ASSERT(r.size() == 4); // We want time as four digits, don't we....
+#else
+    if (s.size() == 1) r = string("0") + s;
+    // HACK: for TIME '0600' => '06'
+    if (s.size() == 4) r = s.substr(0,2);
+    //                '60000' => '06'
+    if (s.size() == 5) r = string("0") + s.substr(0,1);
+    //                '120000' => '12'
+    if (s.size() == 6) r = s.substr(0,2);
+
+    ASSERT(r.size() == 2); // We want time as two digits, don't we....
+#endif
+
+    return r;
+}
+
+string FileMapper::encodeRelative(const std::map<std::string,std::string>& values) const
+{
+    ostream& L(Log::info());
+    L << "FileMapper::encode: values:" << endl;
+    for (map<string,string>::const_iterator it(values.begin()); it != values.end(); ++it)
+        L << "    " <<  it->first << ":" << it->second << endl;
+
+    stringstream r;
+    size_t pi (0);
+    for (size_t i (0); i < separators_.size(); ++i)
+    {
+        r << separators_[i];
+        if (pi < placeholders_.size())
+        {
+            string placeholder (S::upper(placeholders_[pi++]));
+
+            const map<string,string>::const_iterator end(values.end());
+
+            if (values.find(placeholder) == end && values.find(S::lower(placeholder)) == end)
+                throw UserError(string("Could not find value of '") + placeholder + "' in values suplied.");
+
+            const map<string,string>::const_iterator it( values.find(placeholder) != end
+                                                        ? values.find(placeholder)
+                                                        : values.find(S::lower(placeholder)) );
+
+            string value (it->second);
+            string patchedValue
+                ((S::upper(placeholder) == "TIME"
+                  || S::upper(placeholder) == "ANTIME")
+                ? patchTime(value)
+                : value);
+            if (value != patchedValue)
+                L << "FileMapper::encodeRelative: value of '" << placeholder << "' was '" << value << "' changed to '" << patchedValue << "'" << endl;
+
+            r << patchedValue;
+        }
+    }
+    return r.str();
+}
+
+vector<string> FileMapper::encode(const std::map<std::string,std::string>& values) const
+{
+    string path (encodeRelative(values));
+    vector<string> r;
+    for (size_t i (0); i < roots_.size(); ++i)
+        r.push_back(roots_[i] + '/' + path);
+    return r;
+}
diff --git a/odb_api/src/odb_api/FileMapper.h b/odb_api/src/odb_api/FileMapper.h
new file mode 100755
index 0000000..3920dca
--- /dev/null
+++ b/odb_api/src/odb_api/FileMapper.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FileMapper.h
+/// Piotr Kuchta - ECMWF Feb 12
+
+#ifndef FileMapper_H
+#define FileMapper_H
+
+#include "eckit/memory/NonCopyable.h"
+
+class FileMapper : private eckit::NonCopyable {
+public:
+    
+	FileMapper(const std::string& pathNameSchema);
+	~FileMapper();
+	
+	void addRoot(const std::string&);
+	void addRoots(const std::vector<std::string>&);
+	void checkRoots() const;
+
+	std::string encodeRelative(const std::map<std::string,std::string>& values) const;
+	std::vector<std::string> encode(const std::map<std::string,std::string>& values) const;
+
+	std::vector<std::string> keywords() const;
+
+protected:
+    
+	void parsePathNameSchema(const std::string& pathNameSchema);
+
+private:
+	std::vector<std::string> placeholders_;
+	std::vector<std::string> separators_;
+
+	std::vector<std::string> roots_;
+
+    std::string patchTime(const std::string&) const;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/FixedSizeWriterIterator.cc b/odb_api/src/odb_api/FixedSizeWriterIterator.cc
new file mode 100644
index 0000000..4d60f03
--- /dev/null
+++ b/odb_api/src/odb_api/FixedSizeWriterIterator.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file FixedSizeWriterIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "odb_api/ColumnType.h"
+#include "odb_api/FixedSizeWriterIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+
+FixedSizeWriterIterator::FixedSizeWriterIterator(Owner &owner, DataHandle *dh)
+: WriterBufferingIterator(owner, dh, true)
+{}
+
+int FixedSizeWriterIterator::writeRow(const double* values, unsigned long count)
+{
+	double* last = lastValues_;
+
+	ASSERT(count == columns().size());
+
+	unsigned int k = 0;
+	unsigned char* p (encodedDataBuffer_);
+	p = writeNumberOfRepeatedValues(p, k);
+
+	for ( ; k < count; k++) 
+	{
+		Column* col = columns_[k];
+		p = col->coder().encode(p, values[k]); //, *this->f);
+		last[k] = values[k];
+	}
+
+	size_t len = p - encodedDataBuffer_;
+
+	DataStream<SameByteOrder> f(this->f);
+	f.writeBytes(encodedDataBuffer_.cast<char>(), len);
+
+	nrows_++;
+
+	return 0;
+} 
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/FixedSizeWriterIterator.h b/odb_api/src/odb_api/FixedSizeWriterIterator.h
new file mode 100644
index 0000000..6ea7216
--- /dev/null
+++ b/odb_api/src/odb_api/FixedSizeWriterIterator.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file FixedSizeWriterIterator.h
+///
+/// @author Piotr Kuchta, March 2009
+
+#ifndef FixedSizeWriterIterator_H
+#define FixedSizeWriterIterator_H
+
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/Writer.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class FixedSizeWriterIterator : public WriterBufferingIterator 
+{
+public:
+	typedef Writer<WriterBufferingIterator> Owner;
+
+	FixedSizeWriterIterator (Owner& owner, eckit::DataHandle *);
+
+	int writeRow(const double* values, unsigned long count);
+
+private:
+// No copy allowed.
+	FixedSizeWriterIterator(const FixedSizeWriterIterator&);
+	FixedSizeWriterIterator& operator=(const FixedSizeWriterIterator&);
+
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionAND.cc b/odb_api/src/odb_api/FunctionAND.cc
new file mode 100755
index 0000000..4d3f944
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionAND.cc
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionAND.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionAND::FunctionAND(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionAND::FunctionAND(const FunctionAND& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionAND::clone() const { return new FunctionAND(*this); }
+
+FunctionAND::~FunctionAND() {}
+
+const type::SQLType* FunctionAND::type() const { return &type::SQLType::lookup("double"); } // TODO: bool?
+
+double FunctionAND::eval(bool& missing) const
+{
+	return args_[0]->eval(missing) && args_[1]->eval(missing);
+}
+
+bool FunctionAND::andSplit(expression::Expressions& e)
+{
+	bool ok = false;
+
+	if(!args_[0]->andSplit(e))
+	{
+		e.push_back(args_[0]);
+		ok = true;
+	}
+
+	if(!args_[1]->andSplit(e))
+	{
+		e.push_back(args_[1]);
+		ok = true;
+	}
+
+	return ok;
+
+}
+
+SQLExpression* FunctionAND::simplify(bool& changed) 
+{
+	SQLExpression* x = FunctionExpression::simplify(changed);
+	if(x) return x;
+
+	
+	for(int i = 0; i < 2 ; i++)
+	{
+		bool missing = false;
+		if(args_[i]->isConstant())
+		{
+			if(args_[i]->eval(missing))
+			{
+                std::cout << "SYMPLIFY " << *this << " to ";
+				changed = true;
+
+				SQLExpression* x = args_[1-i];
+
+				args_[1-i] = 0; // So we don't delete it
+				delete args_[i];
+				args_.clear();
+
+                std::cout << *x << std::endl;
+
+				return x;
+			}
+			else
+			{
+                std::cout << "SYMPLIFY " << *this << "to 0 " << std::endl;
+				changed = true;
+				return SQLExpression::number(0);
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionAND.h b/odb_api/src/odb_api/FunctionAND.h
new file mode 100755
index 0000000..a4c57e7
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionAND.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionAND.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionAND_H
+#define FunctionAND_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionAND : public FunctionExpression {
+public:
+	FunctionAND(const std::string&, const expression::Expressions&);
+	FunctionAND(const FunctionAND&);
+	~FunctionAND();
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionAND& operator=(const FunctionAND&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	virtual SQLExpression* simplify(bool&);
+	virtual bool andSplit(expression::Expressions&);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionAND& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionAVG.cc b/odb_api/src/odb_api/FunctionAVG.cc
new file mode 100755
index 0000000..5e1bcad
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionAVG.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionAVG.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionAVG::FunctionAVG(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  count_(0),
+  value_(0)
+{}
+
+FunctionAVG::FunctionAVG(const FunctionAVG& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_),
+  value_(other.value_)
+{}
+
+
+SQLExpression* FunctionAVG::clone() const { return new FunctionAVG(*this); }
+
+const type::SQLType* FunctionAVG::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionAVG::~FunctionAVG() {}
+
+double FunctionAVG::eval(bool& missing) const
+{
+	if(!count_)
+	{
+		missing = true;
+		return 0;
+	}
+
+	return value_ / count_;
+}
+
+void FunctionAVG::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = 0;
+	count_ = 0;
+}
+
+void FunctionAVG::cleanup(SQLSelect& sql)
+{
+//cout << "Cleanup  FunctionAVG " << count_ << " " << value_ << std::endl;
+	FunctionExpression::cleanup(sql);
+	value_ = 0;
+	count_ = 0;
+}
+
+void FunctionAVG::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(!missing)
+	{
+		value_ += value;
+		count_++;
+	}
+//	else cout << "missing" << std::endl;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionAVG.h b/odb_api/src/odb_api/FunctionAVG.h
new file mode 100755
index 0000000..6479baf
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionAVG.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionAVG.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionAVG_H
+#define FunctionAVG_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionAVG : public FunctionExpression {
+public:
+	FunctionAVG(const std::string&,const expression::Expressions&);
+	FunctionAVG(const FunctionAVG&);
+	~FunctionAVG(); 
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionAVG& operator=(const FunctionAVG&);
+
+// -- Members
+	unsigned long long count_;
+	double value_;
+
+// -- Overridden methods
+	const type::SQLType* type() const; 
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+
+	bool isAggregate() const { return true; }
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionAVG& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionCOUNT.cc b/odb_api/src/odb_api/FunctionCOUNT.cc
new file mode 100755
index 0000000..0432913
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionCOUNT.cc
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionCOUNT.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+const type::SQLType* FunctionCOUNT::type() const { const type::SQLType& x = type::SQLType::lookup("double"); return &x; }
+
+FunctionCOUNT::FunctionCOUNT(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  count_(0)
+{}
+
+FunctionCOUNT::FunctionCOUNT(const FunctionCOUNT& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_)
+{}
+
+SQLExpression* FunctionCOUNT::clone() const
+{ return new FunctionCOUNT(*this); }
+
+FunctionCOUNT::~FunctionCOUNT() {}
+
+double FunctionCOUNT::eval(bool& missing) const
+{
+//cout << "FunctionCOUNT " << count_ << std::endl;
+	return count_;
+}
+
+void FunctionCOUNT::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	count_ = 0;
+}
+
+void FunctionCOUNT::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	count_ = 0;
+}
+
+void FunctionCOUNT::partialResult() 
+{
+	bool missing = false;
+	args_[0]->eval(missing);
+	if(!missing)
+		count_++;
+//cout << "FunctionCOUNT::partialResult " << count_ << std::endl;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionCOUNT.h b/odb_api/src/odb_api/FunctionCOUNT.h
new file mode 100755
index 0000000..a5d4d59
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionCOUNT.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionCOUNT.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionCOUNT_H
+#define FunctionCOUNT_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionCOUNT : public FunctionExpression {
+public:
+	FunctionCOUNT(const std::string&,const expression::Expressions&);
+	FunctionCOUNT(const FunctionCOUNT&);
+	~FunctionCOUNT();
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionCOUNT& operator=(const FunctionCOUNT&);
+
+	unsigned long long count_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+
+	bool isAggregate() const { return true; }
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionCOUNT& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionDOTP.cc b/odb_api/src/odb_api/FunctionDOTP.cc
new file mode 100755
index 0000000..82bf64a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionDOTP.cc
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionDOTP.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+const type::SQLType* FunctionDOTP::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionDOTP::FunctionDOTP(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  value_(0),
+  resultNULL_(true)
+{}
+
+FunctionDOTP::FunctionDOTP(const FunctionDOTP& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_),
+  resultNULL_(other.resultNULL_)
+{}
+
+SQLExpression* FunctionDOTP::clone() const { return new FunctionDOTP(*this); }
+
+FunctionDOTP::~FunctionDOTP() {}
+
+double FunctionDOTP::eval(bool& missing) const
+{
+	if (resultNULL_)
+		missing = true;
+	return value_;
+}
+
+void FunctionDOTP::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = 0;
+}
+
+void FunctionDOTP::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = 0;
+}
+
+void FunctionDOTP::partialResult() 
+{
+	bool missing = false;
+	double x = args_[0]->eval(missing);
+	double y = args_[1]->eval(missing);
+	if(! missing)
+	{
+		value_ += x*y;
+		resultNULL_ = false;
+	}
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionDOTP.h b/odb_api/src/odb_api/FunctionDOTP.h
new file mode 100755
index 0000000..492b381
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionDOTP.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionDOTP.h
+// ECMWF July 2010
+
+#ifndef FunctionDOTP_H
+#define FunctionDOTP_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionDOTP : public FunctionExpression {
+public:
+	FunctionDOTP(const std::string&,const expression::Expressions&);
+	FunctionDOTP(const FunctionDOTP&);
+	~FunctionDOTP(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionDOTP& operator=(const FunctionDOTP&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+
+	bool isAggregate() const { return true; }
+
+	bool resultNULL_;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionDOTP& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionEQ.cc b/odb_api/src/odb_api/FunctionEQ.cc
new file mode 100755
index 0000000..fb03f80
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ.cc
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+using namespace odb::sql::type;
+
+const type::SQLType* FunctionEQ::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionEQ::FunctionEQ(const FunctionEQ& other)
+: FunctionExpression(other.name_, other.args_),
+  tmp_(other.tmp_)
+{}
+
+FunctionEQ::FunctionEQ(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  tmp_(0)
+{}
+
+SQLExpression* FunctionEQ::clone() const { return new FunctionEQ(*this); }
+
+FunctionEQ::~FunctionEQ() {}
+
+void FunctionEQ::trimStringInDouble(char* &p, size_t& len)
+{
+	len = 0;
+	for(; len < sizeof(double) && isprint(p[len]); ++len)
+		;
+	for(; len > 0 && isspace(p[len - 1]); --len)
+		;
+	size_t plen = len;
+	for (char *pp = p; isspace(*p) && p < pp + plen;)
+	{
+		++p;
+		--len;
+	}
+}
+
+bool FunctionEQ::equal(const SQLExpression& l, const SQLExpression& r, bool& missing)
+{
+	if (l.type()->getKind() == SQLType::stringType)
+	{
+		double v1 = l.eval(missing);
+		double v2 = r.eval(missing);
+		if (missing)
+			return false;
+
+		char *p1 = reinterpret_cast<char*>(&v1);
+		char *p2 = reinterpret_cast<char*>(&v2);
+		
+		size_t len1 = sizeof(double);
+		size_t len2 = sizeof(double);
+
+		trimStringInDouble(p1, len1);
+		trimStringInDouble(p2, len2);
+
+		if (len1 != len2)
+			return false;
+
+		return 0 == strncmp(p1, p2, len1);
+	}
+
+	return l.eval(missing) == r.eval(missing);
+}
+
+double FunctionEQ::eval(bool& missing) const
+{
+	return equal(*args_[0], *args_[1], missing);
+}
+
+SQLExpression* FunctionEQ::simplify(bool& changed) 
+{
+	SQLExpression* x = FunctionExpression::simplify(changed);
+	if(x) return x;
+
+	ColumnExpression* a = dynamic_cast<ColumnExpression*>(args_[0]);
+	ColumnExpression* b = dynamic_cast<ColumnExpression*>(args_[1]);
+
+	if(a && b) {
+		args_[0] = 0;
+		args_[1] = 0;
+		return FunctionFactory::instance().build("join",a,b);
+	}
+
+	//
+	if(args_[0]->isConstant() && !args_[1]->isConstant())
+        std::swap(args_[0],args_[1]);
+
+	return 0;
+}
+
+bool FunctionEQ::useIndex()
+{
+	return args_[0]->indexed() && args_[1]->isConstant();
+}
+
+SQLIndex* FunctionEQ::getIndex(double*)
+{
+	bool missing = false;
+	tmp_ = args_[1]->eval(missing);
+	return args_[0]->getIndex(&tmp_);
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionEQ.h b/odb_api/src/odb_api/FunctionEQ.h
new file mode 100755
index 0000000..5f2ecb3
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionEQ.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionEQ_H
+#define FunctionEQ_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionEQ : public FunctionExpression {
+public:
+	FunctionEQ(const std::string&,const expression::Expressions&);
+	FunctionEQ(const FunctionEQ&);
+	~FunctionEQ(); 
+
+	static bool equal(const SQLExpression& l, const SQLExpression& r, bool& missing);
+	static void trimStringInDouble(char* &p, size_t& len);
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionEQ& operator=(const FunctionEQ&);
+
+	double tmp_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	virtual SQLExpression* simplify(bool&);
+	virtual bool useIndex();
+	virtual SQLIndex* getIndex(double*);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionEQ& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionEQ_BOXLAT.cc b/odb_api/src/odb_api/FunctionEQ_BOXLAT.cc
new file mode 100755
index 0000000..e508a6a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ_BOXLAT.cc
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/EqRegionCache.h"
+#include "odb_api/FunctionEQ_BOXLAT.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+const type::SQLType* FunctionEQ_BOXLAT::type() const { return &type::SQLType::lookup("real"); }
+
+FunctionEQ_BOXLAT::FunctionEQ_BOXLAT(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionEQ_BOXLAT::FunctionEQ_BOXLAT(const FunctionEQ_BOXLAT& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+FunctionEQ_BOXLAT::~FunctionEQ_BOXLAT() {}
+
+SQLExpression* FunctionEQ_BOXLAT::clone() const { return new FunctionEQ_BOXLAT(*this); }
+
+double FunctionEQ_BOXLAT::eval(bool& missing) const
+{
+    double lat_degrees = args_[0]->eval(missing);
+    double lon_degrees = args_[1]->eval(missing);
+    lon_degrees = lon_degrees; // variable not used
+    double resol = args_[2]->eval(missing);
+    double res=0.;
+    EqRegionCache p;
+    res = p.get_midlat(resol, lat_degrees);
+    return res;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionEQ_BOXLAT.h b/odb_api/src/odb_api/FunctionEQ_BOXLAT.h
new file mode 100755
index 0000000..e980c8e
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ_BOXLAT.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionEQ_BOXLAT.h
+/// ECMWF July 2010
+
+#ifndef FunctionEQ_BOXLAT_H
+#define FunctionEQ_BOXLAT_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionEQ_BOXLAT : public FunctionExpression {
+public:
+	FunctionEQ_BOXLAT(const std::string&, const expression::Expressions&);
+	FunctionEQ_BOXLAT(const FunctionEQ_BOXLAT&);
+	~FunctionEQ_BOXLAT();
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionEQ_BOXLAT& operator=(const FunctionEQ_BOXLAT&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionEQ_BOXLAT& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionEQ_BOXLON.cc b/odb_api/src/odb_api/FunctionEQ_BOXLON.cc
new file mode 100755
index 0000000..439782d
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ_BOXLON.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/EqRegionCache.h"
+#include "odb_api/FunctionEQ_BOXLON.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+const type::SQLType* FunctionEQ_BOXLON::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionEQ_BOXLON::FunctionEQ_BOXLON(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionEQ_BOXLON::FunctionEQ_BOXLON(const FunctionEQ_BOXLON& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionEQ_BOXLON::clone() const { return new FunctionEQ_BOXLON(*this); }
+
+FunctionEQ_BOXLON::~FunctionEQ_BOXLON() {}
+
+double FunctionEQ_BOXLON::eval(bool& missing) const
+{
+    double lat_degrees = args_[0]->eval(missing);
+    double lon_degrees = args_[1]->eval(missing);
+    double resol = args_[2]->eval(missing);
+    EqRegionCache p;
+    double res;
+    res = p.get_midlon(resol, lat_degrees, lon_degrees);
+
+	return res;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionEQ_BOXLON.h b/odb_api/src/odb_api/FunctionEQ_BOXLON.h
new file mode 100755
index 0000000..289f6f1
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionEQ_BOXLON.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionEQ_BOXLON.h
+// ECMWF July 2010
+
+#ifndef FunctionEQ_BOXLON_H
+#define FunctionEQ_BOXLON_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionEQ_BOXLON : public FunctionExpression {
+public:
+	FunctionEQ_BOXLON(const std::string&,const expression::Expressions&);
+	FunctionEQ_BOXLON(const FunctionEQ_BOXLON&);
+	~FunctionEQ_BOXLON(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionEQ_BOXLON& operator=(const FunctionEQ_BOXLON&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionEQ_BOXLON& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionExpression.cc b/odb_api/src/odb_api/FunctionExpression.cc
new file mode 100755
index 0000000..3b219d7
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionExpression.cc
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionExpression::FunctionExpression(const std::string& name, const expression::Expressions& args)
+: name_(name),
+  args_(args)
+{
+//  never use any logging here (Log::*)
+//	std::cout << "new FunctionExpression " << name << std::endl;
+}
+
+FunctionExpression::FunctionExpression(const FunctionExpression& other)
+: name_(other.name_),
+  args_(other.args_)
+{}
+
+
+const type::SQLType* FunctionExpression::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionExpression::~FunctionExpression() {}
+
+void FunctionExpression::prepare(SQLSelect& sql)
+{
+	for(expression::Expressions::iterator j = args_.begin(); j != args_.end(); ++j)
+		(*j)->prepare(sql);
+}
+
+void FunctionExpression::cleanup(SQLSelect& sql)
+{
+	for(expression::Expressions::iterator j = args_.begin(); j != args_.end(); ++j)
+		(*j)->cleanup(sql);
+}
+
+void FunctionExpression::partialResult()
+{
+	for(expression::Expressions::iterator j = args_.begin(); j != args_.end(); ++j)
+		(*j)->partialResult();
+}
+
+
+SQLExpression* FunctionExpression::simplify(bool& changed)
+{ 
+	for(expression::Expressions::iterator j = args_.begin(); j != args_.end(); ++j)
+	{
+		SQLExpression* x = *j;
+		SQLExpression* y = x->simplify(changed);
+
+		if(y)
+		{
+			delete x;
+			*j = y;
+			std::cout << "SIMPLIFY " << *this << std::endl;
+			changed = true;
+		}
+	}
+	
+	return SQLExpression::simplify(changed);
+}
+
+
+bool FunctionExpression::isConstant() const
+{ 
+	for(expression::Expressions::const_iterator j = args_.begin(); j != args_.end(); ++j)
+		if(!(*j)->isConstant())
+			return false;
+	return true;
+}
+
+bool FunctionExpression::isAggregate() const
+{ 
+	for(expression::Expressions::const_iterator j = args_.begin(); j != args_.end(); ++j)
+		if((*j)->isAggregate())
+			return true;
+	return false;
+}
+
+void FunctionExpression::print(std::ostream& s) const 
+{
+	s << name_;
+	s << '(';
+	for(expression::Expressions::const_iterator j = args_.begin(); j != args_.end(); ++j)
+	{
+		if(j != args_.begin()) s << ',';
+		s << *(*j);
+	}
+	s << ')';
+		
+}
+
+void FunctionExpression::tables(std::set<SQLTable*>& t)
+{
+	for(expression::Expressions::iterator j = args_.begin(); j != args_.end(); ++j)
+		(*j)->tables(t);
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionExpression.h b/odb_api/src/odb_api/FunctionExpression.h
new file mode 100755
index 0000000..036a7f3
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionExpression.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionExpression_H
+#define FunctionExpression_H
+
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionExpression : public SQLExpression {
+public:
+	FunctionExpression(const std::string&,const expression::Expressions&);
+	FunctionExpression(const FunctionExpression&);
+	~FunctionExpression();
+
+    virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+	virtual bool isConstant() const;
+	virtual SQLExpression* simplify(bool&);
+	//virtual double eval() const;
+	bool isAggregate() const;
+	void partialResult();
+
+	virtual const odb::sql::type::SQLType* type() const;
+
+	// For SQLSelectFactory (maybe it should just friend SQLSelectFactory).
+	expression::Expressions& args() { return args_; }
+
+protected:
+	std::string name_;
+	expression::Expressions args_;
+    // void print(std::ostream&) const;
+
+// -- Overridden methods
+
+    void tables(std::set<SQLTable*>&);
+private:
+// No copy allowed
+	//FunctionExpression(const FunctionExpression&);
+	FunctionExpression& operator=(const FunctionExpression&);
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionFIRST.cc b/odb_api/src/odb_api/FunctionFIRST.cc
new file mode 100755
index 0000000..e3bf33c
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionFIRST.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <climits>
+#include <cfloat>
+
+#include "odb_api/FunctionFIRST.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionFIRST::FunctionFIRST(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args),
+  value_(DBL_MAX),
+  notFirst_(false)
+{}
+
+FunctionFIRST::FunctionFIRST(const FunctionFIRST& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_),
+  notFirst_(other.notFirst_)
+{}
+
+SQLExpression* FunctionFIRST::clone() const { return new FunctionFIRST(*this); }
+
+const odb::sql::type::SQLType* FunctionFIRST::type() const { return args_[0]->type(); }
+
+FunctionFIRST::~FunctionFIRST() {}
+
+double FunctionFIRST::eval(bool& missing) const
+{
+	if (value_ == DBL_MAX)
+		missing = true;
+
+	return value_;
+}
+
+void FunctionFIRST::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionFIRST::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionFIRST::output(SQLOutput& s) const 
+{ 
+	bool missing (false);
+	double d (eval(missing)); 
+	type()->output(s, d, missing);
+}
+
+void FunctionFIRST::partialResult() 
+{
+    if (notFirst_)
+        return;
+
+	bool missing (false);
+    value_ = (args_[0]->eval(missing));
+    notFirst_ = true;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionFIRST.h b/odb_api/src/odb_api/FunctionFIRST.h
new file mode 100755
index 0000000..f133fae
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionFIRST.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionFIRST.h
+// Piotr Kuchta - ECMWF Nov 2016
+
+#ifndef odb_api_FunctionFIRST_H
+#define odb_api_FunctionFIRST_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionFIRST : public FunctionExpression {
+public:
+	FunctionFIRST(const std::string&,const expression::Expressions&);
+	FunctionFIRST(const FunctionFIRST&);
+	~FunctionFIRST(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionFIRST& operator=(const FunctionFIRST&);
+
+	double value_;
+    bool notFirst_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	bool isAggregate() const { return true; }
+
+	virtual void output(SQLOutput&) const;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionFIRST& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionFactory.cc b/odb_api/src/odb_api/FunctionFactory.cc
new file mode 100755
index 0000000..bcd9d5d
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionFactory.cc
@@ -0,0 +1,657 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <limits.h>
+#include <float.h>
+
+#include "eckit/eckit.h"
+#include "eckit/thread/ThreadSingleton.h"
+ 
+#include "odb_api/FunctionAND.h"
+#include "odb_api/FunctionAVG.h"
+#include "odb_api/FunctionCOUNT.h"
+#include "odb_api/FunctionDOTP.h"
+#include "odb_api/FunctionEQ_BOXLAT.h"
+#include "odb_api/FunctionEQ_BOXLON.h"
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/FunctionIN.h"
+#include "odb_api/FunctionIntegerExpression.h"
+#include "odb_api/FunctionJOIN.h"
+#include "odb_api/FunctionJULIAN.h"
+#include "odb_api/FunctionJULIAN_SECONDS.h"
+#include "odb_api/FunctionMAX.h"
+#include "odb_api/FunctionMIN.h"
+#include "odb_api/FunctionFIRST.h"
+#include "odb_api/FunctionLAST.h"
+#include "odb_api/FunctionNORM.h"
+#include "odb_api/FunctionNOT_IN.h"
+#include "odb_api/FunctionNOT_NULL.h"
+#include "odb_api/FunctionNULL.h"
+#include "odb_api/FunctionNVL.h"
+#include "odb_api/FunctionOR.h"
+#include "odb_api/FunctionRGG_BOXLAT.h"
+#include "odb_api/FunctionRGG_BOXLON.h"
+#include "odb_api/FunctionRMS.h"
+#include "odb_api/FunctionROWNUMBER.h"
+#include "odb_api/FunctionSTDEV.h"
+#include "odb_api/FunctionSUM.h"
+#include "odb_api/FunctionTDIFF.h"
+#include "odb_api/FunctionTHIN.h"
+#include "odb_api/FunctionTIMESTAMP.h"
+#include "odb_api/FunctionVAR.h"
+#include "odb_api/FunctionRLIKE.h"
+#include "odb_api/FunctionMATCH.h"
+#include "odb_api/piconst.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+//--------------------------------------------------------------
+
+const double R_Earth_km   = 180*60 / piconst::pi * 1.852;
+const double R_Earth      = 180*60 / piconst::pi * 1.852*100.0;
+const double EPS          = 1e-7;
+const double D2R          = piconst::pi/180.0;
+const double R2D          = 180.0/piconst::pi;
+
+static eckit::ThreadSingleton<FunctionFactory> functionFactory_;
+
+struct FFMap : public std::map<std::pair<std::string,int>, FunctionFactoryBase*> { static FFMap& instance(); };
+static eckit::ThreadSingleton<FFMap> map_;
+FFMap& FFMap::instance() { return map_.instance(); }
+
+struct SQLFunctionHelp : public std::map<std::pair<std::string,int>, std::string> { static SQLFunctionHelp& instance(); };
+static eckit::ThreadSingleton<SQLFunctionHelp> sqlFunctionsHelp_;
+SQLFunctionHelp& SQLFunctionHelp::instance() { return sqlFunctionsHelp_.instance(); }
+
+FunctionFactory& FunctionFactory::instance() { return functionFactory_.instance(); }
+
+FunctionFactoryBase::FunctionFactoryBase(const std::string& name, int arity, const std::string& help)
+: arity_(arity),
+  name_(name),
+  help_(help)
+{
+	std::pair<std::string,int> p(name_,arity_);
+	//if(!map_) map_ = new std::map<pair<std::string,int>,FunctionFactoryBase*>();
+
+	ASSERT(FFMap::instance().find(p) == FFMap::instance().end());
+	FFMap::instance()[p] = this;
+	SQLFunctionHelp::instance()[p] = help_;
+}
+
+FunctionFactory::FunctionInfo& FunctionFactory::functionsInfo()
+{
+	if (functionInfo_.size() == 0)
+        for (std::map<std::pair<std::string,int>,FunctionFactoryBase*>::iterator i = FFMap::instance().begin(); i != FFMap::instance().end(); ++i)
+			functionInfo_.push_back(make_pair(make_pair(i->first.first, i->first.second), SQLFunctionHelp::instance()[i->first]));
+	return functionInfo_;
+}
+
+FunctionFactoryBase::~FunctionFactoryBase()
+{
+
+//	std::pair<std::string,int> p(name_,arity_);
+//	mapa().erase(p);
+//	if (mapa().empty())
+//	{
+//		delete map_;
+//		map_ = 0;
+//	}
+}
+
+FunctionExpression* FunctionFactoryBase::build(const std::string& name, const expression::Expressions& args)
+{
+	std::pair<std::string,int> p(name,args.size());	
+    std::map<std::pair<std::string,int>,FunctionFactoryBase*>::iterator j = FFMap::instance().find(p);
+
+	// Try -1
+	if(j == FFMap::instance().end())
+	{
+        p = std::pair<std::string,int>(name,-1);
+		j = FFMap::instance().find(p);
+	}
+
+	if(j == FFMap::instance().end())
+		throw eckit::UserError(name + ": function not defined");
+
+	return (*j).second->make(name,args);
+
+}
+
+FunctionExpression* FunctionFactoryBase::build(const std::string& name, SQLExpression* arg)
+{
+	expression::Expressions args;
+	args.push_back(arg);
+	return build(name,args);
+}
+
+FunctionExpression* FunctionFactoryBase::build(const std::string& name, SQLExpression* arg1, SQLExpression* arg2)
+{
+	expression::Expressions args;
+	args.push_back(arg1);
+	args.push_back(arg2);
+	return build(name,args);
+}
+
+FunctionExpression* FunctionFactoryBase::build(const std::string& name, SQLExpression* arg1, SQLExpression* arg2, SQLExpression *arg3)
+{
+	expression::Expressions args;
+	args.push_back(arg1);
+	args.push_back(arg2);
+	args.push_back(arg3);
+	return build(name,args);
+}
+
+FunctionExpression* FunctionFactoryBase::build(const std::string& name, const expression::Expressions& matchList, const SelectAST& selectAST)
+{
+    using namespace std;
+    ostream& L( eckit::Log::info() );
+    //eckit::Log::info() << "FunctionFactoryBase::build: name: " << name << std::endl;
+    ASSERT(name == "match"); // this only for now
+
+    return new FunctionMATCH(name, matchList, selectAST);
+}
+
+
+//===============================
+
+//#include <math.h>
+
+template<double (*T)(double)> 
+class MathFunctionExpression_1 : public FunctionExpression {
+	double eval(bool& m) const { double v = args_[0]->eval(m); return m ? this->missingValue_ : T(v); }
+	SQLExpression* clone() const { return new MathFunctionExpression_1<T>(*this); }
+public:
+	MathFunctionExpression_1(const std::string& name, const expression::Expressions& args) : FunctionExpression(name, args) {}
+	MathFunctionExpression_1(const MathFunctionExpression_1& o) : FunctionExpression(o) {}
+};
+
+template<double (*T)(double, double)> 
+class MathFunctionExpression_2 : public FunctionExpression {
+	double eval(bool& m) const {
+		double left = args_[0]->eval(m);
+		if (m) return this->missingValue_;
+		double right = args_[1]->eval(m);
+		if (m) return this->missingValue_;
+		return T(left, right);
+	}
+	SQLExpression* clone() const { return new MathFunctionExpression_2<T>(*this); }
+public:
+	MathFunctionExpression_2(const std::string& name, const expression::Expressions& args) : FunctionExpression(name,args) {}
+	MathFunctionExpression_2(const MathFunctionExpression_2& o) : FunctionExpression(o) {}
+};
+
+template<double (*T)(double,double,double)> 
+class MathFunctionExpression_3 : public FunctionExpression {
+	double eval(bool& m) const {
+		double a0 = args_[0]->eval(m);
+		if (m) return this->missingValue_;
+		double a1 = args_[1]->eval(m);
+		if (m) return this->missingValue_;
+		double a2 = args_[2]->eval(m);
+		if (m) return this->missingValue_;
+		return T(a0, a1, a2);
+	}
+	SQLExpression* clone() const { return new MathFunctionExpression_3<T>(*this); }
+public:
+	MathFunctionExpression_3(const std::string& name,const expression::Expressions& args) : FunctionExpression(name,args) {}
+	MathFunctionExpression_3(const MathFunctionExpression_3& o) : FunctionExpression(o) {}
+};
+
+template<double (*T)(double,double,double,double)> 
+class MathFunctionExpression_4 : public FunctionExpression {
+	double eval(bool& m) const {
+		double a0 = args_[0]->eval(m);
+		if (m) return this->missingValue_;
+		double a1 = args_[1]->eval(m);
+		if (m) return this->missingValue_;
+		double a2 = args_[2]->eval(m);
+		if (m) return this->missingValue_;
+		double a3 = args_[3]->eval(m);
+		if (m) return this->missingValue_;
+		return T(a0, a1, a2, a3);
+	}
+	SQLExpression* clone() const { return new MathFunctionExpression_4<T>(*this); }
+public:
+	MathFunctionExpression_4(const std::string& name,const expression::Expressions& args) : FunctionExpression(name,args) {}
+	MathFunctionExpression_4(const MathFunctionExpression_4& o) : FunctionExpression(o) {}
+};
+
+template<double (*T)(double,double,double,double,double)> 
+class MathFunctionExpression_5 : public FunctionExpression {
+	double eval(bool& m) const {
+		double a0 = args_[0]->eval(m);
+		if (m) return this->missingValue_;
+		double a1 = args_[1]->eval(m);
+		if (m) return this->missingValue_;
+		double a2 = args_[2]->eval(m);
+		if (m) return this->missingValue_;
+		double a3 = args_[3]->eval(m);
+		if (m) return this->missingValue_;
+		double a4 = args_[4]->eval(m);
+		if (m) return this->missingValue_;
+		return T(a0, a1, a2, a3, a4);
+	}
+	SQLExpression* clone() const { return new MathFunctionExpression_5<T>(*this); }
+public:
+	MathFunctionExpression_5(const std::string& name, const expression::Expressions& args) : FunctionExpression(name,args), myArgs_(0) {}
+	MathFunctionExpression_5(const std::string& name, expression::Expressions* args) : FunctionExpression(name,*args), myArgs_(args) {}
+	MathFunctionExpression_5(const MathFunctionExpression_5& o) : FunctionExpression(o), myArgs_(0) {}
+	~MathFunctionExpression_5() { if (myArgs_) delete myArgs_; }
+private:
+	Expressions* myArgs_;
+};
+
+#define DEFINE_MATH_FUNC_1(F,Help) \
+new FunctionMaker<MathFunctionExpression_1<F> > (#F,1,Help)
+
+#define DEFINE_MATH_FUNC_1F(FuncName, Name, Help) \
+new FunctionMaker<MathFunctionExpression_1<FuncName> > (#Name,1,Help)
+
+#define DEFINE_MATH_FUNC_2(F,Help) \
+new FunctionMaker<MathFunctionExpression_2<F> > (#F,2,Help)
+
+#define DEFINE_MATH_FUNC_2F(FuncName, Name, Help) \
+new FunctionMaker<MathFunctionExpression_2<FuncName> > (#Name,2,Help)
+
+#define DEFINE_MATH_FUNC_3(F,Help) \
+new FunctionMaker<MathFunctionExpression_3<F> > (#F,3,Help)
+
+#define DEFINE_MATH_FUNC_4(F,Help) \
+new FunctionMaker<MathFunctionExpression_4<F> > (#F,4,Help)
+
+#define DEFINE_MATH_FUNC_5(F,Help) \
+new FunctionMaker<MathFunctionExpression_5<F> > (#F,5,Help)
+
+//--------------------------------------------------------------
+
+#define DEFINE_UNARY(N,T,Help)  new FunctionMaker<MathFunctionExpression_1<T> > (#N,1,Help)
+#define DEFINE_BINARY(N,T,Help) new FunctionMaker<MathFunctionExpression_2<T> > (#N,2,Help)
+
+inline double abs(double x) { return fabs(x); }
+// Note: ODB's trigonometric funcs require args in degrees 
+// and return degrees (where applicable)
+inline double Func_acos(double x) { return (R2D*acos(x)); }
+inline double Func_asin(double x) { return (R2D*asin(x)); }
+inline double Func_atan(double x) { return (R2D*atan(x)); }
+inline double Func_atan2(double x, double y) { return (R2D*atan2(x,y)); }
+inline double Func_cos(double x) { return (cos(D2R*x)); }
+inline double Func_sin(double x) { return (sin(D2R*x)); }
+inline double Func_tan(double x) { return (tan(D2R*x)); }
+inline double mod(double x, double y) { return fmod(x,y); }
+inline double Func_pow(double x, double y) { return ((y) == 2 ? (x)*(x) : pow(x,y)); }
+inline double ln(double x) { return log(x); }
+inline double lg(double x) { return log10(x); }
+
+const double ZERO_POINT=((double)273.15e0);
+
+inline double celsius(double x) { return x - ZERO_POINT; }
+inline double k2c(double x) { return x - ZERO_POINT; }
+inline double kelvin(double x) { return x + ZERO_POINT; }
+inline double c2k(double x) { return x + ZERO_POINT; }
+inline double c2f(double x) { return ((9*x)/5) + 32; }
+inline double f2c(double x) { return ((x - 32)*5)/9; }
+inline double f2k(double x) { return c2k(f2c(x)); }
+inline double k2f(double x) { return c2f(k2c(x)); }
+inline double fahrenheit(double x) { return c2f(k2c(x)); }
+
+
+inline double radians(double x) { return x * D2R; }
+inline double deg2rad(double x) { return x * D2R; }
+inline double degrees(double x) { return x * R2D; }
+inline double rad2deg(double x) { return x * R2D; }
+
+inline double speed(double u, double v) { return sqrt(u*u + v*v); }
+inline double ff(double u, double v) { return speed(u,v); }
+inline double direction(double u, double v) { return fmod(Func_atan2(-u,-v)+360.,360.); }
+inline double dir(double u, double v) { return direction(u,v); }
+inline double dd(double u, double v) { return direction(u,v); }
+
+/// Distance in meters.
+inline double distance(double lat1,double lon1,double lat2,double lon2) 
+{ return R_Earth*acos(Func_sin(lat1)*Func_sin(lat2)+Func_cos(lat1)*Func_cos(lat2)*Func_cos(lon1-lon2)); }
+
+
+inline double km(double x)
+{ return R_Earth_km*x; }
+
+/// in kilometers
+inline double km(double lat1,double lon1,double lat2,double lon2) 
+{ return R_Earth_km*acos(Func_sin(lat1)*Func_sin(lat2)+Func_cos(lat1)*Func_cos(lat2)*Func_cos(lon1-lon2)); }
+
+/// in kilometers
+inline double dist(double reflat, double reflon, double refdist_km, double obslat, double obslon) 
+{
+	return (double)( R_Earth_km *
+           acos(Func_cos(reflat) * Func_cos(obslat) * Func_cos(obslon-reflon) +
+           Func_sin(reflat) * Func_sin(obslat)) <= (refdist_km) );
+}
+
+inline double circle(double x, double x0, double y, double y0, double r)
+{ return ( Func_pow(x-x0,2) + Func_pow(y-y0,2) <= Func_pow(r,2) ); }
+
+
+
+inline double rad(double reflat, double reflon, double refdeg, double obslat, double obslon)
+{
+    double v (Func_cos(reflat) * Func_cos(obslat) * Func_cos(obslon-reflon) + Func_sin(reflat) * Func_sin(obslat) );
+
+    int digs ( 3 + DBL_MANT_DIG - DBL_MIN_EXP ); 
+    printf("===> acos( %.*e )\n", digs, v);
+
+  return (double)(acos(Func_cos(reflat) * Func_cos(obslat) * Func_cos(obslon-reflon) +
+               Func_sin(reflat) * Func_sin(obslat) ) <= D2R*refdeg);
+}
+
+
+//--------------------------------------------------------------
+inline double between(double x,double a,double b) { return x >= a && x <= b; }
+inline double not_between(double x,double a,double b) { return x < a || x > b; }
+inline double between_exclude_first(double x,double a,double b) { return x > a && x <= b; }
+inline double between_exclude_second(double x,double a,double b) { return x >= a && x < b; }
+inline double between_exclude_both(double x,double a,double b) { return x > a && x < b; }
+inline double twice(double x) { return 2*x; }
+
+
+/* No. of bits for "int" */
+#define MAXBITS 32
+
+#define MASK_0           0U  /*                                 0 */
+#define MASK_1           1U  /*                                 1 */
+#define MASK_2           3U  /*                                11 */
+#define MASK_3           7U  /*                               111 */
+#define MASK_4          15U  /*                              1111 */
+#define MASK_5          31U  /*                             11111 */
+#define MASK_6          63U  /*                            111111 */
+#define MASK_7         127U  /*                           1111111 */
+#define MASK_8         255U  /*                          11111111 */
+#define MASK_9         511U  /*                         111111111 */
+#define MASK_10       1023U  /*                        1111111111 */
+#define MASK_11       2047U  /*                       11111111111 */
+#define MASK_12       4095U  /*                      111111111111 */
+#define MASK_13       8191U  /*                     1111111111111 */
+#define MASK_14      16383U  /*                    11111111111111 */
+#define MASK_15      32767U  /*                   111111111111111 */
+#define MASK_16      65535U  /*                  1111111111111111 */
+#define MASK_17     131071U  /*                 11111111111111111 */
+#define MASK_18     262143U  /*                111111111111111111 */
+#define MASK_19     524287U  /*               1111111111111111111 */
+#define MASK_20    1048575U  /*              11111111111111111111 */
+#define MASK_21    2097151U  /*             111111111111111111111 */
+#define MASK_22    4194303U  /*            1111111111111111111111 */
+#define MASK_23    8388607U  /*           11111111111111111111111 */
+#define MASK_24   16777215U  /*          111111111111111111111111 */
+#define MASK_25   33554431U  /*         1111111111111111111111111 */
+#define MASK_26   67108863U  /*        11111111111111111111111111 */
+#define MASK_27  134217727U  /*       111111111111111111111111111 */
+#define MASK_28  268435455U  /*      1111111111111111111111111111 */
+#define MASK_29  536870911U  /*     11111111111111111111111111111 */
+#define MASK_30 1073741823U  /*    111111111111111111111111111111 */
+#define MASK_31 2147483647U  /*   1111111111111111111111111111111 */
+#define MASK_32 4294967295U  /*  11111111111111111111111111111111 */
+
+#define MASK(n) MASK_##n
+
+#define IOR(x,y)   ((x) | (y))
+#define IAND(x,y)  ((x) & (y))
+#define ISHFTL(x,n) ((x) << (n))
+#define ISHFTR(x,n) ((x) >> (n))
+
+#define GET_BITS(x, pos, len)      IAND(ISHFTR((int)(x), pos), MASK(len))
+#define CASE_GET_BITS(x, pos, len) \
+  case len: rc = GET_BITS(x, pos, len); break
+
+
+double ibits(double X, double Pos, double Len)
+{
+  int rc = 0; /* the default */
+  X = trunc(X);
+  Pos = trunc(Pos);
+  Len = trunc(Len);
+  if (X   >= INT_MIN && X   <= INT_MAX &&
+      Pos >= 0       && Pos <  MAXBITS &&
+      Len >= 1       && Len <= MAXBITS) {
+    int x = X;
+    int pos = Pos;
+    int len = Len;
+    switch (len) {
+      CASE_GET_BITS(x, pos, 1);
+      CASE_GET_BITS(x, pos, 2);
+      CASE_GET_BITS(x, pos, 3);
+      CASE_GET_BITS(x, pos, 4);
+      CASE_GET_BITS(x, pos, 5);
+      CASE_GET_BITS(x, pos, 6);
+      CASE_GET_BITS(x, pos, 7);
+      CASE_GET_BITS(x, pos, 8);
+      CASE_GET_BITS(x, pos, 9);
+      CASE_GET_BITS(x, pos,10);
+      CASE_GET_BITS(x, pos,11);
+      CASE_GET_BITS(x, pos,12);
+      CASE_GET_BITS(x, pos,13);
+      CASE_GET_BITS(x, pos,14);
+      CASE_GET_BITS(x, pos,15);
+      CASE_GET_BITS(x, pos,16);
+      CASE_GET_BITS(x, pos,17);
+      CASE_GET_BITS(x, pos,18);
+      CASE_GET_BITS(x, pos,19);
+      CASE_GET_BITS(x, pos,20);
+      CASE_GET_BITS(x, pos,21);
+      CASE_GET_BITS(x, pos,22);
+      CASE_GET_BITS(x, pos,23);
+      CASE_GET_BITS(x, pos,24);
+      CASE_GET_BITS(x, pos,25);
+      CASE_GET_BITS(x, pos,26);
+      CASE_GET_BITS(x, pos,27);
+      CASE_GET_BITS(x, pos,28);
+      CASE_GET_BITS(x, pos,29);
+      CASE_GET_BITS(x, pos,30);
+      CASE_GET_BITS(x, pos,31);
+      CASE_GET_BITS(x, pos,32);
+    }
+
+  }
+  return (double) rc;
+}
+
+double negate_double(double n) { return -n; }
+double logical_not_double(double n) { return !n; }
+double not_equal_to_double(double l, double r) { return l != r; }
+double greater_double(double l, double r) { return l > r; }
+double greater_equal_double(double l, double r) { return l >= r; }
+double less_double(double l, double r) { return l < r; }
+double less_equal_double(double l, double r) { return l <= r; }
+
+double plus_double(double l, double r) { return l + r; }
+double minus_double(double l, double r) { return l - r; }
+
+class MultiplyExpression : public FunctionExpression {
+	double eval(bool& missing) const
+	{
+		bool leftMissing = false;
+		bool rightMissing = false;
+		double left = args_[0]->eval(leftMissing);
+		double right = args_[1]->eval(rightMissing);
+
+		// Special case: 0 * anything = 0
+		if ( (left == 0 || right == 0) && ! (leftMissing && rightMissing))
+		{
+			missing = false;
+			return 0;
+		}
+		return (missing = leftMissing | rightMissing)
+				? this->missingValue_ 
+				: left * right;
+	}
+	SQLExpression* clone() const { return new MultiplyExpression(name_, 2); }
+public:
+	MultiplyExpression(const std::string& name, const expression::Expressions& args)
+	: FunctionExpression(name,args) {}
+};
+
+double multiplies_double(double l, double r) { return l * r; }
+
+double divides_double(double l, double r) { return l / r; }
+double ldexp_double(double l, double r) { return ldexp(l, r); }
+
+FunctionFactory::FunctionFactory() : FunctionFactoryBase("FunctionFactory", -1, "This is not an SQL function")
+{
+	DEFINE_BINARY(<>,not_equal_to_double,"not equal to");
+	DEFINE_BINARY(>,greater_double,"greater");
+	DEFINE_BINARY(<,less_double,"less");
+	DEFINE_BINARY(>=,greater_equal_double,"greater or equal");
+	DEFINE_BINARY(<=,less_equal_double,"less or equal");
+
+	DEFINE_BINARY(+,plus_double,"add");
+	DEFINE_BINARY(-,minus_double,"subtract");
+	//DEFINE_BINARY(*,multiplies_double);
+	new FunctionMaker<MultiplyExpression> ("*",2,"multiply");
+	DEFINE_BINARY(/,divides_double,"divide");
+
+	DEFINE_UNARY(-,negate_double,"negate");
+	DEFINE_UNARY(not,logical_not_double,"logical not");
+
+	DEFINE_MATH_FUNC_1(abs,"absolute value");
+	DEFINE_MATH_FUNC_1(fabs,"absolute value");
+
+	DEFINE_MATH_FUNC_1F(Func_acos, acos, "arc cosine");
+	DEFINE_MATH_FUNC_1F(Func_asin, asin, "arc sine");
+	DEFINE_MATH_FUNC_1F(Func_atan, atan, "arc tangent of one variable");
+	DEFINE_MATH_FUNC_2F(Func_atan2, atan2, "arc tangent of param1/param2 (y/x)");
+	DEFINE_MATH_FUNC_1F(Func_cos, cos, "cosine");
+	DEFINE_MATH_FUNC_1F(Func_sin, sin, "size");
+	DEFINE_MATH_FUNC_1F(Func_tan, tan, "tangent");
+
+	DEFINE_MATH_FUNC_1(exp, "base-e exponential function, e raised to x");
+	DEFINE_MATH_FUNC_1(cosh, "hyperbolic cosine");
+	DEFINE_MATH_FUNC_1(sinh, "hyperbolic sine");
+	DEFINE_MATH_FUNC_1(tanh, "hyperbolic tangent");
+	DEFINE_MATH_FUNC_1(log, "natural logarithm");
+	DEFINE_MATH_FUNC_1(log10, "base-10 logarithm");
+	DEFINE_MATH_FUNC_1(sqrt, "square root");
+
+	DEFINE_MATH_FUNC_2(ldexp_double, "");
+
+	DEFINE_MATH_FUNC_2(mod, "");
+	DEFINE_MATH_FUNC_2(fmod, "");
+	DEFINE_MATH_FUNC_2(ff, "");
+	DEFINE_MATH_FUNC_2(speed, "");
+	DEFINE_MATH_FUNC_2(dd, "direction");
+	DEFINE_MATH_FUNC_2(dir, "direction");
+
+	DEFINE_MATH_FUNC_2F(Func_pow, pow, "");
+
+	DEFINE_MATH_FUNC_1(ln, "");
+	DEFINE_MATH_FUNC_1(lg, "");
+
+
+	DEFINE_MATH_FUNC_1(celsius, "");
+	DEFINE_MATH_FUNC_1(k2c, "");
+	DEFINE_MATH_FUNC_1(kelvin, "");
+	DEFINE_MATH_FUNC_1(c2k, "");
+	DEFINE_MATH_FUNC_1(c2f, "");
+	DEFINE_MATH_FUNC_1(f2c, "");
+	DEFINE_MATH_FUNC_1(f2k, "");
+	DEFINE_MATH_FUNC_1(k2f, "");
+	DEFINE_MATH_FUNC_1(fahrenheit, "");
+
+	DEFINE_MATH_FUNC_4(distance, "");
+	DEFINE_MATH_FUNC_5(dist, "");
+
+	DEFINE_MATH_FUNC_1(km, "");
+	DEFINE_MATH_FUNC_4(km, "");
+
+	DEFINE_MATH_FUNC_1(radians, "");
+	DEFINE_MATH_FUNC_1(deg2rad, "");
+	DEFINE_MATH_FUNC_1(degrees, "");
+	DEFINE_MATH_FUNC_1(rad2deg, "");
+
+	DEFINE_MATH_FUNC_1(twice, "");
+
+	DEFINE_MATH_FUNC_3(between, "");
+	DEFINE_MATH_FUNC_3(not_between, "");
+	DEFINE_MATH_FUNC_3(between_exclude_first, "");
+	DEFINE_MATH_FUNC_3(between_exclude_second, "");
+	DEFINE_MATH_FUNC_3(between_exclude_both, "");
+
+	DEFINE_MATH_FUNC_3(ibits, "");
+
+	DEFINE_MATH_FUNC_5(circle, "");
+	DEFINE_MATH_FUNC_5(rad, "");
+
+	new FunctionMaker<FunctionAND> ("and",2, "");
+	new FunctionMaker<FunctionAVG> ("avg",1, "Average (aggregate function)");
+	new FunctionMaker<FunctionAVG> ("mean",1, "alias for avg (average)");
+	new FunctionMaker<FunctionCOUNT> ("count",1, "");
+	new FunctionMaker<FunctionDOTP> ("dotp",2, "");
+	new FunctionMaker<FunctionEQ> ("=",2, "");
+	new FunctionMaker<FunctionEQ_BOXLAT> ("eq_boxlat", 3, "");
+	new FunctionMaker<FunctionEQ_BOXLON> ("eq_boxlon",3, "");
+	//#define DEFINE_UNARY(N,T)  new FunctionMaker<MathFunctionExpression_1<T<double> > > make_##T(#N,1)
+	//#define DEFINE_BINARY(N,T) new FunctionMaker<MathFunctionExpression_2<T<double> > > make_##T(#N,2)
+	new FunctionMaker<FunctionIN> ("in", -1, "");
+	new FunctionMaker<FunctionJOIN> ("join",2, "");
+	new FunctionMaker<FunctionJULIAN> ("julian",2, "");
+	new FunctionMaker<FunctionJULIAN> ("jd",2, "");
+	new FunctionMaker<FunctionJULIAN> ("julian_date",2, "");
+	new FunctionMaker<FunctionJULIAN_SECONDS> ("julian_seconds",2, "Returns time in Julian calendar expressed in seconds.");
+	new FunctionMaker<FunctionMAX> ("max",1, "");
+	new FunctionMaker<FunctionMIN> ("min",1, "");
+	new FunctionMaker<FunctionFIRST> ("first",1, "");
+	new FunctionMaker<FunctionLAST> ("last",1, "");
+	new FunctionMaker<FunctionNORM> ("norm",2, "");
+	new FunctionMaker<FunctionNOT_IN> ("not_in", -1, "");
+	new FunctionMaker<FunctionNOT_NULL> ("not_null",1, "");
+	new FunctionMaker<FunctionNULL> ("null",1, "");
+	new FunctionMaker<FunctionNULL> ("isnull",1, "");
+	new FunctionMaker<FunctionNVL> ("nvl",2, "");
+	new FunctionMaker<FunctionOR> ("or",2, "");
+	new FunctionMaker<FunctionRGG_BOXLAT> ("rgg_boxlat", 3, "");
+	new FunctionMaker<FunctionRGG_BOXLON> ("rgg_boxlon",3, "");
+	new FunctionMaker<FunctionRMS> ("rms",1, "");
+	new FunctionMaker<FunctionROWNUMBER> ("rownumber", 0, "");
+	new FunctionMaker<FunctionSTDEV> ("stdev",1, "");
+	new FunctionMaker<FunctionSTDEV> ("stdevp",1, "");
+	new FunctionMaker<FunctionSUM> ("sum",1, "");
+	new FunctionMaker<FunctionTDIFF> ("tdiff",4, "");
+	new FunctionMaker<FunctionTHIN> ("thin", 2, "");
+	new FunctionMaker<FunctionTIMESTAMP> ("timestamp",2, "");
+	new FunctionMaker<FunctionVAR> ("var",1, "");
+	new FunctionMaker<FunctionVAR> ("varp",1, "");
+    new FunctionMaker<FunctionRLIKE> ("rlike",2, "");
+    //TODO: not exactly sure LIKE and RLIKE are the same
+    new FunctionMaker<FunctionRLIKE> ("like",2, "");
+
+	FunctionIntegerExpression::registerIntegerFunctions();
+}
+
+
+FunctionExpression* ast(const std::string& s, SQLExpression* e) 
+{ return FunctionFactory::instance().build(s, e); }
+
+FunctionExpression* ast(const std::string& s, SQLExpression* e1, SQLExpression* e2)
+{ return FunctionFactory::instance().build(s, e1, e2); }
+
+FunctionExpression* ast(const std::string& s, SQLExpression* e1, SQLExpression* e2, SQLExpression* e3)
+{ return FunctionFactory::instance().build(s, e1, e2, e3); }
+
+FunctionExpression* ast(const std::string& s, const expression::Expressions& e) 
+{ return FunctionFactory::instance().build(s, e); }
+
+FunctionExpression* ast(const std::string& s, const expression::Expressions& matchList, const SelectAST& selectAST) 
+{ return FunctionFactory::instance().build(s, matchList, selectAST); }
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionFactory.h b/odb_api/src/odb_api/FunctionFactory.h
new file mode 100755
index 0000000..ccb345b
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionFactory.h
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionFactory.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef odb_api_FunctionFactory_H
+#define odb_api_FunctionFactory_H
+
+#include "odb_api/FunctionExpression.h"
+#include "odb_api/SQLAST.h"
+
+namespace odb { namespace sql { class SelectAST; } }
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionFactoryBase {
+protected:
+	int    arity_;
+	std::string name_;
+	std::string help_;
+	virtual FunctionExpression* make(const std::string&,const expression::Expressions&) = 0;
+
+public:
+	//FunctionFactoryBase() : name_("FunctionFactory"), arity_(-1) {}
+	FunctionFactoryBase(const std::string& name, int arity, const std::string& help);
+	~FunctionFactoryBase();
+
+	FunctionExpression* build(const std::string&, SQLExpression*);
+	FunctionExpression* build(const std::string&, SQLExpression*, SQLExpression*);
+	FunctionExpression* build(const std::string&, SQLExpression*, SQLExpression*, SQLExpression*);
+	FunctionExpression* build(const std::string&, const expression::Expressions&);
+    FunctionExpression* build(const std::string&, const expression::Expressions&, const SelectAST&);
+};
+
+class FunctionFactory : public FunctionFactoryBase {
+public:
+	static FunctionFactory& instance();
+	FunctionFactory(); // : FunctionFactoryBase("FunctionFactory", -1) {}
+
+    typedef std::vector<std::pair<std::pair<std::string, int>, std::string> > FunctionInfo;
+
+	FunctionInfo& functionsInfo();
+
+private:
+	FunctionExpression* make(const std::string&,const expression::Expressions&) { NOTIMP; return 0; }
+
+
+    std::map<std::pair<std::string,int>, FunctionFactoryBase*> map_;
+	FunctionInfo functionInfo_;
+};
+
+template<class T>
+class FunctionMaker : public FunctionFactoryBase {
+	FunctionExpression* make(const std::string& name, const expression::Expressions& args)
+	{ return new T(name, args); }
+public:
+	FunctionMaker(const std::string& name, int arity, const std::string& help) : FunctionFactoryBase(name, arity, help) {}
+};
+
+FunctionExpression* ast(const std::string& s, SQLExpression* e);
+FunctionExpression* ast(const std::string& s, SQLExpression* e1, SQLExpression* e2);
+FunctionExpression* ast(const std::string& s, SQLExpression* e1, SQLExpression* e2, SQLExpression* e3);
+FunctionExpression* ast(const std::string& s, const expression::Expressions& e);
+FunctionExpression* ast(const std::string& s, const expression::Expressions& matchList, const SelectAST& selectAST);
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionIN.cc b/odb_api/src/odb_api/FunctionIN.cc
new file mode 100755
index 0000000..e7a1b4f
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionIN.cc
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/FunctionIN.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionIN::FunctionIN(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  size_(args.size() - 1)
+{}
+
+FunctionIN::FunctionIN(const FunctionIN& other)
+: FunctionExpression(other.name_, other.args_),
+  size_(other.args_.size() - 1)
+{}
+
+FunctionIN::~FunctionIN() {}
+
+const type::SQLType* FunctionIN::type() const { return &type::SQLType::lookup("real"); } //TODO: bool?
+
+SQLExpression* FunctionIN::clone() const { return new FunctionIN(*this); }
+
+double FunctionIN::eval(bool& missing) const
+{
+	const SQLExpression& x = *args_[size_];
+	for(size_t i = 0 ; i < size_ ; ++i)
+		if (FunctionEQ::equal(x, *args_[i], missing))
+			return true;
+	return false;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb 
diff --git a/odb_api/src/odb_api/FunctionIN.h b/odb_api/src/odb_api/FunctionIN.h
new file mode 100755
index 0000000..4c49d87
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionIN.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionIN.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionIN_H
+#define FunctionIN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionIN : public FunctionExpression {
+public:
+	FunctionIN(const std::string&, const expression::Expressions&);
+	FunctionIN(const FunctionIN&);
+	~FunctionIN();
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionIN& operator=(const FunctionIN&);
+
+	size_t size_;
+
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionIN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionIntegerExpression.cc b/odb_api/src/odb_api/FunctionIntegerExpression.cc
new file mode 100755
index 0000000..bb0b7dd
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionIntegerExpression.cc
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/FunctionIntegerExpression.h"
+#include "odb_api/MDI.h"
+#include "odb_api/StringTool.h"
+
+#define ftrunc(x) ((x) -fmod((x), 1))
+#define F90nint(x) ( ((x) > 0) ? (int)((x) + 0.5) : (int)((x) - 0.5) )
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionIntegerExpression::FunctionIntegerExpression(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionIntegerExpression::~FunctionIntegerExpression() {}
+
+const odb::sql::type::SQLType* FunctionIntegerExpression::type() const
+{
+	return &odb::sql::type::SQLType::lookup("integer");
+}
+
+void FunctionIntegerExpression::output(std::ostream& s) const
+{
+	bool missing;
+    double v = eval(missing);
+
+	//Log::info() << "FunctionIntegerExpression::output: v=" << v << ", missing=" << missing << std::endl;
+
+    s << static_cast<long long int>(v);
+}
+
+//===============================
+
+//#include <math.h>
+
+template<double (*T)(double)> 
+class MathFunctionIntegerExpression_1 : public FunctionIntegerExpression {
+	double eval(bool& m) const {
+		double v = args_[0]->eval(m);
+		return m ? this->missingValue_ : T(v);
+	}
+	SQLExpression* clone() const { return new MathFunctionIntegerExpression_1<T>(this->name_,this->args_); }
+public:
+	MathFunctionIntegerExpression_1(const std::string& name,const expression::Expressions& args)
+	: FunctionIntegerExpression(name, args), myArgs_(0) { this->missingValue_ = odb::MDI::integerMDI(); }
+
+	MathFunctionIntegerExpression_1(const std::string& name,expression::Expressions* args)
+	: FunctionIntegerExpression(name, *args), myArgs_(args) { this->missingValue_ = odb::MDI::integerMDI(); }
+
+	~MathFunctionIntegerExpression_1() { delete myArgs_; }
+private:
+	Expressions* myArgs_;
+};
+
+#define DEFINE_MATH_INT_FUNC_1F(FuncName, Name, Help) \
+static FunctionMaker<MathFunctionIntegerExpression_1<FuncName> > make_1_##FuncName(#Name,1,Help)
+
+
+//--------------------------------------------------------------
+inline double year(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)((int)((x)/10000)) : (double)MDI::integerMDI());}
+inline double month(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(((int)((x)/100))%100) : (double)MDI::integerMDI());}
+inline double day(double x)   { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(((int)(x))%100) : (double)MDI::integerMDI());}
+inline double hour(double x)   {return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)((int)((x)/10000)) : (double)MDI::integerMDI());}
+inline double minute(double x) {return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(((int)((x)/100))%100) : (double)MDI::integerMDI());}
+inline double minutes(double x) {return minute(x);}
+inline double second(double x) {return ((fabs(x) != fabs((double)MDI::realMDI())) ? (double)(((int)(x))%100) : (double)MDI::integerMDI());}
+inline double seconds(double x) {return second(x);}
+
+
+inline double Func_ftrunc(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(ftrunc(x)) : (double) MDI::integerMDI()); }
+inline double Func_dnint(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(F90nint(x)) : (double)MDI::integerMDI()); }
+inline double Func_dint(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(ftrunc(x)) : (double) MDI::integerMDI());}
+inline double Func_ceil(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(ceil(x)) : (double) MDI::integerMDI());}
+inline double Func_floor(double x) { return ((fabs(x) != fabs((double) MDI::realMDI())) ? (double)(floor(x)) : (double) MDI::integerMDI());}
+inline double Func_atoi(double x) { return (fabs(x) != fabs((double) MDI::realMDI())) ? (double) atoi(StringTool::double_as_string(x).c_str()) : (double) MDI::integerMDI(); }
+
+void FunctionIntegerExpression::registerIntegerFunctions()
+{
+
+	DEFINE_MATH_INT_FUNC_1F(year,year,"");
+	DEFINE_MATH_INT_FUNC_1F(month,month,"");
+	DEFINE_MATH_INT_FUNC_1F(day,day,"");
+	DEFINE_MATH_INT_FUNC_1F(hour,hour,"");
+	DEFINE_MATH_INT_FUNC_1F(minute,minute,"");
+	DEFINE_MATH_INT_FUNC_1F(minutes,minutes,"");
+	DEFINE_MATH_INT_FUNC_1F(second,second,"");
+	DEFINE_MATH_INT_FUNC_1F(seconds, seconds,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_ceil,ceil,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_floor,floor,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_ftrunc,trunc,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_dint,int,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_dnint,nint,"");
+	DEFINE_MATH_INT_FUNC_1F(Func_atoi,atoi,"Convert string to integer. Return NULL if argument is NULL ");
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionIntegerExpression.h b/odb_api/src/odb_api/FunctionIntegerExpression.h
new file mode 100755
index 0000000..f80be93
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionIntegerExpression.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionIntegerExpression.h
+// ECMWF July 2010
+
+#ifndef FUNCTION_INTEGER_EXPRESSION_H
+#define FUNCTION_INTEGER_EXPRESSION_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionIntegerExpression : public FunctionExpression {
+public:
+	static void registerIntegerFunctions();
+
+	FunctionIntegerExpression(const std::string&,const expression::Expressions&);
+	~FunctionIntegerExpression(); 
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void output(std::ostream& s) const;
+
+private:
+// No copy allowed
+	FunctionIntegerExpression(const FunctionIntegerExpression&);
+	FunctionIntegerExpression& operator=(const FunctionIntegerExpression&);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionIntegerExpression& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionJOIN.cc b/odb_api/src/odb_api/FunctionJOIN.cc
new file mode 100755
index 0000000..81a42b5
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJOIN.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/FunctionJOIN.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionJOIN::FunctionJOIN(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionJOIN::FunctionJOIN(const FunctionJOIN& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionJOIN::clone() const { return new FunctionJOIN(*this); }
+
+FunctionJOIN::~FunctionJOIN() {}
+
+const type::SQLType* FunctionJOIN::type() const { return &type::SQLType::lookup("real"); }
+
+double FunctionJOIN::eval(bool& missing) const
+{
+	return args_[0]->eval(missing) == args_[1]->eval(missing);
+}
+
+bool FunctionJOIN::useIndex()
+{
+//	return args_[0]->indexed() && args_[1]->indexed();
+	return args_[1]->indexed();
+}
+
+SQLIndex* FunctionJOIN::getIndex(double*)
+{
+	ColumnExpression* c = dynamic_cast<ColumnExpression*>(args_[0]); ASSERT(c);
+	return args_[1]->getIndex(c->current());
+	//return args_[0]->getIndex(
+	//return args_[1]->getIndex();
+	return 0;
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionJOIN.h b/odb_api/src/odb_api/FunctionJOIN.h
new file mode 100755
index 0000000..f04c54a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJOIN.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionJOIN.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionJOIN_H
+#define FunctionJOIN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionJOIN : public FunctionExpression {
+public:
+	FunctionJOIN(const std::string&, const expression::Expressions&);
+	FunctionJOIN(const FunctionJOIN&);
+	~FunctionJOIN(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionJOIN& operator=(const FunctionJOIN&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	bool useIndex();
+	SQLIndex* getIndex(double*);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionJOIN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionJULIAN.cc b/odb_api/src/odb_api/FunctionJULIAN.cc
new file mode 100755
index 0000000..237ad22
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJULIAN.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/DateTime.h"
+#include "odb_api/FunctionJULIAN.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionJULIAN::FunctionJULIAN(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionJULIAN::FunctionJULIAN(const FunctionJULIAN& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+
+SQLExpression* FunctionJULIAN::clone() const { return new FunctionJULIAN(*this); }
+
+
+FunctionJULIAN::~FunctionJULIAN() {}
+
+const type::SQLType* FunctionJULIAN::type() const { return &type::SQLType::lookup("real"); }
+
+double FunctionJULIAN::eval(bool& missing) const
+{
+    int indate = (int) args_[0]->eval(missing);
+    int intime = (int) args_[1]->eval(missing);
+	// TODO: shold we return MISSING_VALUE_INT in case missing == true here?
+    int year_target = indate/10000;
+    int month_target = (indate%10000)/100;
+    int day_target = indate%100;
+    int hour_target = intime/10000;
+    int min_target = (intime%10000)/100;
+    int sec_target = intime%100;
+
+    utils::DateTime d1(year_target, month_target, day_target,
+                   hour_target, min_target, sec_target);
+
+    return d1.dateToJulian();
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionJULIAN.h b/odb_api/src/odb_api/FunctionJULIAN.h
new file mode 100755
index 0000000..98ace03
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJULIAN.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionJULIAN.h
+// ECMWF July 2010
+
+#ifndef FunctionJULIAN_H
+#define FunctionJULIAN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionJULIAN : public FunctionExpression {
+public:
+	FunctionJULIAN(const std::string&, const expression::Expressions&);
+	FunctionJULIAN(const FunctionJULIAN&);
+	~FunctionJULIAN(); 
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionJULIAN& operator=(const FunctionJULIAN&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionJULIAN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionJULIAN_SECONDS.cc b/odb_api/src/odb_api/FunctionJULIAN_SECONDS.cc
new file mode 100755
index 0000000..3984eca
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJULIAN_SECONDS.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Date.h"
+#include "odb_api/FunctionJULIAN_SECONDS.h"
+
+#define trunc(x) ((x) -fmod((x), 1))
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionJULIAN_SECONDS::FunctionJULIAN_SECONDS(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionJULIAN_SECONDS::FunctionJULIAN_SECONDS(const FunctionJULIAN_SECONDS& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionJULIAN_SECONDS::clone() const { return new FunctionJULIAN_SECONDS(*this); }
+
+FunctionJULIAN_SECONDS::~FunctionJULIAN_SECONDS() {}
+
+const type::SQLType* FunctionJULIAN_SECONDS::type() const { return &type::SQLType::lookup("real"); }
+
+double FunctionJULIAN_SECONDS::eval(bool& missing) const
+{
+    int indate = (int) args_[0]->eval(missing);
+    int intime = (int) args_[1]->eval(missing);
+	// TODO: shold we return MISSING_VALUE_INT in case missing == true here?
+
+    int year = indate / 10000;
+    int month = (indate % 10000) / 100;
+    int day = indate % 100;
+    int hour = intime / 10000;
+    int min = (intime % 10000) / 100;
+    int sec = intime % 100;
+
+//  " Julianday * 24 * 60 * 60 + hh * 3600 + mm * 60 + ss ";
+
+    return eckit::Date(year, month, day).julian() * 24 * 60 * 60 + hour * 3600 + min * 60 + sec;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionJULIAN_SECONDS.h b/odb_api/src/odb_api/FunctionJULIAN_SECONDS.h
new file mode 100755
index 0000000..f537f5b
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionJULIAN_SECONDS.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionJULIAN_SECONDS.h
+/// ECMWF February 2014
+
+#ifndef FunctionJULIAN_SECONDS_H
+#define FunctionJULIAN_SECONDS_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionJULIAN_SECONDS : public FunctionExpression {
+public:
+	FunctionJULIAN_SECONDS(const std::string&, const expression::Expressions&);
+	FunctionJULIAN_SECONDS(const FunctionJULIAN_SECONDS&);
+	~FunctionJULIAN_SECONDS(); // Change to virtual if base class
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionJULIAN_SECONDS& operator=(const FunctionJULIAN_SECONDS&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend ostream& operator<<(ostream& s,const FunctionJULIAN_SECONDS& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionLAST.cc b/odb_api/src/odb_api/FunctionLAST.cc
new file mode 100755
index 0000000..e56b387
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionLAST.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <climits>
+#include <cfloat>
+
+#include "odb_api/FunctionLAST.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionLAST::FunctionLAST(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args),
+  value_(DBL_MAX)
+{}
+
+FunctionLAST::FunctionLAST(const FunctionLAST& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_)
+{}
+
+SQLExpression* FunctionLAST::clone() const { return new FunctionLAST(*this); }
+
+const odb::sql::type::SQLType* FunctionLAST::type() const { return args_[0]->type(); }
+
+FunctionLAST::~FunctionLAST() {}
+
+double FunctionLAST::eval(bool& missing) const
+{
+	if (value_ == DBL_MAX)
+		missing = true;
+
+	return value_;
+}
+
+void FunctionLAST::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionLAST::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionLAST::output(SQLOutput& s) const 
+{ 
+	bool missing (false);
+	double d (eval(missing)); 
+	type()->output(s, d, missing);
+}
+
+void FunctionLAST::partialResult() 
+{
+    bool missing (false);
+    value_ = (args_[0]->eval(missing));
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionLAST.h b/odb_api/src/odb_api/FunctionLAST.h
new file mode 100755
index 0000000..e443069
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionLAST.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionLAST.h
+// Piotr Kuchta - ECMWF Nov 2016
+
+#ifndef odb_api_FunctionLAST_H
+#define odb_api_FunctionLAST_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionLAST : public FunctionExpression {
+public:
+	FunctionLAST(const std::string&,const expression::Expressions&);
+	FunctionLAST(const FunctionLAST&);
+	~FunctionLAST(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionLAST& operator=(const FunctionLAST&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	bool isAggregate() const { return true; }
+
+	virtual void output(SQLOutput&) const;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionLAST& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionMATCH.cc b/odb_api/src/odb_api/FunctionMATCH.cc
new file mode 100755
index 0000000..97f7fd2
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMATCH.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/FunctionMATCH.h"
+#include "odb_api/SQLMATCHSubquerySession.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLSelect.h"
+
+#include <vector>
+#include <algorithm>
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+using namespace eckit;
+using namespace std;
+
+FunctionMATCH::FunctionMATCH(const std::string& name, const expression::Expressions& args, const SelectAST& selectAST)
+: FunctionExpression(name, args),
+  size_(args.size()),
+  subquery_(selectAST),
+  subqueryResult_()
+{}
+
+FunctionMATCH::FunctionMATCH(const FunctionMATCH& other)
+: FunctionExpression(other.name_, other.args_),
+  size_(other.args_.size()),
+  subquery_(other.subquery_),
+  subqueryResult_(other.subqueryResult_)
+{}
+
+FunctionMATCH::~FunctionMATCH() {}
+
+void FunctionMATCH::prepare(SQLSelect& sql)
+{
+    FunctionExpression::prepare(sql);
+
+    SQLMATCHSubquerySession session(*this);
+    SQLSelect* select(session.selectFactory().create(session, subquery_));
+    session.execute(dynamic_cast<SQLStatement&>(*select), (ecml::ExecutionContext*) 0);
+
+    std::stable_sort(subqueryResult_.begin(), subqueryResult_.end());
+}
+
+FunctionMATCH& FunctionMATCH::operator=(odb::sql::expression::function::FunctionMATCH const&)
+{
+    // TODO:
+    NOTIMP;
+    return *this;
+}
+
+void FunctionMATCH::collect(const std::vector<double>& v)
+{
+    subqueryResult_.push_back(v);
+}
+
+const type::SQLType* FunctionMATCH::type() const { return &type::SQLType::lookup("real"); } //TODO: bool?
+
+SQLExpression* FunctionMATCH::clone() const { return new FunctionMATCH(*this); }
+
+double FunctionMATCH::eval(bool& missing) const
+{
+    std::vector<double> vs (size_);
+    for (size_t i(0); i < size_; ++i)
+    {
+        bool missing (false);
+        vs[i] = args_[i]->eval(missing);
+    }
+    return std::binary_search(subqueryResult_.begin(), subqueryResult_.end(), vs);
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb 
diff --git a/odb_api/src/odb_api/FunctionMATCH.h b/odb_api/src/odb_api/FunctionMATCH.h
new file mode 100755
index 0000000..d9ef3e4
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMATCH.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionMATCH.h
+/// Piotr Kuchta - ECMWF October 2015
+
+#ifndef FunctionMATCH_H
+#define FunctionMATCH_H
+
+#include <vector>
+#include <set>
+
+#include "odb_api/FunctionExpression.h"
+#include "odb_api/SQLType.h"
+#include "odb_api/SQLAST.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionMATCH : public FunctionExpression {
+public:
+    FunctionMATCH(const std::string&, const odb::sql::expression::Expressions&, const odb::sql::SelectAST&);
+    FunctionMATCH(const FunctionMATCH&);
+    ~FunctionMATCH();
+
+    FunctionMATCH& operator=(const FunctionMATCH&);
+
+    SQLExpression* clone() const;
+
+    void collect(const std::vector<double>&);
+
+private:
+    size_t size_;
+    const SelectAST subquery_;
+    std::vector<std::vector<double> > subqueryResult_;
+
+    virtual const odb::sql::type::SQLType* type() const;
+    virtual double eval(bool& missing) const;
+    virtual void prepare(SQLSelect& sql);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionMATCH& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionMAX.cc b/odb_api/src/odb_api/FunctionMAX.cc
new file mode 100755
index 0000000..226b4d8
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMAX.cc
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <climits>
+#include <cfloat>
+
+#include "odb_api/FunctionMAX.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionMAX::FunctionMAX(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args),
+  value_(-DBL_MAX)
+{}
+
+FunctionMAX::FunctionMAX(const FunctionMAX& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_)
+{}
+
+SQLExpression* FunctionMAX::clone() const { return new FunctionMAX(*this); }
+
+const odb::sql::type::SQLType* FunctionMAX::type() const { return args_[0]->type(); }
+
+FunctionMAX::~FunctionMAX() {}
+
+double FunctionMAX::eval(bool& missing) const
+{
+	if (value_ == -DBL_MAX)
+		missing = true;
+
+	return value_;
+}
+
+void FunctionMAX::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = -DBL_MAX;
+}
+
+void FunctionMAX::output(SQLOutput& s) const 
+{ 
+	bool missing = false;
+	double d = eval(missing); 
+	type()->output(s, d, missing);
+}
+
+void FunctionMAX::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = -DBL_MAX;
+}
+
+void FunctionMAX::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(!missing)
+		if(value > value_)
+			value_ = value;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionMAX.h b/odb_api/src/odb_api/FunctionMAX.h
new file mode 100755
index 0000000..fd48c71
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMAX.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionMAX.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionMAX_H
+#define FunctionMAX_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionMAX : public FunctionExpression {
+public:
+	FunctionMAX(const std::string&,const expression::Expressions&);
+	FunctionMAX(const FunctionMAX&);
+	~FunctionMAX(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionMAX& operator=(const FunctionMAX&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	bool isAggregate() const { return true; }
+
+	virtual void output(SQLOutput&) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionMAX& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionMIN.cc b/odb_api/src/odb_api/FunctionMIN.cc
new file mode 100755
index 0000000..2ac65f5
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMIN.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <climits>
+#include <cfloat>
+
+#include "odb_api/FunctionMIN.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionMIN::FunctionMIN(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args),
+  value_(DBL_MAX)
+{}
+
+FunctionMIN::FunctionMIN(const FunctionMIN& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_)
+{}
+
+SQLExpression* FunctionMIN::clone() const { return new FunctionMIN(*this); }
+
+const odb::sql::type::SQLType* FunctionMIN::type() const { return args_[0]->type(); }
+
+FunctionMIN::~FunctionMIN() {}
+
+double FunctionMIN::eval(bool& missing) const
+{
+	if (value_ == DBL_MAX)
+		missing = true;
+
+	return value_;
+}
+
+void FunctionMIN::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionMIN::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = DBL_MAX;
+}
+
+void FunctionMIN::output(SQLOutput& s) const 
+{ 
+	bool missing = false;
+	double d = eval(missing); 
+	type()->output(s, d, missing);
+}
+
+void FunctionMIN::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(!missing)
+		if(value < value_)
+			value_ = value;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionMIN.h b/odb_api/src/odb_api/FunctionMIN.h
new file mode 100755
index 0000000..2578d7d
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionMIN.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionMIN.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionMIN_H
+#define FunctionMIN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionMIN : public FunctionExpression {
+public:
+	FunctionMIN(const std::string&,const expression::Expressions&);
+	FunctionMIN(const FunctionMIN&);
+	~FunctionMIN(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionMIN& operator=(const FunctionMIN&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	bool isAggregate() const { return true; }
+
+	virtual void output(SQLOutput&) const;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionMIN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionNORM.cc b/odb_api/src/odb_api/FunctionNORM.cc
new file mode 100755
index 0000000..da333a7
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNORM.cc
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "odb_api/FunctionNORM.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionNORM::FunctionNORM(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  value_(0),
+  resultNULL_(true)
+{}
+
+FunctionNORM::FunctionNORM(const FunctionNORM& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_),
+  resultNULL_(other.resultNULL_)
+{}
+
+SQLExpression* FunctionNORM::clone() const { return new FunctionNORM(*this); }
+
+FunctionNORM::~FunctionNORM() {}
+
+const type::SQLType* FunctionNORM::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionNORM::eval(bool& missing) const
+{
+	if (resultNULL_)
+	{
+		missing = true;
+		return (double) 0;
+	}
+    double lvalue =  (value_ > 0) ? sqrt(value_) : (double)0;
+	return lvalue;
+}
+
+void FunctionNORM::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = 0;
+}
+
+void FunctionNORM::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = 0;
+}
+
+void FunctionNORM::partialResult() 
+{
+	bool missing = false;
+	double x = args_[0]->eval(missing);
+	double y = args_[1]->eval(missing);
+	if(! missing)
+	{
+		value_ += x*y;
+		resultNULL_ = false;
+	}
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionNORM.h b/odb_api/src/odb_api/FunctionNORM.h
new file mode 100755
index 0000000..ff356b3
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNORM.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionNORM.h
+// ECMWF July 2010
+
+#ifndef FunctionNORM_H
+#define FunctionNORM_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionNORM : public FunctionExpression {
+public:
+	FunctionNORM(const std::string&,const expression::Expressions&);
+	FunctionNORM(const FunctionNORM&);
+	~FunctionNORM();
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionNORM& operator=(const FunctionNORM&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+
+	bool isAggregate() const { return true; }
+	bool resultNULL_;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionNORM& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionNOT_IN.cc b/odb_api/src/odb_api/FunctionNOT_IN.cc
new file mode 100755
index 0000000..861b459
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNOT_IN.cc
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/FunctionNOT_IN.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionNOT_IN::FunctionNOT_IN(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  size_(args.size() - 1)
+{}
+
+FunctionNOT_IN::FunctionNOT_IN(const FunctionNOT_IN& other)
+: FunctionExpression(other.name_, other.args_),
+  size_(other.args_.size() - 1)
+{}
+
+FunctionNOT_IN::~FunctionNOT_IN() {}
+
+SQLExpression* FunctionNOT_IN::clone() const { return new FunctionNOT_IN(*this); }
+
+const type::SQLType* FunctionNOT_IN::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionNOT_IN::eval(bool& missing) const
+{
+	const SQLExpression& x = *args_[size_];
+	for(size_t i = 0; i < size_; ++i)
+	{
+		double y = args_[i]->eval(missing);
+		if (FunctionEQ::equal(x, *args_[i], missing))
+			return false;
+	}
+
+	return true;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionNOT_IN.h b/odb_api/src/odb_api/FunctionNOT_IN.h
new file mode 100755
index 0000000..fd59ffd
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNOT_IN.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionNOT_IN.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionNOT_IN_H
+#define FunctionNOT_IN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionNOT_IN : public FunctionExpression {
+public:
+	FunctionNOT_IN(const std::string&, const expression::Expressions&);
+	FunctionNOT_IN(const FunctionNOT_IN&);
+	~FunctionNOT_IN();
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionNOT_IN& operator=(const FunctionNOT_IN&);
+
+	int size_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionNOT_IN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionNOT_NULL.cc b/odb_api/src/odb_api/FunctionNOT_NULL.cc
new file mode 100755
index 0000000..a54573e
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNOT_NULL.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionNOT_NULL.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionNOT_NULL::FunctionNOT_NULL(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionNOT_NULL::FunctionNOT_NULL(const FunctionNOT_NULL& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+FunctionNOT_NULL::~FunctionNOT_NULL() {}
+
+SQLExpression* FunctionNOT_NULL::clone() const { return new FunctionNOT_NULL(*this); }
+
+const type::SQLType* FunctionNOT_NULL::type() const { return &type::SQLType::lookup("real"); }
+
+// Don't set the missing flags
+double FunctionNOT_NULL::eval(bool&) const
+{
+	bool missing = false;
+#if 0
+	std::cout << "FunctionNOT_NULL " << (*args_[0])  << " " << args_[0]->eval(missing);
+	std::cout << " missing = " << missing << std::endl;
+#else
+	args_[0]->eval(missing);
+#endif
+
+	return !missing;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionNOT_NULL.h b/odb_api/src/odb_api/FunctionNOT_NULL.h
new file mode 100755
index 0000000..f8428b6
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNOT_NULL.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionNOT_NULL.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionNOT_NULL_H
+#define FunctionNOT_NULL_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionNOT_NULL : public FunctionExpression {
+public:
+	FunctionNOT_NULL(const std::string&,const expression::Expressions&);
+	FunctionNOT_NULL(const FunctionNOT_NULL&);
+	~FunctionNOT_NULL(); 
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	virtual const odb::sql::type::SQLType* type() const;
+	FunctionNOT_NULL& operator=(const FunctionNOT_NULL&);
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionNOT_NULL& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionNULL.cc b/odb_api/src/odb_api/FunctionNULL.cc
new file mode 100755
index 0000000..ff0916a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNULL.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionNULL.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionNULL::FunctionNULL(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args)
+{} 
+
+FunctionNULL::FunctionNULL(const FunctionNULL& other)
+: FunctionExpression(other.name_, other.args_)
+{} 
+
+FunctionNULL::~FunctionNULL() {}
+
+SQLExpression* FunctionNULL::clone() const { return new FunctionNULL(*this); }
+
+const type::SQLType* FunctionNULL::type() const { return &type::SQLType::lookup("real"); }
+
+// Don't set the missing flag
+double FunctionNULL::eval(bool& ) const
+{
+	bool missing = false;
+	args_[0]->eval(missing);
+	return missing;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionNULL.h b/odb_api/src/odb_api/FunctionNULL.h
new file mode 100755
index 0000000..76de752
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNULL.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionNULL.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionNULL_H
+#define FunctionNULL_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionNULL : public FunctionExpression {
+public:
+	FunctionNULL(const std::string&,const expression::Expressions&);
+	FunctionNULL(const FunctionNULL&);
+	~FunctionNULL(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	virtual const odb::sql::type::SQLType* type() const;
+	FunctionNULL& operator=(const FunctionNULL&);
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionNULL& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionNVL.cc b/odb_api/src/odb_api/FunctionNVL.cc
new file mode 100755
index 0000000..d98d1f0
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNVL.cc
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionNVL.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionNVL::FunctionNVL(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionNVL::FunctionNVL(const FunctionNVL& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+FunctionNVL::~FunctionNVL() {}
+
+SQLExpression* FunctionNVL::clone() const { return new FunctionNVL(*this); }
+
+const type::SQLType* FunctionNVL::type() const { return &type::SQLType::lookup("real"); }
+
+// Don't set the missing flag
+double FunctionNVL::eval(bool& ) const
+{
+	bool missing = false;
+	double x = args_[0]->eval(missing);
+	return missing ? args_[1]->eval(missing) : x;
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionNVL.h b/odb_api/src/odb_api/FunctionNVL.h
new file mode 100755
index 0000000..df00d90
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionNVL.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionNVL.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionNVL_H
+#define FunctionNVL_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionNVL : public FunctionExpression {
+public:
+	FunctionNVL(const std::string&,const expression::Expressions&);
+	FunctionNVL(const FunctionNVL&);
+	~FunctionNVL(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionNVL& operator=(const FunctionNVL&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionNVL& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionOR.cc b/odb_api/src/odb_api/FunctionOR.cc
new file mode 100755
index 0000000..6c8084c
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionOR.cc
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionOR.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionOR::FunctionOR(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionOR::FunctionOR(const FunctionOR& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+FunctionOR::~FunctionOR() {}
+
+SQLExpression* FunctionOR::clone() const { return new FunctionOR(*this); }
+
+const type::SQLType* FunctionOR::type() const { return &type::SQLType::lookup("real"); }
+
+double FunctionOR::eval(bool& missing) const
+{
+	return args_[0]->eval(missing) || args_[1]->eval(missing);
+}
+
+SQLExpression* FunctionOR::simplify(bool& changed) 
+{
+	SQLExpression* x = FunctionExpression::simplify(changed);
+	if(x) return x;
+
+	for(int i = 0; i < 2 ; i++)
+	{
+		bool missing = false;
+		if(args_[i]->isConstant())
+			if(args_[i]->eval(missing))
+			{
+                std::cout << "SYMPLIFY " << *this << " to 1" << std::endl;;
+				changed = true;
+				return SQLExpression::number(1);
+			}
+	}
+
+	return 0;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionOR.h b/odb_api/src/odb_api/FunctionOR.h
new file mode 100755
index 0000000..0c4adee
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionOR.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionOR.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionOR_H
+#define FunctionOR_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionOR : public FunctionExpression {
+public:
+	FunctionOR(const std::string&,const expression::Expressions&);
+	FunctionOR(const FunctionOR&);
+	~FunctionOR();
+
+// -- Overridden methods
+	SQLExpression* clone() const;
+
+	virtual double eval(bool& missing) const;
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual SQLExpression* simplify(bool&);
+
+private:
+// No copy allowed
+	FunctionOR& operator=(const FunctionOR&);
+
+// -- Friends
+    //friend std::ostream& operator<<(std::ostream& s,const FunctionOR& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionRGG_BOXLAT.cc b/odb_api/src/odb_api/FunctionRGG_BOXLAT.cc
new file mode 100755
index 0000000..db40a10
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRGG_BOXLAT.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionRGG_BOXLAT.h"
+#include "odb_api/RggRegionCache.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+
+FunctionRGG_BOXLAT::FunctionRGG_BOXLAT(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionRGG_BOXLAT::FunctionRGG_BOXLAT(const FunctionRGG_BOXLAT& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionRGG_BOXLAT::clone() const { return new FunctionRGG_BOXLAT(*this); }
+
+FunctionRGG_BOXLAT::~FunctionRGG_BOXLAT() {}
+
+const type::SQLType* FunctionRGG_BOXLAT::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionRGG_BOXLAT::eval(bool& missing) const
+{
+    double lat_degrees = args_[0]->eval(missing);
+    double lon_degrees = args_[1]->eval(missing);
+    lon_degrees = lon_degrees; // not used variable
+    double resol = args_[2]->eval(missing);
+    double res=0.;
+    RggRegionCache p;
+    res = p.get_midlat(resol, lat_degrees);
+    return res;
+}
+
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionRGG_BOXLAT.h b/odb_api/src/odb_api/FunctionRGG_BOXLAT.h
new file mode 100755
index 0000000..45d41d7
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRGG_BOXLAT.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionRGG_BOXLAT.h
+/// ECMWF July 2010
+
+#ifndef FunctionRGG_BOXLAT_H
+#define FunctionRGG_BOXLAT_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionRGG_BOXLAT : public FunctionExpression {
+public:
+	FunctionRGG_BOXLAT(const std::string&, const expression::Expressions&);
+	FunctionRGG_BOXLAT(const FunctionRGG_BOXLAT&);
+	~FunctionRGG_BOXLAT(); 
+
+	SQLExpression* clone() const;
+private:
+// No copy allowed
+	FunctionRGG_BOXLAT& operator=(const FunctionRGG_BOXLAT&);
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+	virtual const odb::sql::type::SQLType* type() const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionRGG_BOXLAT& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionRGG_BOXLON.cc b/odb_api/src/odb_api/FunctionRGG_BOXLON.cc
new file mode 100755
index 0000000..0c7ca9b
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRGG_BOXLON.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionRGG_BOXLON.h"
+#include "odb_api/RggRegionCache.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionRGG_BOXLON::FunctionRGG_BOXLON(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionRGG_BOXLON::FunctionRGG_BOXLON(const FunctionRGG_BOXLON& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionRGG_BOXLON::clone() const { return new FunctionRGG_BOXLON(*this); }
+
+FunctionRGG_BOXLON::~FunctionRGG_BOXLON() {}
+
+const type::SQLType* FunctionRGG_BOXLON::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionRGG_BOXLON::eval(bool& missing) const
+{
+    double lat_degrees = args_[0]->eval(missing);
+    double lon_degrees = args_[1]->eval(missing);
+    double resol = args_[2]->eval(missing);
+    RggRegionCache p;
+    double res;
+    res = p.get_midlon(resol, lat_degrees, lon_degrees);
+
+	return res;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionRGG_BOXLON.h b/odb_api/src/odb_api/FunctionRGG_BOXLON.h
new file mode 100755
index 0000000..a62e0ba
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRGG_BOXLON.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionRGG_BOXLON.h
+// ECMWF July 2010
+
+#ifndef FunctionRGG_BOXLON_H
+#define FunctionRGG_BOXLON_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionRGG_BOXLON : public FunctionExpression {
+public:
+	FunctionRGG_BOXLON(const std::string&,const expression::Expressions&);
+	FunctionRGG_BOXLON(const FunctionRGG_BOXLON&);
+	~FunctionRGG_BOXLON(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionRGG_BOXLON& operator=(const FunctionRGG_BOXLON&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionRGG_BOXLON& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionRLIKE.cc b/odb_api/src/odb_api/FunctionRLIKE.cc
new file mode 100644
index 0000000..d17e0a4
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRLIKE.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/FunctionRLIKE.h"
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/SQLType.h"
+#include "eckit/utils/Regex.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+using namespace odb::sql::type;
+
+const type::SQLType* FunctionRLIKE::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionRLIKE::FunctionRLIKE(const FunctionRLIKE& other)
+: FunctionExpression(other.name_, other.args_),
+  re_(new eckit::Regex(*other.re_))
+{}
+
+FunctionRLIKE::FunctionRLIKE(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  re_()
+{}
+
+SQLExpression* FunctionRLIKE::clone() const { return new FunctionRLIKE(*this); }
+
+FunctionRLIKE::~FunctionRLIKE() {}
+
+void FunctionRLIKE::trimStringInDouble(char* &p, size_t& len)
+{
+	len = 0;
+	for(; len < sizeof(double) && isprint(p[len]); ++len)
+		;
+	for(; len > 0 && isspace(p[len - 1]); --len)
+		;
+	size_t plen = len;
+	for (char *pp = p; isspace(*p) && p < pp + plen;)
+	{
+
+		++p;
+		--len;
+	}
+}
+
+
+void FunctionRLIKE::prepare(SQLSelect& sql)
+{
+    FunctionExpression::prepare(sql);
+
+    SQLExpression &l(*args_[0]), &r(*args_[1]);
+
+	if (l.type()->getKind() != SQLType::stringType
+        || r.type()->getKind() != SQLType::stringType)
+        throw eckit::UserError("Arguments of RLIKE must be of string type");
+
+    bool missing(false);
+    double v2 (r.eval(missing));
+    char *p2 = reinterpret_cast<char*>(&v2);
+    size_t len2 (sizeof(double));
+
+    trimStringInDouble(p2, len2);
+
+    std::string re (p2, len2);
+    //eckit::Log::info() << "FunctionRLIKE::prepare: regex: '" << re << "'" << std::endl;
+    re_.reset(new eckit::Regex(re));
+}
+
+bool FunctionRLIKE::match(const SQLExpression& l, const SQLExpression& r, bool& missing) const
+{
+    double v1 = l.eval(missing);
+    if (missing)
+        return false;
+
+    char *p1 (reinterpret_cast<char*>(&v1));
+    size_t len1 (sizeof(double));
+
+    trimStringInDouble(p1, len1);
+    std::string s1(p1, len1);
+
+    bool ret = re_->match(s1);
+    //eckit::Log::info() << "FunctionRLIKE::match '" << s1 << "' => " << ret << std::endl;
+    return ret;
+}
+
+double FunctionRLIKE::eval(bool& missing) const { return match(*args_[0], *args_[1], missing); }
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionRLIKE.h b/odb_api/src/odb_api/FunctionRLIKE.h
new file mode 100644
index 0000000..eff4d79
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRLIKE.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \File FunctionRLIKE.h
+/// Piotr Kuchta - ECMWF Sep 2014
+
+#ifndef FunctionRLIKE_H
+#define FunctionRLIKE_H
+
+#include "odb_api/FunctionExpression.h"
+#include "eckit/utils/Regex.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionRLIKE : public FunctionExpression {
+public:
+	FunctionRLIKE(const std::string&,const expression::Expressions&);
+	FunctionRLIKE(const FunctionRLIKE&);
+	~FunctionRLIKE(); 
+
+	bool match(const SQLExpression& l, const SQLExpression& r, bool& missing) const;
+	static void trimStringInDouble(char* &p, size_t& len);
+
+	SQLExpression* clone() const;
+    void prepare(SQLSelect&);
+private:
+// No copy allowed
+	FunctionRLIKE& operator=(const FunctionRLIKE&);
+
+	std::auto_ptr<eckit::Regex> re_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionRLIKE& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionRMS.cc b/odb_api/src/odb_api/FunctionRMS.cc
new file mode 100755
index 0000000..70aa86a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRMS.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "odb_api/FunctionRMS.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionRMS::FunctionRMS(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  count_(0),
+  squares_(0)
+{}
+
+FunctionRMS::FunctionRMS(const FunctionRMS& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_),
+  squares_(other.squares_)
+{}
+
+FunctionRMS::~FunctionRMS() {}
+
+SQLExpression* FunctionRMS::clone() const { return new FunctionRMS(*this); } 
+
+const type::SQLType* FunctionRMS::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionRMS::eval(bool& missing) const
+{
+	if(! count_) {
+		missing = true;
+		return 0;
+	}
+
+	return sqrt(squares_/count_);
+}
+
+void FunctionRMS::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	count_ = 0;
+	squares_ = 0;
+}
+
+void FunctionRMS::cleanup(SQLSelect& sql)
+{
+//cout << "Cleanup  FunctionRMS " << count_ << " " << value_ << std::endl;
+	FunctionExpression::cleanup(sql);
+	squares_ = 0;
+	count_ = 0;
+}
+
+void FunctionRMS::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(! missing)
+	{
+		squares_ += value * value;
+		count_++;
+	}
+//	else cout << "missing" << std::endl;
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionRMS.h b/odb_api/src/odb_api/FunctionRMS.h
new file mode 100755
index 0000000..d1fae94
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionRMS.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionRMS.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionRMS_H
+#define FunctionRMS_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionRMS : public FunctionExpression {
+public:
+	FunctionRMS(const std::string&,const expression::Expressions&);
+	FunctionRMS(const FunctionRMS&);
+	~FunctionRMS(); 
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionRMS& operator=(const FunctionRMS&);
+
+	unsigned long long count_;
+	double squares_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+
+	bool isAggregate() const { return true; }
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionRMS& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionROWNUMBER.cc b/odb_api/src/odb_api/FunctionROWNUMBER.cc
new file mode 100755
index 0000000..faf248c
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionROWNUMBER.cc
@@ -0,0 +1,66 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionROWNUMBER.h
+/// Piotr Kuchta - (C) ECMWF July 2009
+
+#include "odb_api/FunctionROWNUMBER.h"
+#include "odb_api/SQLSelect.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionROWNUMBER::FunctionROWNUMBER(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  count_(0)
+{}
+
+
+FunctionROWNUMBER::FunctionROWNUMBER(const FunctionROWNUMBER& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_)
+{}
+
+SQLExpression* FunctionROWNUMBER::clone() const { return new FunctionROWNUMBER(*this); }
+
+FunctionROWNUMBER::~FunctionROWNUMBER() {}
+
+void FunctionROWNUMBER::print(std::ostream& s) const { s << "rownumber()"; }
+
+double FunctionROWNUMBER::eval(bool& missing) const { return *count_; }
+
+void FunctionROWNUMBER::prepare(SQLSelect& sql)
+{
+	count_ = &sql.total_;
+}
+
+void FunctionROWNUMBER::cleanup(SQLSelect& sql) {}
+
+bool FunctionROWNUMBER::isConstant() const { return false; }
+
+SQLExpression* FunctionROWNUMBER::simplify(bool&) { return 0; }
+
+void FunctionROWNUMBER::partialResult() { /*NOTIMP;*/ }
+
+const odb::sql::type::SQLType* FunctionROWNUMBER::type() const { return &odb::sql::type::SQLType::lookup("integer"); }
+
+void FunctionROWNUMBER::output(std::ostream& s) const
+{
+    bool missing;
+    s << static_cast<unsigned long>(eval(missing));
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionROWNUMBER.h b/odb_api/src/odb_api/FunctionROWNUMBER.h
new file mode 100755
index 0000000..b095f08
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionROWNUMBER.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionROWNUMBER.h
+/// Piotr Kuchta - (C) ECMWF July 2009
+
+#ifndef FunctionROWNUMBER_H
+#define FunctionROWNUMBER_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionROWNUMBER : public FunctionExpression {
+public:
+	FunctionROWNUMBER(const std::string&, const expression::Expressions&);
+	FunctionROWNUMBER(const FunctionROWNUMBER&);
+	~FunctionROWNUMBER();
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void output(std::ostream& s) const;
+
+	SQLExpression* clone() const;
+
+protected:
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual bool isConstant() const;
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	virtual SQLExpression* simplify(bool&);
+	bool isAggregate() const { return false; }
+
+private:
+// No copy allowed
+	FunctionROWNUMBER& operator=(const FunctionROWNUMBER&);
+
+	unsigned long long* count_;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionROWNUMBER& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionSTDEV.cc b/odb_api/src/odb_api/FunctionSTDEV.cc
new file mode 100755
index 0000000..206fbd0
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionSTDEV.cc
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "odb_api/FunctionSTDEV.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionSTDEV::FunctionSTDEV(const std::string& name,const expression::Expressions& args)
+: FunctionVAR(name, args)
+{}
+
+FunctionSTDEV::FunctionSTDEV(const FunctionSTDEV& other)
+: FunctionVAR(static_cast<const FunctionVAR&>(other))
+{}
+
+FunctionSTDEV::~FunctionSTDEV() {}
+
+SQLExpression* FunctionSTDEV::clone() const { return new FunctionSTDEV(*this); }
+
+const type::SQLType* FunctionSTDEV::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionSTDEV::eval(bool& missing) const
+{
+	double x = FunctionVAR::eval(missing);
+	if (x < 0) x = 0;
+	return sqrt(x);
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql 
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionSTDEV.h b/odb_api/src/odb_api/FunctionSTDEV.h
new file mode 100755
index 0000000..d08afeb
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionSTDEV.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionSTDEV.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionSTDEV_H
+#define FunctionSTDEV_H
+
+#include "odb_api/FunctionVAR.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionSTDEV : public FunctionVAR {
+public:
+	FunctionSTDEV(const std::string&, const expression::Expressions&);
+	FunctionSTDEV(const FunctionSTDEV&);
+	~FunctionSTDEV(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionSTDEV& operator=(const FunctionSTDEV&);
+
+	virtual double eval(bool& missing) const;
+	virtual const odb::sql::type::SQLType* type() const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionSTDEV& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionSUM.cc b/odb_api/src/odb_api/FunctionSUM.cc
new file mode 100755
index 0000000..cdb0f41
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionSUM.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionSUM.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionSUM::FunctionSUM(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  value_(0),
+  resultNULL_(true)
+{}
+
+FunctionSUM::FunctionSUM(const FunctionSUM& other)
+: FunctionExpression(other.name_, other.args_),
+  value_(other.value_),
+  resultNULL_(other.resultNULL_)
+{}
+
+FunctionSUM::~FunctionSUM() {}
+
+SQLExpression* FunctionSUM::clone() const { return new FunctionSUM(*this); }
+
+const type::SQLType* FunctionSUM::type() const { return &type::SQLType::lookup("double"); }
+
+double FunctionSUM::eval(bool& missing) const
+{
+	if (resultNULL_)
+		missing = true;
+	return value_;
+}
+
+void FunctionSUM::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = 0;
+}
+
+void FunctionSUM::cleanup(SQLSelect& sql)
+{
+	FunctionExpression::cleanup(sql);
+	value_ = 0;
+}
+
+void FunctionSUM::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(! missing)
+	{
+		value_ += value;
+		resultNULL_ = false;
+	}
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sqol
+} // namespace odb 
diff --git a/odb_api/src/odb_api/FunctionSUM.h b/odb_api/src/odb_api/FunctionSUM.h
new file mode 100755
index 0000000..a53eb23
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionSUM.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionSUM.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionSUM_H
+#define FunctionSUM_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionSUM : public FunctionExpression {
+public:
+	FunctionSUM(const std::string&,const expression::Expressions&);
+	FunctionSUM(const FunctionSUM&);
+	~FunctionSUM(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionSUM& operator=(const FunctionSUM&);
+
+	unsigned long long count_;
+	double value_;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+	virtual double eval(bool& missing) const;
+	bool isAggregate() const { return true; }
+	bool resultNULL_;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionSUM& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionTDIFF.cc b/odb_api/src/odb_api/FunctionTDIFF.cc
new file mode 100755
index 0000000..fd0793f
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTDIFF.cc
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/DateTime.h"
+#include "odb_api/FunctionTDIFF.h"
+#include "odb_api/MDI.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionTDIFF::FunctionTDIFF(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args)
+{}
+
+FunctionTDIFF::FunctionTDIFF(const FunctionTDIFF& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionTDIFF::clone() const { return new FunctionTDIFF(*this);  }
+
+FunctionTDIFF::~FunctionTDIFF() {}
+
+double FunctionTDIFF::eval(bool& missing) const
+{
+    int indate = (int) args_[0]->eval(missing);
+    int intime = (int) args_[1]->eval(missing);
+    int andate = (int) args_[2]->eval(missing);
+    int antime = (int) args_[3]->eval(missing);
+
+    int year_target = indate/10000;
+    int month_target = (indate%10000)/100;
+    int day_target = indate%100;
+    int hour_target = intime/10000;
+    int min_target = (intime%10000)/100;
+    int sec_target = intime%100;
+
+    int year_anal = andate/10000;
+    int month_anal = (andate%10000)/100;
+    int day_anal = andate%100;
+    int hour_anal = antime/10000;
+    int min_anal = (antime%10000)/100;
+    int sec_anal = antime%100;
+
+    int seconds = odb::MDI::realMDI();
+
+    utils::DateTime d1(year_target, month_target, day_target,
+                   hour_target, min_target, sec_target);
+    utils::DateTime d2(year_anal, month_anal, day_anal, 
+                   hour_anal, min_anal, sec_anal);
+
+    seconds = d1.secondsDateMinusDate(d2);
+
+    return seconds;
+}
+
+const odb::sql::type::SQLType* FunctionTDIFF::type() const { return &odb::sql::type::SQLType::lookup("integer"); }
+
+void FunctionTDIFF::output(std::ostream& s) const
+{
+	bool missing;
+    s << static_cast<long long int>(eval(missing));
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionTDIFF.h b/odb_api/src/odb_api/FunctionTDIFF.h
new file mode 100755
index 0000000..e37adfe
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTDIFF.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionTDIFF.h
+// ECMWF July 2010
+
+#ifndef FunctionTDIFF_H
+#define FunctionTDIFF_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionTDIFF : public FunctionExpression {
+public:
+	FunctionTDIFF(const std::string&,const expression::Expressions&);
+	FunctionTDIFF(const FunctionTDIFF&);
+	~FunctionTDIFF(); 
+
+// -- Overridden methods
+	virtual void output(std::ostream& s) const;
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	FunctionTDIFF& operator=(const FunctionTDIFF&);
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionTDIFF& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionTHIN.cc b/odb_api/src/odb_api/FunctionTHIN.cc
new file mode 100755
index 0000000..f447c33
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTHIN.cc
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionTHIN.h
+/// (C) ECMWF July 2010
+
+#include "odb_api/FunctionTHIN.h"
+#include "odb_api/SQLSelect.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionTHIN::FunctionTHIN(const std::string& name, const expression::Expressions& args)
+: FunctionExpression(name, args),
+  count_(0)
+{}
+
+FunctionTHIN::FunctionTHIN(const FunctionTHIN& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_)
+{}
+
+FunctionTHIN::~FunctionTHIN() {}
+
+SQLExpression* FunctionTHIN::clone() const { return new FunctionTHIN(*this); }
+
+const odb::sql::type::SQLType* FunctionTHIN::type() const { return &odb::sql::type::SQLType::lookup("integer"); }
+
+void FunctionTHIN::output(std::ostream& s) const
+{
+    bool missing;
+    s << static_cast<unsigned long>(eval(missing));
+}
+
+void FunctionTHIN::print(std::ostream& s) const
+{
+    s << "THIN()";
+}
+
+double FunctionTHIN::eval(bool& missing) const
+{
+    int every_nth = (int) args_[0]->eval(missing);
+    if ((*count_ - 1) % every_nth == 0)
+      return 1.0;
+    else
+      return 0.0;
+}
+
+void FunctionTHIN::prepare(SQLSelect& sql)
+{
+    FunctionExpression::prepare(sql);
+     count_ = &sql.count_;
+}
+
+void FunctionTHIN::cleanup(SQLSelect& sql)
+{
+}
+
+bool FunctionTHIN::isConstant() const
+{
+    return false;
+}
+
+SQLExpression* FunctionTHIN::simplify(bool&)
+{
+    return 0;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/FunctionTHIN.h b/odb_api/src/odb_api/FunctionTHIN.h
new file mode 100755
index 0000000..4ded439
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTHIN.h
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FunctionTHIN.h
+/// ECMWF July 2010
+
+#ifndef FunctionTHIN_H
+#define FunctionTHIN_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionTHIN : public FunctionExpression {
+public:
+	FunctionTHIN(const std::string&, const expression::Expressions&);
+	FunctionTHIN(const FunctionTHIN&);
+	~FunctionTHIN(); 
+
+	SQLExpression* clone() const;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void output(std::ostream& s) const;
+protected:
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual bool isConstant() const;
+	virtual double eval(bool& missing) const;
+	virtual SQLExpression* simplify(bool&);
+	bool isAggregate() const { return false; }
+
+private:
+// No copy allowed
+	FunctionTHIN& operator=(const FunctionTHIN&);
+
+	unsigned long long* count_;
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionTHIN& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionTIMESTAMP.cc b/odb_api/src/odb_api/FunctionTIMESTAMP.cc
new file mode 100755
index 0000000..fdaecb2
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTIMESTAMP.cc
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+#include <limits.h>
+
+#include "odb_api/FunctionTIMESTAMP.h"
+#include "odb_api/MDI.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+FunctionTIMESTAMP::FunctionTIMESTAMP(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args)
+{}
+
+FunctionTIMESTAMP::FunctionTIMESTAMP(const FunctionTIMESTAMP& other)
+: FunctionExpression(other.name_, other.args_)
+{}
+
+SQLExpression* FunctionTIMESTAMP::clone() const { return new FunctionTIMESTAMP(*this); }
+
+FunctionTIMESTAMP::~FunctionTIMESTAMP() {}
+
+double FunctionTIMESTAMP::eval(bool& missing) const
+{
+    double indate = args_[0]->eval(missing);
+    double intime = args_[1]->eval(missing);
+// Merge "YYYYMMDD" and "HHMMSS" into "YYYYMMDDHHMMSS"
+    double outstamp = odb::MDI::realMDI(); // initialized to missing data indicator; indicates error
+    if (indate >=0 && indate <= INT_MAX &&
+        intime >= 0 && intime <= 240000) {
+      long long int lldate = (long long int) indate;
+      long long int lltime = (long long int) intime;
+      long long int tstamp = lldate * 1000000ll + lltime;
+      outstamp = tstamp;
+      outstamp = trunc(outstamp);
+    }
+
+	return outstamp;
+}
+
+const odb::sql::type::SQLType* FunctionTIMESTAMP::type() const { return &odb::sql::type::SQLType::lookup("integer"); }
+
+void FunctionTIMESTAMP::output(std::ostream& s) const
+{
+	bool missing;
+    s << static_cast<long long int>(eval(missing));
+}
+
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionTIMESTAMP.h b/odb_api/src/odb_api/FunctionTIMESTAMP.h
new file mode 100755
index 0000000..7827bcd
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionTIMESTAMP.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionTIMESTAMP.h
+// ECMWF July 2010
+
+#ifndef FunctionTIMESTAMP_H
+#define FunctionTIMESTAMP_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionTIMESTAMP : public FunctionExpression {
+public:
+	FunctionTIMESTAMP(const std::string&,const expression::Expressions&);
+	FunctionTIMESTAMP(const FunctionTIMESTAMP&);
+	~FunctionTIMESTAMP(); 
+
+	SQLExpression* clone() const;
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void output(std::ostream& s) const;
+
+private:
+	FunctionTIMESTAMP& operator=(const FunctionTIMESTAMP&);
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionTIMESTAMP& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/FunctionVAR.cc b/odb_api/src/odb_api/FunctionVAR.cc
new file mode 100755
index 0000000..14db23a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionVAR.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/FunctionVAR.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+const type::SQLType* FunctionVAR::type() const { return &type::SQLType::lookup("double"); }
+
+FunctionVAR::FunctionVAR(const std::string& name,const expression::Expressions& args)
+: FunctionExpression(name,args),
+  count_(0),
+  value_(0),
+  squares_(0)
+{}
+
+FunctionVAR::FunctionVAR(const FunctionVAR& other)
+: FunctionExpression(other.name_, other.args_),
+  count_(other.count_),
+  value_(other.value_),
+  squares_(other.squares_)
+{}
+
+FunctionVAR::~FunctionVAR() {}
+
+SQLExpression* FunctionVAR::clone() const { return new FunctionVAR(*this); }
+
+double FunctionVAR::eval(bool& missing) const
+{
+	double x,y;
+
+	if(!count_) {
+		missing = true;
+		return 0;
+	}
+
+	x = value_   / count_;
+	y = squares_ / count_ ;
+
+	return y - x*x;
+}
+
+void FunctionVAR::prepare(SQLSelect& sql)
+{
+	FunctionExpression::prepare(sql);
+	value_ = 0;
+	count_ = 0;
+	squares_ = 0;
+}
+
+void FunctionVAR::cleanup(SQLSelect& sql)
+{
+//cout << "Cleanup  FunctionVAR " << count_ << " " << value_ << std::endl;
+	FunctionExpression::cleanup(sql);
+	squares_ = 0;
+	value_ = 0;
+	count_ = 0;
+}
+
+void FunctionVAR::partialResult() 
+{
+	bool missing = false;
+	double value = args_[0]->eval(missing);
+	if(!missing)
+	{
+		value_   += value;
+		squares_ += value * value;
+		count_++;
+	}
+//	else cout << "missing" << std::endl;
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/FunctionVAR.h b/odb_api/src/odb_api/FunctionVAR.h
new file mode 100755
index 0000000..d248f9a
--- /dev/null
+++ b/odb_api/src/odb_api/FunctionVAR.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File FunctionVAR.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef FunctionVAR_H
+#define FunctionVAR_H
+
+#include "odb_api/FunctionExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class FunctionVAR : public FunctionExpression {
+public:
+	FunctionVAR(const std::string&, const expression::Expressions&);
+	FunctionVAR(const FunctionVAR&);
+	~FunctionVAR();
+
+// -- Overridden methods
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual void partialResult();
+
+	bool isAggregate() const { return true; }
+
+	SQLExpression* clone() const;
+protected:
+
+// -- Overridden methods
+	virtual double eval(bool& missing) const;
+
+private:
+// No copy allowed
+	FunctionVAR& operator=(const FunctionVAR&);
+
+	unsigned long long count_;
+	double value_;
+	double squares_;
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const FunctionVAR& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace function
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/GribCodes.cc b/odb_api/src/odb_api/GribCodes.cc
new file mode 100644
index 0000000..60ab6c8
--- /dev/null
+++ b/odb_api/src/odb_api/GribCodes.cc
@@ -0,0 +1,147 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file GribCodesBase.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/config/Resource.h"
+#include "eckit/eckit.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/GribCodes.h"
+#include "odb_api/StringTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+
+ClassCodes* GribCodes::classCodes_ = 0; //ClassCodes();
+TypeCodes* GribCodes::typeCodes_ = 0; //TypeCodes();
+StreamCodes* GribCodes::streamCodes_ = 0; //StreamCodes();
+GroupCodes* GribCodes::obsgroupCodes_ = 0;//  GroupCodes();
+
+void GribCodes::load()
+{
+	if (! classCodes_) classCodes_ = new ClassCodes();
+	if (! typeCodes_) typeCodes_ = new TypeCodes();
+	if (! streamCodes_) streamCodes_ = new StreamCodes();
+	if (! obsgroupCodes_) obsgroupCodes_ =  new GroupCodes();
+}
+
+std::string GribCodes::numeric(const std::string& keyword, const std::string& alphanumeric)
+{
+	load();
+	const std::string kw = StringTools::upper(keyword);
+	if (kw == "TYPE")
+	{
+		// HACK:
+		if (alphanumeric == "OFB") return "263";
+		if (alphanumeric == "MFB") return "262";
+
+		return typeCodes_->numeric(alphanumeric);
+	}
+	if (kw == "CLASS") return classCodes_->numeric(alphanumeric);
+	if (kw == "STREAM") return streamCodes_->numeric(alphanumeric);
+	if (kw == "OBSGROUP") return obsgroupCodes_->numeric(alphanumeric);
+
+	throw eckit::UserError(std::string("'") + keyword + "' is not a known GRIB keyword");
+	return "Don't want the compiler to warn me about non-void function not returning anything";
+}
+
+std::string GribCodes::alphanumeric(const std::string& keyword, const std::string& numeric)
+{
+    load();
+    const std::string kw (StringTools::upper(keyword));
+    if (kw == "TYPE") 
+    {
+        // HACK:
+        if (numeric == "262") return "MFB";
+        if (numeric == "263") return "OFB";
+
+        return typeCodes_->alphanumeric(numeric);
+    }
+    if (kw == "CLASS") return classCodes_->alphanumeric(numeric);
+    if (kw == "STREAM") return streamCodes_->alphanumeric(numeric);
+    if (kw == "OBSGROUP") return obsgroupCodes_->alphanumeric(numeric);
+
+    throw eckit::UserError(std::string("'") + keyword + "' is not a known GRIB keyword");
+    return "Don't want the compiler to warn me about non-void function not returning anything";
+}
+
+GribCodesBase::GribCodesBase(const PathName& fileName)
+: configFileName_( std::string(Resource<std::string>("$ODB_API_CODES", "/usr/local/apps/odb_api/codes/")) + "/" + fileName),
+  fieldDelimiter_(" \t"),
+  mapsLoaded_(false),
+  numericIndex_(0),
+  alphanumericIndex_(1)
+{
+    Log::debug() << "GribCodesBase::GribCodesBase: configFileName_:" << configFileName_ << std::endl;
+    readConfig(configFileName_);
+}
+
+GribCodesBase::GribCodesBase(const PathName& fileName, const std::string& fieldDelimiter, size_t numericIndex, size_t alphanumericIndex)
+: configFileName_(std::string(Resource<std::string>("$ODB_API_CODES", "/usr/local/apps/odb_api/codes/")) + "/" + fileName),
+  fieldDelimiter_(fieldDelimiter),
+  mapsLoaded_(false),
+  numericIndex_(numericIndex),
+  alphanumericIndex_(alphanumericIndex)
+{
+    Log::debug() << "GribCodesBase::GribCodesBase: configFileName_:" << configFileName_ << std::endl;
+    readConfig(configFileName_);
+}
+
+void GribCodesBase::readConfig(const PathName& fileName)
+{
+	Log::debug() << "GribCodesBase::readConfig(" << fileName << ")" << std::endl;
+	numeric2alpha_.clear();
+	alpha2numeric_.clear();
+
+	std::vector<std::string> lines (StringTool::readLines(fileName));
+	for (size_t i = 0; i < lines.size(); ++i)
+	{
+		std::vector<std::string> words = StringTools::split(fieldDelimiter_, lines[i]);
+		if (words.size() >= 2)
+		{
+			std::string num (StringTools::trim(words[numericIndex_]));
+			std::string alpha (StringTools::trim(words[alphanumericIndex_]));
+			numeric2alpha_[num] = alpha;
+			alpha2numeric_[alpha] = num;
+			Log::debug() << "GribCodesBase::readConfig: num='" << num << "' alpha='" << alpha << "'" << std::endl;
+		}
+	}
+
+	mapsLoaded_ = true;
+}
+
+std::string GribCodesBase::numeric(const std::string& alphanumeric)
+{
+	//if (!mapsLoaded_) readConfig(configFileName_);
+	if (alpha2numeric_.find(alphanumeric) == alpha2numeric_.end())
+		throw eckit::UserError(std::string("Alphanumeric code '") + alphanumeric + "' not found in '" + configFileName_ + "'");
+	return alpha2numeric_[alphanumeric];
+}
+
+//TODO:
+//int numericAsInt(const std::string& alphanumeric);
+
+std::string GribCodesBase::alphanumeric(const std::string& numeric) {
+	//if (!mapsLoaded_) readConfig(configFileName_);
+	if (numeric2alpha_.find(numeric) == numeric2alpha_.end())
+		throw eckit::UserError(std::string("Numeric code ") + numeric + " not found in '" + configFileName_ + "'");
+	return numeric2alpha_[numeric];
+}
+
+// TODO:
+//std::string alphanumeric(int);
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/GribCodes.h b/odb_api/src/odb_api/GribCodes.h
new file mode 100644
index 0000000..3a2fa78
--- /dev/null
+++ b/odb_api/src/odb_api/GribCodes.h
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file GribCodes.h
+///
+/// @author Piotr Kuchta, ECMWF, April 2011
+
+#ifndef GribCodes_H
+#define GribCodes_H
+
+#include "eckit/filesystem/PathName.h"
+
+namespace eckit { class PathName; }
+
+
+namespace odb {
+
+class GribCodesBase {
+public:
+	GribCodesBase(const eckit::PathName&);
+	GribCodesBase(const eckit::PathName&, const std::string& fieldDelimiter, size_t numericIndex, size_t alphanumericIndex);
+
+	virtual void readConfig(const eckit::PathName& fileName);
+
+	virtual std::string numeric(const std::string& alphanumeric);
+	virtual std::string alphanumeric(const std::string& numeric);
+private:
+	eckit::PathName configFileName_;
+	std::string fieldDelimiter_;
+	bool mapsLoaded_;
+	std::map<std::string,std::string> numeric2alpha_;
+	std::map<std::string,std::string> alpha2numeric_;
+
+    size_t numericIndex_;
+    size_t alphanumericIndex_;
+};
+
+
+class ClassCodes : public GribCodesBase {
+public:
+	ClassCodes() : GribCodesBase("class.table") {}
+};
+
+class TypeCodes : public GribCodesBase {
+public:
+	TypeCodes() : GribCodesBase("type.table") {}
+};
+
+class StreamCodes : public GribCodesBase {
+public:
+	StreamCodes() : GribCodesBase("stream.table") {}
+};
+
+class GroupCodes : public GribCodesBase {
+public:
+	GroupCodes() : GribCodesBase("group.txt", ";", 0, 3) {}
+};
+
+class GribCodes {
+public:
+	static std::string numeric(const std::string& keyword, const std::string& alphanumeric);
+	static std::string alphanumeric(const std::string& keyword, const std::string& numeric);
+
+private:
+	static void load();
+	static ClassCodes* classCodes_;
+	static TypeCodes* typeCodes_;
+	static StreamCodes* streamCodes_;
+	static GroupCodes* obsgroupCodes_;
+};
+
+
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/HashTable.cc b/odb_api/src/odb_api/HashTable.cc
new file mode 100644
index 0000000..6ae2535
--- /dev/null
+++ b/odb_api/src/odb_api/HashTable.cc
@@ -0,0 +1,230 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "odb_api/HashTable.h"
+
+
+using namespace eckit;
+
+namespace odb {
+namespace codec {
+
+HashTable::HashTable()
+: nextIndex_(0),
+  strings_(),
+  cloned_(false)
+{
+	//Log::info() << "HashTable@" << this << "::HashTable()" << std::endl;
+	for (size_t i = 0; i < sizeof(table) / sizeof(hashrec *); i++)
+		table[i] = 0;
+}
+
+HashTable::HashTable(const HashTable& other)
+: nextIndex_(other.nextIndex_),
+  strings_(other.strings_),
+  cloned_(true)
+{
+	for (size_t i = 0; i < sizeof(table) / sizeof(hashrec *); i++)
+		table[i] = other.table[i] ? new hashrec(*other.table[i]) : 0;
+}
+
+HashTable& HashTable::operator=(const HashTable& other)
+{
+	cloned_ = true;
+	strings_.clear();
+
+	nextIndex_ = other.nextIndex_;
+	for (size_t i = 0; i < sizeof(table) / sizeof(hashrec *); i++)
+	{
+		delete table[i];
+		table[i] = other.table[i] ? new hashrec(*other.table[i]) : 0;
+	}
+	
+	strings_ = other.strings_;
+	return *this;
+}
+
+HashTable* HashTable::clone() {
+	HashTable *h = new HashTable;
+	*h = *this;
+	return h;
+}
+
+HashTable::~HashTable()
+{
+	for (size_t i = 0; i < sizeof(table) / sizeof(hashrec *); i++)
+		delete table[i];
+}
+
+int64_t lsb(int64_t x) { return (*reinterpret_cast<uint64_t*>(&x)) & 0xffffffff; }
+
+int HashTable::hash(const char *name)
+{
+    ASSERT(name);
+
+	int64_t n (0);
+
+	while (*name)
+		n = lsb(n + lsb(int64_t(*name++ - 'A') + int64_t(lsb(n << 5))));
+
+	if (n < 0)
+	{
+		int64_t m (int64_t( -n ) / SIZE);
+		n = lsb(n + lsb(lsb((m + 1)) * SIZE));
+	}
+
+	return n % SIZE;
+}
+
+void HashTable::store(const char *name)
+{
+	if (cloned_)
+	{
+		// TODO: leave the current (last seen by the CharCodec) value ?
+		for (size_t i = 0; i < sizeof(table) / sizeof(hashrec *); i++)
+		{
+			delete table[i];
+			table[i] = 0;
+		}
+		strings_.clear();
+		nextIndex_ = 0;
+		cloned_ = false;
+	}
+
+	int32_t n = hash(name);
+	hashrec *h = table[n];
+
+	while(h)
+	{
+		if(h->name == name)
+		{
+			h->cnt++;
+			return;
+		}
+		h = h->next;
+	}
+
+	h = new hashrec(table[n], name, 1, nextIndex_++);
+	table[n] = h;
+
+	//if (nextIndex_ - 1 > strings_.size())
+
+	strings_.push_back(name);
+	//ASSERT(strings_.size() == nextIndex_);
+	//Log::info() << "HashTable@" << this << "::store(" << name << "): n = " << n << ", nextIndex = " << nextIndex_  << std::endl;
+}
+
+int32_t HashTable::findIndex(const char *name)
+{
+	hashrec *h;
+	int32_t n;
+
+	n = hash(name);
+	h = table[n];
+
+	while(h)
+	{
+		if(h->name == name)
+			return h->index;
+		h = h->next;
+	}
+
+	Log::error() << "[" << name << "] not in hash" << std::endl;
+	return -1;
+}
+
+void HashTable::dumpTable(std::ostream &out) const
+{
+	out << "HashTable@" << this << "::dumpTable: begin" << std::endl;
+
+	for(int32_t i = 0; i < SIZE; i++)
+	{
+		hashrec *h  = table[i];
+
+		while(h)
+		{
+			out << "[" << h->name << "] -> " << h->cnt << " " << h->index << std::endl;
+			h = h->next;
+		}
+	}
+
+	out << "HashTable@" << this << "::dumpTable: end" << std::endl;
+}
+
+template<typename BYTEORDER> 
+void HashTable::save(DataStream<BYTEORDER> &f)
+{
+	//Log::info() << "HashTable@" << this << "::save BEGIN" << std::endl;
+
+	int32_t n = 0;
+
+	f.writeInt32(nextIndex_);
+
+	for(int32_t i = 0; i < SIZE; i++)
+	{
+		hashrec *h  = table[i];
+
+		while(h)
+		{
+			//Log::info() << "HashTable::save: '" << h->name << "'" << std::endl;
+
+			f.writeString(h->name);
+			f.writeInt32(h->cnt);
+			f.writeInt32(h->index);
+			n++;
+			h = h->next;
+		}
+	}
+
+	ASSERT(n == nextIndex_);
+
+	//Log::info() << "HashTable@" << this << "::save END" << std::endl;
+}
+
+template<typename BYTEORDER> 
+void HashTable::load(DataStream<BYTEORDER> &f)
+{
+	//Log::info() << "HashTable@" << this << "::load BEGIN" << std::endl;
+
+	f.readInt32(nextIndex_);
+
+	strings_.resize(nextIndex_);
+
+	for(int32_t i = 0; i < nextIndex_; i++)
+	{
+		std::string s;
+		f.readString(s);
+
+		int32_t cnt;
+		f.readInt32(cnt);
+
+		int32_t index;
+		f.readInt32(index);
+
+		//Log::info() << "HashTable::load: " << index << " : '" << s << "'" << std::endl;
+
+		ASSERT(index < nextIndex_);
+		strings_[index] = std::string(s);
+
+	}
+	//Log::info() << "HashTable@" << this << "::load END" << std::endl;
+}
+
+template void HashTable::save<SameByteOrder>(DataStream<SameByteOrder>&);
+template void HashTable::save<OtherByteOrder>(DataStream<OtherByteOrder>&);
+
+template void HashTable::load<OtherByteOrder>(DataStream<OtherByteOrder>&);
+template void HashTable::load<SameByteOrder>(DataStream<SameByteOrder>&);
+
+} // namespace codec
+} // namespace odb 
diff --git a/odb_api/src/odb_api/HashTable.h b/odb_api/src/odb_api/HashTable.h
new file mode 100644
index 0000000..84e116f
--- /dev/null
+++ b/odb_api/src/odb_api/HashTable.h
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef HashTable_H
+#define HashTable_H
+
+#include "odb_api/DataStream.h"
+
+namespace odb {
+
+class Reader;
+namespace sql { class SQLSelect; }
+
+namespace codec {
+
+struct hashrec {
+	hashrec(hashrec *next, const char *name, int cnt, int index)
+	: next(next), name(name), cnt(cnt), index(index)
+	{}
+
+	~hashrec() { delete next; }
+
+	hashrec *next;
+    std::string name;
+	int32_t cnt;
+	int32_t index;
+
+//private:
+//	// No copy allowed.
+	explicit hashrec(const hashrec& other)
+	: next(other.next ? new hashrec(*other.next) : 0),
+	  name(other.name),
+	  cnt(other.cnt),
+	  index(other.index)
+	{}
+
+	hashrec& operator=(const hashrec& other)
+	{
+		if (&other == this) return *this;
+		
+		name = other.name;
+		cnt = other.cnt;
+		index = other.index;
+		next = other.next ? new hashrec(*other.next) : 0;
+		return *this;
+	}
+};
+
+class HashTable {
+	static int const SIZE = 65535;
+public:
+	HashTable();
+	~HashTable();
+	HashTable* clone();
+
+	template<typename BYTEORDER> void save(DataStream<BYTEORDER> &);
+	template<typename BYTEORDER> void load(DataStream<BYTEORDER> &);
+
+    void dumpTable(std::ostream &out) const;
+	void store(const char *name);
+	int32_t findIndex(const char *name);
+    const std::vector<std::string>& strings() const { return strings_; }
+	int32_t nextIndex() const { return nextIndex_; }
+
+//private:
+	HashTable(const HashTable&);
+	HashTable& operator=(const HashTable&);
+
+protected:
+	int32_t nextIndex_;
+	hashrec* table[SIZE];
+    std::vector<std::string> strings_;
+	
+	int32_t hash(const char *);
+private:
+	bool cloned_;
+};
+
+} // namespace codec
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Header.cc b/odb_api/src/odb_api/Header.cc
new file mode 100644
index 0000000..e229db2
--- /dev/null
+++ b/odb_api/src/odb_api/Header.cc
@@ -0,0 +1,188 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Header.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "odb_api/DataStream.h"
+#include "odb_api/InMemoryDataHandle.h"
+#include "odb_api/MD5.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/UnsafeInMemoryDataHandle.h"
+
+namespace odb {
+
+template <typename OWNER>
+Header<OWNER>::Header(OWNER& owner)
+: owner_(owner),
+  dataSize_(0),
+  rowsNumber_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR)
+{} 
+
+template <typename OWNER>
+Header<OWNER>::~Header ()
+{}
+
+template <typename OWNER>
+void Header<OWNER>::load()
+{
+	DataStream<SameByteOrder> f(owner_.dataHandle());
+
+	uint16_t c;
+	f.readUInt16(c);
+	ASSERT(c == ODA_MAGIC_NUMBER);
+
+	unsigned char cc;
+	f.readUChar(cc); ASSERT(cc == 'O');
+	f.readUChar(cc); ASSERT(cc == 'D');
+	f.readUChar(cc); ASSERT(cc == 'A');
+
+	loadAfterMagic();
+}
+
+template <typename OWNER>
+void Header<OWNER>::loadAfterMagic()
+{
+	DataStream<SameByteOrder> f(owner_.dataHandle());
+
+	f.readInt32(byteOrder_);
+
+	if (byteOrder_ != BYTE_ORDER_INDICATOR)
+	{
+		DataStream<OtherByteOrder> ds(owner_.dataHandle());
+		load(ds);
+	}
+	else
+	{
+		DataStream<SameByteOrder> ds(owner_.dataHandle());
+		load(ds);
+	}
+}
+
+template <typename OWNER>
+template <typename DATASTREAM>
+void Header<OWNER>::load(DATASTREAM &ff)
+{
+	int32_t formatVersionMajor;
+	ff.readInt32(formatVersionMajor);
+	ASSERT("File format version not supported" && formatVersionMajor <= FORMAT_VERSION_NUMBER_MAJOR);
+
+	int32_t formatVersionMinor;
+	ff.readInt32(formatVersionMinor);
+	ASSERT("File format version not supported" && formatVersionMinor <= FORMAT_VERSION_NUMBER_MINOR && formatVersionMinor > 3);
+
+	std::string headerDigest; 
+	ff.readString(headerDigest);
+
+	MemoryBlock buffer(0);
+	ff.readBuffer(buffer);
+
+	MD5 md5;
+	md5.add(buffer, buffer.size());
+	std::string actualHeaderDigest = md5.digest();
+
+	if (! (headerDigest == actualHeaderDigest))
+	{
+		ASSERT(headerDigest == actualHeaderDigest);
+	}
+	
+	PrettyFastInMemoryDataHandle memoryHandle(buffer);
+	DataStream<typename DATASTREAM::ByteOrderType, PrettyFastInMemoryDataHandle> f(memoryHandle);
+
+	// 0 means we don't know offset of next header.
+	int64_t nextFrameOffset;
+	f.readInt64(nextFrameOffset);
+	dataSize_ = nextFrameOffset;
+	const_cast<MetaData&>(owner_.columns()).dataSize(dataSize_);
+
+	// Reserved, not used yet.
+	int64_t prevFrameOffset;
+	f.readInt64(prevFrameOffset);
+	ASSERT(prevFrameOffset == 0);
+
+	// TODO: increase file format version
+
+	int64_t numberOfRows;
+	f.readInt64(numberOfRows);
+	rowsNumber_ = numberOfRows;
+	const_cast<MetaData&>(owner_.columns()).rowsNumber(rowsNumber_);
+
+	eckit::Log::debug() << "Header::load: numberOfRows = " << numberOfRows << std::endl;
+
+	// Flags -> ODAFlags
+	Flags flags;
+	f.readFlags(flags);
+
+	f.readProperties(owner_.properties_);
+
+	const_cast<MetaData&>(owner_.columns()).load(f);
+}
+
+template <typename BYTEORDER, typename DATAHANDLE>
+void serializeHeader(DATAHANDLE &dh, size_t dataSize, size_t rowsNumber, const Properties& properties, const MetaData& columns)
+{
+	DataStream<BYTEORDER, DATAHANDLE> ff(dh);
+
+	// Header.
+	uint16_t c = ODA_MAGIC_NUMBER;
+	ff.writeUInt16(c);
+
+	c = 'O'; ff.writeChar(c); 
+	c = 'D'; ff.writeChar(c); 
+	c = 'A'; ff.writeChar(c);
+
+	int32_t byteOrder = BYTE_ORDER_INDICATOR;
+	ff.writeInt32(byteOrder);
+
+	int32_t versionMajor = FORMAT_VERSION_NUMBER_MAJOR;
+	int32_t versionMinor = FORMAT_VERSION_NUMBER_MINOR;
+	ff.writeInt32(versionMajor);
+	ff.writeInt32(versionMinor);
+
+	InMemoryDataHandle memoryHandle;
+	DataStream<BYTEORDER> f(memoryHandle);
+
+	// Reserved.	
+	int64_t nextFrameOffset = dataSize;
+	f.writeInt64(nextFrameOffset);
+
+	// Reserved.	
+	int64_t prevFrameOffset = 0;
+	f.writeInt64(prevFrameOffset);
+
+	int64_t numberOfRows = rowsNumber;
+	f.writeInt64(numberOfRows);
+
+	Flags flags(10, 0);
+	f.writeFlags(flags);
+
+	f.writeProperties(properties);
+
+	columns.save(f);
+
+	eckit::Length len = memoryHandle.openForRead();
+
+	MemoryBlock buffer(len);
+	eckit::Length readBytes = memoryHandle.read(buffer, len);
+	ASSERT(len == readBytes);
+
+	MD5 md5;
+	md5.add(buffer, len);
+	std::string headerDigest = md5.digest();
+	ff.writeString(headerDigest);
+
+	ff.writeBuffer(buffer);
+}
+
+} //namespace odb 
+
diff --git a/odb_api/src/odb_api/Header.h b/odb_api/src/odb_api/Header.h
new file mode 100644
index 0000000..e0ba759
--- /dev/null
+++ b/odb_api/src/odb_api/Header.h
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Header.h
+///
+/// @author Piotr Kuchta, April 2009
+
+#ifndef Header_H
+#define Header_H
+
+#include <stdint.h>
+#include "eckit/eckit.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+const int32_t BYTE_ORDER_INDICATOR = 1;
+const uint16_t ODA_MAGIC_NUMBER = 0xffff;
+
+const int32_t FORMAT_VERSION_NUMBER_MAJOR = 0;
+const int32_t FORMAT_VERSION_NUMBER_MINOR = 5;
+
+template <typename OWNER>
+class Header 
+{
+public:
+	Header (OWNER &owner);
+	~Header ();
+
+	size_t dataSize() const { return dataSize_; }
+	void dataSize(size_t n) { dataSize_ = n; }
+
+	size_t rowsNumber() const { return rowsNumber_; }
+	void rowsNumber(size_t n) { rowsNumber_ = n; }
+
+	int32_t byteOrder() { return byteOrder_; }
+	//size_t dataSizeOffset() const { return sizeof(uint16_t) + 3*1 + 3*sizeof(int32_t) + /*MD5*/ sizeof(int32_t)+32 + sizeof(int32_t); }
+	//size_t rowsNumberOffset() const { return dataSizeOffset() + 2 * sizeof(int64_t); }
+
+	void load();
+
+	//template <typename DATAHANDLE> void save(DATAHANDLE &);
+
+	void loadAfterMagic();
+private:
+// No copy allowed.
+    Header(const Header&);
+    Header& operator=(const Header&);
+
+	template <typename DATASTREAM> void load(DATASTREAM &);
+
+	OWNER& owner_;
+	size_t dataSize_;
+	size_t rowsNumber_;
+
+	int32_t byteOrder_;
+};
+
+} // namespace odb 
+
+#include "odb_api/Header.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/InMemoryDataHandle.cc b/odb_api/src/odb_api/InMemoryDataHandle.cc
new file mode 100644
index 0000000..1aba883
--- /dev/null
+++ b/odb_api/src/odb_api/InMemoryDataHandle.cc
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/InMemoryDataHandle.h"
+
+namespace odb {
+
+InMemoryDataHandle::InMemoryDataHandle()
+: buf_(), readIterator_(buf_.begin())
+{}
+
+InMemoryDataHandle::InMemoryDataHandle(const MemoryBlock& buffer)
+: buf_((const unsigned char *) buffer, (const unsigned char *) buffer + buffer.size()),
+  readIterator_(buf_.begin())
+{
+}
+
+void InMemoryDataHandle::buffer(const MemoryBlock& buffer)
+{
+	buf_ = std::vector<unsigned char>((const unsigned char *) buffer, (const unsigned char *) buffer + buffer.size());
+	readIterator_ = buf_.begin();
+}
+
+InMemoryDataHandle::~InMemoryDataHandle()
+{}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/InMemoryDataHandle.h b/odb_api/src/odb_api/InMemoryDataHandle.h
new file mode 100644
index 0000000..06b7fca
--- /dev/null
+++ b/odb_api/src/odb_api/InMemoryDataHandle.h
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file InMemoryDataHandle.h
+/// Piotr Kuchta - ECMWF April 2009
+
+#ifndef InMemoryDataHandle_H
+#define InMemoryDataHandle_H
+
+#include "eckit/io/DataHandle.h"
+#include "odb_api/MemoryBlock.h"
+
+namespace odb {
+
+class InMemoryDataHandle : public eckit::DataHandle {
+public:
+    InMemoryDataHandle();
+    InMemoryDataHandle(const MemoryBlock&);
+
+    virtual ~InMemoryDataHandle();
+
+	void buffer(const MemoryBlock&);
+
+
+	bool hasSomeData() { return readIterator_ != buf_.end(); }
+
+	/// Return estimated length.
+    virtual eckit::Length openForRead()
+	{
+		readIterator_ = buf_.begin();
+		return buf_.size();
+	}
+
+	// Receive estimated length.
+    void openForWrite(const eckit::Length&) { buf_.clear(); ASSERT(buf_.size() == 0); }
+
+	// Receive estimated length
+    void openForAppend(const eckit::Length&) {}
+
+    long read(void* p, long n)
+	{
+		char *dst = reinterpret_cast<char *>(p);
+		long i = 0;
+		for ( ; i < n && readIterator_ != buf_.end(); ++i, ++readIterator_)
+			dst[i] = *readIterator_;
+		return i;
+	}
+
+    long write(const void* pd, long n)
+	{
+		const unsigned char *p = reinterpret_cast<const unsigned char*>(pd);
+		buf_.insert(buf_.end(), p, p + n);
+		return n;
+	}
+
+    void close() {}
+
+    void rewind()                {}
+	eckit::Length estimate()            { return buf_.size(); }
+	eckit::Offset position()            { return buf_.size(); }
+
+	void print(std::ostream& s) const { /*TODO*/ }
+private:
+
+	std::vector<unsigned char> buf_;
+	std::vector<unsigned char>::iterator readIterator_;
+
+	friend std::ostream& operator<<(std::ostream& s, const InMemoryDataHandle& handle) 
+		{ handle.print(s); return s;}
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Indexer.cc b/odb_api/src/odb_api/Indexer.cc
new file mode 100644
index 0000000..a59ea63
--- /dev/null
+++ b/odb_api/src/odb_api/Indexer.cc
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Indexer.h"
+#include "odb_api/RowsCounter.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+using namespace eckit;
+
+namespace odb {
+
+BlockOffsets Indexer::offsetsOfBlocks(const PathName &db)
+{
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+    BlockOffsets r;
+
+	MDR mdReader(db);
+	MDR::iterator it (mdReader.begin());
+	MDR::iterator end (mdReader.end());
+	for (; it != end; ++it)
+    {
+        Offset offset ((**it).blockStartOffset());
+        Length length ((**it).blockEndOffset() - (**it).blockStartOffset());
+
+        r.push_back(make_pair(offset,length));
+    }
+
+	return r;
+}
+
+std::vector<eckit::PathName> Indexer::createIndex(const vector<PathName> &dataFiles)
+{
+    std::vector<eckit::PathName> indices;
+    for (size_t i(0); i < dataFiles.size(); ++i)
+    {
+        const PathName index (dataFiles[i] + ".idx");
+
+        createIndex(dataFiles[i], index);
+        indices.push_back(index);
+    }
+    return indices;
+}
+
+void Indexer::createIndex(const PathName &dataFile, const PathName& indexFile)
+{
+    BlockOffsets offsets (offsetsOfBlocks(dataFile));
+
+    odb::MetaData metaData;
+    metaData
+        .addColumn("block_begin", "INTEGER")
+        .addColumn("block_length", "INTEGER")
+        .addColumn("seqno", "INTEGER")
+        .addColumn("n_rows", "INTEGER");
+
+    odb::Writer<> write (indexFile);
+    odb::Writer<>::iterator writer (write.begin());
+    writer->columns(metaData);
+    writer->writeHeader();
+
+    for (size_t i(0); i < offsets.size(); ++i)
+    {
+        Offset blockBegin (offsets[i].first);
+        Length blockLength (offsets[i].second);
+
+        PartFileHandle h(dataFile, blockBegin, blockLength);
+        h.openForRead();
+
+        int prevSeqno (-1);
+        int nRows (0);
+
+        odb::Select in("select seqno;", h); 
+        for (odb::Select::iterator it (in.begin()), end (in.end()); 
+             it != end; 
+             ++it)
+        {
+            int seqno ( (*it)[0] );
+            if (seqno == prevSeqno)
+                ++nRows;
+            else
+            {
+                if (nRows > 0)
+                {
+                    (*writer)[0] = blockBegin;
+                    (*writer)[1] = blockLength;
+                    (*writer)[2] = prevSeqno;
+                    (*writer)[3] = nRows;
+                    ++writer;
+                    nRows = 0;
+                }
+                prevSeqno = seqno;
+                ++nRows;
+            }
+        }
+        if (nRows > 0)
+        {
+            (*writer)[0] = blockBegin;
+            (*writer)[1] = blockLength;
+            (*writer)[2] = prevSeqno;
+            (*writer)[3] = nRows;
+            ++writer;
+            nRows = 0;
+        }
+    }
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/Indexer.h b/odb_api/src/odb_api/Indexer.h
new file mode 100644
index 0000000..c5c7538
--- /dev/null
+++ b/odb_api/src/odb_api/Indexer.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, November 2015
+
+#ifndef odb_api_Indexer_H
+#define odb_api_Indexer_H
+
+namespace odb {
+
+typedef std::vector<std::pair<eckit::Offset,eckit::Length> > BlockOffsets;
+typedef unsigned long long ullong;
+
+class Indexer {
+public:
+	static void createIndex(const eckit::PathName&, const eckit::PathName&);
+	static std::vector<eckit::PathName> createIndex(const std::vector<eckit::PathName>&);
+
+    static ullong countRows(const std::vector<eckit::PathName>&, const std::vector<eckit::PathName>&);
+
+private:
+    static BlockOffsets offsetsOfBlocks(const eckit::PathName&);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/IteratorFacade.h b/odb_api/src/odb_api/IteratorFacade.h
new file mode 100644
index 0000000..3d999b8
--- /dev/null
+++ b/odb_api/src/odb_api/IteratorFacade.h
@@ -0,0 +1,184 @@
+/// @file   IteratorFacade.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_ITERATORFACADE_H_
+#define ODBLIB_ITERATORFACADE_H_
+
+#include <iterator>
+
+namespace odb {
+
+template <typename I, typename C, typename V, typename R, typename P>
+class IteratorFacade;
+
+class IteratorFacadeAccess
+{
+    template <typename I, typename C, typename V, typename R, typename P>
+    friend class IteratorFacade;
+
+    template <typename Facade>
+    static typename Facade::reference dereference(const Facade& f)
+    {
+        return f.dereference();
+    }
+
+    template <typename Facade1, typename Facade2>
+    static bool equal(const Facade1& f1, const Facade2& f2)
+    {
+        return f1.equal(f2);
+    }
+
+    template <typename Facade>
+    static void increment(Facade& f)
+    {
+        f.increment();
+    }
+
+    template <typename Facade>
+    static void decrement(Facade& f)
+    {
+        f.decrement();
+    }
+
+    template <typename Facade>
+    static void advance(Facade& f, typename Facade::difference_type n)
+    {
+        f.advance(n);
+    }
+
+    template <typename Facade1, typename Facade2>
+    static typename Facade1::difference_type distance(const Facade1& f1,
+            const Facade2& f2)
+    {
+        return f1.distance(f2);
+    }
+
+    IteratorFacadeAccess();
+};
+
+template <typename Iterator, typename Category, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+
+class IteratorFacade
+    : public std::iterator<Category, Value, std::ptrdiff_t, Reference, Pointer>
+{
+public:
+    typedef std::ptrdiff_t difference_type;
+    typedef Value value_type;
+    typedef Reference reference;
+    typedef Pointer pointer;
+    typedef Category iterator_category;
+
+    reference operator*() const
+    {
+        return IteratorFacadeAccess::dereference(this->iterator());
+    }
+
+    pointer operator->() const
+    {
+        return &IteratorFacadeAccess::dereference(this->iterator());
+    }
+
+    bool operator==(const Iterator& other) const
+    {
+        return IteratorFacadeAccess::equal(this->iterator(), other);
+    }
+
+    bool operator!=(const Iterator& other) const
+    {
+        return !IteratorFacadeAccess::equal(this->iterator(), other);
+    }
+
+    Iterator& operator++()
+    {
+        IteratorFacadeAccess::increment(this->iterator());
+        return this->iterator();
+    }
+
+    Iterator& operator--()
+    {
+        IteratorFacadeAccess::decrement(this->iterator());
+        return this->iterator();
+    }
+
+    Iterator& operator+=(difference_type n)
+    {
+        IteratorFacadeAccess::advance(this->iterator(), n);
+        return this->iterator();
+    }
+
+    Iterator& operator-=(difference_type n)
+    {
+        IteratorFacadeAccess::advance(this->iterator(), -n);
+        return this->iterator();
+    }
+
+    Iterator operator+(difference_type n) const
+    {
+        Iterator it(this->iterator());
+        IteratorFacadeAccess::advance(it, n);
+        return it;
+    }
+
+    Iterator operator-(difference_type n) const
+    {
+        Iterator it(this->iterator());
+        IteratorFacadeAccess::advance(it, -n);
+        return it;
+    }
+
+    difference_type operator-(const Iterator& other) const
+    {
+        return IteratorFacadeAccess::distance(this->iterator(), other);
+    }
+
+private:
+    Iterator& iterator()
+    {
+        return static_cast<Iterator&>(*this);
+    }
+
+    const Iterator& iterator() const
+    {
+        return static_cast<const Iterator&>(*this);
+    }
+};
+
+template <typename Iterator, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+class InputIteratorFacade
+  : public IteratorFacade<Iterator, std::input_iterator_tag,
+           Value, Reference, Pointer>
+{};
+
+template <typename Iterator, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+class OutputIteratorFacade
+  : public IteratorFacade<Iterator, std::output_iterator_tag,
+           Value, Reference, Pointer>
+{};
+
+template <typename Iterator, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+class ForwardIteratorFacade
+  : public IteratorFacade<Iterator, std::forward_iterator_tag,
+           Value, Reference, Pointer>
+{};
+
+template <typename Iterator, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+class BidirectionalIteratorFacade
+  : public IteratorFacade<Iterator, std::bidirectional_iterator_tag,
+           Value, Reference, Pointer>
+{};
+
+template <typename Iterator, typename Value,
+          typename Reference = Value&, typename Pointer = Value*>
+class RandomIteratorFacade
+  : public IteratorFacade<Iterator, std::random_access_iterator_tag,
+           Value, Reference, Pointer>
+{};
+
+} // namespace odb
+
+#endif // ODBLIB_ITERATORFACADE_H_
diff --git a/odb_api/src/odb_api/IteratorProxy.h b/odb_api/src/odb_api/IteratorProxy.h
new file mode 100644
index 0000000..217ef63
--- /dev/null
+++ b/odb_api/src/odb_api/IteratorProxy.h
@@ -0,0 +1,303 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file IteratorProxy.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef IteratorProxy_H
+#define IteratorProxy_H
+
+#include "odb_api/ColumnType.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/Types.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+#ifdef SWIGPYTHON
+
+#include <Python.h>
+
+#include "odb_api/Column.h"
+#include "odb_api/odbcapi.h"
+
+struct ODBStopIteration : public std::exception {
+	const char* what() const throw() { return "end of data"; }
+};
+
+struct ODBIndexError : public std::exception {
+	const char* what() const throw() { return "index out of range"; }
+};
+
+extern "C" void python_api_start()
+{
+    odb_start();
+}
+
+#endif
+
+namespace odb {
+
+class Reader;
+class ReaderIterator;
+class MetaData;
+class MetaDataReaderIterator;
+class SelectIterator;
+
+
+template <typename ITERATOR, typename O, typename DATA>
+class IteratorProxy;
+
+template <typename ITERATOR, typename O, typename DATA, typename ITERATOR_PROXY>
+class Row_
+{
+public:
+#ifdef SWIGPYTHON
+	Row_() : it_() {}
+#endif
+	Row_(ITERATOR_PROXY& it) : it_(&it) {}
+
+	DATA& operator[](size_t i) { return (*it_)->data()[i]; }
+	DATA* data() { return const_cast<DATA*>(((*it_).iter_)->data()); }
+	DATA& data(size_t i) { return ((*it_).iter_)->data(i); }
+	int integer(size_t i) { return int((*it_)->data()[i]); }
+	std::string string(int i)
+	{
+		const char *s = reinterpret_cast<const char *>(&data()[i]);
+		size_t j = 0;
+		for (; j < sizeof(double) && s[j]; ++j)
+			; 
+		return std::string(s, j);
+	}
+
+	const MetaData& columns() { return ((*it_).iter_)->columns(); }
+    void setNumberOfColumns(size_t n) { ((*it_).iter_)->setNumberOfColumns(n); }
+	const MetaData& columns(const MetaData& md) { return ((*it_).iter_)->columns(md); }
+	bool isNewDataset() { return ((*it_).iter_)->isNewDataset(); }
+	bool isMissing(size_t i) { return ((*it_).iter_)->columns()[i]->missingValue() == (*it_)->data()[i]; }
+	double missingValue(size_t i) { return ((*it_).iter_)->columns()[i]->missingValue(); }
+
+	int setColumn(size_t index, const std::string& name, ColumnType type)
+	{ return (*((*it_).iter_)).setColumn(index, name, type); }
+
+	int setBitfieldColumn(size_t index, const std::string& name, ColumnType type, BitfieldDef b)
+	{ return ((*it_).iter_)->setBitfieldColumn(index, name, type, b); }
+
+	void missingValue(size_t index, double v) { ((*it_).iter_)->missingValue(index, v); }
+
+	void writeHeader() { (*((*it_).iter_)).writeHeader(); }
+	void close() { ((*it_).iter_)->close(); }
+
+	template <typename T> unsigned long pass1(T b, const T e) { return ((*it_).iter_)->pass1(b, e); }
+
+	ITERATOR& operator*() { return *((*it_).iter_); }
+private:
+	ITERATOR_PROXY* it_;
+};
+
+template <typename ITERATOR, typename O = Reader, typename DATA = double>
+class IteratorProxy
+{
+public:
+	typedef Row_<ITERATOR,O,DATA,IteratorProxy> Row;
+
+#ifdef SWIGPYTHON
+	IteratorProxy() : iter_(), row_(*this) {}
+#endif
+
+	IteratorProxy(ITERATOR* iter) : iter_(iter), row_(*this)
+	{ if(iter_) ++iter_->refCount_; }
+
+    IteratorProxy(const IteratorProxy& other) : iter_(other.iter_), row_(*this)
+	{ if (iter_) ++iter_->refCount_; }
+
+	~IteratorProxy()
+	{
+		if(iter_ && (--iter_->refCount_ == 0))
+			delete iter_;
+	}
+
+    IteratorProxy& operator=(const IteratorProxy& other)
+	{
+		if (iter_ == other.iter_)
+			return *this;
+
+		if (iter_ && (--iter_->refCount_ == 0))
+			delete iter_;
+
+		iter_ = other.iter_;
+		++iter_->refCount_;
+		return *this;
+	}
+
+	Row* operator->() { return &row_; }
+
+    Row& operator*() { return row_; }
+
+    bool operator!=(const IteratorProxy&) 
+    { 
+        return iter_ != 0 && !iter_->noMore_; 
+    }
+
+    IteratorProxy& operator++()
+	{
+		iter_->next(iter_->context_);
+		return *this;
+	}
+
+#ifdef SWIGPYTHON
+	size_t __len__() { return iter_->columns().size(); } 
+
+	PyObject* getitem(const char* s)
+	{
+        std::string name(s);
+		if (iter_->columns().hasColumn(name))
+			return getitem(iter_->columns().columnIndex(name));
+		else
+			throw ODBIndexError();
+	}
+
+	PyObject* getitem(int i)
+	{
+		Column& column = *iter_->columns()[i];
+		double d = iter_->data()[i];
+		if (d == column.missingValue())
+			Py_RETURN_NONE;
+
+		switch (column.type()) {
+		case STRING:
+		{
+			const char *s = reinterpret_cast<const char *>(&d);
+			size_t j = 0;
+			for (; j < sizeof(double) && s[j]; ++j)
+				; 
+			return PyString_FromStringAndSize(s, j);
+		}
+		case INTEGER: return PyLong_FromDouble(d);
+		case BITFIELD:
+		{
+			//cerr << "BITFIELD" << std::endl;
+			typedef unsigned long B;
+			char buf[sizeof(B) + 1];
+			char *s = buf;
+		
+			B n = d;
+			B mask = 1 << (sizeof(B) - 1);
+			for(size_t j = 0; j < sizeof(B); ++j)
+			{
+				*s++ = (n & mask) ? '1' : '0';
+				mask >>= 1;
+			}
+			buf[sizeof(B)] = 0;
+			return PyString_FromStringAndSize(buf, sizeof(B) + 1);
+		}
+		default: return PyFloat_FromDouble(d);
+		}
+	}
+
+	PyObject* __getitem__(PyObject* i)
+	{
+		//cerr << "__getitem__: start: " << PyString_AsString(PyObject_Repr(i)) << std::endl;
+		if (PyTuple_Check(i))
+		{
+			Py_ssize_t n = PyTuple_Size(i);
+			PyObject* l = PyTuple_New(n);
+			for(int j (0); j < n; ++j)
+			{
+				PyObject* o = PyTuple_GetItem(i, j);
+				PyTuple_SetItem(l, j, __getitem__(o));
+			}
+			return l;
+		}
+		if (PyList_Check(i))
+		{
+			Py_ssize_t n (PyList_Size(i));
+			PyObject* l (PyTuple_New(n));
+			for(ssize_t j (0); j < n; ++j)
+			{
+				PyObject* o (PyList_GetItem(i, j));
+				PyTuple_SetItem(l, j, __getitem__(o));
+			}
+			return l;
+		}
+		if (PyString_Check(i))
+		{
+			//cerr << "__getitem__: start: PyString " << PyString_AsString(PyObject_Repr(i)) << std::endl;
+			return getitem(PyString_AsString(i));
+		}
+		if (PySlice_Check(i))
+		{
+			//cerr << "__getitem__: we've got a PySliceObject here: ";
+			return getslice((PySliceObject*) i);
+		}
+
+		long li = PyLong_AsLong(i);
+		return getitem(li);
+	}
+
+	PyObject* getslice(PySliceObject* slice)
+	{
+		//cerr << "__getslice__(PySliceObject*):" << std::endl;
+		Py_ssize_t start = 0, stop = 0, step = 0, slicelength = 0;
+		PySlice_GetIndicesEx(slice, __len__(), &start, &stop, &step, &slicelength);
+		
+		return getslice(start, stop, step, slicelength);
+	}
+
+	PyObject* getslice(Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step, Py_ssize_t slicelength)
+	{
+		ssize_t maxIndex = __len__();
+		if (start < 0 || start > maxIndex) throw ODBIndexError();
+		if (stop < 0 || stop > maxIndex) throw ODBIndexError();
+
+		//cerr << "__getslice__: start=" << start << ", stop=" << stop << ", step=" << step << ", slicelength=" << slicelength << std::endl; 
+
+		size_t outputSize = 0;
+		for (int index = start; (step > 0) ? (index < stop) : (index > stop); index += step)
+			++outputSize;
+		PyObject* l = PyList_New(outputSize);
+
+		size_t outIndex = 0;
+		for (int index = start; (step > 0) ? (index < stop) : (index > stop); index += step)
+		{
+			ASSERT(outIndex < outputSize);
+			PyList_SetItem(l, outIndex++, getitem(index));
+		}
+		return l;
+	}
+
+	IteratorProxy  __iter__() { return *this; }
+	IteratorProxy  __next__() { return next(); }
+	IteratorProxy next()
+	{
+		if (! iter_->next(iter_->context_))
+			throw ODBStopIteration();
+		return *this;
+	}
+#endif
+
+//private:
+	ITERATOR *iter_;
+	Row row_;
+
+	friend class ReaderIterator;
+	friend class MetaDataReaderIterator;
+    friend std::ostream& operator<<(std::ostream &o, const IteratorProxy& it) {
+		for (size_t i = 0; i < it.iter_->columns().size(); ++i)
+			o << it.iter_->data()[i] << "\t";
+		return o;
+	}
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/MD5.cc b/odb_api/src/odb_api/MD5.cc
new file mode 100644
index 0000000..6ee285c
--- /dev/null
+++ b/odb_api/src/odb_api/MD5.cc
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/MD5.h"
+
+MD5::MD5() { md5_init(&state_); }
+
+MD5::~MD5() {}
+
+void MD5::add(const void* buffer, long length)
+{
+	ASSERT(length > 0);
+
+	md5_add(&state_, static_cast<const unsigned char*>(buffer), length);
+}
+
+std::string MD5::digest()
+{
+	if(digest_.length() == 0)
+	{
+		char digest[33];
+		md5_end(&state_, digest);
+		digest[32] = 0;
+		digest_ = digest;
+	}
+	return digest_;
+}
diff --git a/odb_api/src/odb_api/MD5.h b/odb_api/src/odb_api/MD5.h
new file mode 100644
index 0000000..981496e
--- /dev/null
+++ b/odb_api/src/odb_api/MD5.h
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File MD5.h
+// Baudouin Raoult - ECMWF Dec 04
+
+#ifndef MD5_H
+#define MD5_H
+
+extern "C" {
+#include "md5_hash.h"
+} // extern "C"
+
+#include "eckit/eckit.h"
+// Forward declarations
+
+class MD5 {
+public:
+	MD5();
+	~MD5(); 
+
+	void add(const void*, long);
+	std::string digest();
+
+private:
+// No copy allowed
+	MD5(const MD5&);
+	MD5& operator=(const MD5&);
+
+	md5_state state_;
+	std::string digest_;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/MDI.cc b/odb_api/src/odb_api/MDI.cc
new file mode 100755
index 0000000..15e52b1
--- /dev/null
+++ b/odb_api/src/odb_api/MDI.cc
@@ -0,0 +1,19 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/MDI.h"
+
+namespace odb {
+
+double MDI::realMDI_ = -2147483647;
+double MDI::integerMDI_ = 2147483647;
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/MDI.h b/odb_api/src/odb_api/MDI.h
new file mode 100755
index 0000000..8d9e37b
--- /dev/null
+++ b/odb_api/src/odb_api/MDI.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file MDI.h
+/// @author Piotr Kuchta - ECMWF Nov 13
+
+#ifndef MDI_H
+#define MDI_H
+
+namespace odb {
+
+class MDI {
+public:
+    static double realMDI() { return realMDI_; }
+    static double integerMDI() { return integerMDI_; }
+
+    /// We always use 0 as MDI of Bitfield columns.
+    static double bitfieldMDI() { return 0; }
+
+    static void realMDI(double v) { realMDI_ = v; }
+    static void integerMDI(double v) { integerMDI_ = v; }
+
+private:
+    static double realMDI_;
+    static double integerMDI_;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/MDSetter.cc b/odb_api/src/odb_api/MDSetter.cc
new file mode 100644
index 0000000..1cfa05a
--- /dev/null
+++ b/odb_api/src/odb_api/MDSetter.cc
@@ -0,0 +1,13 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+namespace odb {
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/MDSetter.h b/odb_api/src/odb_api/MDSetter.h
new file mode 100644
index 0000000..afbb3f7
--- /dev/null
+++ b/odb_api/src/odb_api/MDSetter.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MDSetter_H
+#define MDSetter_H
+
+namespace odb {
+
+template <typename T>
+class MDSetter
+{
+public:
+	typedef typename odb::MDUpdatingIterator<T> iterator_class;
+	typedef typename odb::IteratorProxy<iterator_class, MDSetter, const double> iterator;
+
+	MDSetter(const T& b, const T& e, const std::vector<std::string>& columns, const std::vector<std::string>& types)
+	: ii_(b), end_(e), columns_(columns), types_(types)
+	{}
+
+	~MDSetter() {}
+
+	iterator begin() { return iterator(new iterator_class(ii_, end_, columns_, types_)); }
+	const iterator end() { return iterator(new iterator_class(end_)); }
+
+private:
+	T ii_;
+	const T& end_;
+	const std::vector<std::string> columns_;
+	const std::vector<std::string> types_;
+};
+
+} // namespace odb
+
+#include "odb_api/MDSetter.cc"
+
+#endif
+
diff --git a/odb_api/src/odb_api/MDUpdatingIterator.cc b/odb_api/src/odb_api/MDUpdatingIterator.cc
new file mode 100644
index 0000000..4d76d2d
--- /dev/null
+++ b/odb_api/src/odb_api/MDUpdatingIterator.cc
@@ -0,0 +1,108 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file MDUpdatingIterator.cc
+///
+/// @author Piotr Kuchta, March 2012
+
+namespace odb {
+
+template <typename T>
+MDUpdatingIterator<T>::MDUpdatingIterator (T& ii, const T& end, const std::vector<std::string>& columns, const std::vector<std::string>& types)
+: ii_(ii),
+  end_(end),
+  columns_(columns),
+  columnIndices_(columns.size()), 
+  types_(types),
+  bitfieldDefs_(),
+  md_(ii->columns()),
+  data_(0),
+  refCount_(0),
+  noMore_(false)
+{
+	ASSERT(columns.size() == types.size());
+	for (size_t i = 0; i < types.size(); ++i)
+	{
+		eckit::Log::info() << columns[i] << " : " << types[i] << std::endl;
+
+		// Only bitfoelds now:
+		// [active:1;passive:1;rejected:1;blacklisted:1;use_emiskf_only:1;monthly:1;constant:1;experimental:1;whitelist:]
+		ASSERT(types[i].size());
+		ASSERT(types[i][0] == '[');
+		ASSERT(types[i][types[i].size() - 1] == ']');
+
+		BitfieldDef bf;
+		std::vector<std::string> parts(eckit::StringTools::split(";", types[i].substr(1, types[i].size() - 2)));
+		for (size_t p = 0; p < parts.size(); ++p)
+		{
+			std::vector<std::string> field = eckit::StringTools::split(":", parts[p]);
+			bf.first.push_back(field[0]);
+			bf.second.push_back(atoi(field[1].c_str()));
+		}
+		bitfieldDefs_.push_back(bf);
+		eckit::Log::info() << "" << i << ": " << columns[i] << " - " << bf.first << std::endl; // "[" << bf.second << "]" << std::endl;
+	}
+
+	update();
+}
+
+template <typename T>
+MDUpdatingIterator<T>::MDUpdatingIterator (const T& end)
+: ii_(end),
+  end_(end),
+  columnIndices_(),
+  types_(),
+  md_(0),
+  data_(0),
+  refCount_(0),
+  noMore_(true)
+{}
+
+template <typename T>
+MDUpdatingIterator<T>::~MDUpdatingIterator () { }
+
+template <typename T>
+MetaData& MDUpdatingIterator<T>::columns()
+{
+	md_ = ii_->columns();
+	for (size_t i = 0; i < columns_.size(); ++i)
+		md_[md_.columnIndex(columns_[i])]->bitfieldDef(bitfieldDefs_[i]);
+	return md_;
+}
+
+template <typename T>
+void MDUpdatingIterator<T>::update()
+{
+	columns();
+	data_ = const_cast<double*>(ii_->data());
+}
+
+template <typename T>
+bool MDUpdatingIterator<T>::isNewDataset() { return ii_->isNewDataset(); }
+
+template <typename T>
+bool MDUpdatingIterator<T>::next()
+{
+	if (noMore_)
+		return noMore_;
+	++ii_;
+	bool r = ii_ != end_;
+	if (r)
+	{
+		if (ii_->isNewDataset())
+			update();
+	}
+	noMore_ = !r;
+	return r;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/MDUpdatingIterator.h b/odb_api/src/odb_api/MDUpdatingIterator.h
new file mode 100644
index 0000000..274b7d6
--- /dev/null
+++ b/odb_api/src/odb_api/MDUpdatingIterator.h
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file MDUpdatingIterator.h
+///
+/// @author Piotr Kuchta, June 2009
+
+#ifndef MDUpdatingIterator_H
+#define MDUpdatingIterator_H
+
+namespace odb {
+
+class MetaData;
+
+template <typename T>
+class MDUpdatingIterator 
+{
+public:
+	MDUpdatingIterator (T& inputIterator, const T& end, const std::vector<std::string>& columns, const std::vector<std::string>& types);
+	MDUpdatingIterator (const T& end);
+	~MDUpdatingIterator ();
+
+	bool isNewDataset();
+	double* data() { return data_; }
+
+	MetaData& columns(); // { return ii_->columns(); }
+
+    MDUpdatingIterator& operator++() { next(); return *this; }
+
+	bool operator!=(const MDUpdatingIterator& o) { ASSERT(&o == 0); return ii_ != end_; }
+
+//protected:
+	bool next();
+
+private:
+// No copy allowed.
+    MDUpdatingIterator(const MDUpdatingIterator&);
+    MDUpdatingIterator& operator=(const MDUpdatingIterator&);
+
+	void update();
+
+	// Input iterator.
+	T ii_;
+	const T& end_;
+
+	const std::vector<std::string> columns_;
+	std::vector<size_t> columnIndices_;
+	const std::vector<std::string> types_;
+	std::vector<BitfieldDef> bitfieldDefs_;
+
+	MetaData md_;
+
+	double *data_;
+public:
+	int refCount_;
+	bool noMore_;
+};
+
+} // namespace odb 
+
+#include "odb_api/MDUpdatingIterator.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/MemoryBlock.cc b/odb_api/src/odb_api/MemoryBlock.cc
new file mode 100644
index 0000000..6238a41
--- /dev/null
+++ b/odb_api/src/odb_api/MemoryBlock.cc
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MemoryBlock.h"
+
+MemoryBlock::MemoryBlock(size_t size):
+	buffer_(0),
+	size_(size)
+{
+	create();
+}
+
+MemoryBlock::MemoryBlock(const char* p,size_t size):
+	buffer_(0),
+	size_(size)
+{
+	create();
+	::memcpy(buffer_,p,size);
+}
+
+MemoryBlock::MemoryBlock(const std::string& s):
+	buffer_(0),
+	size_(s.length()+1)
+{
+	create();
+	::strcpy((char*)buffer_,s.c_str());
+}
+
+void MemoryBlock::size(size_t newSize)
+{
+	destroy();
+	size_ = newSize;
+	create();
+}
+
+MemoryBlock::~MemoryBlock()
+{ 
+	destroy();
+}
+
+void MemoryBlock::create()
+{
+	buffer_ = new unsigned char[size_];
+}
+
+void MemoryBlock::destroy()
+{
+	delete [] buffer_;
+}
diff --git a/odb_api/src/odb_api/MemoryBlock.h b/odb_api/src/odb_api/MemoryBlock.h
new file mode 100644
index 0000000..a4c434d
--- /dev/null
+++ b/odb_api/src/odb_api/MemoryBlock.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MemoryBlock_H
+#define MemoryBlock_H
+
+// A simple class to implement buffers
+
+class MemoryBlock {
+public:
+	MemoryBlock(size_t size);
+	MemoryBlock(const std::string& s);
+	MemoryBlock(const char*,size_t size);
+
+	~MemoryBlock();
+
+// -- Operators
+
+	operator char*()                 { return (char*)buffer_; }
+	operator const char*() const     { return (char*)buffer_; }
+
+	operator unsigned char*()                 { return (unsigned char*)buffer_; }
+	operator const unsigned char*() const     { return (unsigned char*)buffer_; }
+
+	operator void*()                 { return buffer_; }
+	operator const void*() const     { return buffer_; }
+
+// -- Methods
+
+	size_t size() const		 { return size_; }
+	void size(size_t);
+
+private:
+// No copy allowed
+	MemoryBlock(const MemoryBlock&);
+	MemoryBlock& operator=(const MemoryBlock&);
+
+	void create();
+	void destroy();
+
+	unsigned char *  buffer_;
+	size_t size_;
+
+};
+
+#endif
diff --git a/odb_api/src/odb_api/MetaData.cc b/odb_api/src/odb_api/MetaData.cc
new file mode 100644
index 0000000..fcb5c31
--- /dev/null
+++ b/odb_api/src/odb_api/MetaData.cc
@@ -0,0 +1,273 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/CodecOptimizer.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+
+MetaData::MetaData() : std::vector<Column*>(), rowsNumber_(0), self(*this) {}
+MetaData::MetaData(int i) : std::vector<Column*>(i), rowsNumber_(0), self(*this) {}
+MetaData::MetaData(int i, Column *p) : std::vector<Column*>(i, p), rowsNumber_(0), self(*this) {}
+MetaData::MetaData(const MetaData& md) : std::vector<Column*>(0), rowsNumber_(0), self(*this)
+{ self += md; }
+
+odb::ColumnType MetaData::convertType(const std::string& t)
+{
+    std::string type(t);
+    transform(type.begin(), type.end(), type.begin(), ::toupper);
+
+    if      (type == "INTEGER")  return odb::INTEGER;
+    else if (type == "YYYYMMDD") return odb::INTEGER;
+    else if (type == "HHMMSS")   return odb::INTEGER;
+    else if (type == "PK1INT")   return odb::INTEGER;
+    else if (type == "PK9INT")   return odb::INTEGER;
+    else if (type == "@LINK")    return odb::INTEGER;
+    else if (type == "REAL")     return odb::REAL;
+    else if (type == "FLOAT")    return odb::REAL;
+    else if (type == "DOUBLE")   return odb::DOUBLE;
+    else if (type == "PK9REAL")  return odb::DOUBLE;
+    else if (type == "STRING")   return odb::STRING;
+    else if (type.find("BITFIELD") != string::npos) return odb::BITFIELD;
+    else throw eckit::UserError("Unsupported column type: " + type);
+
+    return odb::IGNORE; // never reached
+}
+
+MetaData* MetaData::clone() const {
+	const MetaData& self(*this);
+	MetaData* md = new MetaData(*this);
+	for (size_t i = 0; i < size(); ++i)
+		(*md)[i]->coder(self[i]->coder().clone());
+	return md;
+}
+
+MetaData MetaData::scanFile(const PathName& fileName)
+{
+    std::ostream& L(Log::debug());
+	L << "Iterating over headers of '" << fileName << "'" <<  std::endl;
+
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+	MDR mdReader(fileName);
+	MDR::iterator it (mdReader.begin());
+	MDR::iterator end (mdReader.end());
+
+	MetaData wholeFileMD(it->columns());
+
+	unsigned long int i = 0;	
+	unsigned long int mds = 0;	
+	for ( ; it != end; ++it)
+	{
+		++i;
+		if (it->isNewDataset())
+		{
+			++mds;
+			L << it->columns() << std::endl;
+			wholeFileMD |= it->columns();
+		}
+	}
+	L << "TestMetaDataReader::test i=" << i << ", mds=" << mds << std::endl;
+
+	codec::CodecOptimizer().setOptimalCodecs<DataStream<> >(wholeFileMD);
+
+	return wholeFileMD;
+}
+
+void MetaData::setSize(size_t n)
+{
+	size_t oldSize = size();
+
+	for (size_t i = n; i < oldSize; ++i)
+		delete at(i);
+
+	std::vector<Column*>::resize(n, 0);
+
+	for (size_t i = oldSize; i < n; ++i)
+		at(i) = new Column(*this);
+}
+
+MetaData::~MetaData()
+{
+	for (size_type i = 0; i < size(); i++)
+		delete at(i);
+}
+
+//const
+Column* MetaData::columnByName(const std::string& name) const
+{ return at(columnIndex(name)); }
+
+bool MetaData::hasColumn(const std::string& name) const
+{ 
+	for (size_t i = 0; i < size(); i++)
+		if (at(i)->name() == name || at(i)->name().find(name + "@") == 0)
+			return true;
+	return false;
+}
+
+size_t MetaData::columnIndex(const std::string& name) const
+{
+	std::vector<size_t> indices;
+
+	for (size_t i = 0; i < size(); i++)
+		if (at(i)->name() == name || at(i)->name().find(name + "@") == 0)
+			indices.push_back(i);
+
+	if (indices.size() > 1)
+		throw eckit::UserError(std::string("Ambiguous column name: '") + name + "'");
+
+	if (indices.size() == 0)
+		throw eckit::UserError(std::string("Column '") + name + "' not found.");
+
+	return indices[0];
+}
+
+MetaData& MetaData::operator=(const MetaData& other)
+{
+	if(this == &other)
+		return self;
+
+	if (self.size() != other.size())
+	{
+		for (size_type i=0; i < self.size(); i++)
+			delete self[i];
+		self.clear();
+
+		typedef Column* PColumn;
+		self.resize(other.size(), PColumn(0));
+
+		for (size_type i=0; i < self.size(); i++)
+			self[i] = new Column(self);
+	}
+	
+	for (size_type i=0; i < self.size(); i++)
+		*self[i] = *other[i];
+
+	return self;
+}
+
+MetaData& MetaData::operator+=(const MetaData& rhs)
+{
+	for (size_t i = 0; i < rhs.size(); ++i)
+	{
+		Column& rhsColumn = *rhs[i];
+
+		//Log::debug() << "MetaData::operator+=: adding " << rhsColumn << std::endl;
+
+		Column* c = new Column(rhsColumn);
+		ASSERT(c);
+		push_back(c);
+	}
+	return *this;
+}
+
+void MetaData::operator|=(const MetaData& other)
+{
+	ASSERT(size() == other.size());
+	for (size_t i = 0; i < size(); ++i)
+	{
+		ASSERT(self[i]->name() == other[i]->name());
+		ASSERT(self[i]->type() == other[i]->type());
+
+		self[i]->coder().gatherStats(other[i]->max());
+		self[i]->coder().gatherStats(other[i]->min());
+	}
+}
+
+MetaData MetaData::operator+(const MetaData& rhs)
+{
+	MetaData r = *this;
+	r += rhs;
+	return r;
+}
+
+bool MetaData::equalsIncludingConstants(const MetaData& other, const std::vector<std::string>& constColumns) const 
+{
+    std::ostream& L = Log::debug();
+	for (size_t i = 0; i < constColumns.size(); ++i)
+	{
+		const std::string& columnName = constColumns[i];
+		L << "MetaData::equalsIncludingConstants: check " << columnName << std::endl;
+
+		if ( !self.hasColumn(columnName) && !other.hasColumn(columnName))
+			continue;
+
+		Column& c1 = *(self.columnByName(columnName));
+		Column& c2 = *(other.columnByName(columnName));
+
+		if ( ! c1.isConstant() || ! c2.isConstant())
+		{
+			L << "MetaData::equalsIncludingConstants: c1 " << c1 << " " << c1.coder() << std::endl;
+			L << "MetaData::equalsIncludingConstants: c2 " << c2 << " " << c2.coder() << std::endl;
+			L << "MetaData::equalsIncludingConstants: column '" << columnName << "'" << std::endl;
+			return false;
+		}
+		else
+		{
+			odb::codec::Codec& codec1 = c1.coder();
+			odb::codec::Codec& codec2 = c2.coder();
+			if ( codec1.min() != codec2.min() )
+			{
+				L << "MetaData::equalsIncludingConstants: column '" << columnName << "'" << std::endl;
+				L << "MetaData::equalsIncludingConstants: min1=" << codec1.min() << ", max1=" << codec1.max() << std::endl;
+				L << "MetaData::equalsIncludingConstants: min2=" << codec2.min() << ", max2=" << codec2.max() << std::endl;
+				L << "MetaData::equalsIncludingConstants: c1.coder: " << codec1 << std::endl;
+				L << "MetaData::equalsIncludingConstants: c2.coder: " << codec2 << std::endl;
+				return false;
+			}
+		}
+	}
+
+	L << "MetaData::equalsIncludingConstants: yes" << std::endl;
+	return true;
+}
+
+bool MetaData::operator==(const MetaData& other) const
+{
+	if (self.size() != other.size())
+		return false;
+
+	for (size_t i = 0; i < self.size(); ++i)
+		if (*self[i] != *other[i])
+			return false;
+
+	return true;
+}
+
+void MetaData::resetStats()
+{
+	//Log::debug() << "MetaData::resetStats" << std::endl;
+	for (size_t i = 0; i < size(); i++)
+		(*this)[i]->resetStats();
+}
+
+void MetaData::print(std::ostream& s) const
+{
+	for (size_t i = 0; i < size(); i++)
+		s << i << ". " << *at(i) << std::endl;
+}
+
+MetaData& MetaData::addColumn(const std::string& name, const std::string& type)
+{
+    return addColumnPrivate<odb::DataStream<odb::SameByteOrder, eckit::DataHandle> >(name, type);
+}
+
+MetaData& MetaData::addBitfield(const std::string& name, const BitfieldDef& bf)
+{
+    return addBitfieldPrivate<odb::DataStream<odb::SameByteOrder, eckit::DataHandle> >(name, bf);
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/MetaData.h b/odb_api/src/odb_api/MetaData.h
new file mode 100644
index 0000000..69631af
--- /dev/null
+++ b/odb_api/src/odb_api/MetaData.h
@@ -0,0 +1,160 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MetaData_H
+#define MetaData_H
+
+#include "odb_api/Column.h"
+
+#ifdef SWIGPYTHON
+#include "odb_api/IteratorProxy.h"
+#endif
+
+namespace eckit { class PathName; }
+
+namespace odb {
+
+typedef std::vector<Column*> MetaDataBase;
+
+class MetaData : public MetaDataBase {
+public:
+	MetaData();
+	MetaData(int);
+	MetaData(int, Column *);
+	MetaData(const MetaData&);
+	MetaData* clone() const;
+
+	static MetaData scanFile(const eckit::PathName&);
+
+	unsigned long long rowsNumber() const { return rowsNumber_; }
+	void rowsNumber(unsigned long long n) { rowsNumber_ = n; }
+
+	unsigned long long dataSize() const { return dataSize_; }
+	void dataSize(unsigned long long n) { dataSize_ = n; }
+
+	MetaData& operator=(const MetaData&);
+	MetaData& operator+=(const MetaData&);
+	MetaData operator+(const MetaData&);
+
+	/// Check if number of columns, column names and column types are equal. Values not checked.
+	bool operator==(const MetaData&) const;
+
+	bool equalsIncludingConstants(const MetaData&, const std::vector<std::string>& constColumns) const; 
+
+	bool operator!=(const MetaData& other) const { return ! (self == other); }
+
+	void operator|=(const MetaData& other);
+
+	template<typename DATASTREAM> void save(DATASTREAM &) const;
+	template<typename DATASTREAM> void load(DATASTREAM &);
+
+	void setSize(size_t);
+
+	MetaData& addColumn(const std::string& name, const std::string& type);
+
+	template<typename DATASTREAM>
+	MetaData& addColumnPrivate(const std::string& name, const std::string& type);
+
+
+	MetaData& addBitfield(const std::string& name, const BitfieldDef&);
+	template<typename DATASTREAM> MetaData& addBitfieldPrivate(const std::string& name, const BitfieldDef&);
+
+	bool hasColumn(const std::string&) const;
+	Column* columnByName(const std::string&) const;
+	size_t columnIndex(const std::string&) const;
+
+    static odb::ColumnType convertType(const std::string&);
+#ifdef SWIGPYTHON
+	std::string __str__()
+	{
+        std::stringstream s;
+		s << "[";
+		for (size_t i = 0; i < size(); ++i)
+		{
+			s << at(i)->__repr__() << ",";
+		}
+		s << "]";
+		return s.str();
+	} 
+#endif
+
+	void resetStats();
+
+	virtual ~MetaData();
+
+	virtual void print(std::ostream& s) const;
+
+	friend std::ostream& operator<<(std::ostream& s, const MetaData& p)
+		{ p.print(s); return s; }
+
+private:
+	unsigned long long rowsNumber_;
+	unsigned long long dataSize_;
+	MetaData& self;
+};
+
+
+template<typename DATASTREAM>
+void MetaData::save(DATASTREAM &f) const
+{
+	int32_t count = size();
+	f.writeInt32(count);
+	for(size_t i = 0; i < size(); i++)
+		at(i)->save(f);
+}
+
+template<typename DATASTREAM>
+void MetaData::load(DATASTREAM &f)
+{
+	for (size_t i = 0; i < size(); i++)
+		delete at(i);
+	clear();
+
+	int32_t nCols;
+	f.readInt32(nCols);
+	resize(nCols, NULL);
+	for (size_t i = 0; i < size(); i++)
+	{
+		delete at(i);
+		at(i) = new Column(*this);
+		at(i)->load(f);
+	}
+}
+
+template <typename DATASTREAM>
+MetaData& MetaData::addColumnPrivate(const std::string& name, const std::string& type)
+{
+	Column* c = new Column(*this);
+	ASSERT(c);
+
+	c->name(name);
+	c->type<DATASTREAM>(odb::Column::type(type), false);
+
+	push_back(c);
+	return *this;
+}
+
+template<typename DATASTREAM> 
+MetaData& MetaData::addBitfieldPrivate(const std::string& name, const BitfieldDef& bd)
+{
+	Column* c = new Column(*this);
+	ASSERT(c);
+	c->name(name);
+	c->type<DATASTREAM>(BITFIELD, false);
+	c->bitfieldDef(bd);
+	push_back(c);
+
+	return *this;
+}
+
+} // namespace odb
+
+#endif
+
diff --git a/odb_api/src/odb_api/MetaDataReader.cc b/odb_api/src/odb_api/MetaDataReader.cc
new file mode 100644
index 0000000..9b449e4
--- /dev/null
+++ b/odb_api/src/odb_api/MetaDataReader.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODA.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <math.h>
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/FileHandle.h"
+#include "odb_api/Codec.h"
+#include "odb_api/Column.h"
+#include "odb_api/DataStream.h"
+#include "odb_api/FixedSizeWriterIterator.h"
+#include "odb_api/HashTable.h"
+#include "odb_api/Header.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/MemoryBlock.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/SQLExpression.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLTable.h"
+#include "odb_api/SQLType.h"
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/Writer.h"
+
+using namespace std;
+
+namespace odb {
+
+template <typename T>
+MetaDataReader<T>::MetaDataReader()
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  path_(""),
+  skipData_(true)
+{} 
+
+template <typename T>
+MetaDataReader<T>::MetaDataReader(const eckit::PathName& path, bool skipData)
+: dataHandle_(path.fileHandle()),
+  deleteDataHandle_(true),
+  path_(path),
+  skipData_(skipData)
+{
+	dataHandle_->openForRead();
+} 
+
+template <typename T>
+MetaDataReader<T>::~MetaDataReader()
+{
+	if (dataHandle_ && deleteDataHandle_)
+	{
+		dataHandle_->close();
+		delete dataHandle_;
+	}
+}
+
+template <typename T>
+typename MetaDataReader<T>::iterator* MetaDataReader<T>::createReadIterator(const eckit::PathName& pathName)
+{
+	return new T(*this, pathName, skipData_);
+}
+
+template <typename T>
+typename MetaDataReader<T>::iterator MetaDataReader<T>::begin()
+{
+    T* it = new T(this->dataHandle(), skipData_);
+	it->next(0); // TODO: get a context...
+	return iterator(it);
+}
+
+template <typename T>
+const typename MetaDataReader<T>::iterator MetaDataReader<T>::end() { return iterator(0); }
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/MetaDataReader.h b/odb_api/src/odb_api/MetaDataReader.h
new file mode 100644
index 0000000..60049b5
--- /dev/null
+++ b/odb_api/src/odb_api/MetaDataReader.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file MetaDataReader.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef MetaDataReader_H
+#define MetaDataReader_H
+
+#ifdef SWIGPYTHON
+#include <Python.h>
+#endif
+
+#include "odb_api/IteratorProxy.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class MetaDataReaderIterator;
+
+template <typename T>
+class MetaDataReader
+{
+public:
+	typedef IteratorProxy<T,MetaDataReader,const double> iterator;
+	//typedef typename iterator::Row row;
+
+	MetaDataReader(const eckit::PathName &path, bool skipData = true);
+	MetaDataReader();
+
+	virtual ~MetaDataReader();
+
+	iterator begin();
+	const iterator end(); 
+
+    eckit::DataHandle& dataHandle() { return *dataHandle_; }
+    
+	// For C API
+	iterator* createReadIterator(const eckit::PathName&);
+
+#ifdef SWIGPYTHON
+	iterator __iter__() { return begin(); }
+#endif
+
+private:
+// No copy allowed
+    MetaDataReader(const MetaDataReader&);
+    MetaDataReader& operator=(const MetaDataReader&);
+
+	eckit::DataHandle* dataHandle_;
+	bool deleteDataHandle_;
+	//const eckit::PathName path_;
+	const std::string path_;
+	bool skipData_;
+
+	friend class IteratorProxy<MetaDataReaderIterator,MetaDataReader,const double>;
+	friend class MetaDataReaderIterator;
+};
+
+} // namespace odb
+
+#include "odb_api/MetaDataReader.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/MetaDataReaderIterator.cc b/odb_api/src/odb_api/MetaDataReaderIterator.cc
new file mode 100644
index 0000000..e64ac27
--- /dev/null
+++ b/odb_api/src/odb_api/MetaDataReaderIterator.cc
@@ -0,0 +1,269 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file MetaDataReaderIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <arpa/inet.h>
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/MetaDataReaderIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+
+MetaDataReaderIterator::MetaDataReaderIterator(DataHandle &handle,bool skipData):
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(&handle),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  skipData_(skipData),
+  encodedData_(0),
+  sizeOfEncodedData_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0),
+  fileSize_(0)
+{
+	ASSERT(f_);
+	fileSize_ = f_->estimate();
+}
+
+
+MetaDataReaderIterator::MetaDataReaderIterator(DataHandle *handle,bool skipData):
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(handle),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(true),
+  headerCounter_(0),
+  skipData_(skipData),
+  encodedData_(0),
+  sizeOfEncodedData_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0),
+  fileSize_(0)
+{
+    ASSERT(f_);
+    fileSize_ = f_->estimate();
+}
+
+MetaDataReaderIterator::MetaDataReaderIterator(const eckit::PathName & path, bool skipData):
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(path.fileHandle()),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  skipData_(skipData),
+  encodedData_(0),
+  sizeOfEncodedData_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0),
+  fileSize_(0)
+{
+	ASSERT(f_);
+	ownsF_ = true;
+	f_->openForRead();
+	fileSize_ = f_->estimate();
+}
+
+
+void MetaDataReaderIterator::loadHeaderAndBufferData()
+{
+	Header<MetaDataReaderIterator> header(*this);
+	header.load();
+	byteOrder_ = header.byteOrder();
+	++headerCounter_;
+
+	initRowBuffer();
+
+	size_t dataSize = header.dataSize();
+	if (dataSize && !skip(dataSize))
+		ASSERT(0 && "Could not read the amount of data indicated by file's header");
+	blockEndOffset_ = f_->position();
+}
+
+MetaDataReaderIterator::~MetaDataReaderIterator ()
+{
+	Log::debug() << "MetaDataReaderIterator::~MetaDataReaderIterator: headers read: " << headerCounter_ << " rows:" << nrows_ << std::endl;
+
+	close();
+	delete [] lastValues_;
+	delete [] codecs_;
+}
+
+
+bool MetaDataReaderIterator::operator!=(const MetaDataReaderIterator& other)
+{
+	return noMore_;
+}
+
+void MetaDataReaderIterator::initRowBuffer()
+{
+	size_t nCols = columns().size();
+
+	delete [] lastValues_;
+	lastValues_ = new double [nCols];
+
+	delete [] codecs_;
+	codecs_ = new odb::codec::Codec* [nCols];
+
+	for(size_t i = 0; i < nCols; i++)
+	{
+		codecs_[i] = &columns()[i]->coder();
+		lastValues_[i] = codecs_[i]->missingValue(); 
+		codecs_[i]->dataHandle(&memDataHandle_);
+	}
+}
+
+bool MetaDataReaderIterator::skip(size_t dataSize)
+{
+	Log::debug() << "MetaDataReaderIterator::skip: skipData_=" << skipData_ << std::endl;
+
+	if (skipData_)
+	{
+        Log::debug() << "MetaDataReaderIterator::readBuffer: skip(" << dataSize << ")" << std::endl;
+		if (fileSize_ && f_->position() + Offset(dataSize) > fileSize_)
+            throw eckit::ShortFile("MetaDataReaderIterator::skip");
+
+        f_->skip(dataSize);
+        return true;
+	}
+
+	Log::debug() << "MetaDataReaderIterator::skip: sizeOfEncodedData_=" << sizeOfEncodedData_ << std::endl;
+	if (sizeOfEncodedData_ < dataSize)
+	{
+		Log::debug() << "MetaDataReaderIterator::skip: allocating " << dataSize << " bytes." << std::endl;
+		delete [] encodedData_;
+		encodedData_ = new char[dataSize];
+	}
+	
+	size_t actualNumberOfBytes = 0;
+	if ((actualNumberOfBytes = f_->read(encodedData_, dataSize)) != dataSize)
+	{
+		Log::warning() << "MetaDataReaderIteratorReadingData::skip: expected " << dataSize 
+						<< " could read only " << actualNumberOfBytes << std::endl;
+		return false;
+	}
+
+	sizeOfEncodedData_ = dataSize;
+	return true;
+}
+
+bool MetaDataReaderIterator::next(ecml::ExecutionContext* context)
+{
+	newDataset_ = false;
+	if (noMore_)
+		return false; 
+
+	uint16_t c = 0;
+    long bytesRead = 0;
+
+	blockStartOffset_ = f_->position();
+
+	if ( (bytesRead = memDataHandle_.read(&c, 2)) == 0)
+	{
+
+        if ( (bytesRead = f_->read(&c, 2)) <= 0)
+			return ! (noMore_ = true);
+		ASSERT(bytesRead == 2);
+
+		if (c == ODA_MAGIC_NUMBER) 
+		{
+			DataStream<SameByteOrder> ds(f_);
+
+			unsigned char cc;
+			ds.readUChar(cc); ASSERT(cc == 'O');
+			ds.readUChar(cc); ASSERT(cc == 'D');
+			ds.readUChar(cc); ASSERT(cc == 'A');
+
+			Header<MetaDataReaderIterator> header(*this);
+			header.loadAfterMagic();
+			byteOrder_ = header.byteOrder();
+			++headerCounter_;
+			nrows_ += columns().rowsNumber();
+			initRowBuffer();
+
+			newDataset_ = true;
+
+			size_t dataSize = header.dataSize();
+			if (! skip(dataSize)) {
+				blockEndOffset_ = f_->position();
+				return ! (noMore_ = true);
+			} else {
+				blockEndOffset_ = f_->position();
+				return true;
+			}
+		}
+	}
+	c = ntohs(c);
+
+	size_t nCols = columns().size();
+	for(size_t i = c; i < nCols; i++)
+		lastValues_[i] = codecs_[i]->decode();
+
+	return nCols;
+}
+
+bool MetaDataReaderIterator::isNewDataset() { return newDataset_; }
+
+const double* MetaDataReaderIterator::data() { return lastValues_; }
+
+int MetaDataReaderIterator::close()
+{
+	if (ownsF_ && f_)
+	{
+		f_->close();
+		delete f_;
+		f_ = 0;
+	}
+
+	return 0;
+}
+
+
+void MetaDataReaderIterator::property(std::string key, std::string value)
+{
+	properties_[key] = value;
+}
+
+std::string MetaDataReaderIterator::property(std::string key)
+{
+	return properties_[key];
+}
+
+
+ColumnType MetaDataReaderIterator::columnType(unsigned long index) { return columns_[index]->type(); }
+const std::string& MetaDataReaderIterator::columnName(unsigned long index) const { return columns_[index]->name(); }
+const std::string& MetaDataReaderIterator::codecName(unsigned long index) const { return columns_[index]->coder().name(); }
+double MetaDataReaderIterator::columnMissingValue(unsigned long index) { return columns_[index]->missingValue(); }
+const BitfieldDef& MetaDataReaderIterator::bitfieldDef(unsigned long index) { return columns_[index]->bitfieldDef(); }
+
+eckit::DataHandle* MetaDataReaderIterator::dataHandle() { return f_; }
+
+
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/MetaDataReaderIterator.h b/odb_api/src/odb_api/MetaDataReaderIterator.h
new file mode 100644
index 0000000..4d4ebe2
--- /dev/null
+++ b/odb_api/src/odb_api/MetaDataReaderIterator.h
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file MetaDataReaderIterator.h
+///
+/// @author Piotr Kuchta, January 2011
+
+#ifndef MetaDataReaderIterator_H
+#define MetaDataReaderIterator_H
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "odb_api/Header.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/TReadOnlyMemoryDataHandle.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class FileHandle; }
+namespace eckit { class DataHandle; }
+
+extern "C" {
+	typedef void oda;
+	typedef void oda_read_iterator;
+	typedef void oda_write_iterator;
+	oda_write_iterator* oda_create_write_iterator(oda*, const char *,int *);
+	int oda_read_iterator_get_next_row(oda_read_iterator*, int, double*, int*);
+}
+
+namespace odb {
+	namespace codec { class Codec; } 
+	namespace sql { class ODATableIterator; }
+}
+
+namespace odb {
+
+class MetaDataReaderIterator 
+{
+public:
+
+    MetaDataReaderIterator (eckit::DataHandle* handle, bool skipData);
+    MetaDataReaderIterator (eckit::DataHandle& handle, bool skipData);
+    MetaDataReaderIterator (const eckit::PathName&, bool skipData);
+	~MetaDataReaderIterator ();
+
+	bool isNewDataset();
+	const double* data();
+
+	bool operator!=(const MetaDataReaderIterator& other);
+
+	void property(std::string, std::string);
+	std::string property(std::string);
+
+	const MetaData& columns() { return columns_; }
+	const MetaData& columns(MetaData& md) { return columns_ = md; }
+
+	eckit::Offset blockStartOffset() { return blockStartOffset_; }
+	eckit::Offset blockEndOffset() { return blockEndOffset_; }
+
+	int32_t byteOrder() const { return byteOrder_; }
+
+	ColumnType columnType(unsigned long index);
+	const std::string& columnName(unsigned long index) const;
+	const std::string& codecName(unsigned long index) const;
+	double columnMissingValue(unsigned long index);
+	const BitfieldDef& bitfieldDef(unsigned long index);
+	
+	char *encodedData() { return encodedData_; }
+	size_t sizeOfEncodedData() { return sizeOfEncodedData_; }
+//protected:
+
+    int close();
+
+    bool next(ecml::ExecutionContext*);
+
+    eckit::DataHandle* dataHandle();
+protected:
+    bool skip(size_t dataSize);
+
+private:
+// No copy allowed.
+    MetaDataReaderIterator(const MetaDataReaderIterator&);
+    MetaDataReaderIterator& operator=(const MetaDataReaderIterator&);
+
+    void initRowBuffer();
+    void loadHeaderAndBufferData();
+
+    MetaData columns_;
+    double* lastValues_;
+    odb::codec::Codec** codecs_;
+    unsigned long long nrows_;
+protected:
+    eckit::DataHandle *f_;
+    Properties properties_;
+    bool newDataset_;
+public:
+    bool noMore_;
+
+    bool ownsF_;
+private:
+    ReadOnlyMemoryDataHandle memDataHandle_;
+
+    unsigned long headerCounter_;
+
+    eckit::Offset blockStartOffset_;
+    eckit::Offset blockEndOffset_;
+    eckit::Length fileSize_; // This will be positive if we read data from a file, not a socket.
+
+protected:
+    bool skipData_;
+    char *encodedData_;
+    size_t sizeOfEncodedData_;
+    int32_t byteOrder_;
+
+public:
+    int refCount_;
+    ecml::ExecutionContext* context_;
+
+    friend class MetaDataReader<MetaDataReaderIterator>;
+    friend class odb::Header<odb::MetaDataReaderIterator>;
+    friend class odb::sql::ODATableIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/NullColumn.cc b/odb_api/src/odb_api/NullColumn.cc
new file mode 100644
index 0000000..811bcfd
--- /dev/null
+++ b/odb_api/src/odb_api/NullColumn.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/NullColumn.h"
+
+namespace odb {
+namespace sql {
+
+NullColumn::NullColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double
+missingValue, const BitfieldDef& bitfieldDef, double* value)
+: SQLColumn(type, owner, name, index, hasMissingValue, missingValue, bitfieldDef),
+   value_(value)
+{}
+
+NullColumn::NullColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double
+missingValue, double* value)
+: SQLColumn(type, owner, name, index, hasMissingValue, missingValue),
+   value_(value)
+{}
+
+NullColumn::~NullColumn() {}
+
+void NullColumn::rewind() { *value_ = missingValue_; }
+
+double NullColumn::next(bool& missing)
+{
+	missing = true;
+	return missingValue_;
+}
+
+void NullColumn::advance(unsigned long) { NOTIMP; }
+
+} // namespace sql 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/NullColumn.h b/odb_api/src/odb_api/NullColumn.h
new file mode 100644
index 0000000..4443f81
--- /dev/null
+++ b/odb_api/src/odb_api/NullColumn.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file NullColumn.h
+
+#ifndef NullColumn_H
+#define NullColumn_H
+
+#include "odb_api/SQLColumn.h"
+
+namespace odb {
+namespace sql {
+
+class SQLTable;
+
+class NullColumn : public SQLColumn {
+public:
+	NullColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue, const BitfieldDef&, double*);
+	NullColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue, double*);
+	~NullColumn();
+
+	void value(double* p) { value_ = p; }
+	double * value() const { return const_cast<double *>(&missing_); }
+
+private:
+	NullColumn(const NullColumn&);
+	NullColumn& operator=(const NullColumn&);
+
+	double* value_;
+	double  missing_;
+
+	virtual void rewind();
+	virtual double next(bool& missing);
+	virtual void advance(unsigned long);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const NullColumn& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/NumberExpression.cc b/odb_api/src/odb_api/NumberExpression.cc
new file mode 100755
index 0000000..447948c
--- /dev/null
+++ b/odb_api/src/odb_api/NumberExpression.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/NumberExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+NumberExpression::NumberExpression(double value)
+: value_(value)
+{}
+
+NumberExpression::NumberExpression(const NumberExpression& other)
+: value_(other.value_)
+{}
+
+SQLExpression* NumberExpression::clone() const { return new NumberExpression(*this); }
+
+NumberExpression::~NumberExpression() {}
+
+const type::SQLType* NumberExpression::type() const { return &type::SQLType::lookup("real"); }
+
+double NumberExpression::eval(bool& missing) const { return value_; }
+
+void NumberExpression::prepare(SQLSelect& sql) {}
+
+void NumberExpression::cleanup(SQLSelect& sql) {}
+
+void NumberExpression::print(std::ostream& s) const { s << value_; }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/NumberExpression.h b/odb_api/src/odb_api/NumberExpression.h
new file mode 100755
index 0000000..f5fc15d
--- /dev/null
+++ b/odb_api/src/odb_api/NumberExpression.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File NumberExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef NumberExpression_H
+#define NumberExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class NumberExpression : public SQLExpression {
+public:
+	NumberExpression(double value);
+	NumberExpression(const NumberExpression&);
+	~NumberExpression();
+
+	SQLExpression* clone() const;
+
+	void value(double v) { value_ = v; }
+
+private:
+// No copy allowed
+	NumberExpression& operator=(const NumberExpression&);
+
+	double value_;
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+
+	virtual const odb::sql::type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	virtual bool isConstant() const { return true; }
+	virtual bool isNumber() const { return true; }
+};
+
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ODAColumn.cc b/odb_api/src/odb_api/ODAColumn.cc
new file mode 100644
index 0000000..b3fd064
--- /dev/null
+++ b/odb_api/src/odb_api/ODAColumn.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/ODAColumn.h"
+
+namespace odb {
+namespace sql {
+
+ODAColumn::ODAColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double
+missingValue, const BitfieldDef& bitfieldDef, double* value)
+: SQLColumn(type, owner, name, index, hasMissingValue, missingValue, bitfieldDef),
+   value_(value)
+{}
+
+ODAColumn::ODAColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double
+missingValue, double* value)
+: SQLColumn(type, owner, name, index, hasMissingValue, missingValue),
+   value_(value)
+{}
+
+ODAColumn::~ODAColumn() {}
+
+void ODAColumn::rewind() { *value_ = missingValue_; }
+
+double ODAColumn::next(bool& missing)
+{
+	missing = (*value_ == missingValue_);
+	return *value_;
+}
+
+void ODAColumn::advance(unsigned long) { NOTIMP; }
+
+} // namespace sql 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ODAColumn.h b/odb_api/src/odb_api/ODAColumn.h
new file mode 100644
index 0000000..dacf507
--- /dev/null
+++ b/odb_api/src/odb_api/ODAColumn.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODAColumn.h
+
+#ifndef ODAColumn_H
+#define ODAColumn_H
+
+#include "odb_api/SQLColumn.h"
+
+namespace odb {
+namespace sql {
+
+class SQLTable;
+
+class ODAColumn : public SQLColumn {
+public:
+	ODAColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue, const BitfieldDef&, double*);
+	ODAColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue, double*);
+	~ODAColumn();
+
+	void value(double* p) { value_ = p; }
+	double * value() const { return value_; }
+
+private:
+	ODAColumn(const ODAColumn&);
+	ODAColumn& operator=(const ODAColumn&);
+
+	double* value_;
+	double  missing_;
+
+	virtual void rewind();
+	virtual double next(bool& missing);
+	virtual void advance(unsigned long);
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const ODAColumn& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ODADatabase.cc b/odb_api/src/odb_api/ODADatabase.cc
new file mode 100644
index 0000000..35d66f7
--- /dev/null
+++ b/odb_api/src/odb_api/ODADatabase.cc
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "odb_api/ODADatabase.h"
+#include "odb_api/Reader.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/TextReaderIterator.h"
+#include "odb_api/TODATable.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+ODADatabase::ODADatabase(const PathName& path,const std::string& name)
+: SQLDatabase(path,name)
+{
+    setIncludePath(Resource<std::string>("$ODB_INCLUDE_PATH", ""));
+}
+
+ODADatabase::~ODADatabase() {}
+
+void ODADatabase::open() {}
+
+void ODADatabase::close() { SQLDatabase::close(); }
+
+SQLTable* ODADatabase::table(const Table& t)
+{
+    std::map<std::string,SQLTable*>::iterator j (tablesByName_.find(t.name));
+
+    Log::debug() << "ODADatabase::table(" << t.name << ") tablesByName_.size()==" << tablesByName_.size() << std::endl;
+
+    for (std::map<std::string,SQLTable*>::iterator it(tablesByName_.begin()); it != tablesByName_.end(); ++it)
+        Log::info() << " : " << it->first << std::endl;
+
+    if(j == tablesByName_.end())
+    {
+        if (t.dataDescriptor)
+        {
+            // FIXME (?): path_ is '.'. ignore now
+            //tablesByName_[name] = new TODATable<Reader>(*this,path_,name);
+            tablesByName_[t.name] = new TODATable<Reader>(*this, t.name, t.name);
+            j = tablesByName_.find(t.name);
+        }
+        else
+        {
+            // Table is refered to by its name, not a path or another data descriptor
+            const TableDef& tableDef (schemaAnalyzer().findTable(t.name));
+            const string& location (tableDef.location());
+            if  (location.empty())
+                throw UserError(std::string("Table ") + t.name + " is not associated with a physical file.");
+            tablesByName_[t.name] = new TODATable<Reader>(*this, location, t.name);
+            j = tablesByName_.find(t.name);
+
+        }
+    }
+    return (*j).second;
+}
+
+SQLTable* ODADatabase::openDataHandle(DataHandle& dh, DataFormat dataFormat)
+{
+	std::string name ("dataHandle@");
+	//name += std::string(&dh);
+
+	if (dataFormat == ODA) return tablesByName_[name] = new TODATable<Reader>(*this, dh);
+	// TODO
+	//if (dataFormat == CSV) return tablesByName_[name] = new TODATable<TextReader>(*this, dh);
+
+	ASSERT(0 && "Format not supported");
+	return 0;
+}
+
+SQLTable* ODADatabase::openDataStream(std::istream& is, const std::string& delimiter, DataFormat dataFormat) 
+{
+	std::string name ("dataHandle@");
+	//name += std::string(&dh);
+
+	if (dataFormat == CSV) return tablesByName_[name] = new TODATable<TextReader>(*this, is, delimiter);
+	// TODO
+	//if (dataFormat == CSV) return tablesByName_[name] = new TODATable<TextReader>(*this, dh);
+
+	ASSERT(0 && "Format not supported");
+	return 0;
+}
+
+
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ODADatabase.h b/odb_api/src/odb_api/ODADatabase.h
new file mode 100644
index 0000000..c7abca3
--- /dev/null
+++ b/odb_api/src/odb_api/ODADatabase.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ODADatabase.h
+// Baudouin Raoult - ECMWF Oct 04
+
+#ifndef odb_api_ODADatabase_H
+#define odb_api_ODADatabase_H
+
+namespace eckit { class PathName; }
+
+#include "odb_api/SQLDatabase.h"
+
+namespace odb {
+namespace sql {
+
+class ODADatabase : public SQLDatabase {
+public:
+	ODADatabase(const eckit::PathName&,const std::string&);
+	~ODADatabase(); 
+
+private:
+// No copy allowed
+	ODADatabase(const ODADatabase&);
+	ODADatabase& operator=(const ODADatabase&);
+
+// -- Overridden methods
+	// From SQLDatabase
+
+	virtual void open();
+	virtual void close();
+	virtual SQLTable* table(const Table&);
+	virtual SQLTable* openDataHandle(eckit::DataHandle&, DataFormat = ODA);
+	virtual SQLTable* openDataStream(std::istream&, const std::string& delimiter, DataFormat = CSV); 
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const ODADatabase& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ODAHandle.cc b/odb_api/src/odb_api/ODAHandle.cc
new file mode 100644
index 0000000..e8f23e3
--- /dev/null
+++ b/odb_api/src/odb_api/ODAHandle.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "odb_api/ODAHandle.h"
+
+using namespace eckit;
+
+namespace odb {
+
+
+
+ODAHandle::ODAHandle(Offset start, Offset end)
+: start_(start),
+  end_(end)
+{
+	Log::debug() << "ODAHandle::ODAHandle(" << start << ", " << end << ")" << std::endl; 
+}
+
+void ODAHandle::print(std::ostream& o) const
+{
+    o << "[start:" << start_<< ", end_:" << end_ << ", values_:" /*<< values_ <<*/ "]";
+}
+
+ODAHandle::~ODAHandle()
+{
+	Log::debug() << "ODAHandle::~ODAHandle()" << std::endl;
+}
+
+void ODAHandle::addValue(const std::string& columnName, double v)
+{
+	Log::debug() << "ODAHandle::addValue('" << columnName << "', '" << v << "')" << std::endl;
+	ASSERT(values_.find(columnName) == values_.end());
+	values_[columnName] = v;
+}
+
+}
diff --git a/odb_api/src/odb_api/ODAHandle.h b/odb_api/src/odb_api/ODAHandle.h
new file mode 100644
index 0000000..1504ffb
--- /dev/null
+++ b/odb_api/src/odb_api/ODAHandle.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODAHandle.h
+/// @author Piotr Kuchta - ECMWF Aug 2011
+
+#ifndef ODAHandle_H
+#define ODAHandle_H
+
+#include "eckit/io/Offset.h"
+#include "odb_api/ODATranslator.h"
+
+namespace odb {
+
+
+class ODAHandle {
+public:
+	ODAHandle(eckit::Offset, eckit::Offset);
+	~ODAHandle(); 
+
+	void addValue(const std::string& columnName, double v);
+
+	template <typename T>
+		void getValue(const std::string& name, T& value)
+	{
+		value = ODATranslator<T>()(values_[name]);
+		eckit::Log::debug() << "ODAHandle::getValue('" << name << "',=>" << value << ")" << std::endl;
+	}
+
+	void print(std::ostream&) const;
+
+	eckit::Offset start() { return start_; }
+	void start(const eckit::Offset& n) { start_ = n; }
+
+	eckit::Offset end() { return end_; }
+	void end(const eckit::Offset& n) { end_ = n; }
+
+private:
+// No copy allowed
+	ODAHandle(const ODAHandle&);
+	ODAHandle& operator=(const ODAHandle&);
+
+	eckit::Offset start_;
+	eckit::Offset end_;
+	std::map<std::string, double> values_;
+
+	friend std::ostream& operator<<(std::ostream& s, const ODAHandle& p)
+		{ p.print(s); return s; }
+
+};
+
+}
+
+#endif
diff --git a/odb_api/src/odb_api/ODATranslator.h b/odb_api/src/odb_api/ODATranslator.h
new file mode 100644
index 0000000..94bca77
--- /dev/null
+++ b/odb_api/src/odb_api/ODATranslator.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODATranslator.h
+/// Piotr Kuchta - ECMWF June 2009
+
+#ifndef odb_api_ODATranslator_H
+#define odb_api_ODATranslator_H
+
+#include "eckit/eckit.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/types/Date.h"
+#include "eckit/types/Time.h"
+#include "odb_api/StringTool.h"
+
+template <typename T>
+struct ODATranslator {
+	T operator()(double n) { return T(n); }
+};
+
+template <>
+struct ODATranslator<std::string> {
+    std::string operator()(double n) 
+    {
+        std::string r (odb::StringTool::double_as_string(n));
+        eckit::Log::debug() << "ODATranslator<std::string>::operator()(double n=" << n << ") => " << r << std::endl;
+        return r;
+    }
+};
+
+template <>
+struct ODATranslator<eckit::Time> {
+    eckit::Time operator()(double n)
+    {
+        static const char * zeroes = "000000";
+
+        std::string t (eckit::Translator<double, std::string>()(n));
+        if (t.size() < 6)
+            t = std::string(zeroes + t.size()) + t;
+
+        eckit::Time tm (t);
+        eckit::Log::debug() << "ODATranslator<Time>::operator()(double n=" << n << ") => " << tm << std::endl;
+        return tm;
+    }
+};
+
+template <>
+struct ODATranslator<eckit::Date> {
+    eckit::Date operator()(double n)
+    {
+        static const char * zeroes ("000000");
+
+        std::string t (eckit::Translator<long, std::string>()(n));
+        if (t.size() < 6)
+            t = std::string(zeroes + t.size()) + t;
+
+        eckit::Date d (t);
+        eckit::Log::debug() << "ODATranslator<Date>::operator()(double n=" << n << ") => " << d << std::endl;
+        return d;
+    }
+};
+
+#endif
diff --git a/odb_api/src/odb_api/ODAUpdatingIterator.cc b/odb_api/src/odb_api/ODAUpdatingIterator.cc
new file mode 100644
index 0000000..1212f4d
--- /dev/null
+++ b/odb_api/src/odb_api/ODAUpdatingIterator.cc
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODAUpdatingIterator.cc
+///
+/// @author Piotr Kuchta, June 2009
+
+#include "odb_api/MetaData.h"
+
+namespace odb {
+
+template <typename T>
+ODAUpdatingIterator<T>::ODAUpdatingIterator (T& ii, const T& end, const std::vector<std::string>& columns, const std::vector<double>& values)
+: ii_(ii),
+  end_(end),
+  columns_(columns),
+  columnIndices_(columns.size()), 
+  values_(values),
+  data_(0),
+  refCount_(0),
+  noMore_(false)
+{
+	ASSERT(columns.size() == values.size());
+
+	updateIndices();
+    std::copy(ii_->data(), ii_->data() + ii_->columns().size(), data_);
+	update();
+}
+
+template <typename T>
+void ODAUpdatingIterator<T>::updateIndices()
+{
+	const MetaData& md (ii_->columns());
+
+	delete [] data_;
+	data_ = new double[md.size()];
+
+	for (size_t i = 0; i < columns_.size(); ++i)
+		columnIndices_[i] = md.columnIndex(columns_[i]);
+}
+
+template <typename T>
+ODAUpdatingIterator<T>::ODAUpdatingIterator (const T& end)
+: ii_(end),
+  end_(end),
+  columnIndices_(),
+  values_(),
+  data_(0),
+  refCount_(0),
+  noMore_(true)
+{}
+
+template <typename T>
+ODAUpdatingIterator<T>::~ODAUpdatingIterator () { delete [] data_; }
+
+template <typename T>
+void ODAUpdatingIterator<T>::update()
+{
+	for (size_t i = 0; i < columnIndices_.size(); ++i)
+		data_[columnIndices_[i]] = values_[i];
+}
+
+template <typename T>
+bool ODAUpdatingIterator<T>::isNewDataset()
+{
+	return ii_->isNewDataset();
+}
+
+template <typename T>
+bool ODAUpdatingIterator<T>::next(eckit::ExecutionContext*)
+{
+	if (noMore_)
+		return noMore_;
+	++ii_;
+	bool r = ii_ != end_;
+	if (r)
+	{
+		if (ii_->isNewDataset())
+			updateIndices();
+
+        std::copy(ii_->data(), ii_->data() + ii_->columns().size(), data_);
+		update();
+	}
+	noMore_ = !r;
+	return r;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ODAUpdatingIterator.h b/odb_api/src/odb_api/ODAUpdatingIterator.h
new file mode 100644
index 0000000..f443b3d
--- /dev/null
+++ b/odb_api/src/odb_api/ODAUpdatingIterator.h
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODAUpdatingIterator.h
+///
+/// @author Piotr Kuchta, June 2009
+
+#ifndef ODAUpdatingIterator_H
+#define ODAUpdatingIterator_H
+
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+
+template <typename T>
+class ODAUpdatingIterator 
+{
+public:
+	ODAUpdatingIterator (T& inputIterator, const T& end, const std::vector<std::string>& columns, const std::vector<double>& values);
+	ODAUpdatingIterator (const T& end);
+	~ODAUpdatingIterator ();
+
+	bool isNewDataset();
+	double* data() { return data_; }
+
+	const MetaData& columns() { return ii_->columns(); }
+	const MetaData& columns(MetaData& md) { return ii_->columns(md); }
+
+    ODAUpdatingIterator& operator++() { next(0); return *this; }
+
+	bool operator!=(const ODAUpdatingIterator& o) { ASSERT(&o == 0); return ii_ != end_; }
+
+//protected:
+	bool next(eckit::ExecutionContext*);
+
+private:
+// No copy allowed.
+    ODAUpdatingIterator(const ODAUpdatingIterator&);
+    ODAUpdatingIterator& operator=(const ODAUpdatingIterator&);
+
+	void update();
+	void updateIndices();
+
+	// Input iterator.
+	T ii_;
+	const T& end_;
+
+	std::vector<std::string> columns_;
+	std::vector<size_t> columnIndices_;
+	const std::vector<double> values_;
+
+	double *data_;
+public:
+	int refCount_;
+	bool noMore_;
+    eckit::ExecutionContext* context_;
+};
+
+} // namespace odb 
+
+#include "odb_api/ODAUpdatingIterator.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/ODBAPISettings.cc b/odb_api/src/odb_api/ODBAPISettings.cc
new file mode 100644
index 0000000..ddbcd8d
--- /dev/null
+++ b/odb_api/src/odb_api/ODBAPISettings.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <unistd.h>
+
+#include "eckit/config/Resource.h"
+#ifdef HAVE_AIO_H    
+# include "eckit/io/AIOHandle.h"
+#endif
+#include "eckit/io/FileHandle.h"
+#include "eckit/thread/ThreadSingleton.h"
+#include "eckit/parser/StringTools.h"
+
+#include "odb_api/ODBAPISettings.h"
+
+using namespace eckit;
+using namespace std;
+
+inline size_t MEGA(size_t n) { return n*1024*1204; }
+
+template class eckit::ThreadSingleton<odb::ODBAPISettings>;
+static ThreadSingleton<odb::ODBAPISettings> instance_;
+
+bool odb::ODBAPISettings::debug = false;
+
+void odb::ODBAPISettings::setHome(const char *argv0)
+{
+    const char* env(getenv("ODB_API_HOME"));
+    if (env) {
+        home_ = env;
+        Log::info() << "ODB_API_HOME set to " << home_ << endl;
+    } else {
+        string full;
+        if (argv0[0] == '/') {
+            char *absoluteArgv0;
+            // The resolved_path == NULL feature, not standardized in POSIX.1-2001, but standardized in POSIX.1-2008
+            if(0 == (absoluteArgv0 = ::realpath(argv0, 0)))
+                throw eckit::FailedSystemCall(string("realpath ") + argv0);
+            full = string(absoluteArgv0);
+            ::free(absoluteArgv0);
+        } else if (argv0[0] == '.' && argv0[1] == '/')
+        {
+            size_t bufferLen =1024*8;
+            char buffer[bufferLen];
+            full = string( ::getcwd(buffer, bufferLen) ) + string(argv0 + 1);
+        } else
+        {
+            vector<string> ps(StringTools::split(":", getenv("PATH")));
+            for (size_t i(0); i < ps.size(); ++i)
+            {
+                // TODO: perhaps we should also check if the file is readable, executable, etc...
+                if (PathName(ps[i] + "/" + argv0).exists())
+                {
+                    full = ps[i] + "/" + argv0;
+                    if (ps[i][0] != '/') {
+                        size_t bufferLen =1024*8;
+                        char buffer[bufferLen];
+                        full = string( ::getcwd(buffer, bufferLen) ) + full;
+                    }
+                    break;
+                }
+            }
+            
+        }
+        vector<string> ps(StringTools::split("/", full));
+        Log::debug() << "ODBAPISettings::setHome: argv0: " << ps << endl;
+        ASSERT("odb executable should be in a bin directory" && ps.size() >= 2 && ps[ps.size() - 2] == "bin");
+        ps.pop_back(); // odb
+        ps.pop_back(); // bin
+        home_ = "/" + StringTools::join("/", ps);
+        Log::info() << "ODB_API_HOME inferred as " << home_ << endl;
+    }
+}
+
+string odb::ODBAPISettings::fileInHome(const string& fileName)
+{
+    ASSERT(fileName[0] == '~');
+    ASSERT(fileName[1] == '/');
+    return home_ + fileName.substr(1);
+}
+
+void debugMeNow() {
+	Log::info() << "Debug me now" << endl;
+	odb::ODBAPISettings::debug = true;
+}
+
+namespace odb {
+
+ODBAPISettings& ODBAPISettings::instance()
+{
+	//ASSERT( &instance_.instance() != 0 );
+	return instance_.instance();
+}
+
+ODBAPISettings::ODBAPISettings()
+: headerBufferSize_(Resource<long>("$ODB_HEADER_BUFFER_SIZE;-headerBufferSize;headerBufferSize", MEGA(4))),
+  setvbufferSize_(Resource<long>("$ODB_SETVBUFFER_SIZE;-setvbufferSize;setvbufferSize", MEGA(8))),
+  useAIO_(Resource<bool>("$ODB_API_USE_AIO", false))
+{}
+
+size_t ODBAPISettings::headerBufferSize() { return headerBufferSize_; }
+void ODBAPISettings::headerBufferSize(size_t n) { headerBufferSize_ = n; }
+
+size_t ODBAPISettings::setvbufferSize() { return setvbufferSize_; }
+void ODBAPISettings::setvbufferSize(size_t n) { setvbufferSize_ = n; }
+
+void ODBAPISettings::createDirectories(const PathName& path)
+{
+    vector<string> parts (StringTools::split("/", path));
+    if (parts.size() < 2)
+        return;
+
+    parts.pop_back();
+    PathName directory ((string(path)[0] == '/' ? "/" : "") + StringTools::join("/", parts));
+
+    Log::debug() << "Making sure diretory " << directory << " exists" << endl;
+
+    directory.mkdir();
+}
+
+DataHandle* ODBAPISettings::writeToFile(const PathName& fn, const Length& length, bool openDataHandle)
+{
+    // ODB-122 Create subdirectories before creating a file
+    createDirectories(fn);
+
+#ifdef HAVE_AIO_H    
+	DataHandle* h (useAIO_
+                    ? static_cast<DataHandle*>(new AIOHandle(fn))
+                    : static_cast<DataHandle*>(new FileHandle(fn)));
+#else
+	DataHandle* h (static_cast<DataHandle*>(new FileHandle(fn)));
+#endif
+	if (openDataHandle) h->openForWrite(length);
+	return h;
+}
+
+DataHandle* ODBAPISettings::appendToFile(const PathName& fn, const Length& length, bool openDataHandle)
+{
+#ifdef HAVE_AIO_H    
+	DataHandle *h (useAIO_ 
+                    ? static_cast<DataHandle*>(new AIOHandle(fn))
+                    : static_cast<DataHandle*>(new FileHandle(fn)));
+#else
+	DataHandle* h (static_cast<DataHandle*>(new FileHandle(fn)));
+#endif
+	if (openDataHandle) h->openForAppend(length);
+	return h;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/ODBAPISettings.h b/odb_api/src/odb_api/ODBAPISettings.h
new file mode 100644
index 0000000..561b356
--- /dev/null
+++ b/odb_api/src/odb_api/ODBAPISettings.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODBAPISettings_H
+#define ODBAPISettings_H
+
+#include "eckit/thread/ThreadSingleton.h"
+#include "eckit/io/Length.h"
+
+namespace eckit {
+class PathName;
+class DataHandle;
+}
+
+namespace odb {
+
+class ODBAPISettings : private eckit::NonCopyable {
+public:
+
+	static ODBAPISettings& instance();
+
+	size_t headerBufferSize();
+	void headerBufferSize(size_t);
+
+	size_t setvbufferSize();
+	void setvbufferSize(size_t);
+
+    eckit::DataHandle* writeToFile(const eckit::PathName&, const eckit::Length& = eckit::Length(0), bool openDataHandle = true);
+    eckit::DataHandle* appendToFile(const eckit::PathName&, const eckit::Length& = eckit::Length(0), bool openDataHandle = true);
+
+    void setHome(const char *argv0);
+    std::string fileInHome(const std::string&);
+
+	static bool debug;
+
+private:
+    ODBAPISettings();
+
+    static void createDirectories(const eckit::PathName& path);
+
+	size_t headerBufferSize_;
+	size_t setvbufferSize_;
+
+	bool useAIO_;
+
+    friend struct eckit::NewAlloc0<ODBAPISettings>;
+    std::string home_;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ODBAPIVersion.cc b/odb_api/src/odb_api/ODBAPIVersion.cc
new file mode 100644
index 0000000..5a0f18d
--- /dev/null
+++ b/odb_api/src/odb_api/ODBAPIVersion.cc
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// This file will be updated autmatically by the make.sms script
+///
+
+#include "odb_api_config.h"
+#include "odb_api/Header.h"
+#include "odb_api/ODBAPIVersion.h"
+
+namespace odb {
+
+    const char *ODBAPIVersion::version() { return ODB_API_VERSION_STR; }
+
+	unsigned int ODBAPIVersion::formatVersionMajor() { return FORMAT_VERSION_NUMBER_MAJOR; }
+	unsigned int ODBAPIVersion::formatVersionMinor() { return FORMAT_VERSION_NUMBER_MINOR; }
+	const char *ODBAPIVersion::installPrefix()       { return ODB_API_INSTALL_PREFIX; }
+	const char *ODBAPIVersion::buildDirectory()      { return ODB_API_BINARY_DIR; }
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ODBAPIVersion.h b/odb_api/src/odb_api/ODBAPIVersion.h
new file mode 100644
index 0000000..2c672bb
--- /dev/null
+++ b/odb_api/src/odb_api/ODBAPIVersion.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// This file will be updated autmatically by the make.sms script
+///
+
+namespace odb {
+
+class ODBAPIVersion {
+public:
+    static const char *version();
+    static const char *gitsha1();
+	static unsigned int formatVersionMajor();
+	static unsigned int formatVersionMinor();
+
+	static const char *installPrefix();
+	static const char *buildDirectory();
+};
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ODBAPIVersionSHA1.cc.in b/odb_api/src/odb_api/ODBAPIVersionSHA1.cc.in
new file mode 100644
index 0000000..728dab9
--- /dev/null
+++ b/odb_api/src/odb_api/ODBAPIVersionSHA1.cc.in
@@ -0,0 +1,18 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ODBAPIVersion.h"
+
+namespace odb {
+
+    const char *ODBAPIVersion::gitsha1() { return "@ODB_API_GIT_SHA1@"; }
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ODBApplication.cc b/odb_api/src/odb_api/ODBApplication.cc
new file mode 100755
index 0000000..ab522e6
--- /dev/null
+++ b/odb_api/src/odb_api/ODBApplication.cc
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODBApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/config/Resource.h"
+#include "eckit/log/OStreamTarget.h"
+#include "eckit/log/Colour.h"
+#include "eckit/log/ColouringTarget.h"
+
+#include "odb_api/ODBApplication.h"
+#include "odb_api/ODBTarget.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+ODBApplication::ODBApplication (int argc, char **argv)
+: Tool(argc, argv),
+  clp_(argc, argv) {
+}
+
+ODBApplication::~ODBApplication() {}
+
+CommandLineParser& ODBApplication::commandLineParser() { return clp_; }
+
+static LogTarget* cerr_target() { return new OStreamTarget(std::cerr); }
+
+eckit::LogTarget* ODBApplication::createInfoLogTarget() const { return new ODBTarget("(I)", cerr_target()); }
+eckit::LogTarget* ODBApplication::createDebugLogTarget() const { return new ODBTarget("(D)", cerr_target()); }
+eckit::LogTarget* ODBApplication::createErrorLogTarget() const { return new ODBTarget("(E)", new ColouringTarget(cerr_target(), &Colour::red)); }
+eckit::LogTarget* ODBApplication::createWarningLogTarget() const { return new ODBTarget("(W)", new ColouringTarget(cerr_target(), &Colour::yellow)); }
+
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ODBApplication.h b/odb_api/src/odb_api/ODBApplication.h
new file mode 100755
index 0000000..4aaa8a0
--- /dev/null
+++ b/odb_api/src/odb_api/ODBApplication.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODBApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef ODBApplication_H
+#define ODBApplication_H
+
+#include "eckit/runtime/Tool.h"
+#include "odb_api/CommandLineParser.h"
+
+namespace odb {
+namespace tool {
+
+class ODBApplication : public eckit::Tool {
+public:
+
+    ODBApplication (int argc, char **argv);
+	virtual ~ODBApplication ();
+
+	CommandLineParser& commandLineParser();
+
+private:
+
+    virtual eckit::LogTarget* createInfoLogTarget() const;
+    virtual eckit::LogTarget* createWarningLogTarget() const;
+    virtual eckit::LogTarget* createErrorLogTarget() const;
+    virtual eckit::LogTarget* createDebugLogTarget() const;
+
+    CommandLineParser clp_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ODBModule.cc b/odb_api/src/odb_api/ODBModule.cc
new file mode 100644
index 0000000..4c915cf
--- /dev/null
+++ b/odb_api/src/odb_api/ODBModule.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ODBModule.cc
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#include <string>
+
+#include "ecml/parser/Request.h"
+
+#include "ecml/core/RequestHandler.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/SpecialFormHandler.h"
+
+#include "odb_api/ecml_verbs/ArchiveHandler.h"
+#include "odb_api/ecml_verbs/RetrieveHandler.h"
+#include "odb_api/ecml_verbs/StageHandler.h"
+#include "odb_api/ecml_verbs/SQLHandler.h"
+#include "odb_api/ecml_verbs/CompareHandler.h"
+#include "odb_api/ecml_verbs/ChunkHandler.h"
+#include "odb_api/ecml_verbs/SQLTestHandler.h"
+#include "odb_api/ecml_verbs/CreatePartitionsHandler.h"
+#include "odb_api/ecml_verbs/CreateIndexHandler.h"
+#include "odb_api/ecml_verbs/ImportTextHandler.h"
+
+#include "ODBModule.h"
+
+namespace odb {
+
+using namespace std;
+using namespace ecml;
+using namespace eckit;
+
+ODBModule::ODBModule() {}
+ODBModule::~ODBModule() {}
+
+void ODBModule::importInto(ExecutionContext& context)
+{
+    static ArchiveHandler archive("odb.archive");
+    static RetrieveHandler retrieve("odb.retrieve", false);
+    static RetrieveHandler local_retrieve("odb.local_retrieve", true);
+    static StageHandler stage("odb.stage", false);
+    static StageHandler local_stage("odb.local_stage", true);
+    static SQLHandler sql("odb.sql");
+    static SQLHandler split("odb.split");
+    static CompareHandler compare("odb.compare");
+    static ChunkHandler chunk("odb.chunk");
+    static SQLTestHandler sql_test("odb.sql_test");
+    static CreatePartitionsHandler create_partitions("odb.create_partitions");
+    static CreateIndexHandler create_index("odb.create_index");
+    static ImportTextHandler import_text("odb.import_text");
+
+    context.registerHandler("archive", archive);
+    context.registerHandler("retrieve", retrieve);
+    context.registerHandler("local_retrieve", local_retrieve);
+    context.registerHandler("stage", stage);
+    context.registerHandler("local_stage", local_stage);
+    context.registerHandler("sql", sql);
+    context.registerHandler("split", split);
+    context.registerHandler("compare", compare);
+    context.registerHandler("chunk", chunk);
+    context.registerHandler("sql_test", sql_test);
+    context.registerHandler("create_partitions", create_partitions);
+    context.registerHandler("create_index", create_index);
+    context.registerHandler("import_text", import_text);
+}
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/ODBModule.h b/odb_api/src/odb_api/ODBModule.h
new file mode 100644
index 0000000..91151de
--- /dev/null
+++ b/odb_api/src/odb_api/ODBModule.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ODBModule.h
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#ifndef odb_api_ODBModule_H
+#define odb_api_ODBModule_H
+
+#include "ecml/core/Module.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace odb {
+
+class ODBModule : public ecml::Module {
+public:
+    ODBModule();
+    ~ODBModule();
+    void importInto(ecml::ExecutionContext&);
+};
+
+}  // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ODBTarget.cc b/odb_api/src/odb_api/ODBTarget.cc
new file mode 100644
index 0000000..6d4227a
--- /dev/null
+++ b/odb_api/src/odb_api/ODBTarget.cc
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "eckit/runtime/Monitor.h"
+#include "eckit/log/TimeStamp.h"
+
+#include "odb_api/ODBTarget.h"
+
+namespace odb {
+
+ODBTarget::ODBTarget(const char* tag, eckit::LogTarget* target)
+: eckit::WrapperTarget(target),
+  tag_(tag)
+{}
+
+void ODBTarget::writePrefix() {
+
+    std::ostringstream oss;
+    oss //<< std::setw(3)
+        //<< std::setfill('0')
+        //<< Monitor::instance().self()
+        << "000"
+        << std::setfill(' ') << ' '
+        << eckit::TimeStamp() << ' ';
+
+    if(tag_ && *tag_) {
+        oss << tag_ << ' ';
+    }
+
+    const std::string& s (oss.str());
+    const char* p (s.c_str());
+    target_->write(p, p + s.size());
+}
+
+void ODBTarget::print(std::ostream& s) const
+{
+    s << "ODBTarget";
+}
+
+void ODBTarget::writeSuffix() {} 
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/ODBTarget.h b/odb_api/src/odb_api/ODBTarget.h
new file mode 100644
index 0000000..15fcaee
--- /dev/null
+++ b/odb_api/src/odb_api/ODBTarget.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2016 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file ODBTarget.h
+/// @author Piotr Kuchta
+
+#ifndef odbapi_ODBTarget_h
+#define odbapi_ODBTarget_h
+
+#include <utility>
+
+#include "eckit/log/WrapperTarget.h"
+
+namespace odb {
+
+//----------------------------------------------------------------------------------------------------------------------
+
+class ODBTarget : public eckit::WrapperTarget {
+public:
+
+    ODBTarget(const char* tag = "", eckit::LogTarget* target = 0);
+
+private:
+
+    virtual void writePrefix();
+    virtual void writeSuffix();
+
+    void print(std::ostream& s) const;
+
+private:
+
+    const char* tag_;
+
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Odb2Hub.cc b/odb_api/src/odb_api/Odb2Hub.cc
new file mode 100644
index 0000000..fc84f59
--- /dev/null
+++ b/odb_api/src/odb_api/Odb2Hub.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+
+#include "odb_api/odbcapi.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/DataStream.h"
+#include "odb_api/HashTable.h"
+#include "odb_api/Codec.h"
+#include "odb_api/Column.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/CommandLineParser.h"
+#include "odb_api/StringTool.h"
+
+#include "odb_api/FileMapper.h"
+#include "Odb2Hub.h"
+
+using namespace eckit;
+using namespace odb;
+using namespace odb::tool;
+
+PathName Odb2Hub::getPath(const string& schema, const string& pathName, const string& keywordToColumnMapping)
+{
+    ostream& L(Log::info());
+	FileMapper mapper(schema);
+	
+	vector<string> keywords(mapper.keywords());
+	L << "Odb2Hub::getPath: schema = " << schema << endl;
+	L << "Odb2Hub::getPath: pathName = " << pathName << endl;
+	L << "Odb2Hub::getPath: keywordToColumnMapping = " << keywordToColumnMapping << endl;
+
+	FastODA2Request<ODA2RequestServerTraits> o2r;
+	//o.parseConfig(StringTool::readFile(cfgFile));
+	o2r.parseConfig(keywordToColumnMapping);
+
+	OffsetList offsets;
+	LengthList lengths;
+	vector<ODAHandle*> handles;
+	bool rc = o2r.scanFile(pathName, offsets, lengths, handles);
+	ASSERT(rc == true);
+
+	// This will throw an exception if values of interest are not constant.
+	map<string, double> uniqueValues(o2r.getUniqueValues());
+	map<string, string> values;
+	for (map<string, double>::iterator it(uniqueValues.begin()); it != uniqueValues.end(); ++it)
+	{
+		const string keyword(it->first);
+		const set<string>& vs(o2r.getValues(keyword));
+		ASSERT (vs.size() == 1);
+        string value (StringTools::trim(*vs.begin()));
+
+		L << "getPath: '" << keyword << "' = '" << value << "'" << endl;
+
+		values[keyword] = value;
+	}
+	
+	return mapper.encodeRelative(values);
+}
diff --git a/odb_api/src/odb_api/Odb2Hub.h b/odb_api/src/odb_api/Odb2Hub.h
new file mode 100644
index 0000000..32293b4
--- /dev/null
+++ b/odb_api/src/odb_api/Odb2Hub.h
@@ -0,0 +1,18 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Odb2Hub_H
+#define Odb2Hub_H
+
+struct Odb2Hub {
+    static eckit::PathName getPath(const std::string& schema, const std::string& pathName, const std::string& keywordToColumnMapping);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/OrderByExpressions.cc b/odb_api/src/odb_api/OrderByExpressions.cc
new file mode 100755
index 0000000..fe34756
--- /dev/null
+++ b/odb_api/src/odb_api/OrderByExpressions.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/OrderByExpressions.h"
+#include "odb_api/SQLExpression.h"
+#include "odb_api/StringTool.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+bool OrderByExpressions::operator<(const OrderByExpressions& o) const
+{
+	size_t n = size();
+	//ASSERT(n == o.size());
+
+	for (size_t i = 0; i < n; ++i)
+	{
+		bool asc = ascending_[i];
+
+		const SQLExpression& left = asc ? *(*this)[i] : *o[i];
+		const SQLExpression& right = asc ? *o[i] : *(*this)[i];
+
+		bool leftMissing = false;
+		bool rightMissing = false;
+
+		double leftValue = left.eval(leftMissing);
+		double rightValue = right.eval(rightMissing);
+
+		//TODO: handle missing value
+		if (leftValue == rightValue)
+			continue;
+
+		if (left.type()->getKind() == type::SQLType::stringType)
+		{
+			std::string lv = StringTool::double_as_string(leftValue);
+			std::string rv = StringTool::double_as_string(rightValue);
+			if (! (lv.compare(rv) < 0))
+				return false;
+		} else {
+			if (! (leftValue < rightValue))
+				return false;
+		}
+
+		return true;
+	}
+	return false;
+}
+
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/OrderByExpressions.h b/odb_api/src/odb_api/OrderByExpressions.h
new file mode 100755
index 0000000..06a676d
--- /dev/null
+++ b/odb_api/src/odb_api/OrderByExpressions.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file OrderByExpressions.h
+/// Piotr Kuchta - ECMWF Nov 11
+
+#ifndef OrderByExpressions_H
+#define OrderByExpressions_H
+
+#include "eckit/eckit.h"
+//#include <vector>
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class OrderByExpressions : public Expressions
+{
+public:
+	OrderByExpressions(const OrderByExpressions& o)
+	: Expressions(o), ascending_(o.ascending_)
+	{}
+
+	OrderByExpressions(const std::vector<bool>& ascending) : ascending_(ascending) {}
+
+	bool operator<(const OrderByExpressions&) const;
+
+
+private:
+	const std::vector<bool>& ascending_;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ParameterExpression.cc b/odb_api/src/odb_api/ParameterExpression.cc
new file mode 100755
index 0000000..80ccc6b
--- /dev/null
+++ b/odb_api/src/odb_api/ParameterExpression.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ParameterExpression.h"
+#include "odb_api/SQLSession.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+ParameterExpression::ParameterExpression(int which)
+: value_(0),
+  which_(which)
+{
+// don't use any Log::* here
+//	std::cout << "new ParameterExpression " << name << std::endl;
+}
+
+ParameterExpression::ParameterExpression(const ParameterExpression& other)
+: value_(other.value_),
+  which_(other.which_)
+{}
+
+
+SQLExpression* ParameterExpression::ParameterExpression::clone() const { return new ParameterExpression(*this); }
+
+ParameterExpression::~ParameterExpression() {}
+
+// TODO: are only real parameters allowed?
+const type::SQLType* ParameterExpression::type() const { return &type::SQLType::lookup("real"); }
+
+double ParameterExpression::eval(bool& missing) const { return value_; }
+
+void ParameterExpression::prepare(SQLSelect& sql)
+{
+    NOTIMP;
+    //TODO: 
+	//value_ = SQLSession::current().getParameter(which_);
+//	std::cout << "ParameterExpression " << name_ << " " << value_ << std::endl;
+}
+
+void ParameterExpression::cleanup(SQLSelect& sql) { value_ = 0; }
+
+void ParameterExpression::print(std::ostream& s) const
+{
+	s << '?' << which_ << '=' << value_;
+}
+
+bool ParameterExpression::isConstant() const { return false; }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ParameterExpression.h b/odb_api/src/odb_api/ParameterExpression.h
new file mode 100755
index 0000000..ca6566c
--- /dev/null
+++ b/odb_api/src/odb_api/ParameterExpression.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ParameterExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef ParameterExpression_H
+#define ParameterExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class ParameterExpression : public SQLExpression {
+public:
+	ParameterExpression(int);
+	ParameterExpression(const ParameterExpression&);
+	~ParameterExpression(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	ParameterExpression& operator=(const ParameterExpression&);
+
+// -- Members
+	// None
+	double value_;
+	int    which_;
+
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+
+	virtual double eval(bool& missing) const;
+	const type::SQLType* type() const;
+	virtual bool isConstant() const;
+};
+
+} // namespace expression 
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/Partition.cc b/odb_api/src/odb_api/Partition.cc
new file mode 100644
index 0000000..de67edd
--- /dev/null
+++ b/odb_api/src/odb_api/Partition.cc
@@ -0,0 +1,241 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Partition.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/RequestParser.h"
+
+#include "odb_api/WriterBufferingIterator.h"
+
+using namespace eckit;
+using namespace ecml;
+using namespace std;
+
+namespace odb {
+
+std::ostream& operator<< (std::ostream& o, const Partition& p)
+{
+    o << " number of blocks: " << p.blocks_.size() << " ";
+
+    for (size_t i (0); i < p.blocks_.size(); ++i)
+        o << p.blocks_[i] << ", ";
+    return o;
+}
+
+std::ostream& Partition::save(std::ostream& o, size_t poolNumber) const
+{
+    for (size_t i (0); i < blocks_.size(); ++i)
+        o << poolNumber << "\t" << blocks_[i] << endl;
+    return o;
+}
+
+Partition::Partition(const PathName& fileName, size_t partitionNumber)
+{
+    Log::info() << "Partition::Partition: read info on partition " << partitionNumber << " from " << fileName << endl;
+    if (! fileName.exists())
+        throw UserError(string(fileName) + " does not exist");
+
+    std::ifstream f (string(fileName).c_str());
+
+    string line;
+    while (std::getline(f, line))
+    {
+        Log::info() << "Partition::Partition: line: " << line << endl;
+
+        vector<string> fs (StringTools::split("\t", line));
+        ASSERT(fs.size() == 2);
+
+        size_t part (atol(fs[0].c_str()));
+        string blockDescription (fs[1]);
+
+        Request requests (RequestParser::parse(blockDescription));
+
+        ecml::ExecutionContext context;
+        context.pushEnvironmentFrame(requests->value());
+
+        if (part == partitionNumber)
+            add( Block (eckit::PathName(context.getValueAsList("file")[0]), //eckit::PathName(fs[1]),
+                        eckit::Offset(atol(context.getValueAsList("start")[0].c_str())),//eckit::Offset(atol(fs[2].c_str())),
+                        eckit::Offset(atol(context.getValueAsList("end")[0].c_str())),//eckit::Offset(atol(fs[3].c_str())),
+                        atol(context.getValueAsList("firstRow")[0].c_str()),//atol(fs[4].c_str()),
+                        atol(context.getValueAsList("lastRow")[0].c_str()))); //atol(fs[5].c_str())));
+    }
+}
+
+Partition::Partition()
+: blocks_ (),
+  startOfLastBlock_(0),
+  rowsOnLastBlock_(0)
+{}
+
+Partition::Partition(const Partition& other)
+: blocks_ (other.blocks_),
+  startOfLastBlock_ (other.startOfLastBlock_),
+  rowsOnLastBlock_ (other.rowsOnLastBlock_)
+{}
+
+Partition& Partition::operator=(const Partition& other)
+{
+    blocks_ = other.blocks_;
+    startOfLastBlock_ = other.startOfLastBlock_;
+    rowsOnLastBlock_ = other.rowsOnLastBlock_;
+    return *this;
+}
+
+ullong Partition::numberOfRows() const
+{
+    ullong r (0);
+    for (size_t i(0); i < blocks_.size(); ++i)
+        r += blocks_[i].lastRow - blocks_[i].firstRow;
+    return r;
+}
+
+ullong Partition::numberOfRowsOnLastBlock() const
+{
+    ASSERT(blocks_.size());
+    return blocks_.back().lastRow;
+}
+
+void Partition::add(const Block& block)
+{
+    blocks_.push_back(block);
+}
+
+/*
+void Partition::add(const PathName& fileName, ullong start, ullong length, ullong seqno, ullong firstRow, ullong nRows)
+{
+    if (! blocks_.size())
+    {
+        startOfLastBlock_ = Offset(start);
+        rowsOnLastBlock_ = firstRow + nRows;
+        blocks_.push_back(Block(fileName, start, length, firstRow, firstRow + nRows));
+    }
+    else
+    {
+        ASSERT(firstRow == 0); // new file, should start from beginning
+
+        Block& last (blocks_.back());
+        if (last.fileName != fileName)
+            blocks_.push_back(Block(fileName, start, length, firstRow, firstRow + nRows));
+        else
+        {
+            last.lastRow += nRows;
+
+            if (startOfLastBlock_ == Offset(start))
+                rowsOnLastBlock_ += nRows;
+            else
+            {
+                startOfLastBlock_ = Offset(start);
+                rowsOnLastBlock_ = firstRow + nRows;
+                last.length += Length(length);
+            }
+        }
+    }
+}
+*/
+
+
+ullong writeBlock(DataHandle& in, const Block& block, Writer<>::iterator& out)
+{
+    odb::Reader reader(in);
+    odb::Reader::iterator it (reader.begin()), end (reader.end());
+
+    ullong rowNumber (0);
+    for (size_t r(0); r < block.lastRow; ++r, ++it)
+    {
+        ASSERT(it != end);
+        if (r >= block.firstRow)
+        {
+            if (out->columns() != it->columns())
+            {
+                out->columns(it->columns());
+                out->writeHeader();
+            }
+
+            for (size_t fi (0); fi < it->columns().size(); ++fi)
+                (*out)[fi] = (*it)[fi];
+
+            ++out;
+            ++rowNumber;
+        }
+    }
+    return rowNumber;
+}
+
+ullong Partition::write(DataHandle& dh) const
+{
+    long long rowNumber (0);
+    const vector<Block>& blocks (blocks_);
+    for (size_t i (0); i < blocks.size(); ++i)
+    {
+        const Block& block (blocks[i]);
+        Log::info() << "Partition::write: writing block " << i << ":" << block << endl;
+        Writer<> writer(&dh, false, false);
+        Writer<>::iterator out (writer.begin(/*openDataHandle*/ false));
+
+        PartFileHandle fh (block.fileName, block.start, block.end - block.start);
+
+        Log::info() << "Partition::write: writing PartFileHandle: " << fh << endl;
+
+        fh.openForRead();
+        ullong nr (writeBlock(fh, block, out));
+        rowNumber += nr;
+    }
+    return rowNumber;
+
+}
+
+ullong Partition::write(const PathName& fileName) const
+//{
+//    FileHandle oh (fileName);
+//    oh.openForWrite(Length(1024 * 1024 * 10));
+//    return write(oh);
+//}
+
+//ullong Partition::write(DataHandle& dh) const
+{
+    long long rowNumber (0);
+    const vector<Block>& blocks (blocks_);
+
+    { PathName(fileName).unlink(); }
+
+    for (size_t i (0); i < blocks.size(); ++i)
+    {
+        const Block& block (blocks[i]);
+
+        Log::info() << "Partition::write: writing block " << i << ":" << block << endl;
+
+        FileHandle dh (fileName);
+        dh.openForAppend(Length(1024 * 1024 * 10));
+
+        Writer<> writer(&dh, false, false);
+        Writer<>::iterator out (writer.begin(/*openDataHandle*/ false));
+
+        PartFileHandle fh (block.fileName, block.start, block.end - block.start);
+        fh.openForRead();
+        ullong nr (writeBlock(fh, block, out));
+        rowNumber += nr;
+    }
+    return rowNumber;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/Partition.h b/odb_api/src/odb_api/Partition.h
new file mode 100644
index 0000000..0f824d5
--- /dev/null
+++ b/odb_api/src/odb_api/Partition.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, Oct 2015
+
+#ifndef odb_api_Partition_H
+#define odb_api_Partition_H
+
+#include "odb_api/Block.h"
+
+#include <vector>
+
+namespace eckit { class PathName; }
+
+namespace odb {
+
+class Partition {
+public:
+    Partition();
+    /// Read pool info from partitions info file
+    Partition(const eckit::PathName&, size_t);
+    Partition(const Partition&);
+    Partition& operator=(const Partition&);
+   
+    //void add(const eckit::PathName&, ullong start, ullong end, ullong seqno, ullong firstRow, ullong nRows);
+    void add(const Block&);
+
+    ullong numberOfRows() const;
+    ullong numberOfRowsOnLastBlock() const;
+
+    std::vector<Block>& blocks() { return blocks_; }
+
+    ullong rowsOnLastBlock() const { return rowsOnLastBlock_; }
+    void rowsOnLastBlock(ullong n) { rowsOnLastBlock_ = n; }
+
+    ullong startOfLastBlock() { return startOfLastBlock_; }
+    void startOfLastBlock(ullong n) { startOfLastBlock_ = n; }
+
+    ullong write(const eckit::PathName& fileName) const;
+    ullong write(eckit::DataHandle& dh) const;
+
+    std::string str() const;
+    std::ostream& save(std::ostream&, size_t poolNumber) const;
+
+private:
+    std::vector<Block> blocks_;
+    ullong startOfLastBlock_;
+    ullong rowsOnLastBlock_;
+
+    friend std::ostream& operator<< (std::ostream&, const Partition&);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/Partitioner.cc b/odb_api/src/odb_api/Partitioner.cc
new file mode 100644
index 0000000..6de15a1
--- /dev/null
+++ b/odb_api/src/odb_api/Partitioner.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Partitioner.h"
+#include "odb_api/RowsCounter.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+using namespace eckit;
+
+namespace odb {
+
+Partitions Partitioner::createPartitions(const std::vector<eckit::PathName>& files, size_t numberOfPartitions)
+{
+    vector<PathName> indices;
+    for (size_t i (0); i < files.size(); ++i)
+        indices.push_back(files[i] + ".idx");
+
+    return createPartitions(files, indices, numberOfPartitions);
+}
+
+Partitions Partitioner::createPartitions(const std::vector<eckit::PathName>& files, const std::vector<eckit::PathName>& indices, size_t numberOfPartitions)
+{
+    if (numberOfPartitions > 2) --numberOfPartitions;
+
+    Partitions parts;
+    ullong totalRowsNumber (countRows (files, indices));
+    ullong rowsPerPartition ((totalRowsNumber / numberOfPartitions));
+
+    //Log::info() << "*** createPartitions: numberOfPartitions: " << numberOfPartitions << ", totalRowsNumber: " << totalRowsNumber << ", rowsPerPartition: " << rowsPerPartition <<  endl;
+    for (size_t i(0); i < indices.size(); ++i)
+    {
+        odb::Select in("select block_begin, block_length, seqno, n_rows;", indices[i]); 
+        for (odb::Select::iterator it (in.begin()), end (in.end()); it != end; ++it)
+        {
+            const ullong blockStart ((*it)[0]),
+                         blockLength ((*it)[1]),
+                         seqno ((*it)[2]),
+                         nRows ((*it)[3]);
+
+            parts.addReport(files[i], blockStart, blockLength, seqno, nRows, rowsPerPartition);
+
+/*
+            if ( parts.back().numberOfRows() + nRows <= rowsPerPartition)
+                parts.back().add(files[i], blockStart, blockLength, seqno, 0, nRows);
+            else
+            {
+                const Block& last (parts.back().blocks().back());
+                ullong firstRow ( newFile ? 0
+                                  :  last.blockStart + last.blockLength == Offset(blockStart) + Length(blockLength) 
+                                     ? parts.back().rowsOnLastBlock()
+                                     : 0 );
+
+                parts.push_back(Partition());
+                parts.back().add(files[i], blockStart, blockLength, seqno, firstRow, nRows);
+            }
+            newFile = false;
+*/
+        }
+    }
+    return parts;
+}
+
+ullong Partitioner::countRows(const std::vector<eckit::PathName>& files, const std::vector<eckit::PathName>& indices)
+{
+    ullong n (0);
+    for (size_t i(0); i < files.size(); ++i)
+        n += RowsCounter::fastRowCount(files[i]);
+    return n;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/Partitioner.h b/odb_api/src/odb_api/Partitioner.h
new file mode 100644
index 0000000..7bdf738
--- /dev/null
+++ b/odb_api/src/odb_api/Partitioner.h
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, January 2016
+
+#ifndef odb_api_Partitioner_H
+#define odb_api_Partitioner_H
+
+#include "odb_api/Partition.h"
+#include "odb_api/Partitions.h"
+#include "odb_api/Indexer.h"
+
+namespace odb {
+
+class Partitioner {
+public:
+    static Partitions createPartitions(const std::vector<eckit::PathName>&, size_t);
+    static Partitions createPartitions(const std::vector<eckit::PathName>&, const std::vector<eckit::PathName>&, size_t);
+
+private:
+    static ullong countRows(const std::vector<eckit::PathName>& files, const std::vector<eckit::PathName>& indices);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/Partitions.cc b/odb_api/src/odb_api/Partitions.cc
new file mode 100644
index 0000000..5904604
--- /dev/null
+++ b/odb_api/src/odb_api/Partitions.cc
@@ -0,0 +1,133 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Partitions.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+#include "eckit/filesystem/PathName.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+
+std::ostream& operator<< (std::ostream& o, const Partitions& p)
+{
+    for (size_t i (0); i < p.size(); ++i)
+        p[i].save(o, i);
+        //o << "[" << i << ":" << i << p[i] <<  "], ";
+    return o;
+}
+
+vector<PathName> Partitions::write(const PathName& fileNamePrefix) const
+{
+    vector<PathName> r;
+
+    for (size_t i(0); i < size(); ++i)
+    {
+        const Partition& p (at(i));
+
+        stringstream ss;
+        ss << fileNamePrefix << ".pool_" << i;
+        PathName partitionFileName (ss.str());
+
+        Log::info() << "" << " ##### Writing partition " << i << " to file " << partitionFileName << endl;
+
+        ullong n (p.write(partitionFileName));
+
+        r.push_back(partitionFileName);
+    }
+    return r;
+}
+
+void Partitions::save(const PathName& partitionFile)
+{
+    Log::info() << "Saving partitions info to " << partitionFile << endl;
+
+    ofstream f;
+    f.exceptions(ofstream::badbit | ofstream::failbit);
+    f.open(string(partitionFile).c_str());
+    f << *this;
+    f.close();
+}
+
+std::string Partitions::str() const
+{
+    stringstream ss;
+    ss << *this;
+    return ss.str();
+}
+
+void Partitions::addReport(const PathName& fileName, ullong blockStart, ullong blockLength, ullong seqno, ullong nRows, ullong rowsPerPartition)
+{
+    Partitions& parts (*this);
+    if (parts.empty()) 
+        parts.push_back(Partition());
+
+    Partition& currentPartition (parts.back());
+    // Do we have to open new partition?
+    if (! (currentPartition.numberOfRows() + nRows <= rowsPerPartition)) {
+        // Open new partition
+        ullong firstRow (0);
+        if (! currentPartition.blocks().empty()) {
+            const Block& lastBlock (currentPartition.blocks().back());
+            if (lastBlock.fileName == fileName && currentPartition.startOfLastBlock() == Offset(blockStart))
+                firstRow = currentPartition.rowsOnLastBlock();
+        }
+
+        parts.push_back(Partition());
+        Partition& newPartition (parts.back());
+
+        newPartition.add(Block(fileName, blockStart, blockStart + blockLength, firstRow, firstRow + nRows));
+        newPartition.startOfLastBlock(Offset(blockStart));
+        newPartition.rowsOnLastBlock(nRows);
+    } else {
+        // Add to existing partition.
+        // First block seen?
+        if (currentPartition.blocks().empty()) {
+            currentPartition.add(Block(fileName, blockStart, blockStart + blockLength, /*firstRow*/ 0, /*lastRow*/ nRows));
+            currentPartition.startOfLastBlock(Offset(blockStart));
+            currentPartition.rowsOnLastBlock(/*firstRow*/ 0 + nRows);
+        } else {
+            // If this is a new file then we need to create a new block
+            Block& currentBlock (currentPartition.blocks().back());
+
+            if (currentBlock.fileName != fileName) {
+                currentPartition.add(Block(fileName, blockStart, blockStart + blockLength, /*firstRow*/ 0, /*lastRow*/ nRows));
+                currentPartition.startOfLastBlock(Offset(blockStart));
+                currentPartition.rowsOnLastBlock(/*firstRow*/ 0 + nRows);
+            } else {
+                // It's a block on the same file as the previously processed report.
+                // If this is a report in a new physical block then we need to adjust current block's boundaries
+                if (currentBlock.end < Offset(blockStart + blockLength))
+                    currentBlock.end = blockStart + blockLength;
+
+                currentBlock.lastRow += nRows;
+
+                if (currentPartition.startOfLastBlock() == Offset(blockStart))
+                    currentPartition.rowsOnLastBlock(currentPartition.rowsOnLastBlock() + nRows);
+                else {
+                    currentPartition.startOfLastBlock(Offset(blockStart));
+                    currentPartition.rowsOnLastBlock(/*firstRow*/ 0 + nRows);
+                }
+            }
+        }
+    }
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/Partitions.h b/odb_api/src/odb_api/Partitions.h
new file mode 100644
index 0000000..18b4434
--- /dev/null
+++ b/odb_api/src/odb_api/Partitions.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, November 2015
+
+#ifndef odb_api_Partitions_H
+#define odb_api_Partitions_H
+
+#include "odb_api/Block.h"
+#include "odb_api/Partition.h"
+
+#include <vector>
+
+namespace eckit { class PathName; }
+
+namespace odb {
+
+typedef std::vector<Partition> PartitionsBase;
+
+class Partitions : public PartitionsBase {
+public:
+    // Write data to files fileNamePrefix.pool_[i] 
+    std::vector<eckit::PathName> write(const eckit::PathName& fileNamePrefix) const;
+
+    void addReport(const eckit::PathName& fileName, ullong blockStart, ullong blockLength, ullong seqno, ullong nRows, ullong rowsPerPartition); 
+
+    // Write partiions info to a text file
+    void save(const eckit::PathName&);
+
+    std::string str() const;
+
+private:
+    friend std::ostream& operator<< (std::ostream&, const Partitions&);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/Reader.cc b/odb_api/src/odb_api/Reader.cc
new file mode 100644
index 0000000..2861ace
--- /dev/null
+++ b/odb_api/src/odb_api/Reader.cc
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODA.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "ecml/data/DataHandleFactory.h"
+#include "odb_api/Reader.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+Reader::Reader(DataHandle &dh)
+: dataHandle_(&dh),
+  deleteDataHandle_(false),
+  context_(0)
+{}
+
+Reader::Reader(DataHandle &dh, ecml::ExecutionContext* context)
+: dataHandle_(&dh),
+  deleteDataHandle_(false),
+  context_(context)
+{}
+
+Reader::Reader()
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  path_(""),
+  context_(0)
+{}
+
+Reader::Reader(ecml::ExecutionContext* context)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  path_(""),
+  context_(context)
+{}
+
+Reader::Reader(const std::string& path, ecml::ExecutionContext* context)
+: dataHandle_(ecml::DataHandleFactory::openForRead(path)),
+  deleteDataHandle_(true),
+  path_(path),
+  context_(context)
+{}
+
+Reader::Reader(const std::string& path)
+: dataHandle_(ecml::DataHandleFactory::openForRead(path)),
+  deleteDataHandle_(true),
+  path_(path),
+  context_(0)
+{}
+
+Reader::~Reader()
+{
+    if (dataHandle_ && deleteDataHandle_)
+    {
+        dataHandle_->close();
+        delete dataHandle_;
+    }
+}
+
+ReaderIterator* Reader::createReadIterator(const PathName& pathName)
+{
+    return new ReaderIterator(*this, pathName);
+}
+
+ReaderIterator* Reader::createReadIterator()
+{
+return createReadIterator(path_);
+}
+
+Reader::iterator Reader::begin()
+{
+    ReaderIterator * it = new ReaderIterator(*this);
+    it->next(context_);
+    return iterator(it);
+}
+
+const Reader::iterator Reader::end() { return iterator(0); }
+
+void Reader::noMoreData()
+{
+    if (dataHandle_ && deleteDataHandle_)
+    {
+        dataHandle_->close();
+        delete dataHandle_;
+    }
+    dataHandle_ = 0;
+}
+
+eckit::DataHandle* Reader::dataHandle() 
+{
+    // Assume the Reader was constructed with a path, and not a DataHandle*
+    if (! dataHandle_)
+        dataHandle_ = ecml::DataHandleFactory::openForRead(path_);
+    return dataHandle_; 
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/Reader.h b/odb_api/src/odb_api/Reader.h
new file mode 100644
index 0000000..0cf46be
--- /dev/null
+++ b/odb_api/src/odb_api/Reader.h
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Reader.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef Reader_H
+#define Reader_H
+
+#ifdef SWIGPYTHON
+#include <Python.h>
+#endif
+
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/ReaderIterator.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+namespace odb {
+
+class Reader
+{
+public:
+	typedef IteratorProxy<ReaderIterator,Reader,const double> iterator;
+	typedef iterator::Row row;
+
+	Reader(eckit::DataHandle &);
+	Reader(eckit::DataHandle &, ecml::ExecutionContext*);
+    Reader(const std::string& path);
+    Reader(const std::string& path, ecml::ExecutionContext*);
+	Reader();
+	Reader(ecml::ExecutionContext*);
+
+	virtual ~Reader();
+
+	iterator begin();
+	const iterator end(); 
+
+	eckit::DataHandle* dataHandle();
+	// For C API
+	ReaderIterator* createReadIterator(const eckit::PathName&);
+	ReaderIterator* createReadIterator();
+
+#ifdef SWIGPYTHON
+	iterator __iter__() { return iterator(createReadIterator()); }
+#endif
+
+    // For the iterator to signal all data has been slurped.
+    void noMoreData();
+
+private:
+// No copy allowed
+    Reader(const Reader&);
+    Reader& operator=(const Reader&);
+
+	eckit::DataHandle* dataHandle_;
+	bool deleteDataHandle_;
+	const std::string path_;
+    ecml::ExecutionContext* context_;
+
+	friend class IteratorProxy<ReaderIterator,Reader,const double>;
+	friend class ReaderIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ReaderIterator.cc b/odb_api/src/odb_api/ReaderIterator.cc
new file mode 100644
index 0000000..a5ed17a
--- /dev/null
+++ b/odb_api/src/odb_api/ReaderIterator.cc
@@ -0,0 +1,279 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ReaderIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <arpa/inet.h>
+
+#include "ecml/data/DataHandleFactory.h"
+
+#include "odb_api/Header.h"
+#include "odb_api/Reader.h"
+#include "odb_api/ReaderIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+
+ReaderIterator::ReaderIterator(Reader &owner)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0)
+{
+	f_ = owner.dataHandle();
+	ASSERT(f_);
+
+	loadHeaderAndBufferData();
+}
+
+ReaderIterator::ReaderIterator(Reader &owner, ecml::ExecutionContext*)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0)
+{
+	f_ = owner.dataHandle();
+	ASSERT(f_);
+
+	loadHeaderAndBufferData();
+}
+
+eckit::DataHandle* ReaderIterator::dataHandle()
+{
+    return f_;
+}
+
+ReaderIterator::ReaderIterator(Reader &owner, const PathName& pathName)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0)
+{
+    f_ = ecml::DataHandleFactory::openForRead(pathName);
+	ASSERT(f_);
+	ownsF_ = true;
+
+	loadHeaderAndBufferData();
+}
+
+ReaderIterator::ReaderIterator(Reader &owner, const PathName& pathName, ecml::ExecutionContext*)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  codecs_(0),
+  nrows_(0),
+  f_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  headerCounter_(0),
+  byteOrder_(BYTE_ORDER_INDICATOR),
+  refCount_(0)
+{
+	f_ = ecml::DataHandleFactory::openForRead(pathName);
+	ASSERT(f_);
+	ownsF_ = true;
+
+	loadHeaderAndBufferData();
+}
+
+void ReaderIterator::loadHeaderAndBufferData()
+{
+	Header<ReaderIterator> header(*this);
+	header.load();
+	byteOrder_ = header.byteOrder();
+	++headerCounter_;
+
+	initRowBuffer();
+
+	size_t dataSize = header.dataSize();
+	memDataHandle_.size(dataSize);
+	unsigned long bytesRead = f_->read(reinterpret_cast<char*>(memDataHandle_.buffer()), dataSize);
+
+    if (bytesRead != dataSize)
+        throw eckit::SeriousBug("Could not read the amount of data indicated by file's header");
+
+    newDataset_ = true;
+}
+
+ReaderIterator::~ReaderIterator ()
+{
+	Log::debug() << "ReaderIterator::~ReaderIterator: headers read: " << headerCounter_ << " rows:" << nrows_ << std::endl;
+
+	close();
+	delete [] lastValues_;
+	delete [] codecs_;
+}
+
+
+bool ReaderIterator::operator!=(const ReaderIterator& other)
+{
+	//ASSERT(&other == 0);
+	return noMore_;
+}
+
+void ReaderIterator::initRowBuffer()
+{
+	size_t nCols = columns().size();
+
+	delete [] lastValues_;
+	lastValues_ = new double [nCols];
+
+	delete [] codecs_;
+	codecs_ = new odb::codec::Codec* [nCols];
+
+	for(size_t i = 0; i < nCols; i++)
+	{
+		codecs_[i] = &columns()[i]->coder();
+		lastValues_[i] = codecs_[i]->missingValue(); 
+		codecs_[i]->dataHandle(&memDataHandle_);
+	}
+}
+
+size_t ReaderIterator::readBuffer(size_t dataSize)
+{
+	memDataHandle_.size(dataSize);
+
+	unsigned long bytesRead;
+	if( (bytesRead = f_->read(memDataHandle_.buffer(), dataSize)) == 0)
+		return 0;
+	ASSERT(bytesRead == dataSize);
+	return bytesRead;
+}
+
+bool ReaderIterator::next(ecml::ExecutionContext* context)
+{
+    newDataset_ = false;
+    if (noMore_)
+        return false; 
+
+    uint16_t c = 0;
+    long bytesRead = 0;
+
+	if ( (bytesRead = memDataHandle_.read(&c, 2)) == 0)
+	{
+        if ( (bytesRead = f_->read(&c, 2)) <= 0)
+        {
+            owner_.noMoreData();
+			return ! (noMore_ = true);
+        }
+		ASSERT(bytesRead == 2);
+
+		if (c == ODA_MAGIC_NUMBER) 
+		{
+			DataStream<SameByteOrder> ds(f_);
+
+			unsigned char cc;
+			ds.readUChar(cc); ASSERT(cc == 'O');
+			ds.readUChar(cc); ASSERT(cc == 'D');
+			ds.readUChar(cc); ASSERT(cc == 'A');
+
+			Header<ReaderIterator> header(*this);
+			header.loadAfterMagic();
+			byteOrder_ = header.byteOrder();
+			++headerCounter_;
+			initRowBuffer();
+
+			size_t dataSize = header.dataSize();
+			if (! readBuffer(dataSize))
+            {
+                owner_.noMoreData();
+				return ! (noMore_ = true);
+            }
+
+            if( (bytesRead = memDataHandle_.read(&c, 2)) == 0)
+            {
+                owner_.noMoreData();
+				return ! (noMore_ = true);
+            }
+			ASSERT(bytesRead == 2);
+
+			newDataset_ = true;
+		}
+	}
+	c = ntohs(c);
+
+	size_t nCols = columns().size();
+	for(size_t i = c; i < nCols; i++)
+		lastValues_[i] = codecs_[i]->decode();
+
+	++nrows_ ;
+	return nCols;
+}
+
+bool ReaderIterator::isNewDataset() { return newDataset_; }
+
+const double* ReaderIterator::data() { return lastValues_; }
+double& ReaderIterator::data(size_t i)
+{
+	ASSERT(i >= 0 && i < columns().size());
+	return lastValues_[i];
+}
+
+int ReaderIterator::close()
+{
+	if (ownsF_ && f_)
+	{
+		f_->close();
+		delete f_;
+		f_ = 0;
+	}
+
+	return 0;
+}
+
+
+void ReaderIterator::property(std::string key, std::string value)
+{
+	properties_[key] = value;
+}
+
+std::string ReaderIterator::property(std::string key)
+{
+	return properties_[key];
+}
+
+
+ColumnType ReaderIterator::columnType(unsigned long index) { return columns_[index]->type(); }
+const std::string& ReaderIterator::columnName(unsigned long index) const { return columns_[index]->name(); }
+const std::string& ReaderIterator::codecName(unsigned long index) const { return columns_[index]->coder().name(); }
+double ReaderIterator::columnMissingValue(unsigned long index) { return columns_[index]->missingValue(); }
+const BitfieldDef& ReaderIterator::bitfieldDef(unsigned long index) { return columns_[index]->bitfieldDef(); }
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ReaderIterator.h b/odb_api/src/odb_api/ReaderIterator.h
new file mode 100644
index 0000000..a7b1088
--- /dev/null
+++ b/odb_api/src/odb_api/ReaderIterator.h
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ReaderIterator.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef ReaderIterator_H
+#define ReaderIterator_H
+
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/TReadOnlyMemoryDataHandle.h"
+
+#include "odb_api/MetaData.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+extern "C" {
+	typedef void oda;
+	typedef void oda_read_iterator;
+	typedef void oda_write_iterator;
+	oda_write_iterator* odb_create_write_iterator(oda*, const char *,int *);
+	int odb_read_iterator_get_next_row(oda_read_iterator*, int, double*, int*);
+}
+
+namespace odb {
+	namespace codec { class Codec; }
+	namespace sql { class ODATableIterator; }
+	template <typename O> class Header;
+}
+
+namespace odb {
+
+class Reader;
+
+class ReaderIterator
+{
+public:
+	ReaderIterator (Reader &owner);
+	ReaderIterator (Reader &owner, ecml::ExecutionContext* context);
+	ReaderIterator (Reader &owner, const eckit::PathName&);
+	ReaderIterator (Reader &owner, const eckit::PathName&, ecml::ExecutionContext*);
+
+	~ReaderIterator ();
+
+	bool isNewDataset();
+	const double* data();
+
+	bool operator!=(const ReaderIterator& other);
+
+	void property(std::string, std::string);
+	std::string property(std::string);
+
+	const MetaData& columns() { return columns_; }
+	const MetaData& columns(const MetaData& md) { return columns_ = md; }
+    void setNumberOfColumns(size_t n) { columns_.setSize(n); }
+
+#ifdef SWIGPYTHON
+    int setColumn(size_t, const std::string&, ColumnType) { NOTIMP; }
+	void writeHeader() { NOTIMP; }
+    int setBitfieldColumn(size_t, const std::string&, ColumnType, BitfieldDef) { NOTIMP; }
+	void missingValue(size_t, double) { NOTIMP; }
+#endif
+
+	ColumnType columnType(unsigned long index);
+    const std::string& columnName(unsigned long index) const;
+    const std::string& codecName(unsigned long index) const;
+	double columnMissingValue(unsigned long index);
+	const BitfieldDef& bitfieldDef(unsigned long index);
+
+	int32_t byteOrder() const { return byteOrder_; }
+    eckit::DataHandle* dataHandle();
+//protected:
+
+	int close();
+
+	bool next(ecml::ExecutionContext*);
+protected:
+	size_t readBuffer(size_t dataSize);
+
+private:
+// No copy allowed.
+    ReaderIterator(const ReaderIterator&);
+    ReaderIterator& operator=(const ReaderIterator&);
+
+	void initRowBuffer();
+	void loadHeaderAndBufferData();
+
+	Reader& owner_;
+	MetaData columns_;
+	double* lastValues_;
+	odb::codec::Codec** codecs_;
+	unsigned long long nrows_;
+
+	eckit::DataHandle *f_;
+	Properties properties_;
+
+	bool newDataset_;
+
+public:
+	bool noMore_;
+	bool ownsF_;
+private:
+
+	//PrettyFastInMemoryDataHandle
+	ReadOnlyMemoryDataHandle memDataHandle_;
+
+	unsigned long headerCounter_;
+	int32_t byteOrder_;
+    ecml::ExecutionContext* context_;
+
+public:
+	int refCount_;
+	double& data(size_t);
+
+protected:
+
+	friend ::oda_write_iterator* ::odb_create_write_iterator(::oda*, const char *,int *); // for next()
+	friend int ::odb_read_iterator_get_next_row(::oda_read_iterator*, int, double*, int*);
+
+	friend class odb::Reader;
+	friend class odb::IteratorProxy<odb::ReaderIterator, odb::Reader, const double>;
+	friend class odb::Header<odb::ReaderIterator>;
+	friend class odb::sql::ODATableIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/RegionCache.cc b/odb_api/src/odb_api/RegionCache.cc
new file mode 100755
index 0000000..8c544f9
--- /dev/null
+++ b/odb_api/src/odb_api/RegionCache.cc
@@ -0,0 +1,305 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/thread/ThreadSingleton.h"
+#include "odb_api/MDI.h"
+#include "odb_api/RegionCache.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+static ThreadSingleton<VectorRegionCache> region_cache_;
+
+double mfmod(double x,double y) { double a = x/y; return (a-int(a))*y; }
+
+//==============================================================================
+RegionCache::RegionCache()
+//==============================================================================
+: nboxes_(0),
+  resol_(0),
+  nbands_(0),
+  latband_(0),
+  midlat_(0),
+  loncnt_(0),
+  sum_loncnt_(0),
+  stlon_(0),
+  deltalon_(0)
+{
+}
+
+//==============================================================================
+RegionCache::~RegionCache() {}
+//==============================================================================
+
+//==============================================================================
+VectorRegionCache& RegionCache::instance()
+//==============================================================================
+{
+	return region_cache_.instance();
+}
+
+//==============================================================================
+double RegionCache::get_resol(const double & nval)
+//==============================================================================
+{
+ return -1;
+}
+//==============================================================================
+int RegionCache::interval_bsearch(const double &key,
+                                  const int &n,
+                                  const double x[ /* with n+1 elements */ ],
+                                  const double *delta, /* if present, only x[0] will be used */
+                                  const double &add,
+                                  const double &sign /* +1 forward and -1 for reverse search */)
+//==============================================================================
+{
+  const double eps = 1.0e-7;
+  double Delta = delta ? *delta : 0;
+  bool wrap_around = (delta && add != 0) ? true : false;
+  double halfDelta = Delta/2;
+  double x0 = x[0] + halfDelta;
+  double Key = wrap_around ? sign*mfmod(key + halfDelta + add, add) : sign*(key + halfDelta);
+  int lo = 0, hi = n-1;
+  while (lo <= hi) {
+    int k = (lo+hi)/2;
+    double xk = wrap_around ? sign*mfmod(x0 + k*Delta + add, add) : sign*(x[k] + halfDelta);
+    if (wrap_around && k > 0 && ABS(xk) < eps) xk = add;
+    if (Key < xk) {
+      // Search the lower section
+      hi = k-1;
+    }
+    else {
+      int kp1 = k+1;
+      double xkp1 = wrap_around ? sign*mfmod(x0 + kp1*Delta + add, add) : sign*(x[kp1] + halfDelta);
+      if (wrap_around && ABS(xkp1) < eps) xkp1 = add;
+      if (Key > xkp1) {
+    // Search the upper section
+    lo = kp1;
+      }
+      else {
+    // The interval has been found
+    return k;
+      }
+    }
+  }
+ return -1;
+}
+
+//==============================================================================
+int RegionCache::find_lonbox(const int & jb, const double & lon,
+                               double *midlon, double *leftlon, double *rightlon)
+//==============================================================================
+{
+  const double three_sixty = 360;
+  double mid = odb::MDI::realMDI();
+  double left = odb::MDI::realMDI();
+  double right = odb::MDI::realMDI();
+  int boxid = -1;
+  if (jb >= 0 && jb < *nbands_) {
+    double Lon = mfmod(lon + three_sixty, three_sixty);
+    double deltalon = deltalon_[jb];
+    if (last_->boxid >= 0) {
+      int k = last_->lonbox;
+      double startlon = stlon_[jb] + k * deltalon;
+      double endlon = stlon_[jb] + (k+1) * deltalon;
+      startlon = mfmod(startlon + three_sixty, three_sixty);
+      endlon = mfmod(endlon + three_sixty, three_sixty);
+      if (endlon >= startlon) {
+        if (Lon >= startlon && Lon < endlon) boxid = last_->boxid;
+      } else {
+        if ((Lon >= 0 && Lon < endlon) || (Lon >= startlon && Lon < three_sixty)) boxid = last_->boxid;
+      }
+      }
+
+      if (boxid == -1) {
+        const double *stlon = &stlon_[jb];
+        int loncnt = loncnt_[jb];
+        int lonbox = interval_bsearch(lon, loncnt, stlon, &deltalon, three_sixty, +1);
+        if (lonbox != -1) {
+          int k = lonbox;
+          left = (*stlon) + k * deltalon;
+          mid = (*stlon) + (k+0.5) * deltalon;
+          right = (*stlon) + (k+1) * deltalon;
+
+          last_->left  = left  = mfmod(left + three_sixty, three_sixty);
+          last_->mid   = mid   = mfmod(mid + three_sixty, three_sixty);
+          last_->right = right = mfmod(right + three_sixty, three_sixty);
+
+          last_->jb = jb;
+          last_->lonbox = k;
+          last_->boxid = boxid = sum_loncnt_[jb] + k;
+        }
+      } else {
+        left  = last_->left;
+        mid   = last_->mid;
+        right = last_->right;
+      }
+    }
+   if (midlon) {
+      const double one_eighty = 180;
+      if (mid > one_eighty) mid -= three_sixty;
+      *midlon = mid;
+    }
+    if (leftlon) *leftlon = left;
+    if (rightlon) *rightlon = right;
+    return boxid;
+}
+//==============================================================================
+int RegionCache::find_latband(const double & lat) {
+//==============================================================================
+  int jb;
+  int lastjb = last_->jb;
+  if (lastjb >= 0 &&
+      /* Note: In the following we go from N->S pole, not S->N ==> thus the minus sign!! */
+      -lat >= -latband_[lastjb] && -lat < -latband_[lastjb+1]) {
+    jb = lastjb;
+  } else if (lastjb == *nbands_ - 1 && lat == latband_[lastjb+1]) {
+    /* Exactly at the South pole */
+    jb = lastjb;
+  }  else {
+    jb = interval_bsearch(lat, *nbands_,latband_, NULL, 0, -1);
+    if (jb != -1) {
+      // New latitude band found
+      last_->jb = jb;
+     // Don't know anything about the final box numbers yet
+      last_->lonbox = -1;
+      last_->boxid = -1;
+    }
+  }
+
+  return jb;
+}
+
+//==============================================================================
+double RegionCache::get_midlat(const double & resol, const double & lat)
+//==============================================================================
+{
+   double res = odb::MDI::realMDI(); // should be initialised to missing
+   get_cache(resol);
+   int jb = find_latband(lat);
+    if (jb >= 0 && jb < *nbands_) {
+      res = midlat_[jb];
+    }
+    return res;
+}
+
+//==============================================================================
+double RegionCache::get_midlon(const double & resol, const double & lat, const double & lon)
+//==============================================================================
+{
+   double res = odb::MDI::realMDI(); // should be initialised to missing
+   get_cache(resol);
+   int jb = find_latband(lat);
+   int boxid = find_lonbox(jb, lon, &res, NULL, NULL);
+   boxid = boxid;
+
+   return res;
+}
+
+//==============================================================================
+void RegionCache::get_cache(const double & nval) 
+//==============================================================================
+{
+   double resol=get_resol(nval);
+   bool cache_save=true;
+  int n = resol;
+// We need to check whether it was already done for this resolution
+   VectorRegionCache::const_iterator it_cache = instance().begin();
+
+   while (it_cache != instance().end() && cache_save) {
+     RegionCache* cache = const_cast<RegionCache*>(*it_cache);
+     if (resol == *(cache->resol_)) {
+       cache_save=false; // not need to do it because we have it already!
+       kind_  = cache->kind_;
+       resol_ = cache->resol_;
+       nbands_= cache->nbands_;
+       latband_ = cache->latband_;
+       midlat_ = cache->midlat_;
+       loncnt_ = cache->loncnt_;
+       stlon_ = cache->stlon_;
+       deltalon_ = cache->deltalon_;
+       nboxes_ = cache->nboxes_;
+       sum_loncnt_ =  cache->sum_loncnt_;
+       last_ = cache->last_;
+     } else {
+        ++it_cache;
+     }
+   }
+  if (cache_save) 
+      create_cache(resol, n);
+
+}
+//==============================================================================
+void RegionCache::create_cache(const double & resol, const int & n)
+//==============================================================================
+{
+// to be overloaded because it is specific to the grid
+}
+
+//==============================================================================
+void RegionCache::put_cache(const RegionCacheKind & kind, const double &resol, const int &nb, double latband[], 
+                                         double midlat[], double stlon[], 
+                                         double deltalon[], int loncnt[])
+//==============================================================================
+{
+VectorRegionCache *p = &(region_cache_.instance());
+  int nelm=p->size();
+  p->resize(nelm+1);
+  p->at(nelm)= new RegionCache;
+  p->at(nelm)->kind_ = new RegionCacheKind;
+  *(p->at(nelm)->kind_) = kind;
+  p->at(nelm)->resol_ = new double;
+  *(p->at(nelm)->resol_) = resol;
+  p->at(nelm)->nbands_ = new int;
+  *(p->at(nelm)->nbands_) = nb;
+  resol_ = p->at(nelm)->resol_;
+  nbands_=p->at(nelm)->nbands_;
+  kind_ = p->at(nelm)->kind_;
+  p->at(nelm)->latband_ = latband; 
+  p->at(nelm)->midlat_ = midlat;
+  p->at(nelm)->loncnt_ = loncnt;
+  p->at(nelm)->stlon_ = stlon;
+  p->at(nelm)->deltalon_ = deltalon;
+  latband_ = latband;
+  midlat_ = midlat;
+  loncnt_ = loncnt;
+  stlon_ = stlon;
+  deltalon_ = deltalon;
+
+  {
+   int jb, sum = 0;
+    p->at(nelm)->sum_loncnt_ = new int[nb];
+    for (jb=0; jb<nb; jb++) {
+      p->at(nelm)->sum_loncnt_[jb] = sum;
+      sum += loncnt[jb];
+    }
+    p->at(nelm)->nboxes_ = new int;
+    *(p->at(nelm)->nboxes_) = sum;
+    nboxes_ = p->at(nelm)->nboxes_;
+    sum_loncnt_ =  p->at(nelm)->sum_loncnt_;
+  }
+  p->at(nelm)->last_ = new Last;
+  p->at(nelm)->last_->jb = -1;
+  p->at(nelm)->last_->lonbox = -1;
+  p->at(nelm)->last_->boxid = -1;
+  last_ = p->at(nelm)->last_;
+
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/RegionCache.h b/odb_api/src/odb_api/RegionCache.h
new file mode 100755
index 0000000..525c375
--- /dev/null
+++ b/odb_api/src/odb_api/RegionCache.h
@@ -0,0 +1,105 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file RegionCache.h
+/// ECMWF July 2010
+
+#ifndef RegionCache_H
+#define RegionCache_H
+
+#include <vector>
+
+#include "odb_api/piconst.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+static const int dim=2;
+static const double min_resol = 0.1e0;
+static const double sphere_area = piconst::four_pi; /* Actually: 4 * pi * R^2 */
+
+#define NINT(x) F90nint(x)
+
+/* Fortran90 compatible NINT-function */
+#define F90nint(x) ( ((x) > 0) ? (int)((x) + 0.5) : (int)((x) - 0.5) )
+
+#undef ABS
+#define ABS(x)   ( ((x) >= 0)  ? (x) : -(x) )
+
+#define R2D(x) ( (180/piconst::pi) * ( ((x) >  piconst::pi) ? ((x) - 2*piconst::pi) : (x) ) )
+
+typedef enum { eq_cache_kind = 1, rgg_cache_kind = 2 } RegionCacheKind;
+
+class RegionCache;
+typedef std::vector<RegionCache *> VectorRegionCache;
+
+// To store results from the last find_latband, find_lonbox
+   class Last {
+    public:
+      int jb;
+      int lonbox;
+      int boxid;
+      double left, mid, right;
+   };
+
+class RegionCache {
+public:
+
+	RegionCache();
+	~RegionCache();
+
+ 	static VectorRegionCache &  instance();
+
+    double get_midlat(const double &, const double &);
+    double get_midlon(const double &, const double &, const double &);
+
+private:
+// No copy allowed
+	RegionCache(const RegionCache&);
+	RegionCache& operator=(const RegionCache&);
+
+// -- Members
+    RegionCacheKind *kind_; // type of cache (rgg, eq_boxes)
+    int *nboxes_;        // Actual number of boxes
+    double *resol_;      // Approximate resolution in degrees at Equator
+    int *nbands_;        // number of latitude bands
+    double *latband_;   // starting latitudes for each latitude band : size nbands+1 
+    double *midlat_;    // mid latitudes for each latitude band : size nbands 
+    int *loncnt_;       // # of longitude boxes for each latitude band : size nbands
+    int *sum_loncnt_;   // Sum of (longitude) boxes BEFORE this latitude band : size nbands
+    double *stlon_;     // starting longitudes for each latitude band : size nb 
+    double *deltalon_;  // longitudinal delta for each latitude band : size nb 
+    Last *last_;
+
+// -- Class members
+     virtual double get_resol(const double & val);
+     virtual void create_cache(const double &, const int &);
+     void get_cache(const double &);
+     void put_cache(const RegionCacheKind & kind,const  double &,const  int &,double [],double [],double [],
+                    double [],int []);
+     int find_latband(const double &);
+     int find_lonbox(const int &, const double &, double *, double *, double *);
+     int interval_bsearch(const double &, const int &, const double [], const double *, const double &, const double &);
+
+     friend class EqRegionCache;
+     friend class RggRegionCache;
+	//friend std::ostream& operator<<(std::ostream& s,const RegionCache& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Retriever.cc b/odb_api/src/odb_api/Retriever.cc
new file mode 100755
index 0000000..6065872
--- /dev/null
+++ b/odb_api/src/odb_api/Retriever.cc
@@ -0,0 +1,186 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include <ctype.h>
+#include <fstream>
+#include <algorithm>    // std::find
+
+#include "eckit/config/Resource.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/PartFileHandle.h"
+
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/FileCollector.h"
+#include "odb_api/FileMapper.h"
+#include "odb_api/ODBModule.h"
+#include "odb_api/InMemoryDataHandle.h"
+#include "odb_api/Partition.h"
+#include "odb_api/Writer.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Retriever.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+using namespace odb::tool;
+
+void Retriever::checkKeywordsHaveValues(const std::map<std::string,std::vector<std::string> >& request, const vector<string>& keywords)
+{
+    typedef std::map<std::string,std::vector<std::string> > ms;
+    ms r;
+    for (ms::const_iterator it (request.begin()); it != request.end(); ++it)
+        r[eckit::StringTools::lower(it->first)] = it->second;
+
+    for (size_t i (0); i < keywords.size(); ++i)
+    {
+        const vector<string>& values ( r[ keywords[i] ]);
+        if (values.size() < 1)
+            throw eckit::UserError( "At least one value required for keyword '" + keywords[i] + "'");
+        Log::info()  << ":: - " << keywords[i] << " " << values << endl;
+    }
+}
+
+void Retriever::retrieve(MultiHandle&                                           output, 
+                         const std::vector<std::string>&                        keywords, 
+                         const std::map<std::string,std::vector<std::string> >& r)
+{
+    std::map<std::string,std::vector<std::string> > request(unquoteValues(r));
+
+    if (r.count("odbpathnameschema") == 0)
+        throw UserError("RETRIEVE: odbpathnameschema not set");
+
+    FileMapper mapper(r.at("odbpathnameschema")[0]);
+
+    vector<string> odbServerRoots (eckit::StringTools::split(":", r.at("odbserverroots")[0]));
+    for (size_t i(0); i < odbServerRoots.size(); ++i)
+        odbServerRoots[i] = FileCollector::expandTilde(odbServerRoots[i]);
+    mapper.addRoots(odbServerRoots);
+    mapper.checkRoots();
+
+    checkKeywordsHaveValues(request, keywords);
+
+    const vector<string> partitionNumbers ( request["part_number"] );
+
+    if (partitionNumbers.size())
+    {
+        const string partitionsInfo (FileCollector::expandTilde(request["partitionsinfo"][0]));
+
+        vector<size_t> parts;
+        for (size_t i(0); i < partitionNumbers.size(); ++i)
+            parts.push_back( atoll(partitionNumbers[i].c_str()) );
+
+        sendPartitions (output, PathName(partitionsInfo), parts);
+        //sendSavedPartitions (output, PathName(partitionsInfo + ".files"), parts);
+    } 
+    else
+    {
+        // Check server_side 
+        vector<string> serverSide (request ["server_side"]);
+        if (serverSide.size()) 
+        {
+#ifdef HAVE_ODB_API_SERVER_SIDE
+            MultiHandle mh;
+            FileCollector fileCollector (mapper, mh);
+            fileCollector.findFiles(keywords, request);
+
+            if(mh.estimate() == Length(0)) 
+                Log::userWarning() << "Data not found" << endl;
+
+            handleServerSide(output, fileCollector, serverSide);
+#else
+            Log::error() << "RETRIEVE: SERVER_SIDE Server side processing disabled at compile time" << endl;
+            throw UserError("SERVER_SIDE Server side processing disabled at compile time");
+#endif
+        } 
+        else 
+        {
+            Log::debug() << "No server side processing" << endl;
+            FileCollector fileCollector (mapper, output);
+            fileCollector.findFiles(keywords, request);
+
+            if(output.estimate() == Length(0)) 
+                Log::userWarning() << "Data not found" << endl;
+        }
+    }
+}
+
+void Retriever::sendSavedPartitions(MultiHandle& output, const PathName& savedPartitionsListFile, const std::vector<size_t>& partitionNumbers)
+{
+    vector<std::string> files (StringTool::readLines(savedPartitionsListFile, true));
+    for (size_t i (0); i < files.size(); ++i)
+    {
+        if (std::find(partitionNumbers.begin(), partitionNumbers.end(), i) != partitionNumbers.end())
+            output += PathName(files[i]).fileHandle();
+    }
+}
+
+void Retriever::sendPartitions(MultiHandle& output, const PathName& partitionsInfo, const std::vector<size_t>& partitionNumbers)
+{
+    for (size_t i(0); i < partitionNumbers.size(); ++i)
+    {
+        Partition partition (partitionsInfo, partitionNumbers[i]);
+
+        InMemoryDataHandle* dh (new InMemoryDataHandle);
+        dh->openForWrite(0);
+
+        partition.write(*dh);
+
+        output += dh;
+    }
+}
+
+void Retriever::handleServerSide(MultiHandle& output, const FileCollector& fileCollector, const vector<string>& serverSide)
+{
+    if (serverSide.size() > 1)
+        throw UserError("Currently SERVER_SIDE can have only one value");
+
+    const string foundFiles ( StringTools::join("\"/\"", fileCollector.foundFiles()) );
+    string code (string() 
+        + "apply, closure=" + serverSide[0] + ","
+          "       args=(let,source=\"" + foundFiles + "\")");
+
+    Log::info() << "server_side: '" << endl << code << endl << "'" << endl;
+
+    ecml::ExecutionContext context;
+    odb::ODBModule odbModule;
+    context.import(odbModule);
+
+    // server_side should return a list of files
+    ecml::Values result (context.execute(code));
+    Log::info() << "server_side result: " << result << endl;
+
+    for (ecml::Cell* v (result); v; v = v->rest())
+    {
+        ASSERT(v->value());
+        string fileName (v->value()->text());
+        Log::info() << "SERVER_SIDE: adding '" << fileName << "' to output" << endl;
+        output += PathName(fileName).fileHandle();
+    }
+}
+
+std::map<std::string,std::vector<std::string> > Retriever::unquoteValues(const std::map<std::string,std::vector<std::string> >& request)
+{
+    std::map<std::string,std::vector<std::string> > r;
+
+    for ( std::map<std::string,std::vector<std::string> >::const_iterator it (request.begin()); it != request.end(); ++it)
+    {
+        const std::string& key (it->first);
+        const std::vector<std::string>& values (it->second);
+        std::vector<std::string> vs;
+        for (size_t i(0); i < values.size(); ++i)
+            vs.push_back(StringTool::unQuote(values[i]));
+        r[key] = vs;
+    }
+
+    return r;
+}
diff --git a/odb_api/src/odb_api/Retriever.h b/odb_api/src/odb_api/Retriever.h
new file mode 100755
index 0000000..bf3d66b
--- /dev/null
+++ b/odb_api/src/odb_api/Retriever.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Retriever.h
+// Piotr Kuchta - ECMWF December 2015
+
+#ifndef odb_api_Retriever_H
+#define odb_api_Retriever_H
+
+class FileMapper;
+class FileCollector;
+
+namespace eckit { class MultiHandle; }
+namespace ecml { class ExecutionContext; }
+
+class Retriever {
+public:
+    static void checkKeywordsHaveValues (const std::map<std::string,std::vector<std::string> >& request, const std::vector<std::string>& keywords);
+
+    static void retrieve (eckit::MultiHandle&                                    output, 
+                          const std::vector<std::string>&                        keywords, 
+                          const std::map<std::string,std::vector<std::string> >& request);
+    static std::map<std::string,std::vector<std::string> > unquoteValues(const std::map<std::string,std::vector<std::string> >&);
+private:
+    static void sendPartitions(eckit::MultiHandle& output, const eckit::PathName& partitionsInfo, const std::vector<size_t>& partitionNumbers);
+    static void sendSavedPartitions(eckit::MultiHandle& output, const eckit::PathName& savedPartitionsListFile, const std::vector<size_t>& partitionNumbers);
+    static void handleServerSide(eckit::MultiHandle&, const FileCollector&, const std::vector<std::string>&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/RggRegionCache.cc b/odb_api/src/odb_api/RggRegionCache.cc
new file mode 100755
index 0000000..59b0ef2
--- /dev/null
+++ b/odb_api/src/odb_api/RggRegionCache.cc
@@ -0,0 +1,340 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <cmath>
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "odb_api/RggRegionCache.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+RggRegionCache::RggRegionCache() : RegionCache() {}
+
+RggRegionCache::~RggRegionCache() {}
+
+double RggRegionCache::get_resol(const double & nval) { return nval; }
+
+/// Reads F90 namelist file $ODB_RTABLE_PATH/rtablel_2<xxxx>
+/// to find out how many latitude bands there are (must be xxxx+1)
+/// and how many longitudes boxes per latband there are */
+int * RggRegionCache::read_rtablel_2_file(const int & Txxxx, int *NRGRI_len, int *Nlons)
+{
+    int *NRGRI = NULL;
+    int nb = 0;
+    int nlons = 0;
+    int nexp = Txxxx + 1; /* Expect this many latitude bands */
+
+    std::string rtable_file;
+    std::stringstream sr;
+    sr.width(3);
+    sr.setf(std::ios_base::right, std::ios_base::adjustfield);
+    sr.fill('0');
+
+    // I use an environment variable; changing DHSHOME does not work...
+    PathName fpath = Resource<PathName>("$ODB_RTABLE_PATH","~/odb/include");
+    sr << Txxxx;
+    rtable_file = fpath + "/rtablel_2" + sr.str();
+
+    Log::info() << " gaussian grid table = " << rtable_file << std::endl;
+    std::ifstream input(rtable_file.c_str());
+    if (input) {
+        NRGRI = new int [nexp];
+        char line[1024];
+        //char *lb;
+        //char *rb;
+        //char *eq;
+        while(input.getline(line,sizeof(line)-1)) {
+            char *lb = strchr(line,'(');
+            char *rb = lb ? strchr(line,')') : NULL;
+            char *eq = rb ? strchr(line,'=') : NULL;
+            if (lb && rb && rb) {
+                if (lb < rb && rb < eq) {
+                    char *comma = strchr(line,',');
+                    int id = 0;
+                    int npts = 0;
+                    if (comma && eq < comma) *comma = '\0';
+                    ++lb;
+                    *rb = '\0';
+                    ++eq;
+                    id = atoi(lb) - 1;
+                    npts = atoi(eq);
+                    if (id >= 0 && id < nexp && npts > 0) {
+                        NRGRI[id] = npts;
+                        nlons += npts;
+                    }
+                }
+            }
+        }
+        nb = nexp;
+    } else {
+        Log::info()  << "read_rtablel_2_file(): Unsupported resolution Txxxx = " << Txxxx << " or $ODB_RTABLE_PATH not defined" << std::endl;
+    }
+
+    if (NRGRI_len) *NRGRI_len = nb;
+    if (Nlons) *Nlons = nlons;
+
+    return NRGRI;
+}
+
+/// compute knum zeros, or if knum>50, knum approximate zeros of the 
+/// bessel function J0.
+///
+///  pbes - array, imensione knum, to receive the values
+///  knum - number of zeros requeste.
+///
+/// Method:
+/// -------
+///      The first 50 values are obtained from a lookup table.
+///      Any additional values requested are interpolated.
+///
+void RggRegionCache::bsslzr(double pbes[], const int & knum)
+{
+    int inum;
+
+    double zapi, zpi;
+
+    double zbes[50];
+
+    zbes[0]=2.4048255577e0;
+    zbes[1]=5.5200781103e0;
+    zbes[2]=8.6537279129e0;
+    zbes[3]=11.7915344391e0;
+    zbes[4]=14.9309177086e0;
+    zbes[5]=18.0710639679e0;
+    zbes[6]=21.2116366299e0;
+    zbes[7]=24.3524715308e0;
+    zbes[8]=27.4934791320e0;
+    zbes[9]=30.6346064684e0;
+    zbes[10]=33.7758202136e0;
+    zbes[11]=36.9170983537e0;
+    zbes[12]=40.0584257646e0;
+    zbes[13]=43.1997917132e0;
+    zbes[14]=46.3411883717e0;
+    zbes[15]=49.4826098974e0;
+    zbes[16]=52.6240518411e0;
+    zbes[17]=55.7655107550e0;
+    zbes[18]=58.9069839261e0;
+    zbes[19]=62.0484691902e0;
+    zbes[20]=65.1899648002e0;
+    zbes[21]=68.3314693299e0;
+    zbes[22]=71.4729816036e0;
+    zbes[23]=74.6145006437e0;
+    zbes[24]=77.7560256304e0;
+    zbes[25]=80.8975558711e0;
+    zbes[26]=84.0390907769e0;
+    zbes[27]=87.1806298436e0;
+    zbes[28]=90.3221726372e0;
+    zbes[29]=93.4637187819e0;
+    zbes[30]=96.6052679510e0;
+    zbes[31]=99.7468198587e0;
+    zbes[32]=102.8883742542e0;
+    zbes[33]=106.0299309165e0;
+    zbes[34]=109.1714896498e0;
+    zbes[35]=112.3130502805e0;
+    zbes[36]=115.4546126537e0;
+    zbes[37]=118.5961766309e0;
+    zbes[38]=121.7377420880e0;
+    zbes[39]=124.8793089132e0;
+    zbes[40]=128.0208770059e0;
+    zbes[41]=131.1624462752e0;
+    zbes[42]=134.3040166383e0;
+    zbes[43]=137.4455880203e0;
+    zbes[44]=140.5871603528e0;
+    zbes[45]=143.7287335737e0;
+    zbes[46]=146.8703076258e0;
+    zbes[47]=150.0118824570e0;
+    zbes[48]=153.1534580192e0;
+    zbes[49]=156.2950342685e0;
+
+    //
+    // 1. Extract values from look up table.
+    //
+    zapi = 2.0e0*asin(1.0e0);
+    inum=std::min(knum,50);
+
+    for (int j=0; j<inum; j++)
+        pbes[j] = zbes[j];
+
+    //
+    // 2. Interpolate remaining values
+    //
+    if (knum > 50) {
+        zpi=zapi;
+        for (int j=50; j<knum; j++)
+            pbes[j] = pbes[j-1]+zpi;
+    }
+
+}
+
+
+/// gauaw - compute abscissas and weights for gaussian integration
+int RggRegionCache::gauaw(double pa[], double pw[], const int &k)
+{
+    double zeps = 1e-14;
+    double zpi, zc, zxz, zkm2, zkm1, zfn, zpk, zkmrk, zsp, zvsp;
+    int ifk, ikk, /*js,*/ iter, jn, il;
+    int iret;
+
+    // ------------------------------------------------------------------
+    // 1. set constants and find zeros of bessel function.
+
+    iret=0;
+    zpi=2.0e0*asin(1.0e0);
+    zc=(1.0e0-pow((2.0e0/zpi),2))*0.25e0;
+    ifk= k;
+    ikk= k/2;
+
+    bsslzr(pa, ikk);
+
+    bool cont=true;
+    for (int js=0; js<ikk; js++) {
+        zxz = cos(pa[js]/sqrt(pow((ifk+0.5e0),2)+zc));
+        iter=0;
+        cont=true;
+        do {
+            // ------------------------------------------------------------------
+            // 2. Compute abscissas an weights.
+
+            // 2.1 set values for next iteration.
+
+            zkm2=1.0e0;
+            zkm1=zxz;
+            ++iter;
+            if (iter > 10) {
+                cont=false;
+                iret=10;
+            } else {
+                // 2.2 Computation of the legendre polynomial.
+                for (jn=2; jn<= k; jn++) {
+                    zfn = jn;
+                    zpk = ((2.0e0*zfn-1.0e0)*zxz*zkm1-(zfn-1.0e0)*zkm2)/zfn;
+                    zkm2=zkm1;
+                    zkm1=zpk;
+                }
+
+                zkm1=zkm2;
+                zkmrk=(ifk*(zkm1-zxz*zpk))/(1.0e0-zxz*zxz);
+                zsp=zpk/zkmrk;
+                zxz=zxz-zsp;
+                zvsp=fabs(zsp);
+                if (zvsp<=zeps) cont=false;
+            }
+
+        } while (cont);
+
+        // 2.3 Abscissas and weights.
+        if (iter <= 10) {
+            pa[js] = zxz;
+            pw[js] = (2.0e0*(1.0e0-zxz*zxz))/pow((ifk*zkm1),2);
+
+            // 2.4 odd k computation of weight at the equator
+            if (k != ikk*2) {
+                pa[ikk]=0.0e0;
+                zpk=2.0e0/(ifk*ifk);
+                for (jn=2; jn<=k; jn+=2) {
+                    zfn=jn;
+                    zpk=zpk*zfn*zfn/pow((zfn-1.0e0),2);
+                }
+                pw[ikk] = zpk;
+            } else {
+                for (jn=0; jn<ikk; jn++) {
+                    il = k - jn -1;
+                    pa[il] = -pa[jn];
+                    pw[il] = pw[jn];
+                }
+            }
+        }
+    }
+    return iret;
+}
+
+void RggRegionCache::create_cache(const double & resol, const int & n) 
+{
+    // nothing done for this resolution
+    int iret = 0;
+    int ndgl = n + 1; // Number of latitude bands i.e. "nb"
+    int nlons = 0;
+    int *loncnt = read_rtablel_2_file(n, &ndgl, &nlons);
+    double *latband = NULL;
+    double *midlat = NULL;
+    double *stlon = NULL;
+    double *deltalon = NULL;
+    double *zlmu = NULL;
+    double *zw = NULL;
+    double *zlatedge = NULL;
+    const double zfact = piconst::recip_pi_over_180;
+    double zfact2 = ((double)45)/atan((double) 1);
+    int ii, iequator = ndgl/2;
+
+    zlmu = new double [ndgl];
+    zw = new double [ndgl];
+
+    iret=gauaw(zlmu,zw,ndgl);
+
+    midlat = zlmu;
+    for (ii=0; ii<ndgl; ii++)
+        midlat[ii] = zfact2*asin(zlmu[ii]);
+
+    zlatedge = new double [ndgl + 1];
+    zlatedge[0] = piconst::half_pi;
+    for (ii=0; ii<ndgl/2; ii++)
+        zlatedge[ii+1] = asin( std::max(-1.0, std::min(1.0,sin(zlatedge[ii])-zw[ii]) ) );
+
+    delete [] zw;
+
+    zlatedge[iequator] = 0;
+
+    for (ii=iequator+1; ii<ndgl+1; ii++)
+        zlatedge[ii] = -zlatedge[ndgl-ii];
+
+    latband = new double [ndgl + 1];
+
+    for (ii=0; ii<ndgl+1; ii++)
+        latband[ii] = zfact*zlatedge[ii];
+
+    delete [] zlatedge;
+
+    // Starting longitudes, deltalon's
+
+    stlon = new double [ndgl];
+    deltalon = new double [ndgl];
+
+    {
+        int cnt;
+        const double three_sixty = 360;
+        for (ii=0; ii<ndgl; ii++) {
+            cnt = loncnt[ii];
+            double delta = three_sixty/cnt;
+            deltalon[ii] = delta;
+            stlon[ii] = -delta/2;
+        }
+    }
+
+    // Store in cache
+    //double rgg_resol = (deltalon[iequator] + deltalon[iequator+1])/2;
+    RegionCacheKind kind = rgg_cache_kind;
+    RegionCache::put_cache(kind, resol, ndgl,
+                           latband, midlat,
+                           stlon, deltalon,
+                           loncnt);
+}
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/RggRegionCache.h b/odb_api/src/odb_api/RggRegionCache.h
new file mode 100755
index 0000000..a37f550
--- /dev/null
+++ b/odb_api/src/odb_api/RggRegionCache.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file RggRegionCache.h
+/// ECMWF July 2010
+
+#ifndef RggRegionCache_H
+#define RggRegionCache_H
+
+//#include <vector>
+
+//#include "odb_api/piconst.h"
+#include "odb_api/RegionCache.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+namespace function {
+
+class RggRegionCache : public RegionCache {
+public:
+	RggRegionCache();
+	~RggRegionCache();
+
+private:
+// No copy allowed
+    RggRegionCache(const RggRegionCache&);
+    RggRegionCache& operator=(const RggRegionCache&);
+
+// -- Overridden methods
+    virtual double get_resol(const double & val);
+    virtual void create_cache(const double &, const int &);
+
+// -- Class methods
+    int * read_rtablel_2_file(const int &, int *, int *);
+    int gauaw(double [], double [], const int &);
+    void bsslzr(double [], const int &); 
+};
+
+} // namespace function
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/RowsCounter.cc b/odb_api/src/odb_api/RowsCounter.cc
new file mode 100644
index 0000000..7e1169f
--- /dev/null
+++ b/odb_api/src/odb_api/RowsCounter.cc
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/RowsCounter.h"
+
+using namespace eckit;
+
+namespace odb {
+
+unsigned long long RowsCounter::fastRowCount(const PathName &db)
+{
+	unsigned long long n = 0;
+
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+	MDR mdReader(db);
+	MDR::iterator it = mdReader.begin();
+	MDR::iterator end = mdReader.end();
+	for (; it != end; ++it)
+		n += it->columns().rowsNumber();
+	return n;
+}
+
+unsigned long long RowsCounter::rowCount(const PathName &db)
+{
+	odb::Reader oda(db);
+	odb::Reader::iterator i = oda.begin();
+	odb::Reader::iterator end = oda.end();
+
+	unsigned long long n = 0;
+	for ( ; i != end; ++i)
+		++n;
+	return n;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/RowsCounter.h b/odb_api/src/odb_api/RowsCounter.h
new file mode 100644
index 0000000..6126f83
--- /dev/null
+++ b/odb_api/src/odb_api/RowsCounter.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef RowsCounter_H
+#define RowsCounter_H
+
+namespace odb {
+
+class RowsCounter {
+public:
+	static unsigned long long rowCount(const eckit::PathName &);
+	static unsigned long long fastRowCount(const eckit::PathName &);
+
+private:
+// No copy allowed
+    RowsCounter(const RowsCounter&);
+    RowsCounter& operator=(const RowsCounter&);
+};
+
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/SQLAST.cc b/odb_api/src/odb_api/SQLAST.cc
new file mode 100644
index 0000000..ac90199
--- /dev/null
+++ b/odb_api/src/odb_api/SQLAST.cc
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLAST.h"
+
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+ColumnDef::ColumnDef()
+{}
+
+ColumnDef::ColumnDef(const std::string& name, const std::string& type, const Range& range,
+        const std::string& defaultValue)
+  : name_(name),
+    type_(type),
+    range_(range),
+    defaultValue_(defaultValue),
+    hasDefaultValue_(!defaultValue.empty())
+{}
+
+ConstraintDef::ConstraintDef()
+  : type_(NONE),
+    name_(""),
+    columns_(0),
+    relatedTable_(""),
+    relatedColumns_(0)
+{}
+
+ConstraintDef::ConstraintDef(const std::string& name, const std::vector<std::string>& primaryKey)
+  : type_(PRIMARY_KEY),
+    name_(name),
+    columns_(primaryKey),
+    relatedTable_(""),
+    relatedColumns_(0)
+{}
+
+ConstraintDef::ConstraintDef(const std::string& name, const std::vector<std::string>& foreignKey,
+        const std::string& relatedTable, const std::vector<std::string>& relatedColumns)
+  : type_(FOREIGN_KEY),
+    name_(name),
+    columns_(foreignKey),
+    relatedTable_(relatedTable),
+    relatedColumns_(relatedColumns)
+{}
+
+TableDef::TableDef()
+{}
+
+TableDef::TableDef(const std::string& name, 
+                   const ColumnDefs& columns,
+                   const ConstraintDefs& constraints, 
+                   const std::vector<std::string>& parents,
+                   const std::string& location)
+  : name_(name),
+    columns_(columns),
+    constraints_(constraints),
+    parents_(parents),
+    location_(location)
+{}
+
+SchemaDef::SchemaDef()
+{}
+
+SchemaDef::SchemaDef(const TableDefs& tables)
+  : tables_(tables)
+{}
+
+Definitions::Definitions()
+{}
+
+Definitions::Definitions(const SchemaDefs& schemas, const TableDefs& tables)
+  : schemas_(schemas),
+    tables_(tables)
+{}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLAST.h b/odb_api/src/odb_api/SQLAST.h
new file mode 100644
index 0000000..d0a16be
--- /dev/null
+++ b/odb_api/src/odb_api/SQLAST.h
@@ -0,0 +1,244 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file SQLAST.h
+/// @author Piotr Kuchta, ECMWF April 2009
+
+#ifndef odb_api_SQLAST_H
+#define odb_api_SQLAST_H
+
+#include "eckit/eckit.h"
+#include "odb_api/Types.h"
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+
+typedef std::pair<long,long> Range;
+
+class ColumnDef
+{
+public:
+	ColumnDef();
+
+	ColumnDef(const std::string& name, const std::string& type, const Range& range, const std::string& defaultValue);
+
+    const std::string& name() const { return name_; }
+    void name(const std::string& name) { name_ = name; }
+    const std::string& type() const { return type_; }
+    const Range& range() const { return range_; }
+    const std::string& defaultValue() const { return defaultValue_; }
+    bool hasDefaultValue() const { return hasDefaultValue_; }
+    const BitfieldDef& bitfieldDef() const { return bitfieldDef_; }
+    void bitfieldDef(const BitfieldDef& b) { bitfieldDef_ = b; }
+
+private:
+    std::string name_;
+    std::string type_;
+    Range range_;
+    std::string defaultValue_;
+    bool hasDefaultValue_;
+    BitfieldDef bitfieldDef_;
+};
+
+typedef std::vector<ColumnDef> ColumnDefs;
+
+class ConstraintDef
+{
+    enum Type { NONE, PRIMARY_KEY, FOREIGN_KEY };
+public:
+	ConstraintDef();
+
+    ConstraintDef(const std::string& name, const std::vector<std::string>& primaryKey); 
+
+    ConstraintDef(const std::string& name,
+        const std::vector<std::string>& foreignKey,
+        const std::string& relatedTable,
+        const std::vector<std::string>& relatedColumn); 
+
+    bool isPrimaryKey() const { return type_ == PRIMARY_KEY; }
+    bool isForeignKey() const { return type_ == FOREIGN_KEY; }
+
+    const std::string& name() const { return name_; }
+    const std::vector<std::string>& columns() const { return columns_; }
+    const std::string& relatedTable() const { return relatedTable_; }
+    const std::vector<std::string>& relatedColumns() const { return relatedColumns_; }
+private:
+    Type type_;
+    std::string name_;
+    std::vector<std::string> columns_;
+    std::string relatedTable_;
+    std::vector<std::string> relatedColumns_;
+};
+
+typedef std::vector<ConstraintDef> ConstraintDefs;
+
+class TableDef
+{
+public:
+    TableDef();
+    TableDef(const std::string& name, 
+             const ColumnDefs& columns,
+             const ConstraintDefs& constraints, 
+             const std::vector<std::string>& parents,
+             const std::string& location);
+
+    const std::string& name() const { return name_; }
+    void name(const std::string& name) { name_ = name; }
+    ColumnDefs& columns() { return columns_; }
+	const ColumnDefs& columns() const { return columns_; }
+    const ConstraintDefs& constraints() const { return constraints_; }
+    const std::vector<std::string>& parents() const { return parents_; }
+    const std::string location() const { return location_; }
+private:
+    std::string name_;
+    ColumnDefs columns_;
+    ConstraintDefs constraints_;
+	std::vector<std::string> parents_;
+    std::string location_;
+};
+
+typedef std::map<std::string, TableDef> TableDefs;
+
+class SchemaDef
+{
+public:
+	SchemaDef();
+	SchemaDef(const TableDefs& tables);
+	TableDefs& tables() { return tables_; }
+	const TableDefs& tables() const { return tables_; }
+private:
+	TableDefs tables_;
+};
+
+typedef std::map<std::string, SchemaDef> SchemaDefs;
+
+class Definitions
+{
+public:
+    Definitions();
+    Definitions(const SchemaDefs& schemas, const TableDefs& tables);
+    const SchemaDefs& schemas() const { return schemas_; }
+    const TableDefs& tables() const { return tables_; }
+private:
+    SchemaDefs schemas_;
+    TableDefs tables_;
+};
+
+struct Table {
+    Table() : name(), database(), embeddedCode(), dataDescriptor() {}
+
+    Table(const std::string& n, const std::string& db, const bool c)
+    : name(n), database(db), embeddedCode(c), dataDescriptor() {}
+
+    Table(const std::string& n, const std::string& db, const bool c, const bool d)
+    : name(n), database(db), embeddedCode(c), dataDescriptor(d) {}
+
+    Table(const Table& o)
+    : name(o.name), 
+      database(o.database), 
+      embeddedCode(o.embeddedCode),
+      dataDescriptor(o.dataDescriptor)
+    {}
+
+    friend std::ostream& operator<< (std::ostream& s, const Table& t)
+    {
+        return s << "[table,name=" << t.name 
+                 << ",database=" << t.database 
+                 << ",embeddedCode=" << t.embeddedCode 
+                 << ",dataDescriptor=" << t.dataDescriptor 
+                 << "]"
+                 //<< std::endl
+                 ;
+    }
+
+    std::string name;
+    std::string database;
+    bool embeddedCode;   // data to be computed
+    bool dataDescriptor; // we got something in quotes, e.g. file name, instead of identifier (table name)
+};
+
+struct SelectAST {
+    SelectAST(
+        bool                                             distinct,
+        bool                                             all,
+        const Expressions&                               selectList,
+        const std::string&                               into,
+        const std::vector<Table>&                        from,
+        SQLExpression*                                   where,
+        const Expressions&                               groupBy,
+        const std::pair<Expressions,std::vector<bool> >& orderBy )
+
+    : distinct(distinct),
+      all(all),
+      selectList(selectList),
+      into(into),
+      from(from),
+      where(where),
+      groupBy(groupBy),
+      orderBy(orderBy)
+    {}
+
+    SelectAST(const SelectAST& o)
+    : distinct(o.distinct),
+      all(o.all),
+      selectList(o.selectList),
+      into(o.into),
+      from(o.from),
+      where(o.where),
+      groupBy(o.groupBy),
+      orderBy(o.orderBy)
+    {}
+
+    SelectAST() {}
+
+    bool                                      distinct;
+    bool                                      all;
+    Expressions                               selectList;
+    std::string                               into;
+    std::vector<Table>                        from;
+    SQLExpression*                            where;
+    Expressions                               groupBy;
+    std::pair<Expressions,std::vector<bool> > orderBy;
+};
+
+struct InsertAST {
+    InsertAST () {}
+
+    InsertAST (const Table& table, const std::vector<std::string>& columns, const std::vector<std::string>& values)
+    : table_(table),
+      columns_(columns),
+      values_(values)
+    {}
+
+    InsertAST (const InsertAST& other) 
+    : table_(other.table_), 
+      columns_ (other.columns_), 
+      values_(other.values_) 
+    {}
+
+    Table table_;
+    std::vector<std::string> columns_;
+    std::vector<std::string> values_;
+};
+
+struct EmbeddedAST {
+    EmbeddedAST() {}
+
+    EmbeddedAST (const std::string& s) : code_ (s) {}
+    EmbeddedAST (const EmbeddedAST& ast) : code_ (ast.code_) {}
+
+    std::string code_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLBit.cc b/odb_api/src/odb_api/SQLBit.cc
new file mode 100755
index 0000000..9138313
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBit.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLBit.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLBit::SQLBit(const std::string& name, unsigned long mask, unsigned long shift):
+	type::SQLType(name),
+	mask_(mask),
+	shift_(shift)
+{}
+
+SQLBit::~SQLBit() {}
+
+size_t SQLBit::size() const
+{
+	NOTIMP;
+	// This should not be calles
+	return sizeof(long);
+}
+
+void SQLBit::output(SQLOutput& o, double x, bool missing) const
+{
+	//Log::info() << "SQLBit::output: x=" << x << ", missing=" << missing << std::endl;
+	//s << ((m & mask_) >> shift_);
+	// TODO: does it work like this? test!
+	o.outputUnsignedInt(x, missing);
+}
+
+} // namespace type 
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/SQLBit.h b/odb_api/src/odb_api/SQLBit.h
new file mode 100755
index 0000000..9b8e267
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBit.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLBit.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLBit_H
+#define SQLBit_H
+
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+class SQLBit : public SQLType {
+public:
+	SQLBit(const std::string& ,unsigned long ,unsigned long );
+	~SQLBit(); 
+
+	unsigned long mask()  const { return mask_; }
+	unsigned long shift() const { return shift_; }
+
+private:
+// No copy allowed
+	SQLBit(const SQLBit&);
+	SQLBit& operator=(const SQLBit&);
+
+	unsigned long mask_;
+	unsigned long shift_;
+
+// -- Overridden methods
+	// None
+
+	virtual size_t size() const;
+	void output(SQLOutput&, double, bool) const;
+	virtual int getKind() const { return integerType; }
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLBit& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace type
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLBitColumn.cc b/odb_api/src/odb_api/SQLBitColumn.cc
new file mode 100755
index 0000000..3e4d552
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBitColumn.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/log/Log.h"
+#include "odb_api/SQLBitColumn.h"
+#include "odb_api/SQLBitfield.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SQLBitColumn::SQLBitColumn(SQLColumn& column,const std::string& field):
+	SQLColumn(column),
+	field_(field),
+	mask_(0),
+	shift_(0)
+{
+	const type::SQLBitfield& t = dynamic_cast<const type::SQLBitfield&>(type());
+	mask_  = t.mask(field);
+	shift_ = t.shift(field);
+	
+    Log::info() << "here " << field << " mask=" << std::hex << mask_ << std::dec << " shift=" << shift_ << std::endl;
+}
+
+SQLBitColumn::~SQLBitColumn() {}
+
+void SQLBitColumn::rewind() { SQLColumn::rewind(); }
+
+double SQLBitColumn::next(bool& missing)
+{
+	Log::info() << "SQLBitColumn::next: " << std::endl;
+
+	unsigned long value = static_cast<unsigned long>(SQLColumn::next(missing));
+	return (value >> shift_) & mask_;
+}
+
+void SQLBitColumn::advance(unsigned long n) { SQLColumn::advance(n); }
+
+void SQLBitColumn::print(std::ostream& s) const
+{
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLBitColumn.h b/odb_api/src/odb_api/SQLBitColumn.h
new file mode 100755
index 0000000..8d52396
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBitColumn.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLBitColumn.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLBitColumn_H
+#define SQLBitColumn_H
+
+#include "odb_api/SQLColumn.h"
+
+namespace odb {
+namespace sql {
+
+class SQLBitColumn : public SQLColumn {
+public:
+	SQLBitColumn(SQLColumn&,const std::string&);
+	~SQLBitColumn(); 
+private:
+// No copy allowed
+	SQLBitColumn(const SQLBitColumn&);
+	SQLBitColumn& operator=(const SQLBitColumn&);
+	
+	std::string     field_;
+	unsigned long mask_;
+	unsigned long shift_;
+
+// -- Overridden methods
+
+	// From ODBIterator
+	virtual void rewind();
+	virtual double next(bool& missing);
+	virtual void advance(unsigned long);
+	virtual void print(std::ostream&) const; 	
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLBitColumn& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLBitfield.cc b/odb_api/src/odb_api/SQLBitfield.cc
new file mode 100755
index 0000000..a17cda6
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBitfield.cc
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/log/Log.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/types/Types.h"
+#include "eckit/parser/Tokenizer.h"
+#include "odb_api/Decoder.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/SQLBit.h"
+#include "odb_api/SQLOutput.h"
+#include "odb_api/Types.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLBitfield::SQLBitfield(const std::string& name, const FieldNames& fields, const Sizes& sizes, const std::string& ddlName)
+: SQLType(name, ddlName),
+  bitfieldDef_(make_pair(fields, sizes))
+{
+	int shift = 0;
+	for(size_t i = 0; i < fields.size(); i++ )
+	{
+		shift_[fields[i]] = shift;
+		mask_[fields[i]]  = Decoder::makeMask(sizes[i]) << shift;
+		shift += sizes[i];
+	}
+	width_ = shift;
+}
+
+SQLBitfield::~SQLBitfield() {}
+
+unsigned long SQLBitfield::mask(const std::string& n) const
+{
+	std::map<std::string,unsigned long>::const_iterator j = mask_.find(n);
+	
+	if(j == mask_.end())
+		throw eckit::UserError("SQLBitfield no field", n);
+
+	return (*j).second;
+}
+
+unsigned long SQLBitfield::shift(const std::string& n) const
+{
+	std::map<std::string,unsigned long>::const_iterator j = shift_.find(n);
+
+	if(j == shift_.end())
+		throw eckit::UserError("SQLBitfield no field", n);
+
+	return (*j).second;
+}
+
+std::string SQLBitfield::make(const std::string& name, const FieldNames& fields, const Sizes& sizes, const char *ddlName)
+{
+
+    std::stringstream s;
+	s << name << "[";
+	for(size_t i = 0; i < fields.size(); ++i)
+		s << fields[i] << ":" << Translator<int,std::string>()(sizes[i])
+		  << ((i + 1 != fields.size()) ? ";" : "");
+	s << "]";
+	std::string typeName = s.str();
+
+	if(! exists(typeName))
+		SQLType::registerType(new SQLBitfield(typeName, fields, sizes, ddlName));
+	else
+		SQLType::createAlias(typeName, ddlName);
+
+	return typeName;
+}
+
+size_t SQLBitfield::width() const { return width_; }
+
+size_t SQLBitfield::size() const
+{
+	return sizeof(long);
+}
+
+void SQLBitfield::output(SQLOutput& o, double d, bool missing) const
+{
+	o.outputBitfield(d, missing);
+}
+
+const SQLType* SQLBitfield::subType(const std::string& name) const
+{
+	std::vector<std::string> v;
+	Tokenizer(".@")(name,v);
+
+	if(v.size() == 1) return this;
+	if(v.size() == 2 && name.find('@') != std::string::npos) return this;
+
+	ASSERT(v.size() == 3 || v.size() == 2); // name was e.g: "status.active at body" or "status.active"
+
+	std::string field = v[1];
+	std::string full  = name; //this->name() + "." + field;
+
+	if(exists(full))
+		return &lookup(full);
+
+	return SQLType::registerType(new SQLBit(full, mask(field), shift(field)));
+}
+
+} // namespace type 
+} // namespace sql 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/SQLBitfield.h b/odb_api/src/odb_api/SQLBitfield.h
new file mode 100755
index 0000000..d9aad7f
--- /dev/null
+++ b/odb_api/src/odb_api/SQLBitfield.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLBitfield.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLBitfield_H
+#define SQLBitfield_H
+
+#include "odb_api/Types.h"
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+class SQLBitfield : public SQLType {
+public:
+	SQLBitfield(const std::string&, const odb::FieldNames&, const odb::Sizes&, const std::string&);
+	~SQLBitfield();
+
+	unsigned long mask(const std::string& n) const;
+	unsigned long shift(const std::string& n) const;
+
+	const BitfieldDef& bitfieldDef() const { return bitfieldDef_; }
+	const FieldNames& fields() const { return bitfieldDef_.first; }
+	const Sizes& sizes() const { return bitfieldDef_.second; }
+
+	static std::string make(const std::string&, const FieldNames&, const Sizes&, const char *ddlName = NULL);
+
+private:
+	SQLBitfield(const SQLBitfield&);
+	SQLBitfield& operator=(const SQLBitfield&);
+
+	BitfieldDef bitfieldDef_;
+    std::map<std::string, unsigned long> mask_;
+    std::map<std::string, unsigned long> shift_;
+
+	virtual size_t size() const;
+	virtual void output(SQLOutput& s, double, bool) const;
+	virtual const SQLType* subType(const std::string&) const;
+	virtual int getKind() const { return bitmapType; }
+
+	size_t width_;
+	virtual size_t width() const;
+	//friend std::ostream& operator<<(std::ostream& s,const SQLBitfield& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace type 
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLCallbackOutput.cc b/odb_api/src/odb_api/SQLCallbackOutput.cc
new file mode 100755
index 0000000..d384f3f
--- /dev/null
+++ b/odb_api/src/odb_api/SQLCallbackOutput.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/parser/Cell.h"
+
+#include "ecml/core/Environment.h"
+#include "ecml/core/Interpreter.h"
+
+#include "odb_api/Decoder.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/SQLCallbackOutput.h"
+#include "odb_api/ecml_data/ResultSet.h"
+#include "odb_api/ecml_data/ResultSetStore.h"
+
+using namespace eckit;
+using namespace ecml;
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+SQLCallbackOutput::SQLCallbackOutput(ecml::ExecutionContext& context)
+{
+    ecml::Environment& e (context.environment());
+
+    Request callback ( e.lookup("callback") );
+
+    Log::info() << "createOutput: callback: " << callback << endl;
+
+    vector<string> sources (e.lookupList("source", context)),
+                   targets (e.lookupList("target", context));
+
+    ASSERT(sources.size() == 1);
+    ASSERT(targets.size() == 1);
+
+    string source (sources[0]),
+           target (targets[0]);
+
+    Log::info() << "createOutput: source: " << source << endl;
+    Log::info() << "createOutput: target: " << target << endl;
+}
+
+SQLCallbackOutput::~SQLCallbackOutput() {}
+
+void SQLCallbackOutput::print(std::ostream& s) const
+{
+	s << "SQLCallbackOutput";
+}
+
+void SQLCallbackOutput::size(int) {}
+void SQLCallbackOutput::reset() { count_ = 0; }
+
+void SQLCallbackOutput::flush(ecml::ExecutionContext* context)
+{
+    Log::info() << "SQLCallbackOutput::flush: resultSet: " << resultSet_ << endl;
+
+    ASSERT(context);
+
+    string callback ( context->environment().lookup("callback", "", *context) );
+    ASSERT(callback.size());
+
+    stringstream ss;
+    ss << resultSet_;
+
+    const string id (ss.str());
+
+    ResultSetStore::put(id, resultSet_);
+
+    Values values (new Cell("_list", "", new Cell("", id, 0, 0), 0));
+    Request frame (new Cell("_frame", "SQLCallbackOutput", 0, new Cell("", "result_set", values, 0)));
+    context->pushEnvironmentFrame(frame);
+
+
+    string xxx ( context->environment().lookup("result_set", "XXXXX", *context) );
+    ASSERT(xxx == id);
+
+    Log::info() << " -------- SQLCallbackOutput::flush: set result_set to " << xxx << endl;
+
+    // callback is a name of the verb we want to call(symbol). 
+    context->interpreter().eval(RequestParser::parse(callback), *context);
+
+    Log::info() << " -------- SQLCallbackOutput::flush: executed callback " << callback << endl;
+
+    ResultSetStore::remove(id);
+}
+
+bool SQLCallbackOutput::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+    ostream& L(Log::info());
+
+    vector<bool> mask;
+    vector<double> row;
+    for(size_t i(0); i < results.size(); ++i)
+    {
+        bool missing (false);
+        double v (results[i]->eval(missing));
+
+        row.push_back(v);
+        mask.push_back(missing);
+    }
+
+    resultSet_.append(row, mask);
+    return true;
+}
+
+void SQLCallbackOutput::outputValue(double x, bool missing) { NOTIMP; }
+void SQLCallbackOutput::outputReal(double x, bool missing) { outputValue(x, missing); }
+void SQLCallbackOutput::outputDouble(double x, bool missing) { outputValue(x, missing); }
+void SQLCallbackOutput::outputInt(double x, bool missing) { outputValue(x, missing); }
+void SQLCallbackOutput::outputUnsignedInt(double x, bool missing) { outputValue(x, missing); }
+void SQLCallbackOutput::outputString(double x, bool missing) { outputValue(x, missing); }
+void SQLCallbackOutput::outputBitfield(double x, bool missing) { outputValue(x, missing); }
+
+void SQLCallbackOutput::prepare(SQLSelect& sql)
+{
+    /*
+	const expression::Expressions& columns(sql.output());
+	for (size_t i (0); i < columns.size(); i++)
+	{
+		std::string name (columns[i]->title());
+		const type::SQLType* type (columns[i]->type());
+	}
+    */
+}
+
+void SQLCallbackOutput::cleanup(SQLSelect& sql)
+{
+    //SQLSelectFactory::instance().reset(); //?
+}
+
+unsigned long long SQLCallbackOutput::count() { return count_; }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLCallbackOutput.h b/odb_api/src/odb_api/SQLCallbackOutput.h
new file mode 100755
index 0000000..61a0ac4
--- /dev/null
+++ b/odb_api/src/odb_api/SQLCallbackOutput.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLCallbackOutput.h
+/// Piotr Kuchta - ECMWF May 2015
+
+#ifndef SQLCallbackOutput_H
+#define SQLCallbackOutput_H
+
+#include "odb_api/SQLOutput.h"
+#include "odb_api/ecml_data/ResultSet.h"
+
+namespace odb {
+namespace sql {
+
+class SQLCallbackOutput : public SQLOutput {
+public:
+	SQLCallbackOutput(ecml::ExecutionContext&);
+	virtual ~SQLCallbackOutput(); 
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+	SQLCallbackOutput(const SQLCallbackOutput&);
+	SQLCallbackOutput& operator=(const SQLCallbackOutput&);
+
+	unsigned long long count_;
+    //std::vector<double> values_;
+    ResultSet resultSet_;
+
+// -- Overridden methods
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+    void outputValue(double x, bool missing);
+	virtual void outputReal(double, bool);
+	virtual void outputDouble(double, bool);
+	virtual void outputInt(double, bool);
+	virtual void outputUnsignedInt(double, bool);
+	virtual void outputString(double, bool);
+	virtual void outputBitfield(double, bool);
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLColumn.cc b/odb_api/src/odb_api/SQLColumn.cc
new file mode 100755
index 0000000..4b43c61
--- /dev/null
+++ b/odb_api/src/odb_api/SQLColumn.cc
@@ -0,0 +1,165 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLColumn.h"
+#include "odb_api/SQLIndex.h"
+#include "odb_api/SQLTable.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SQLColumn::SQLColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double missingValue): 
+	SQLIterator(type),
+	noRows_(0),
+	owner_(owner),
+	name_(name),
+	index_(index),
+	current_(0),
+	last_(0),
+	position_(0),
+	iterator_(0),
+	hasMissingValue_(hasMissingValue),
+	missingValue_(missingValue),
+	isBitfield_(false),
+	bitfieldDef_()
+{}
+
+SQLColumn::SQLColumn(const type::SQLType& type, SQLTable& owner, const std::string& name, int index, bool hasMissingValue, double missingValue, 
+                     const BitfieldDef& bitfieldDef):
+	SQLIterator(type),
+	noRows_(0),
+	owner_(owner),
+	name_(name),
+	index_(index),
+	current_(0),
+	last_(0),
+	position_(0),
+	iterator_(0),
+	hasMissingValue_(hasMissingValue),
+	missingValue_(missingValue),
+	isBitfield_(true),
+	bitfieldDef_(bitfieldDef)
+{}
+
+SQLColumn::SQLColumn(const SQLColumn& other):
+	SQLIterator(other.type()),
+	noRows_(0),
+	owner_(other.owner_),
+	name_(other.name_),
+	index_(other.index_),
+	current_(0),
+	last_(0),
+	position_(0),
+	iterator_(0),
+	hasMissingValue_(other.hasMissingValue_),
+	missingValue_(other.missingValue_),
+	isBitfield_(other.isBitfield_),
+	bitfieldDef_(other.bitfieldDef_)
+{}
+
+SQLColumn::~SQLColumn()
+{}
+
+void SQLColumn::rewind()
+{
+//	Timer timer("SQLColumn::rewind");
+	rows_.clear();
+	iterators_.clear();
+
+	noRows_ = 0;
+	//setPool(0);
+
+}
+
+void SQLColumn::setPool(int n)
+{
+
+	if(iterator_)
+		iterator_->unload();
+
+	current_  = n;
+	position_ = 0;
+	last_     = rows_[n];
+	iterator_ = iterators_[n];
+
+	iterator_->rewind();
+
+	//cout << "pool " << n << std::endl;
+	// cout << "pool " << n << " " << last_ << std::endl;
+}
+
+PathName SQLColumn::indexPath()
+{
+	PathName path  = owner_.path();
+	return path + "/" + name_ + "@" + owner_.name() + ".index";
+}
+
+double SQLColumn::next(bool& missing)
+{
+	if(position_ == last_)
+		setPool(current_+1);
+	
+	position_++;
+	return iterator_->next(missing);
+}
+
+void SQLColumn::advance(unsigned long)
+{
+	NOTIMP;
+}
+
+unsigned long long SQLColumn::noRows() const
+{
+	if(!noRows_) { 
+		SQLColumn* col = const_cast<SQLColumn*>(this); 	
+		col->rewind(); 
+	}
+	return noRows_;
+} 
+
+void SQLColumn::print(std::ostream& s) const
+{
+}
+
+std::string SQLColumn::fullName() const
+{
+	// FIXME:
+	return name(); // + "@" + owner_.fullName();
+}
+
+SQLTable* SQLColumn::table() const
+{
+	return &owner_;
+}
+
+void SQLColumn::createIndex()
+{
+    std::auto_ptr<SQLIndex> index(new SQLIndex(*this));
+	indexing_ = index;
+	indexing_->update();
+}
+
+SQLIndex* SQLColumn::getIndex(double* value)
+{
+	ASSERT(indexing_.get());
+	indexing_->rewind(value);
+	return indexing_.get();
+}
+
+void SQLColumn::loadIndex()
+{
+	PathName path = indexPath();
+	if(path.exists()) createIndex();
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLColumn.h b/odb_api/src/odb_api/SQLColumn.h
new file mode 100755
index 0000000..28ad55a
--- /dev/null
+++ b/odb_api/src/odb_api/SQLColumn.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLColumn.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLColumn_H
+#define SQLColumn_H
+
+namespace eckit { class PathName; }
+
+#include "odb_api/SQLIterator.h"
+#include "odb_api/Types.h"
+
+namespace odb {
+namespace sql {
+
+class SQLBitColumn;
+class SQLIndex;
+class SQLTable;
+
+class SQLColumn : public SQLIterator {
+public:
+	SQLColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue, const BitfieldDef&);
+	SQLColumn(const type::SQLType&, SQLTable&, const std::string&, int, bool hasMissingValue, double missingValue);
+	virtual ~SQLColumn();
+
+	void scan();
+
+	unsigned long long noRows() const;
+
+	const std::string& name() const { return name_; }
+	int index() { return index_; }
+	void index(int i) { index_ = i; }
+	std::string fullName()    const;
+	SQLTable* table()    const;
+
+
+	bool hasMissingValue() const { return hasMissingValue_; }
+	double missingValue() const { return missingValue_; }
+	bool isBitfield() const { return isBitfield_; }
+	const BitfieldDef& bitfieldDef() const { return bitfieldDef_; }
+
+// -- Overridden methods
+	// From SQLIterator
+
+	virtual void rewind();
+	virtual double next(bool& missing);
+	virtual void advance(unsigned long);
+
+	bool hasIndex() const { return indexing_.get() != 0; }
+	void createIndex();
+	void loadIndex();
+	SQLIndex* getIndex(double*);
+
+	eckit::PathName indexPath();
+protected:
+	unsigned long long noRows_;
+
+	virtual void print(std::ostream&) const; 	
+//private:
+protected:
+	SQLColumn(const SQLColumn&);
+	SQLColumn& operator=(const SQLColumn&);
+
+	void setPool(int);
+
+
+	SQLTable& owner_;
+	std::string    name_;
+	int       index_;
+
+	std::vector<int>          rows_;
+	std::vector<SQLIterator*> iterators_;
+
+	long long current_;
+	long long last_;
+	long long position_;
+	SQLIterator* iterator_;
+
+    std::auto_ptr<SQLIndex> indexing_;
+
+	bool hasMissingValue_;
+	double missingValue_;
+	bool isBitfield_;
+	const BitfieldDef bitfieldDef_;
+
+	friend class odb::sql::SQLBitColumn;
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLCreateTable.cc b/odb_api/src/odb_api/SQLCreateTable.cc
new file mode 100644
index 0000000..6cafafe
--- /dev/null
+++ b/odb_api/src/odb_api/SQLCreateTable.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include "eckit/log/Log.h"
+#include "eckit/utils/Translator.h"
+
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/SQLCreateTable.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLSession.h"
+
+#include "odb_api/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/MDI.h"
+
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SQLCreateTable::SQLCreateTable(std::string tableName, ColumnDefs &cols)
+: tableName_(tableName), cols_(cols)
+{}
+
+SQLCreateTable::~SQLCreateTable() {}
+
+void SQLCreateTable::print(std::ostream& s) const
+{
+}
+
+unsigned long long SQLCreateTable::execute()
+{
+    SQLDatabase &db = SQLSession::current().currentDatabase();
+    // SQLTable::SQLTable(SQLDatabase& owner,const PathName& path,const std::string& name,int no_rows,int index)
+    // TODO:
+    SQLTable *table = NULL; //new SQLTable(db, tableName_ /*?*/, tableName_, /*no_rows (columns, really..)*/ 0, /*index*/ 0);
+
+    int index = 1;
+    for (ColumnDefs::const_iterator i = cols_.begin(); i != cols_.end(); i++)
+    {
+        const std::string columnName (i->name()),
+                     typeName (i->type());
+
+		const long start (i->range().first),
+                   end (i->range().second);
+		bool isVector = ! (start == 0 && end == 0); // e.g: colname[1:3] pk1real
+		BitfieldDef bitfieldDef;
+		if (typeName == "@LINK")
+		{
+			ASSERT(! isVector);
+
+			db.links()[tableName_].insert(columnName);
+
+			const type::SQLType& intType = type::SQLType::lookup("pk5int");
+			table->addColumn(columnName + ".offset", index++, intType, false, odb::MDI::integerMDI(), false, bitfieldDef);
+			table->addColumn(columnName + ".length", index++, intType, false, odb::MDI::integerMDI(), false, bitfieldDef);
+		}
+		else
+		{
+			const type::SQLType& typ = type::SQLType::lookup(typeName);
+			bool isBitmap = typ.getKind() == type::SQLType::bitmapType;
+			if (isBitmap)
+				//bitmap = static_cast<const type::SQLBitfield&>(typ).fields();
+				bitfieldDef = static_cast<const type::SQLBitfield&>(typ).bitfieldDef();
+
+			if (!isVector)
+				// FIXME: no information about missing value in the CREATE TABLE syntax
+				// TODO: choose a sensible default based upon type
+				table->addColumn(columnName, index++, typ, true, odb::MDI::realMDI(), isBitmap, bitfieldDef);
+			else
+			{
+				for (int i = start; i <= end; i++)
+				{
+					std::string expandedName = columnName + "_" + Translator<int,std::string>()(i);
+					Log::debug() << "  === expandedName: " << expandedName << std::endl;
+					// TODO: choose a sensible default based upon type
+					table->addColumn(expandedName, index++, typ, true, odb::MDI::realMDI(), isBitmap, bitfieldDef);
+				}
+			}
+		}
+	}
+	db.addTable(table);
+	//db.setLinks(links);
+	return 0;
+}
+
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLCreateTable.h b/odb_api/src/odb_api/SQLCreateTable.h
new file mode 100755
index 0000000..e6d34df
--- /dev/null
+++ b/odb_api/src/odb_api/SQLCreateTable.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLCreateTable.h
+/// 
+/// Piotr Kuchta - ECMWF Jan 09
+/// 
+
+#ifndef SQLCreateTable_H
+#define SQLCreateTable_H
+
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+
+// Forward declarations
+class SQLCreateTable {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	SQLCreateTable(std::string tableName, ColumnDefs &cols);
+
+// -- Destructor
+
+	virtual ~SQLCreateTable(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	virtual unsigned long long execute();
+
+	// TODO: This method makes no sense for statements not producing
+	// result sets, like e.g. CREATE TABLE, so I think it could be
+	// removed from SQLStatement. Perhaps SQLStatement should have two
+	// abstract subclasses, one for statements which produce result sets
+	// (rows of data), one for those producing update count
+	// (like UPDATE, DELETE, CREATE TABLE|INDEX|...), similarly as in JDBC.
+	virtual Expressions output() const { return Expressions(); }
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+	virtual void print(std::ostream&) const; 	
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	//virtual void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	SQLCreateTable(const SQLCreateTable&);
+	SQLCreateTable& operator=(const SQLCreateTable&);
+
+// -- Members
+	std::string tableName_;
+	ColumnDefs cols_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLCreateTable& p)
+		{ p.print(s); return s; }
+
+};
+
+} // namespace sql 
+} // namespace odb 
+#endif
diff --git a/odb_api/src/odb_api/SQLDataColumn.cc b/odb_api/src/odb_api/SQLDataColumn.cc
new file mode 100644
index 0000000..0581277
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataColumn.cc
@@ -0,0 +1,33 @@
+#include "odb_api/SQLDataColumn.h"
+#include "eckit/exception/Exceptions.h"
+
+namespace odb {
+
+SQLDataColumn::SQLDataColumn(const odb::sql::type::SQLType& type,
+        odb::sql::SQLTable& owner, const std::string& name, int index,
+        bool hasMissingValue, double missingValue, 
+        const odb::BitfieldDef& bitfieldDef, double* value)
+  : odb::sql::SQLColumn(type, owner, name, index, hasMissingValue, missingValue, bitfieldDef),
+    value_(value)
+{}
+
+SQLDataColumn::SQLDataColumn(const odb::sql::type::SQLType& type,
+        odb::sql::SQLTable& owner, const std::string& name, int index,
+        bool hasMissingValue, double missingValue, double* value)
+  : odb::sql::SQLColumn(type, owner, name, index, hasMissingValue, missingValue),
+    value_(value)
+{}
+
+SQLDataColumn::~SQLDataColumn() {}
+
+void SQLDataColumn::rewind() { *value_ = missingValue_; }
+
+double SQLDataColumn::next(bool& missing)
+{
+    missing = (*value_ == missingValue_);
+    return *value_;
+}
+
+void SQLDataColumn::advance(unsigned long) { NOTIMP; }
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDataColumn.h b/odb_api/src/odb_api/SQLDataColumn.h
new file mode 100644
index 0000000..78d8863
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataColumn.h
@@ -0,0 +1,41 @@
+#ifndef SQLDATACOLUMN_H_
+#define SQLDATACOLUMN_H_
+
+//#include <string>
+
+#include "odb_api/SQLColumn.h"
+
+namespace odb {
+
+class SQLDataColumn : public odb::sql::SQLColumn
+{
+public:
+	SQLDataColumn(const odb::sql::type::SQLType& type, odb::sql::SQLTable& table,
+            const std::string& name, int index, bool hasMissingValue,
+            double missingValue, const odb::BitfieldDef& bitfieldDef,
+            double* value);
+
+	SQLDataColumn(const odb::sql::type::SQLType& type, odb::sql::SQLTable& table,
+            const std::string& name, int index, bool hasMissingValue,
+            double missingValue, double* value);
+
+    ~SQLDataColumn();
+
+    void value(double* p) { value_ = p; }
+    double * value() const { return value_; }
+
+private:
+    SQLDataColumn(const SQLDataColumn&);
+    SQLDataColumn& operator=(const SQLDataColumn&);
+
+    virtual void rewind();
+    virtual double next(bool& missing);
+    virtual void advance(unsigned long n);
+
+    double* value_;
+    double missing_;
+};
+
+} // namespace odb
+
+#endif // SQLDATACOLUMN_H_
diff --git a/odb_api/src/odb_api/SQLDataSet.cc b/odb_api/src/odb_api/SQLDataSet.cc
new file mode 100644
index 0000000..fd8777c
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataSet.cc
@@ -0,0 +1,44 @@
+/// @file SQLDataSet.cc
+/// @author Tomas Kral
+
+#include "odb_api/SQLDataSet.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SQLDataSet::SQLDataSet()
+  : SQLDatabase("anonymous")
+{}
+
+SQLDataSet::SQLDataSet(const std::string& name)
+  : SQLDatabase(name)
+{}
+
+SQLDataSet::~SQLDataSet()
+{}
+
+void SQLDataSet::open()
+{}
+
+void SQLDataSet::close()
+{
+    SQLDatabase::close();
+}
+
+SQLTable* SQLDataSet::openDataHandle(DataHandle&, DataFormat)
+{
+    NOTIMP;
+    return 0;
+}
+
+SQLTable* SQLDataSet::openDataStream(std::istream&, const std::string &, DataFormat)
+{
+    NOTIMP;
+    return 0;
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDataSet.h b/odb_api/src/odb_api/SQLDataSet.h
new file mode 100644
index 0000000..8d56889
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataSet.h
@@ -0,0 +1,33 @@
+/// @file   SQLDataSet.h
+/// @author Tomas Kral
+
+#ifndef SQLDATASET_H_
+#define SQLDATASET_H_
+
+#include "odb_api/SQLDatabase.h"
+
+namespace odb {
+namespace sql {
+
+class SQLDataSet : public SQLDatabase
+{
+public:
+    SQLDataSet();
+    SQLDataSet(const std::string& name);
+    ~SQLDataSet();
+
+private:
+    SQLDataSet(const SQLDataSet&);
+    SQLDataSet& operator=(const SQLDataSet&);
+
+    virtual void open();
+    virtual void close();
+
+    virtual odb::sql::SQLTable* openDataHandle(eckit::DataHandle&, DataFormat = ODA);
+    virtual odb::sql::SQLTable* openDataStream(std::istream&, const std::string& delimiter, DataFormat = CSV);
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif // SQLDATASET_H_
diff --git a/odb_api/src/odb_api/SQLDataTable.cc b/odb_api/src/odb_api/SQLDataTable.cc
new file mode 100644
index 0000000..6ea5e55
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataTable.cc
@@ -0,0 +1,155 @@
+//#include "odb_api/SQLDataTable.h"
+
+//#include <map>
+
+#include "odb_api/SQLDataTable.h"
+#include "odb_api/DataTable.h"
+
+#include "odb_api/SQLDataColumn.h"
+#include "odb_api/SQLDataTableIterator.h"
+#include "odb_api/SQLBitfield.h"
+
+
+using namespace std;
+using namespace odb;
+using namespace odb::sql;
+
+using namespace eckit;
+
+namespace odb {
+
+SQLDataTable::SQLDataTable(odb::sql::SQLDatabase& db, const DataTable& table)
+  : SQLTable(db, PathName("<>"), table.name()),
+    table_(table),
+    data_(0)
+{
+    populateColumns();
+}
+
+SQLDataTable::~SQLDataTable()
+{
+    delete [] data_;
+}
+
+SQLColumn* SQLDataTable::column(const std::string& name)
+{
+    SQLColumn* column = 0;
+    std::map<std::string, SQLColumn*>::iterator it = columnsByName_.begin();
+
+    for (; it != columnsByName_.end(); ++it)
+    {
+        std::string s = it->first;
+
+        if (s.find(name + "@") == 0)
+        {
+            if (column)
+                throw eckit::UserError(std::string("SQLDataTable::column: name \"") + name + "\" is ambiguous");
+            else 
+                column = it->second;
+        }
+    }
+
+    if (column)
+        return column;
+
+    return SQLTable::column(name);
+}
+
+bool SQLDataTable::hasColumn(const std::string& name, std::string* fullName)
+{
+    if (SQLTable::hasColumn(name))
+    {
+        if (fullName)
+            *fullName = name;
+
+        return true;
+    }
+
+    int n = 0;
+    std::map<std::string,SQLColumn*>::iterator it = columnsByName_.begin();
+
+    for (; it != columnsByName_.end(); ++it)
+    {
+        if (it->first.find(name + "@") == 0)
+        {
+            n++;
+
+            if (fullName)
+                *fullName = it->first;
+        }
+    }
+
+    if (n == 0) return false;
+    if (n == 1) return true;
+
+    throw eckit::UserError(std::string("SQLDataTable:hasColumn(\"") + name + "\"): ambiguous name");
+    return false;
+}
+
+SQLTableIterator* SQLDataTable::iterator(const std::vector<SQLColumn*>& columns) const
+{
+    return new SQLDataTableIterator(table_, const_cast<double*>(data_), columns);
+}
+
+SQLColumn* SQLDataTable::createSQLColumn(const type::SQLType& type, const std::string& name,
+    int index, bool hasMissingValue, double missingValue, 
+    const BitfieldDef& bitfieldDef)
+{
+    return new SQLDataColumn(type, *this, name, index, hasMissingValue, missingValue,
+        bitfieldDef, &data_[index]);
+}
+
+SQLColumn* SQLDataTable::createSQLColumn(const type::SQLType& type, const std::string& name,
+    int index, bool hasMissingValue, double missingValue)
+{
+    return new SQLDataColumn(type, *this, name, index, hasMissingValue, missingValue,
+        &data_[index]);
+}
+
+void SQLDataTable::populateColumns()
+{
+    size_t count = table_.columns().size();
+
+    data_ = new double[count]; ASSERT(data_);
+
+    for (size_t index = 0; index < count; ++index)
+    {
+        const DataColumn& column = table_.columns()[index];
+
+        const std::string name = column.name();
+        bool hasMissingValue = true; // TODO: implement DataColumn::hasMissing() method
+        double missingValue = column.missingValue();
+        odb::BitfieldDef bitfieldDef = column.bitfieldDef();
+    
+        std::string sqlType;
+
+        switch (column.type())
+        {
+            case INTEGER: sqlType = "integer"; break;
+            case REAL:    sqlType = "real";    break;
+            case DOUBLE:  sqlType = "double";  break;
+            case STRING:  sqlType = "string";  break;
+
+            case BITFIELD:
+            {
+                std::string signature = type::SQLBitfield::make("Bitfield",
+                    bitfieldDef.first, bitfieldDef.second, "DummyTypeAlias");
+                addColumn(name, index, type::SQLType::lookup(signature),
+                    hasMissingValue, missingValue, true, bitfieldDef);
+                continue;
+            } break;
+
+            default:
+                ASSERT(!"Unknown type");
+                break;
+        }
+
+        SQLColumn *c = column.type() == BITFIELD 
+                    ? new SQLDataColumn(type::SQLType::lookup(sqlType), *this, name, index, hasMissingValue, missingValue, bitfieldDef, &data_[index])
+                    : new SQLDataColumn(type::SQLType::lookup(sqlType), *this, name, index, hasMissingValue, missingValue, &data_[index]);
+
+        addColumn(c, name, index);
+    }
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDataTable.h b/odb_api/src/odb_api/SQLDataTable.h
new file mode 100644
index 0000000..036443d
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataTable.h
@@ -0,0 +1,51 @@
+#ifndef odb_sql_SQLDataTable_H
+#define odb_sql_SQLDataTable_H
+
+#include "odb_api/SQLTable.h"
+
+namespace odb {
+namespace sql {
+
+    class SQLColumn;
+    class SQLDatabase;
+    class SQLTableIterator;
+
+}}
+
+namespace odb {
+
+class DataTable;
+
+class SQLDataTable : public odb::sql::SQLTable
+{
+public:
+    SQLDataTable(odb::sql::SQLDatabase& db, const DataTable& table);
+
+    ~SQLDataTable();
+
+    virtual odb::sql::SQLColumn* column(const std::string& name);
+    virtual bool hasColumn(const std::string& name, std::string* fullName = 0);
+    virtual odb::sql::SQLTableIterator* iterator(const std::vector<odb::sql::SQLColumn*>&) const;
+
+protected:
+	virtual odb::sql::SQLColumn* createSQLColumn(const odb::sql::type::SQLType& type,
+            const std::string& name, int index, bool hasMissingValue,
+            double missingValue, const odb::BitfieldDef&);
+
+	virtual odb::sql::SQLColumn* createSQLColumn(const odb::sql::type::SQLType& type,
+            const std::string& name, int index, bool hasMissingValue,
+            double missingValue);
+
+private:
+    SQLDataTable(const SQLDataTable&);
+    SQLDataTable& operator=(const SQLDataTable&);
+
+    void populateColumns();
+
+    const DataTable& table_;
+    double* data_;
+};
+
+} // namespace odb
+
+#endif // odb_sql_SQLDataTable_H
diff --git a/odb_api/src/odb_api/SQLDataTableIterator.cc b/odb_api/src/odb_api/SQLDataTableIterator.cc
new file mode 100644
index 0000000..b159f8e
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataTableIterator.cc
@@ -0,0 +1,46 @@
+
+#include "odb_api/SQLDataTableIterator.h"
+//#include "odb_api/SQLDataColumn.h"
+
+namespace odb {
+
+SQLDataTableIterator::SQLDataTableIterator(const DataTable& table, double* data,
+        const std::vector<odb::sql::SQLColumn*>& columns)
+  : table_(table),
+    it_(table.begin()),
+    end_(table.end()), 
+    columns_(columns),
+    data_(data),
+    firstRow_(true)
+{
+    if (it_ != end_)
+    {
+        copyRow();
+    }
+}
+
+SQLDataTableIterator::~SQLDataTableIterator()
+{}
+
+void SQLDataTableIterator::rewind()
+{}
+
+bool SQLDataTableIterator::next()
+{
+    if (firstRow_) firstRow_ = false;
+    else ++it_;
+
+    if (it_ == end_)
+        return false;
+
+    copyRow();
+
+    return true;
+}
+
+void SQLDataTableIterator::copyRow()
+{
+    std::copy(it_->data(), it_->data() + table_.columns().size(), data_);
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDataTableIterator.h b/odb_api/src/odb_api/SQLDataTableIterator.h
new file mode 100644
index 0000000..a1810cb
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDataTableIterator.h
@@ -0,0 +1,36 @@
+#ifndef SQLDATATABLEITERATOR_H_
+#define SQLDATATABLEITERATOR_H_
+
+#include "odb_api/DataTable.h"
+#include "odb_api/SQLTable.h"
+
+namespace odb { namespace sql { class SQLColumn; } }
+
+namespace odb {
+
+class SQLDataTableIterator : public odb::sql::SQLTableIterator
+{
+public:
+    SQLDataTableIterator(const DataTable& table, double* data, const std::vector<odb::sql::SQLColumn*>&);
+    ~SQLDataTableIterator();
+
+    virtual void rewind();
+    virtual bool next();
+
+private:
+    SQLDataTableIterator(const SQLDataTableIterator&);
+    SQLDataTableIterator& operator=(const SQLDataTableIterator&);
+
+    void copyRow();
+
+    const DataTable& table_;
+    DataTable::const_iterator it_;
+    DataTable::const_iterator end_;
+    const std::vector<odb::sql::SQLColumn*>& columns_;
+    double* data_;
+    bool firstRow_;
+};
+
+} // namespace odb
+
+#endif // SQLDATATABLEITERATOR_H_
diff --git a/odb_api/src/odb_api/SQLDatabase.cc b/odb_api/src/odb_api/SQLDatabase.cc
new file mode 100755
index 0000000..2bd58b9
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDatabase.cc
@@ -0,0 +1,362 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/Tokenizer.h"
+
+#include "odb_api/DataColumns.h"
+#include "odb_api/DataTable.h"
+
+#include "odb_api/SQLSession.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLDataTable.h"
+#include "odb_api/SQLType.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+void SQLDatabase::setUpVariablesTable()
+{
+	tablesByName_["dual"] = dualTable();
+}
+
+SQLDatabase::SQLDatabase(const PathName& path,const std::string& name)
+: path_(path),
+  name_(name),
+  dualTable_(0),
+  dual_(0)
+{
+	setUpVariablesTable();
+} 
+
+SQLDatabase::SQLDatabase(const std::string& name)
+: path_("."), 
+  name_(name),
+  dualTable_(0),
+  dual_(0)
+{
+	setUpVariablesTable();
+} 
+
+SQLDatabase::~SQLDatabase()
+{
+	close();
+	for (Variables::iterator it = variables_.begin(); it != variables_.end(); ++it)
+	{
+	
+		//std::string var(it->first);
+		//cout << "SQLDatabase::~SQLDatabase: " <<  var << std::endl;
+		//SQLExpression* e (it->second);
+		//if (e->isVector())
+		//	std::cout << var << " = " << *e << std::endl;
+		//else
+		//{
+		//	bool missing = false;
+		//	double value(it->second->eval(missing));
+		//	std::cout << var << " = " << value << std::endl;
+		//}
+		//FIXME:
+		//delete it->second;
+	}
+	variables_.clear();
+    // TODO: FIXME
+    //delete dual_;
+    //delete dualTable_;
+}
+
+void SQLDatabase::open()
+{
+	Log::info() << "Opening " << path_ << " as " << name_ << std::endl;
+#if 0
+	loadDD();
+	loadIOMAP();
+	loadFLAGS();
+#endif
+}
+
+void SQLDatabase::close()
+{
+    for(std::map<std::string,SQLTable*>::iterator j = tablesByName_.begin();
+        j != tablesByName_.end(); ++j)
+    {
+        SQLTable* table = (*j).second;
+        delete table;
+    }
+
+    tablesByName_.clear();
+}
+
+SQLTable* SQLDatabase::dualTable()
+{
+    if (dualTable_ == 0)
+    {
+        DataColumns columns;
+        columns.add("dummy", "INTEGER");
+        DataTableProperties properties;
+        properties.blockSizeInNumberOfRows(10); // ?
+        dualTable_ = new odb::DataTable("dual", columns, properties);
+        double row = 0;
+        dualTable_->push_back(&row);
+        dual_ = new SQLDataTable(*this, *dualTable_);
+    }
+    return dual_;
+}
+
+#if 0
+void SQLDatabase::loadFLAGS()
+{
+	std::string name   = path_.baseName(false);
+	PathName path = path_ + "/" + name + ".flags";
+	ifstream in(path.c_str());
+	if(!in) throw CantOpenFile(path);
+
+	Tokenizer parse("=(),");
+	char line[1024];
+	while(in.getline(line,sizeof(line)-1))
+	{
+		if(line[0] == '-' && line[1] == 'A')
+		{
+			std::string s(line+2);
+			std::vector<std::string> v;
+			parse(s,v);
+
+			if(tablesByName_.find(v[0]) == tablesByName_.end())
+			{
+				Log::warning() << path << ": Cannot find master table '" << v[0] << "'" << std::endl;
+			}
+			else
+			{
+				SQLTable* master = tablesByName_[v[0]];
+
+				for(int i = 1 ; i < v.size(); i++)
+				{
+					if(tablesByName_.find(v[i]) == tablesByName_.end())
+						Log::warning() << path << ": Cannot find slave table '" 
+							<< v[i] << "' of '" << master->name() 
+							<< "'" << std::endl;
+					else					
+						tablesByName_[v[i]]->master(master);
+				}
+			}
+			
+		}
+	}
+}
+
+void SQLDatabase::loadDD()
+{
+	std::string name   = path_.baseName(false);
+	PathName path = path_ + "/" + name + ".dd";
+
+	ifstream in(path.c_str());
+	if(!in) throw CantOpenFile(path);
+
+	Tokenizer parse(":@");
+	Tokenizer braket("()");
+
+	std::string junk;
+	std::string s;
+
+	//cout << path << std::endl;
+
+	in >> junk >> junk >> junk; // version ?
+	in >> junk >> junk ; // dates ?
+	in >> junk >> junk ; // dates ?
+	in >> junk >> junk ; // dates ?
+
+	int no_pools = 0,no_tables = 0;
+
+	in >> no_pools;
+	in >> no_tables;
+
+	//cout << "no_pools " << no_pools << std::endl;
+	//cout << "no_tbales " << no_tables << std::endl;
+
+	for(int i = 0; i < no_tables; i++)
+	{
+		int id; std::string name;
+		int n;
+		in >> id >> name >> n;
+		while(n-->0) in >> junk;
+	}
+
+	std::map<std::string,set<std::string> > links;
+
+	for(int i = 0; i < no_tables; i++)
+	{
+		std::string name; int no_cols;
+		in >> name >> no_cols; name.erase(0,1);
+		ASSERT(tablesByName_.find(name) == tablesByName_.end());
+
+		SQLTable* table = new SQLTable(*this,path_,name,no_cols,i);
+		tablesByName_[name] = table;
+
+
+		for(int j = 0; j < no_cols; j++)
+		{
+			std::string type; int no_fields;
+			in >> type >> no_fields;
+
+			//cout << type << " " << no_fields << std::endl;
+
+			std::vector<std::string> v;
+			parse(type,v);
+
+			//cout << name << " " << v[0] << " " << v[1] << std::endl;
+			std::vector<std::string> b;
+			braket(v[1],b);
+
+			if(b.size() > 1)
+			{
+				if(b[0] == "LINKOFFSET") { v[1] = b[1] + "." + "offset"; }
+				if(b[0] == "LINKLEN")    { v[1] = b[1] + "." + "length"; }
+
+				links[name].insert(b[1]);
+			}
+
+			std::vector<std::string> bitmap;
+
+			if(no_fields)
+			{
+				std::vector<std::string> fields;
+				std::vector<int>    sizes;
+		
+				for(int k = 0; k < no_fields; k++)
+				{
+					std::string name; int size;
+					in >> name >> size;
+
+					fields.push_back(name);
+					sizes.push_back(size);
+				//cout <<  name << " " << size << std::endl;
+					// table->addBitColumn(...)
+					bitmap.push_back(name);
+				}
+				v[0] = SQLBitfield::make(v[0],fields,sizes);
+			}
+
+			table->addColumn(v[1], j,SQLType::lookup(v[0]), bitmap);
+		}
+	}
+
+	int no_variables;
+	in >> no_variables;
+
+	for(int i = 0; i < no_variables; i++)
+	{
+		std::string name; double value;
+		in >> name >> value;
+		variables_[name] = value;
+
+		//Log::info() << "Variable " << name << " = " << value << std::endl;
+	}
+	setLinks(links);
+}
+#endif
+
+void SQLDatabase::setLinks(const Links& links)
+{
+	for(Links::const_iterator j = links.begin(); j != links.end() ; ++j)
+	{
+		const std::string&      from = (*j).first;
+        const std::set<std::string>& to   = (*j).second;
+
+		ASSERT(tablesByName_.find(from) != tablesByName_.end());
+		SQLTable *f = tablesByName_[from];
+
+        for(std::set<std::string>::const_iterator k = to.begin(); k != to.end() ; ++k)
+		{
+			ASSERT(tablesByName_.find(*k) != tablesByName_.end());
+			SQLTable* t = tablesByName_[*k];
+
+			f->addLinkTo(t);
+			t->addLinkFrom(f);
+		}
+	}
+}
+
+#if 0
+void SQLDatabase::loadIOMAP()
+{
+	std::string name   = path_.baseName(false);
+	PathName path = path_ + "/" + name + ".iomap";
+
+	ifstream in(path.c_str());
+	if(!in) throw CantOpenFile(path);
+
+	int junk;
+	std::string s;
+	std::string table;
+
+	int no_tables,no_pools;
+
+	in >> junk;
+	in >> s;
+
+	in >> no_tables >> no_pools >> junk;
+	in >> junk >> junk >> junk;
+
+	for(;;)
+	{
+		std::string name; int id, n_cols;
+
+		in >> id >> n_cols >> name; name.erase(0,1);
+		if(id == -1) break;
+
+		ASSERT(tablesByName_.find(name) != tablesByName_.end());
+		tablesByName_[name]->loadIOMAP(in);
+
+	}
+}
+#endif
+
+SQLTable* SQLDatabase::defaultTable()
+{
+    std::map<std::string,SQLTable*>::iterator j (tablesByName_.find("defaultTable"));
+    if (j == tablesByName_.end())
+        throw UserError("No default table");
+	return (*j).second;
+}
+
+SQLTable* SQLDatabase::table(const Table& t)
+{
+	std::map<std::string,SQLTable*>::iterator j (tablesByName_.find(t.name));
+	ASSERT(j != tablesByName_.end());
+	return (*j).second;
+}
+
+void SQLDatabase::setVariable(const std::string& name, expression::SQLExpression* value) {
+	variables_[name] = value;
+}
+
+expression::SQLExpression* SQLDatabase::getVariable(const std::string& name) const
+{
+	Variables::const_iterator j = variables_.find(name);
+	if(j == variables_.end())
+		throw eckit::UserError("Undefined variable", name);
+	return (*j).second;
+}
+
+void SQLDatabase::setIncludePath(const std::string& includePath)
+{
+    Tokenizer tokenize(":");
+    std::vector<std::string> tokens;
+    tokenize(includePath, tokens);
+    copy(tokens.begin(), tokens.end(), back_inserter(includePath_));
+}
+
+bool SQLDatabase::sameAs(const SQLDatabase& other) const
+{
+	return path_ == other.path_;
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDatabase.h b/odb_api/src/odb_api/SQLDatabase.h
new file mode 100755
index 0000000..28ea4fb
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDatabase.h
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLDatabase.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLDatabase_H
+#define SQLDatabase_H
+
+#include <map>
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/SQLTable.h"
+#include "odb_api/SchemaAnalyzer.h"
+
+namespace eckit { class DataHandle; }
+namespace odb { class DataTable; }
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLStatement;
+namespace expression { class SQLExpression; }
+
+
+typedef std::map<std::string, std::set<std::string> > Links;
+typedef std::map<std::string, expression::SQLExpression*> Variables;
+
+class SQLDatabase {
+public:
+	typedef enum { ODA, CSV } DataFormat;
+
+	SQLDatabase(const eckit::PathName&,const std::string&);
+	SQLDatabase(const std::string& = "default");
+	virtual ~SQLDatabase(); 
+
+// -- Methods
+	virtual void open();
+	virtual void close();
+
+    //virtual SQLTable* table(const std::string&);
+    virtual SQLTable* table(const Table&);
+    virtual SQLTable* defaultTable();
+	virtual SQLTable* openDataHandle(eckit::DataHandle&, DataFormat = ODA) = 0; 
+    virtual SQLTable* openDataStream(std::istream&, const std::string& delimiter, DataFormat = CSV) = 0;
+	virtual void addTable(SQLTable *table) { tablesByName_[table->name()] = table; }
+
+	void setLinks(const Links&);
+	void setLinks() { setLinks(links_); }
+
+	void addLinks(const Links& ls) { links_.insert(ls.begin(), ls.end()); }
+	Links& links() { return links_; }
+
+	virtual const std::string& name() const { return name_; }
+
+    expression::SQLExpression* getVariable(const std::string&) const;
+    void   setVariable(const std::string&, expression::SQLExpression*);
+	Variables& variables() { return variables_; }
+
+	virtual bool sameAs(const SQLDatabase& other) const;
+	SchemaAnalyzer& schemaAnalyzer() { return schemaAnalyzer_; }
+
+    void setIncludePath(const std::string& includePath);
+    const std::vector<eckit::PathName>& includePath() const { return includePath_; }
+
+    SQLTable* dualTable();
+
+protected:
+	Links links_;
+    std::map<std::string,SQLTable*> tablesByName_;
+
+    eckit::PathName path_;
+    std::vector<eckit::PathName> includePath_;
+
+	Variables variables_;
+	std::string name_;
+	SchemaAnalyzer schemaAnalyzer_;
+
+    odb::DataTable* dualTable_;
+    SQLTable* dual_;
+
+private:
+// No copy allowed
+	SQLDatabase(const SQLDatabase&);
+	SQLDatabase& operator=(const SQLDatabase&);
+
+	void loadIOMAP();
+	void loadDD();
+	void loadFLAGS();
+
+	void setUpVariablesTable();
+
+// -- Friends
+	friend std::ostream& operator<< (std::ostream& s, const odb::sql::SQLDatabase& p)
+	{ 
+        s << "[SQLDatabase@" << &p << " tables: ";
+        for (std::map<std::string,SQLTable*>::const_iterator it (p.tablesByName_.begin()); it != p.tablesByName_.end(); ++it)
+            s << it->first << ",";
+
+        s << "]";
+        return s; 
+    }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLDistinctOutput.cc b/odb_api/src/odb_api/SQLDistinctOutput.cc
new file mode 100755
index 0000000..36b36a7
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDistinctOutput.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLDistinctOutput.h"
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+
+SQLDistinctOutput::SQLDistinctOutput(SQLOutput* output)
+: output_(output)
+{} 
+
+SQLDistinctOutput::~SQLDistinctOutput() {}
+
+const SQLOutputConfig& SQLDistinctOutput::config() { return output_->config(); }
+void SQLDistinctOutput::config(SQLOutputConfig& cfg) { output_->config(cfg); }
+
+void SQLDistinctOutput::print(std::ostream& s) const
+{
+	s << "SQLDistinctOutput[" << *output_ << "]";
+}
+
+void SQLDistinctOutput::size(int count)
+{
+	output_->size(count);
+	tmp_ = std::vector<double>(count);
+}
+
+void SQLDistinctOutput::reset()
+{
+	output_->reset();
+	seen_.clear();
+}
+
+void SQLDistinctOutput::flush(ecml::ExecutionContext* context) { output_->flush(context); }
+
+unsigned long long SQLDistinctOutput::count() { return output_->count(); }
+
+bool SQLDistinctOutput::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+	for(size_t i = 0; i < results.size(); i++)
+	{
+		bool missing = false;
+		tmp_[i] = results[i]->eval(missing);
+		// What do we do with missing? Or has it been already evaluated somewhere before and it doesn't matter???...
+	}
+
+	if(seen_.find(tmp_) == seen_.end())
+	{
+		seen_.insert(tmp_);
+		output_->output(results, context);
+		return true;
+	}
+	return false;
+}
+
+void SQLDistinctOutput::prepare(SQLSelect& sql) { output_->prepare(sql); }
+
+void SQLDistinctOutput::cleanup(SQLSelect& sql) { output_->cleanup(sql); }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDistinctOutput.h b/odb_api/src/odb_api/SQLDistinctOutput.h
new file mode 100755
index 0000000..482793d
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDistinctOutput.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLDistinctOutput..h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLDistinctOutput_H
+#define SQLDistinctOutput_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+
+class SQLDistinctOutput : public SQLOutput {
+public:
+	SQLDistinctOutput(SQLOutput* output);
+	virtual ~SQLDistinctOutput(); 
+
+protected:
+	virtual void print(std::ostream&) const; 	
+private:
+// No copy allowed
+	SQLDistinctOutput(const SQLDistinctOutput&);
+	SQLDistinctOutput& operator=(const SQLDistinctOutput&);
+
+	virtual const SQLOutputConfig& config();
+	virtual	void config(SQLOutputConfig&);
+// -- Members
+    std::auto_ptr<SQLOutput>   output_;
+	std::set<std::vector<double> >  seen_;
+	std::vector<double>        tmp_;
+// -- Overridden methods
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+	virtual void outputReal(double, bool) { NOTIMP; };
+	virtual void outputDouble(double, bool) { NOTIMP; };
+	virtual void outputInt(double, bool) { NOTIMP; };
+	virtual void outputUnsignedInt(double, bool) { NOTIMP; };
+	virtual void outputString(double, bool) { NOTIMP; };
+	virtual void outputBitfield(double, bool) { NOTIMP; };
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLDouble.cc b/odb_api/src/odb_api/SQLDouble.cc
new file mode 100644
index 0000000..da44d7c
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDouble.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLDouble.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLDouble::SQLDouble(const std::string& name): SQLType(name) {}
+
+SQLDouble::~SQLDouble() {}
+
+size_t SQLDouble::size() const { return sizeof(double); }
+
+void SQLDouble::output(SQLOutput& o, double d, bool m) const { o.outputDouble(d, m); }
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLDouble.h b/odb_api/src/odb_api/SQLDouble.h
new file mode 100644
index 0000000..66d4d24
--- /dev/null
+++ b/odb_api/src/odb_api/SQLDouble.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLDouble.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLDouble_H
+#define SQLDouble_H
+
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace type {
+
+class SQLDouble : public SQLType {
+public:
+	SQLDouble(const std::string& );
+	~SQLDouble(); 
+
+// -- Overridden methods
+	virtual void output(SQLOutput&, double, bool) const;
+
+private:
+// No copy allowed
+	SQLDouble(const SQLDouble&);
+	SQLDouble& operator=(const SQLDouble&);
+
+	virtual size_t size() const;
+	virtual int getKind() const { return doubleType; }
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const SQLDouble& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLEmbedded.cc b/odb_api/src/odb_api/SQLEmbedded.cc
new file mode 100755
index 0000000..65b092b
--- /dev/null
+++ b/odb_api/src/odb_api/SQLEmbedded.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/core/ExecutionContext.h"
+#include "eckit/types/Types.h"
+
+#include "odb_api/SQLEmbedded.h"
+
+namespace odb {
+namespace sql {
+
+SQLEmbedded::SQLEmbedded(const EmbeddedAST& a) 
+: code_(a.code_)
+{}
+
+SQLEmbedded::~SQLEmbedded() {}
+
+void SQLEmbedded::print(std::ostream& s) const {}
+
+unsigned long long SQLEmbedded::execute(ecml::ExecutionContext* context)
+{
+    NOTIMP;
+    return 0; // TODO:
+}
+
+expression::Expressions SQLEmbedded::output() const
+{
+    NOTIMP;
+}
+
+} // namespace sql 
+} // namespace odb 
+
+
diff --git a/odb_api/src/odb_api/SQLEmbedded.h b/odb_api/src/odb_api/SQLEmbedded.h
new file mode 100755
index 0000000..672dee2
--- /dev/null
+++ b/odb_api/src/odb_api/SQLEmbedded.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLEmbedded.h
+// Piotr Kuchta - ECMWF September 2016
+
+#ifndef SQLEmbedded_H
+#define SQLEmbedded_H
+
+#include "ecml/core/ExecutionContext.h"
+
+//#include "odb_api/Expressions.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLAST.h"
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLDatabase;
+
+class SQLEmbedded : public SQLStatement {
+public:
+	SQLEmbedded(const EmbeddedAST&);
+	virtual ~SQLEmbedded(); 
+
+	virtual unsigned long long execute(ecml::ExecutionContext*);
+	virtual expression::Expressions output() const;
+
+    const std::string& code() { return code_; }
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLEmbedded(const SQLEmbedded&);
+	SQLEmbedded& operator=(const SQLEmbedded&);
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLEmbedded& p)
+		{ p.print(s); return s; }
+
+    std::string code_;
+
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLExpression.cc b/odb_api/src/odb_api/SQLExpression.cc
new file mode 100755
index 0000000..3ddbae5
--- /dev/null
+++ b/odb_api/src/odb_api/SQLExpression.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLExpression.h"
+#include "odb_api/NumberExpression.h"
+//#include "odb_api/SQLType.h"
+#include "odb_api/SQLOutput.h"
+#include "odb_api/Expressions.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+double const MISSING_VALUE_REAL = -2147483647.0;
+long const MISSING_VALUE_INT =  2147483647;
+
+SQLExpression::SQLExpression()
+: isBitfield_(false), hasMissingValue_(false), missingValue_(MISSING_VALUE_REAL)
+{}
+
+SQLExpression::~SQLExpression() {}
+
+bool SQLExpression::isVector() const { return false; }
+
+Expressions& SQLExpression::vector()
+{
+	NOTIMP;
+	return *((Expressions*) 0);
+}
+
+Dictionary& SQLExpression::dictionary()
+{
+	NOTIMP;
+	return *((Dictionary*) 0);
+}
+
+SQLExpression* SQLExpression::number(double value) { return new NumberExpression(value); } 
+
+SQLExpression* SQLExpression::simplify(bool& changed)
+{
+	if(isConstant() && !isNumber())
+	{
+		changed = true;
+		bool missing = false;
+		Log::info() << "SIMPLIFY " << *this << " to " << eval(missing) << std::endl;
+		return new NumberExpression(eval(missing));
+	}
+	return 0;
+}
+
+void SQLExpression::output(SQLOutput& s) const 
+{ 
+	bool missing = false;
+	double d = eval(missing); 
+	s.outputReal(d, missing);
+}
+
+void SQLExpression::title(const std::string& t)
+{
+	title_ = t;
+}
+
+std::string SQLExpression::title() const
+{
+	if(title_.size())
+		return title_;
+
+    std::ostringstream s;
+    s << *this;
+
+    return s.str();
+}
+
+
+//const type::SQLType* SQLExpression::type() const { const type::SQLType& x = type::SQLType::lookup("real"); return &x; }
+
+void SQLExpression::expandStars(const std::vector<SQLTable*>&, expression::Expressions& e)
+{
+	e.push_back(this);
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLExpression.h b/odb_api/src/odb_api/SQLExpression.h
new file mode 100755
index 0000000..95ec57b
--- /dev/null
+++ b/odb_api/src/odb_api/SQLExpression.h
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLExpression_H
+#define SQLExpression_H
+
+#include "SQLType.h"
+#include "Types.h"
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLSelect;
+class SQLTable;
+class SQLIndex;
+class SQLOutput;
+
+namespace expression {
+
+class Expressions;
+class SQLExpression;
+class Dictionary;
+
+class SQLExpression {
+public:
+	SQLExpression();
+	virtual ~SQLExpression(); 
+
+	virtual void prepare(SQLSelect&) = 0;
+	virtual void cleanup(SQLSelect&) = 0;
+
+	// -- For WHERE
+	virtual double eval(bool& missing) const = 0;
+
+	virtual bool andSplit(expression::Expressions&) { return false; }
+    virtual void tables(std::set<SQLTable*>&) {}
+
+	virtual bool isConstant() const = 0;
+	virtual bool isNumber() const { return false; }
+	virtual bool isVector() const;
+	virtual Expressions& vector(); 
+	virtual bool isDictionary() const { return false; }
+	virtual Dictionary& dictionary();
+
+	virtual SQLExpression* simplify(bool&);
+
+    virtual void title(const std::string&);
+    virtual std::string title() const;
+
+	virtual const odb::sql::type::SQLType* type() const = 0;
+	// ----
+
+	virtual SQLExpression* clone() const = 0;
+	
+	virtual bool isAggregate() const { return false; }
+	// For select expression
+
+	virtual void output(SQLOutput&) const;
+	virtual void partialResult() {}
+	virtual void expandStars(const std::vector<SQLTable*>&,expression::Expressions&);
+
+	virtual bool isBitfield() const { return isBitfield_; }
+	BitfieldDef bitfieldDef() const { return bitfieldDef_; }
+
+	virtual bool hasMissingValue() const { return hasMissingValue_; }
+	double missingValue() const { return missingValue_; }
+
+	virtual bool indexed()  { return false; }
+	virtual bool useIndex() { return false; }
+	virtual SQLIndex* getIndex(double* = 0) { return 0; }
+
+	static SQLExpression* number(double);
+
+    virtual void print(std::ostream&) const = 0;
+
+protected:
+	SQLExpression(Expressions*);
+
+	bool isBitfield_;
+	BitfieldDef bitfieldDef_;
+	bool hasMissingValue_;
+	double missingValue_;
+	//bool isVector_;
+	//Vector* vector_;
+
+private:
+	SQLExpression(const SQLExpression&);
+	SQLExpression& operator=(const SQLExpression&);
+
+    std::string title_;
+
+    friend std::ostream& operator<<(std::ostream& s,const SQLExpression& p) { p.print(s); return s; }
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+//#include "odb_api/Expressions.h"
+
+using namespace odb::sql::expression;
+
+#endif
diff --git a/odb_api/src/odb_api/SQLExpressionEvaluated.cc b/odb_api/src/odb_api/SQLExpressionEvaluated.cc
new file mode 100755
index 0000000..66b744a
--- /dev/null
+++ b/odb_api/src/odb_api/SQLExpressionEvaluated.cc
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLExpressionEvaluated.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+SQLExpressionEvaluated::SQLExpressionEvaluated(SQLExpression& e)
+    : type_(e.type()),
+      missing_(false),
+      value_(e.eval(missing_)),
+      missingValue_(e.missingValue())
+{}
+
+SQLExpressionEvaluated::~SQLExpressionEvaluated() {}
+
+void SQLExpressionEvaluated::print(std::ostream& o) const
+{
+    if (missing_)
+        o << "NULL";
+    else
+        o << value_;
+    o << ", ";
+}
+
+void SQLExpressionEvaluated::prepare(SQLSelect&) { NOTIMP; }
+void SQLExpressionEvaluated::cleanup(SQLSelect&) { NOTIMP; }
+double SQLExpressionEvaluated::eval(bool& missing) const { if (missing_) missing = true; return value_; }
+bool SQLExpressionEvaluated::isConstant() const { NOTIMP; /*?*/ return true; }
+bool SQLExpressionEvaluated::isNumber() const { NOTIMP; /**/ return false; }
+SQLExpression* SQLExpressionEvaluated::simplify(bool&) { NOTIMP; return 0; }
+SQLExpression* SQLExpressionEvaluated::clone() const { NOTIMP; return 0; }
+bool SQLExpressionEvaluated::isAggregate() const { NOTIMP; return false; }
+
+const odb::sql::type::SQLType* SQLExpressionEvaluated::type() const { return type_; }
+
+void SQLExpressionEvaluated::output(SQLOutput& o) const { type_->output(o, value_, missing_); }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLExpressionEvaluated.h b/odb_api/src/odb_api/SQLExpressionEvaluated.h
new file mode 100755
index 0000000..341e5c0
--- /dev/null
+++ b/odb_api/src/odb_api/SQLExpressionEvaluated.h
@@ -0,0 +1,63 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLExpressionEvaluated.h
+/// Piotr Kuchta - ECMWF Nov 11
+
+#ifndef SQLExpressionEvaluated_H
+#define SQLExpressionEvaluated_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class SQLExpressionEvaluated : public SQLExpression {
+public:
+	SQLExpressionEvaluated(SQLExpression&);
+	~SQLExpressionEvaluated(); 
+
+	// Overriden
+
+    virtual void prepare(SQLSelect&);
+    virtual void cleanup(SQLSelect&);
+    virtual double eval(bool& missing) const ;
+    virtual bool isConstant() const ;
+    virtual bool isNumber() const ;
+    virtual SQLExpression* simplify(bool&) ;
+    virtual SQLExpression* clone() const;
+    virtual bool isAggregate() const ;
+
+    virtual const odb::sql::type::SQLType* type() const ;
+
+    virtual void output(SQLOutput& o) const ;
+
+protected:
+	virtual void print(std::ostream&) const;
+
+private:
+	SQLExpressionEvaluated(const SQLExpressionEvaluated&);
+	SQLExpressionEvaluated& operator=(const SQLExpressionEvaluated&);
+
+	friend std::ostream& operator<<(std::ostream& s, const SQLExpressionEvaluated& p) { p.print(s); return s; }
+
+	const odb::sql::type::SQLType* type_;
+	bool missing_;
+	double value_;
+	double missingValue_;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+
+#endif
diff --git a/odb_api/src/odb_api/SQLIndex.cc b/odb_api/src/odb_api/SQLIndex.cc
new file mode 100755
index 0000000..a05d3e6
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIndex.cc
@@ -0,0 +1,119 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include "eckit/log/Timer.h"
+
+#include "odb_api/SQLIndex.h"
+//#include "odb_api/SQLColumn.h"
+//#include "odb_api/SQLTable.h"
+
+namespace odb {
+namespace sql {
+
+struct _data {
+	unsigned long offset_;
+	unsigned long pool_;
+};
+
+
+SQLIndex::SQLIndex(SQLColumn& owner):
+	owner_(owner),
+	value_(0)
+{} 
+
+SQLIndex::~SQLIndex() {}
+
+void SQLIndex::update()
+{
+#if 0
+	PathName path  = owner_.indexPath();
+
+
+	Log::info() << "Loading index " << path << std::endl;
+
+	owner_.rewind();
+    bool create = !path.exists();
+
+
+	if(create)
+	{
+		BeDB db(path,true);
+
+		_data d;
+		Timer timer("Create index");
+
+		unsigned long long rows = owner_.noRows();
+
+		for(unsigned long long i = 0; i < rows; i++)
+		{
+			bool missing = false;
+			d.offset_    = owner_.offset();
+			d.pool_      = owner_.currentPool();
+			double value = owner_.next(missing);
+
+			db.add(&value,sizeof(value),&d,sizeof(d));
+
+		}
+	}
+
+	auto_ptr<BeDB> db(new BeDB(path,false));
+	db_ = db;
+#endif
+}
+
+void SQLIndex::rewind(double* value)
+{
+#if 0
+	value_ = value;
+	db_->rewind();
+//	std::cout << "SQLIndex::rewind " << *value << std::endl;
+#endif
+}
+
+bool SQLIndex::next(unsigned long& pool,unsigned long& offset)
+{
+#if 0
+	_data d;
+	size_t size = sizeof(d);
+
+//	std::cout << "SQLIndex::next " << value_ << std::endl;
+
+//	for(;;)
+	{
+		if(db_->get(value_,sizeof(*value_),&d,size))
+		{
+//			if(pool_ < 0 || (pool_ == d.pool_ && d.offset_ >= offset1_ && d.offset_ <= offset2_))
+			{
+//				std::cout << "SQLIndex::next " << d.pool_ << " " << d.offset_ << std::endl;
+				pool   = d.pool_;
+				offset = d.offset_;
+				return true;
+			}
+		}
+		else
+			return false;
+	}
+
+#endif
+return false;
+}
+
+#if 0
+void SQLIndex::restrict(unsigned long pool,unsigned long offset,unsigned long length)
+{
+	pool_    = pool;
+	offset1_ = offset;
+	offset2_ = offset + length - 1;
+	std::cout << "Restrict " << pool_ << " " << offset1_ << " " << offset2_ << std::endl;
+}
+#endif
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLIndex.h b/odb_api/src/odb_api/SQLIndex.h
new file mode 100755
index 0000000..5ba5df4
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIndex.h
@@ -0,0 +1,115 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLIndex.h
+// Baudouin Raoult - ECMWF Jan 04
+
+#ifndef SQLIndex_H
+#define SQLIndex_H
+
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLColumn;
+
+class SQLIndex {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	SQLIndex(SQLColumn&);
+
+// -- Destructor
+
+	~SQLIndex(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+
+	void update();
+
+	void rewind(double* value);
+	bool next(unsigned long&,unsigned long&);
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	// void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	SQLIndex(const SQLIndex&);
+	SQLIndex& operator=(const SQLIndex&);
+
+// -- Members
+
+	SQLColumn&           owner_;
+#if 0
+	auto_ptr<BeDB> db_;
+#endif
+	double*              value_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLIndex& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLInsert.cc b/odb_api/src/odb_api/SQLInsert.cc
new file mode 100755
index 0000000..f9d24a7
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInsert.cc
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ecml/core/ExecutionContext.h"
+#include "eckit/types/Types.h"
+
+#include "odb_api/SQLInsert.h"
+
+namespace odb {
+namespace sql {
+
+SQLInsert::SQLInsert(const InsertAST& a) 
+: table_(a.table_),
+  columns_(a.columns_),
+  values_(a.values_)
+{}
+
+SQLInsert::~SQLInsert() {}
+
+void SQLInsert::print(std::ostream& s) const {}
+
+unsigned long long SQLInsert::execute(ecml::ExecutionContext* context)
+{
+    NOTIMP;
+    return 0; // TODO:
+}
+
+expression::Expressions SQLInsert::output() const
+{
+    NOTIMP;
+}
+
+} // namespace sql 
+} // namespace odb 
+
+
diff --git a/odb_api/src/odb_api/SQLInsert.h b/odb_api/src/odb_api/SQLInsert.h
new file mode 100755
index 0000000..b0e60b0
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInsert.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLInsert.h
+// Piotr Kuchta - ECMWF July 2016
+
+#ifndef SQLInsert_H
+#define SQLInsert_H
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLAST.h"
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLDatabase;
+
+class SQLInsert : public SQLStatement {
+public:
+	SQLInsert(const InsertAST&);
+	virtual ~SQLInsert(); 
+
+	virtual unsigned long long execute(ecml::ExecutionContext*);
+	virtual expression::Expressions output() const;
+
+
+    const Table& table() const { return table_; }
+    const std::vector<std::string>& columns() const { return columns_; }
+    const std::vector<std::string>& values() const { return values_; }
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLInsert(const SQLInsert&);
+	SQLInsert& operator=(const SQLInsert&);
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLInsert& p)
+		{ p.print(s); return s; }
+
+    Table table_;
+    std::vector<std::string> columns_;
+    std::vector<std::string> values_;
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLInsertFactory.cc b/odb_api/src/odb_api/SQLInsertFactory.cc
new file mode 100644
index 0000000..bcee9ae
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInsertFactory.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+
+
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLInsertFactory.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/Writer.h"
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLInsert.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+SQLInsertFactory::SQLInsertFactory()
+{}
+
+SQLInsert* SQLInsertFactory::create (SQLSession& session, const InsertAST& a)
+{
+    return new SQLInsert(a);
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLInsertFactory.h b/odb_api/src/odb_api/SQLInsertFactory.h
new file mode 100644
index 0000000..72c7bcf
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInsertFactory.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLInsert.h
+// Piotr Kuchta - ECMWF July 2016
+
+#ifndef SQLInsertFactory_H
+#define SQLInsertFactory_H
+
+#include "eckit/thread/ThreadSingleton.h"
+#include "Expressions.h"
+#include "SQLOutputConfig.h"
+#include "SchemaAnalyzer.h"
+#include "SQLAST.h"
+#include "SQLInsert.h"
+
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+namespace odb { namespace sql { class DataTable; } }
+namespace odb { namespace sql { class SQLDatabase; } }
+
+namespace odb { 
+namespace sql {
+
+class SQLSession;
+
+class SQLInsertFactory {
+public:
+    SQLInsertFactory();
+
+	SQLInsert* create(odb::sql::SQLSession& session, const InsertAST&);
+
+private:
+    // No copy allowed
+    SQLInsertFactory(const SQLInsertFactory&);
+    SQLInsertFactory& operator=(const SQLInsertFactory&);
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLInt.cc b/odb_api/src/odb_api/SQLInt.cc
new file mode 100644
index 0000000..d255fb4
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInt.cc
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include "odb_api/SQLInt.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLInt::SQLInt(const std::string& name): SQLType(name) {} 
+
+SQLInt::~SQLInt() {}
+
+size_t SQLInt::size() const { return sizeof(long); }
+
+void SQLInt::output(SQLOutput& o, double d, bool missing) const { o.outputInt(d, missing); }
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLInt.h b/odb_api/src/odb_api/SQLInt.h
new file mode 100644
index 0000000..7eb8472
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInt.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLInt.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLInt_H
+#define SQLInt_H
+
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace type {
+
+class SQLInt : public SQLType {
+public:
+	SQLInt(const std::string& );
+	~SQLInt(); 
+
+private:
+// No copy allowed
+	SQLInt(const SQLInt&);
+	SQLInt& operator=(const SQLInt&);
+
+	virtual size_t size() const;
+	virtual void output(SQLOutput& s, double, bool) const;
+	virtual int getKind() const { return integerType; }
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLInt& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLInteractiveSession.cc b/odb_api/src/odb_api/SQLInteractiveSession.cc
new file mode 100755
index 0000000..c5f4d48
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInteractiveSession.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLSimpleOutput.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace sql {
+
+SQLInteractiveSession::SQLInteractiveSession(std::ostream &out)
+: SQLSession(odb::sql::SQLOutputConfig::defaultConfig(), ","),
+  out_(out),
+  sql_(0)
+{
+    loadDefaultSchema();
+}
+
+SQLInteractiveSession::~SQLInteractiveSession()
+{}
+
+SQLOutput* SQLInteractiveSession::defaultOutput()
+{
+	return new SQLSimpleOutput(out_);
+}
+
+void SQLInteractiveSession::statement(SQLStatement *sql)
+{
+	ASSERT(sql);
+    sql_ = sql;
+}
+
+SQLStatement *SQLInteractiveSession::statement()
+{
+    return sql_;
+}
+
+void SQLInteractiveSession::interactive()
+{
+    typedef odb::sql::SQLStatement* P;
+    if (gotSelectAST())
+    {
+        gotSelectAST(false);
+        sql_ = P(selectFactory().create(*this, selectAST()));
+    }
+    
+    if (sql_)
+    {
+        ecml::ExecutionContext context; // TODO
+        execute(*sql_, &context);
+        delete sql_;
+        sql_ = 0;
+    }
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLInteractiveSession.h b/odb_api/src/odb_api/SQLInteractiveSession.h
new file mode 100755
index 0000000..b3efa07
--- /dev/null
+++ b/odb_api/src/odb_api/SQLInteractiveSession.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLInteractiveSession.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLInteractiveSession_H
+#define SQLInteractiveSession_H
+
+#include "SQLSession.h"
+
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+namespace sql {
+
+class SQLInteractiveSession : public SQLSession {
+public:
+    SQLInteractiveSession(std::ostream & = std::cout);
+	~SQLInteractiveSession(); 
+
+    void interactive();
+
+private:
+// No copy allowed
+	SQLInteractiveSession(const SQLInteractiveSession&);
+	SQLInteractiveSession& operator=(const SQLInteractiveSession&);
+
+    std::ostream &out_;
+    SQLStatement* sql_;
+
+// -- Overridden methods
+	void statement(SQLStatement*);
+	SQLStatement* statement();
+	SQLOutput* defaultOutput();
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLInteractiveSession& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLIterator.h b/odb_api/src/odb_api/SQLIterator.h
new file mode 100755
index 0000000..5e0af65
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIterator.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLIterator
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLIterator_H
+#define SQLIterator_H
+
+#include "eckit/memory/MemoryPool.h"
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+
+// Forward declarations
+class SQLIterator {
+public:
+
+    void *operator new(size_t s)          { return eckit::MemoryPool::fastAllocate(s);}
+	//void *operator new(size_t s,void *p)  { return p;                          }
+    void operator delete(void* p)         { eckit::MemoryPool::fastDeallocate(p);     }
+
+	SQLIterator(const type::SQLType& type): type_(type) {}
+
+    virtual ~SQLIterator() {} 
+
+	const type::SQLType& type() const { return type_; }
+
+	virtual void rewind()  = 0;
+	virtual double next(bool& missing)                = 0;
+	virtual void advance(unsigned long) = 0;
+
+	virtual void load()   {}
+	virtual void unload() {}
+
+protected:
+	const type::SQLType& type_;
+	
+	virtual void print(std::ostream&) const = 0; 	
+
+private:
+// No copy allowed
+
+	SQLIterator(const SQLIterator&);
+	SQLIterator& operator=(const SQLIterator&);
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLIterator& p)
+		{ p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLIteratorOutput.cc b/odb_api/src/odb_api/SQLIteratorOutput.cc
new file mode 100755
index 0000000..27089c5
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIteratorOutput.cc
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+
+template <typename T>
+SQLIteratorOutput<T>::SQLIteratorOutput(T& it)
+: iterator_(it),
+  count_(0)
+{}
+
+template <typename T>
+SQLIteratorOutput<T>::~SQLIteratorOutput() {}
+
+template <typename T>
+void SQLIteratorOutput<T>::print(std::ostream& s) const
+{
+	s << "SQLIteratorOutput";
+}
+
+template <typename T>
+void SQLIteratorOutput<T>::size(int) {}
+
+template <typename T>
+void SQLIteratorOutput<T>::reset() { count_ = 0; }
+
+template <typename T>
+void SQLIteratorOutput<T>::flush(ecml::ExecutionContext*) {}
+
+template <typename T>
+bool SQLIteratorOutput<T>::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+	size_t nCols = results.size();
+	///ASSERT(nCols == iterator_.columns().size());
+	if (iterator_.isCachingRows())
+		iterator_.cacheRow(results);
+	else
+		for(size_t i = 0; i < nCols; i++)
+		{
+			bool missing = false;
+			iterator_.data_[i] = results[i]->eval(missing /*=false*/);
+		}
+	count_++;
+	return true;
+}
+
+template <typename T>
+void SQLIteratorOutput<T>::prepare(SQLSelect& sql) {}
+
+template <typename T>
+void SQLIteratorOutput<T>::cleanup(SQLSelect& sql) {}
+
+template <typename T>
+unsigned long long SQLIteratorOutput<T>::count() { return count_; }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLIteratorOutput.h b/odb_api/src/odb_api/SQLIteratorOutput.h
new file mode 100755
index 0000000..ababe26
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIteratorOutput.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLIteratorOutput.h
+/// Piotr Kuchta - ECMWF Feb 09
+
+#ifndef SQLIteratorOutput_H
+#define SQLIteratorOutput_H
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+
+class SelectIterator;
+
+namespace sql {
+
+class ReaderIterator;
+
+template <typename T = odb::SelectIterator>
+class SQLIteratorOutput : public SQLOutput {
+public:
+	SQLIteratorOutput(T &);
+	virtual ~SQLIteratorOutput(); 
+
+protected:
+
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLIteratorOutput(const SQLIteratorOutput&);
+	SQLIteratorOutput& operator=(const SQLIteratorOutput&);
+
+// -- Members
+	T& iterator_;
+	//bool headerSaved = true;
+	unsigned long long count_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+	virtual void outputReal(double, bool) { NOTIMP; };
+	virtual void outputDouble(double, bool) { NOTIMP; };
+	virtual void outputInt(double, bool) { NOTIMP; };
+	virtual void outputUnsignedInt(double, bool) { NOTIMP; };
+	virtual void outputString(double, bool) { NOTIMP; };
+	virtual void outputBitfield(double, bool) { NOTIMP; };
+};
+
+} // namespace sql
+} // namespace odb
+
+#include "odb_api/SQLIteratorOutput.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/SQLIteratorSession.cc b/odb_api/src/odb_api/SQLIteratorSession.cc
new file mode 100755
index 0000000..0a86c35
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIteratorSession.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SelectIterator.h"
+#include "odb_api/SQLIteratorOutput.h"
+#include "odb_api/SQLIteratorSession.h"
+
+namespace odb {
+namespace sql {
+
+SQLIteratorSession::SQLIteratorSession(SelectIterator &it, SQLSession& s)
+: SQLSession(s.outputConfig(), s.csvDelimiter()),
+  statement_(0),
+  iterator_(it),
+  session_(s)
+{}
+
+SQLIteratorSession::~SQLIteratorSession() {}
+
+SQLOutput* SQLIteratorSession::defaultOutput() { return new SQLIteratorOutput<>(iterator_); }
+
+void SQLIteratorSession::statement(odb::sql::SQLStatement *sql)
+{
+	ASSERT(sql);
+	statement_ = sql;
+}
+
+SQLStatement* SQLIteratorSession::statement()
+{
+    typedef odb::sql::SQLStatement* P;
+
+    if (session_.gotSelectAST())
+    {
+        session_.gotSelectAST(false);
+        statement_ = P(session_.selectFactory().create(*this, session_.selectAST()));
+    }
+    return statement_;
+}
+
+
+SQLDatabase& SQLIteratorSession::openDatabase(const eckit::PathName& p,const std::string& name) { return session_.openDatabase(p, name); }
+void SQLIteratorSession::closeDatabase(const std::string& name) { session_.closeDatabase(name); }
+
+void SQLIteratorSession::createIndex(const std::string& a, const std::string& b) { session_.createIndex(a,b); }
+
+SQLDatabase* SQLIteratorSession::getDatabase(const std::string& name) { return session_.getDatabase(name); }
+
+SQLSelectFactory& SQLIteratorSession::selectFactory() { return session_.selectFactory(); }
+SQLInsertFactory& SQLIteratorSession::insertFactory() { return session_.insertFactory(); }
+
+SQLTable* SQLIteratorSession::findTable(const odb::sql::Table& t) { return session_.findTable(t); }
+
+SQLTable* SQLIteratorSession::openDataHandle(eckit::DataHandle &h) { return session_.openDataHandle(h); }
+SQLTable* SQLIteratorSession::openDataStream(std::istream &is, const std::string &s) { return session_.openDataStream(is, s); }
+
+void SQLIteratorSession::statement(const SelectAST& s) { session_.statement(s); }
+//void SQLIteratorSession::statement(SQLStatement* s) { session_.statement(s); }
+//SQLStatement* SQLIteratorSession::statement() { return session_.statement(); }
+//SQLOutput* SQLIteratorSession::defaultOutput() { return session_.defaultOutput(); }
+
+SQLDatabase& SQLIteratorSession::currentDatabase() const { return session_.currentDatabase(); }
+SQLDatabase& SQLIteratorSession::currentDatabase(SQLDatabase* s) { return session_.currentDatabase(s); }
+
+unsigned long long SQLIteratorSession::execute(SQLStatement& s, ecml::ExecutionContext* e) { return session_.execute(s,e); }
+
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/SQLIteratorSession.h b/odb_api/src/odb_api/SQLIteratorSession.h
new file mode 100755
index 0000000..5211223
--- /dev/null
+++ b/odb_api/src/odb_api/SQLIteratorSession.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLIteratorSession.h
+// Piotr Kuchta - ECMWF Feb 09
+
+#ifndef SQLIteratorSession_H
+#define SQLIteratorSession_H
+
+#include "odb_api/SQLSession.h"
+
+namespace odb {
+
+class SelectIterator;
+
+namespace sql {
+
+class SQLIteratorSession : public SQLSession {
+public:
+	SQLIteratorSession(odb::SelectIterator &, SQLSession&);
+	~SQLIteratorSession(); 
+
+// -- Overridden methods
+
+	SQLDatabase& openDatabase(const eckit::PathName&,const std::string& name = "");
+	void closeDatabase(const std::string& name);
+
+	void createIndex(const std::string&,const std::string&);
+
+	SQLDatabase* getDatabase(const std::string& name);
+
+    SQLSelectFactory& selectFactory();
+    SQLInsertFactory& insertFactory();
+
+	SQLTable* findTable(const odb::sql::Table&);
+
+	SQLTable* openDataHandle(eckit::DataHandle &);
+    SQLTable* openDataStream(std::istream &, const std::string &);
+
+	void statement(const SelectAST& s);
+	void statement(SQLStatement*);
+	SQLStatement* statement();
+	SQLOutput* defaultOutput();
+
+	SQLDatabase& currentDatabase() const;
+	SQLDatabase& currentDatabase(SQLDatabase*);
+
+	unsigned long long execute(SQLStatement&, ecml::ExecutionContext*);
+
+private:
+// No copy allowed
+	SQLIteratorSession(const SQLIteratorSession&);
+	SQLIteratorSession& operator=(const SQLIteratorSession&);
+
+	SQLStatement* statement_;
+	odb::SelectIterator& iterator_;
+    SQLSession& session_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLMATCHSubquerySession.cc b/odb_api/src/odb_api/SQLMATCHSubquerySession.cc
new file mode 100755
index 0000000..049848d
--- /dev/null
+++ b/odb_api/src/odb_api/SQLMATCHSubquerySession.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLMATCHSubquerySession.h"
+#include "odb_api/FunctionMATCH.h"
+#include "odb_api/SQLMATCHSubquerySessionOutput.h"
+
+namespace odb {
+namespace sql {
+
+SQLMATCHSubquerySession::SQLMATCHSubquerySession(expression::function::FunctionMATCH& f)
+: SQLSession(odb::sql::SQLOutputConfig::defaultConfig(), ","),
+  statement_(0),
+  f_(f)
+{
+    loadDefaultSchema();
+}
+
+SQLMATCHSubquerySession::~SQLMATCHSubquerySession() {}
+
+SQLOutput* SQLMATCHSubquerySession::defaultOutput()
+{
+	return new SQLMATCHSubquerySessionOutput(f_);
+}
+
+void SQLMATCHSubquerySession::statement(odb::sql::SQLStatement *sql)
+{
+	ASSERT(sql);	
+	statement_ = sql;
+}
+
+SQLStatement* SQLMATCHSubquerySession::statement()
+{
+    typedef odb::sql::SQLStatement* P;
+    if (gotSelectAST())
+    {
+        gotSelectAST(false);
+        statement_ = P(selectFactory().create(*this, selectAST()));
+    }
+    return statement_;
+}
+
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/SQLMATCHSubquerySession.h b/odb_api/src/odb_api/SQLMATCHSubquerySession.h
new file mode 100755
index 0000000..f5501c1
--- /dev/null
+++ b/odb_api/src/odb_api/SQLMATCHSubquerySession.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLMATCHSubquerySession.h
+// Piotr Kuchta - ECMWF Octover 2015
+
+#ifndef SQLMATCHSubquerySession_H
+#define SQLMATCHSubquerySession_H
+
+#include "odb_api/SQLSession.h"
+
+namespace eckit { class ExecutionContext; }
+namespace odb { class ReaderIterator; }
+namespace odb { class SelectIterator; }
+namespace odb { namespace sql { namespace expression { namespace function { class FunctionMATCH; } } } }
+
+namespace odb {
+namespace sql {
+
+class SQLMATCHSubquerySession : public SQLSession {
+public:
+	SQLMATCHSubquerySession(expression::function::FunctionMATCH &);
+	~SQLMATCHSubquerySession(); 
+
+	SQLStatement* statement();
+
+private:
+// No copy allowed
+	SQLMATCHSubquerySession(const SQLMATCHSubquerySession&);
+	SQLMATCHSubquerySession& operator=(const SQLMATCHSubquerySession&);
+
+	SQLStatement* statement_;
+    expression::function::FunctionMATCH& f_;
+
+// -- Overridden methods
+	void statement(SQLStatement*);
+	SQLOutput* defaultOutput();
+
+// -- Friends
+	//friend std::ostream& operator<<(std::ostream& s,const SQLMATCHSubquerySession& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.cc b/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.cc
new file mode 100755
index 0000000..a34e355
--- /dev/null
+++ b/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.cc
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLMATCHSubquerySessionOutput.h"
+#include "odb_api/FunctionMATCH.h"
+
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+SQLMATCHSubquerySessionOutput::SQLMATCHSubquerySessionOutput(const SQLMATCHSubquerySessionOutput& other)
+: f_(other.f_),
+  count_(0)
+{}
+
+SQLMATCHSubquerySessionOutput::SQLMATCHSubquerySessionOutput(odb::sql::expression::function::FunctionMATCH& f)
+: f_(f),
+  count_(0)
+{}
+
+SQLMATCHSubquerySessionOutput& SQLMATCHSubquerySessionOutput::operator=(const SQLMATCHSubquerySessionOutput& other)
+{
+     f_ = other.f_;
+     count_ = other.count_;
+     return *this;
+}
+
+SQLMATCHSubquerySessionOutput::~SQLMATCHSubquerySessionOutput() {}
+
+void SQLMATCHSubquerySessionOutput::print(std::ostream& s) const
+{
+	s << "SQLMATCHSubquerySessionOutput";
+}
+
+void SQLMATCHSubquerySessionOutput::size(int) {}
+
+void SQLMATCHSubquerySessionOutput::reset() { }
+
+void SQLMATCHSubquerySessionOutput::flush(ecml::ExecutionContext*) {}
+
+bool SQLMATCHSubquerySessionOutput::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+	const size_t nCols (results.size());
+    vector<double> v(nCols);
+    for(size_t i(0); i < nCols; ++i)
+    {
+        bool missing (false);
+        v[i] = results[i]->eval(missing);
+    }
+    f_.collect(v);
+    ++count_;
+	return true;
+}
+
+unsigned long long SQLMATCHSubquerySessionOutput::count() { return count_; }
+
+void SQLMATCHSubquerySessionOutput::prepare(SQLSelect& sql) {}
+
+void SQLMATCHSubquerySessionOutput::cleanup(SQLSelect& sql) {}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.h b/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.h
new file mode 100755
index 0000000..b33e522
--- /dev/null
+++ b/odb_api/src/odb_api/SQLMATCHSubquerySessionOutput.h
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLMATCHSubquerySessionOutput.h
+/// Piotr Kuchta - ECMWF Feb 09
+
+#ifndef SQLMATCHSubquerySessionOutput_H
+#define SQLMATCHSubquerySessionOutput_H
+
+#include "odb_api/SQLOutput.h"
+
+class SelectIterator;
+namespace odb { namespace sql { namespace expression { namespace function { class FunctionMATCH; } } } } 
+
+namespace odb {
+namespace sql {
+
+class ReaderIterator;
+
+class SQLMATCHSubquerySessionOutput : public SQLOutput {
+public:
+	SQLMATCHSubquerySessionOutput(odb::sql::expression::function::FunctionMATCH&);
+	SQLMATCHSubquerySessionOutput(const SQLMATCHSubquerySessionOutput&);
+	virtual ~SQLMATCHSubquerySessionOutput(); 
+
+	SQLMATCHSubquerySessionOutput& operator=(const SQLMATCHSubquerySessionOutput&);
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+
+// -- Members
+	odb::sql::expression::function::FunctionMATCH& f_;
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	virtual unsigned long long count();
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+
+	virtual void outputReal(double, bool) { NOTIMP; };
+	virtual void outputDouble(double, bool) { NOTIMP; };
+	virtual void outputInt(double, bool) { NOTIMP; };
+	virtual void outputUnsignedInt(double, bool) { NOTIMP; };
+	virtual void outputString(double, bool) { NOTIMP; };
+	virtual void outputBitfield(double, bool) { NOTIMP; };
+
+private:
+    unsigned long long count_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLNonInteractiveSession.cc b/odb_api/src/odb_api/SQLNonInteractiveSession.cc
new file mode 100755
index 0000000..585b1f4
--- /dev/null
+++ b/odb_api/src/odb_api/SQLNonInteractiveSession.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/SQLNonInteractiveSession.h"
+#include "odb_api/SQLSimpleOutput.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace sql {
+
+SQLNonInteractiveSession::SQLNonInteractiveSession(const odb::sql::SQLOutputConfig& config, const std::string& csvDelimiter)
+: SQLSession(config, csvDelimiter),
+  statement_(0)
+{
+    loadDefaultSchema();
+}
+
+SQLNonInteractiveSession::~SQLNonInteractiveSession()
+{}
+
+SQLOutput* SQLNonInteractiveSession::defaultOutput()
+{
+    return new SQLSimpleOutput(std::cout);
+}
+
+void SQLNonInteractiveSession::statement(SQLStatement *sql)
+{
+    delete statement_;
+    statement_ = sql;
+}
+
+SQLStatement * SQLNonInteractiveSession::statement()
+{
+    typedef odb::sql::SQLStatement* P;
+    if (gotSelectAST())
+    {
+        gotSelectAST(false);
+        statement_ = P(selectFactory().create(*this, selectAST()));
+    }
+    return statement_;
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLNonInteractiveSession.h b/odb_api/src/odb_api/SQLNonInteractiveSession.h
new file mode 100755
index 0000000..e28f5c2
--- /dev/null
+++ b/odb_api/src/odb_api/SQLNonInteractiveSession.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLNonInteractiveSession.h
+// Piotr Kuchta - ECMWF May 2015
+
+#ifndef odb_api_SQLNonInteractiveSession_H
+#define odb_api_SQLNonInteractiveSession_H
+
+#include "odb_api/SQLSession.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutputConfig;
+
+class SQLNonInteractiveSession : public SQLSession {
+public:
+    SQLNonInteractiveSession(const odb::sql::SQLOutputConfig&, const std::string&);
+	~SQLNonInteractiveSession(); 
+
+	SQLStatement* statement();
+private:
+// No copy allowed
+	SQLNonInteractiveSession(const SQLNonInteractiveSession&);
+	SQLNonInteractiveSession& operator=(const SQLNonInteractiveSession&);
+
+    SQLStatement* statement_;
+
+// -- Overridden methods
+	void statement(SQLStatement*);
+	SQLOutput* defaultOutput();
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLODAOutput.cc b/odb_api/src/odb_api/SQLODAOutput.cc
new file mode 100755
index 0000000..8e58193
--- /dev/null
+++ b/odb_api/src/odb_api/SQLODAOutput.cc
@@ -0,0 +1,125 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ColumnType.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLODAOutput.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/Writer.h"
+
+namespace odb {
+namespace sql {
+
+template<typename WRITER>
+SQLODAOutput<WRITER>::SQLODAOutput(WRITER* writer, const MetaData& columns)
+: writer_(writer), it_(writer->begin()), count_(0), metaData_(0)
+{
+    metaData_ = columns;
+}
+
+template<typename WRITER>
+SQLODAOutput<WRITER>::SQLODAOutput(WRITER* writer)
+: writer_(writer), it_(writer->begin()), count_(0), metaData_(0)
+{}
+
+template<typename WRITER>
+SQLODAOutput<WRITER>::~SQLODAOutput()
+{
+    delete writer_;
+}
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::print(std::ostream& s) const
+{ 
+    s << "SQLODAOutput: iterator: " << it_ << " metaData_: " <<  metaData_ << std::endl;;
+}
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::size(int) {}
+
+template<typename WRITER>
+unsigned long long SQLODAOutput<WRITER>::count() { return count_; }
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::reset() { count_ = 0; }
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::flush(ecml::ExecutionContext*) {}
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::cleanup(SQLSelect& sql) { sql.outputFiles((**it_).outputFiles()); }
+
+template<typename WRITER>
+bool SQLODAOutput<WRITER>::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+    size_t nCols (results.size());
+    for(size_t i (0); i < nCols; ++i)
+    {
+        bool missing = false;
+        // TODO: pass the context to it_
+        (*it_)[i] = results[i]->eval(missing);
+    }
+
+    ++it_;
+    ++count_;
+    return true;
+}
+
+template<typename WRITER>
+void SQLODAOutput<WRITER>::prepare(SQLSelect& sql) 
+{
+    const expression::Expressions& columns (sql.output());
+    size_t n (columns.size());
+
+    std::ostream& L(eckit::Log::debug());
+
+    if (metaData_.size()) {
+        L << "SQLODAOutput: Using meta of INTO table" << std::endl;
+        ASSERT(metaData_.size() == n);
+        const_cast<MetaData&>(it_->columns()) = metaData_;
+    }
+    else
+    {
+        const_cast<MetaData&>(it_->columns()).setSize(n);
+        for(size_t i (0); i < n; i++)
+        {
+            SQLExpression& c(*columns[i]);
+            std::string name(c.title());
+            const type::SQLType& type(*c.type());
+            std::string t(type.name());
+            ColumnType typ =
+                t == "integer" ? INTEGER
+                : t == "string" ? STRING
+                : t == "real" ? REAL
+                : t == "double" ? DOUBLE
+                : t.find("Bitfield") == 0 ? BITFIELD
+                : IGNORE;
+
+            if (! (typ == BITFIELD))
+                (**it_).setColumn(i, name, typ);
+            else
+                (**it_).setBitfieldColumn(i, name, typ, c.bitfieldDef());
+
+            (**it_).missingValue(i, c.missingValue());
+        }
+    }
+    (**it_).writeHeader();
+    L << " => SQLODAOutput: " << std::endl << (**it_).columns() << std::endl;
+}
+
+
+// Explicit template instantiations.
+
+template class SQLODAOutput<Writer<> >;
+template class SQLODAOutput<DispatchingWriter>;
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLODAOutput.h b/odb_api/src/odb_api/SQLODAOutput.h
new file mode 100755
index 0000000..15f9bee
--- /dev/null
+++ b/odb_api/src/odb_api/SQLODAOutput.h
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLODAOutput.h
+/// Piotr Kuchta - ECMWF Jan 09
+
+#ifndef odb_api_SQLODAOutput_H
+#define odb_api_SQLODAOutput_H
+
+#include "eckit/exception/Exceptions.h"
+
+#include "odb_api/SQLOutput.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/MetaData.h"
+
+namespace odb {
+namespace sql {
+
+class Reader;
+
+template<typename WRITER>
+class SQLODAOutput : public SQLOutput {
+public:
+	SQLODAOutput(WRITER*);
+	SQLODAOutput(WRITER*, const MetaData&);
+	virtual ~SQLODAOutput(); // Change to virtual if base class
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+	SQLODAOutput(const SQLODAOutput&);
+	SQLODAOutput& operator=(const SQLODAOutput&);
+
+// -- Members
+	WRITER* writer_;
+	typename WRITER::iterator it_;
+    MetaData metaData_;
+
+	unsigned long long count_;
+
+// -- Overridden methods
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+	virtual void outputReal(double, bool) { NOTIMP; };
+	virtual void outputDouble(double, bool) { NOTIMP; };
+	virtual void outputInt(double, bool) { NOTIMP; };
+	virtual void outputUnsignedInt(double, bool) { NOTIMP; };
+	virtual void outputString(double, bool) { NOTIMP; };
+	virtual void outputBitfield(double, bool) { NOTIMP; };
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLOrderOutput.cc b/odb_api/src/odb_api/SQLOrderOutput.cc
new file mode 100755
index 0000000..01bd56f
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOrderOutput.cc
@@ -0,0 +1,116 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLExpressionEvaluated.h"
+#include "odb_api/SQLOrderOutput.h"
+
+namespace odb {
+namespace sql {
+
+SQLOrderOutput::SQLOrderOutput(SQLOutput* output, const std::pair<Expressions,std::vector<bool> >& by)
+: output_(output),
+  by_(by)
+{
+	eckit::Log::debug() << *this << std::endl;
+}
+
+SQLOrderOutput::~SQLOrderOutput()
+{
+	for (SortedResults::iterator it(sortedResults_.begin()); it != sortedResults_.end(); ++it)
+	{
+		VectorOfExpressions& v(it->second);
+		for (size_t i = 0; i < v.size(); ++i)
+		{
+			Expressions& es(v[i]);
+			for (size_t j = 0; j < es.size(); ++j)
+				delete es[j];
+		}
+	}
+}
+
+void SQLOrderOutput::print(std::ostream& s) const
+{
+	s << "SQLOrderOutput[" << *output_ << " ORDER BY ";
+	for(size_t i = 0; i < by_.first.size(); i++)
+		s << *(by_.first[i]) << (by_.second[i] ? " ASC " : " DESC ") << ", ";
+	s << "]";
+}
+
+void SQLOrderOutput::size(int count) { output_->size(count); }
+
+unsigned long long SQLOrderOutput::count() { return output_->count(); }
+
+void SQLOrderOutput::reset() { output_->reset(); }
+
+void SQLOrderOutput::flush(ecml::ExecutionContext* context)
+{
+	for (SortedResults::iterator it = sortedResults_.begin(); it != sortedResults_.end(); ++it)
+	{
+		std::vector<Expressions>& rows = it->second;
+		for (size_t i = 0; i < rows.size(); ++i)
+			output_->output(rows[i], context);
+	}
+	output_->flush(context);
+}
+
+bool SQLOrderOutput::output(const Expressions& results, ecml::ExecutionContext* context)
+{
+	OrderByExpressions byValues(by_.second);
+    Expressions& byExpressions(by_.first);
+	for (size_t i = 0; i < byExpressions.size(); ++i)
+		byValues.push_back(new SQLExpressionEvaluated(
+            byIndices_[i]
+            ? *results[byIndices_[i] - 1]
+            : *byExpressions[i]));
+
+	Expressions resultValues;
+	for (size_t i = 0; i < results.size(); ++i)
+		resultValues.push_back(new SQLExpressionEvaluated(*results[i]));
+
+	sortedResults_[byValues].push_back(resultValues);
+	return false;
+}
+
+void SQLOrderOutput::prepare(SQLSelect& sql)
+{
+	output_->prepare(sql);
+    Expressions& ex(by_.first);
+    for(size_t i(0); i < ex.size(); ++i)
+    {
+        if (! ex[i]->isConstant())
+        {
+            ex[i]->prepare(sql);
+            byIndices_.push_back(0);
+        }
+        else
+        {
+            bool missing(false);
+            size_t index(ex[i]->eval(missing));
+            ASSERT(! missing);
+            if (index < 1) throw eckit::UserError("ORDER BY: indices of columns must be positive");
+            byIndices_.push_back(index);
+        } 
+    }
+}
+
+void SQLOrderOutput::cleanup(SQLSelect& sql)
+{
+	output_->cleanup(sql);
+	for(Expressions::iterator j = by_.first.begin(); j != by_.first.end() ; ++j)
+		(*j)->cleanup(sql);
+}
+
+const SQLOutputConfig& SQLOrderOutput::config() { return output_->config(); }
+void SQLOrderOutput::config(SQLOutputConfig& cfg) { output_->config(cfg); }
+
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLOrderOutput.h b/odb_api/src/odb_api/SQLOrderOutput.h
new file mode 100755
index 0000000..2b5de94
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOrderOutput.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLOrderOutput.h
+/// Piotr Kuchta - ECMWF Nov 11
+
+#ifndef SQLOrderOutput_H
+#define SQLOrderOutput_H
+
+#include "odb_api/Expressions.h"
+#include "odb_api/OrderByExpressions.h"
+#include "odb_api/SQLOutput.h"
+
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+namespace sql {
+
+class SQLOrderOutput : public SQLOutput {
+public:
+    SQLOrderOutput(SQLOutput* output, const std::pair<Expressions,std::vector<bool> >& by);
+	virtual ~SQLOrderOutput();
+
+protected:
+	virtual void print(std::ostream&) const;
+
+private:
+// No copy allowed
+	SQLOrderOutput(const SQLOrderOutput&);
+	SQLOrderOutput& operator=(const SQLOrderOutput&);
+
+// -- Members
+    std::auto_ptr<SQLOutput> output_;
+	std::pair<Expressions,std::vector<bool> > by_;
+	
+	typedef std::map<OrderByExpressions, VectorOfExpressions> SortedResults;
+
+	SortedResults sortedResults_;
+    std::vector<size_t> byIndices_;
+
+// -- Overridden methods
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+	virtual void outputReal(double, bool) { NOTIMP; };
+	virtual void outputDouble(double, bool) { NOTIMP; };
+	virtual void outputInt(double, bool) { NOTIMP; };
+	virtual void outputUnsignedInt(double, bool) { NOTIMP; };
+	virtual void outputString(double, bool) { NOTIMP; };
+	virtual void outputBitfield(double, bool) { NOTIMP; };
+
+	virtual const SQLOutputConfig& config();
+	virtual void config(SQLOutputConfig&);
+
+	friend std::ostream& operator<<(std::ostream& s, const SQLOrderOutput& o)
+		{ o.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLOutput.cc b/odb_api/src/odb_api/SQLOutput.cc
new file mode 100755
index 0000000..9cabe02
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOutput.cc
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+
+SQLOutput::SQLOutput() {}
+
+SQLOutput::~SQLOutput() {}
+
+void SQLOutput::print(std::ostream& s) const
+{
+	s << "SQLOutput" << std::endl;
+}
+
+const SQLOutputConfig& SQLOutput::config() { return config_; }
+
+void SQLOutput::config(SQLOutputConfig& cfg) { config_ = cfg; }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLOutput.h b/odb_api/src/odb_api/SQLOutput.h
new file mode 100755
index 0000000..64f5cfe
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOutput.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLOutput.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef odb_api_SQLOutput_H
+#define odb_api_SQLOutput_H
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace sql {
+
+namespace expression {
+class Expressions;
+}
+
+class SQLSelect;
+
+class SQLOutput {
+public:
+	SQLOutput();
+	virtual ~SQLOutput(); 
+
+	virtual void size(int) = 0;
+
+	virtual void prepare(SQLSelect&) = 0;
+	virtual void cleanup(SQLSelect&) = 0;
+
+	virtual void reset() = 0;
+	virtual void flush(ecml::ExecutionContext*) = 0;
+
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*) = 0;
+
+	virtual void outputReal(double, bool) = 0;
+	virtual void outputDouble(double, bool) = 0;
+	virtual void outputInt(double, bool) = 0;
+	virtual void outputUnsignedInt(double, bool) = 0;
+	virtual void outputString(double, bool) = 0;
+	virtual void outputBitfield(double, bool) = 0;
+
+	virtual const SQLOutputConfig& config();
+	virtual	void config(SQLOutputConfig&);
+
+	virtual unsigned long long count() = 0;
+
+protected:
+	SQLOutputConfig config_;
+
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLOutput(const SQLOutput&);
+	SQLOutput& operator=(const SQLOutput&);
+// -- Friends
+	friend std::ostream& operator<<(std::ostream& s,const SQLOutput& p)
+		{ p.print(s); return s; }
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLOutputConfig.cc b/odb_api/src/odb_api/SQLOutputConfig.cc
new file mode 100755
index 0000000..136a179
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOutputConfig.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace sql {
+
+SQLOutputConfig::SQLOutputConfig(const SQLOutputConfig& that)
+: doNotWriteColumnNames_(that.doNotWriteColumnNames_),
+  doNotWriteNULL_(that.doNotWriteNULL_),
+  fieldDelimiter_(that.fieldDelimiter_),
+  outputFile_(that.outputFile_),
+  outputFormat_(that.outputFormat_),
+  displayBitfieldsBinary_(that.displayBitfieldsBinary_),
+  displayBitfieldsHexadecimal_(that.displayBitfieldsHexadecimal_),      
+  disableAlignmentOfColumns_(that.disableAlignmentOfColumns_)
+{}
+
+SQLOutputConfig& SQLOutputConfig::operator=(const SQLOutputConfig& that)
+{
+    doNotWriteColumnNames_ = that.doNotWriteColumnNames_;
+    doNotWriteNULL_ = that.doNotWriteNULL_;
+    fieldDelimiter_ = that.fieldDelimiter_;
+    outputFile_ = that.outputFile_;
+    outputFormat_ = that.outputFormat_;
+    displayBitfieldsBinary_ = that.displayBitfieldsBinary_;
+    displayBitfieldsHexadecimal_ = that.displayBitfieldsHexadecimal_;        
+    disableAlignmentOfColumns_ = that.disableAlignmentOfColumns_;
+    fullPrecision_ = that.fullPrecision_;
+    return *this;
+}
+
+SQLOutputConfig::SQLOutputConfig(bool cn,
+                bool n,
+                const std::string& d,
+                const std::string& output,
+                const std::string& format,
+                bool displayBitfieldsBinary,
+                bool displayBitfieldsHexadecimal,                    
+                bool disableAlignmentOfColumns,
+                bool fullPrecision)
+: doNotWriteColumnNames_(cn),
+  doNotWriteNULL_(n),
+  fieldDelimiter_(d),
+  outputFile_(output),
+  outputFormat_(format),
+  displayBitfieldsBinary_(displayBitfieldsBinary),
+  displayBitfieldsHexadecimal_(displayBitfieldsHexadecimal),
+  disableAlignmentOfColumns_(disableAlignmentOfColumns),
+  fullPrecision_(fullPrecision)
+{}
+
+bool SQLOutputConfig::doNotWriteColumnNames () const { return doNotWriteColumnNames_; }
+void SQLOutputConfig::doNotWriteColumnNames(bool b) { doNotWriteColumnNames_ = b; }
+
+bool SQLOutputConfig::doNotWriteNULL () const { return  doNotWriteNULL_; }
+void SQLOutputConfig::doNotWriteNULL (bool b) { doNotWriteNULL_ = b; }
+
+const std::string& SQLOutputConfig::fieldDelimiter() const { return fieldDelimiter_; }
+void SQLOutputConfig::fieldDelimiter(const std::string& d) { fieldDelimiter_ = d; }
+
+const std::string& SQLOutputConfig::outputFile () const { return outputFile_; }
+void SQLOutputConfig::outputFile (const std::string& fn) { outputFile_ = fn; }
+
+const std::string& SQLOutputConfig::outputFormat () const { return outputFormat_; }
+void SQLOutputConfig::outputFormat (const std::string& s) { outputFormat_ = s; }
+
+bool SQLOutputConfig::displayBitfieldsBinary () const { return displayBitfieldsBinary_; }
+void SQLOutputConfig::displayBitfieldsBinary (bool b) { displayBitfieldsBinary_ = b; }
+
+bool SQLOutputConfig::displayBitfieldsHexadecimal () const { return displayBitfieldsHexadecimal_; }
+void SQLOutputConfig::displayBitfieldsHexadecimal (bool b) { displayBitfieldsHexadecimal_ = b; }
+
+bool SQLOutputConfig::disableAlignmentOfColumns () const { return disableAlignmentOfColumns_; }
+void SQLOutputConfig::disableAlignmentOfColumns (bool b) { disableAlignmentOfColumns_ = b; }
+
+bool SQLOutputConfig::fullPrecision() const { return fullPrecision_; }
+void SQLOutputConfig::fullPrecision(bool b) { fullPrecision_ = b; }
+
+const SQLOutputConfig SQLOutputConfig::defaultConfig() { return SQLOutputConfig(); }
+const char* SQLOutputConfig::defaultDelimiter() { return "	"; }
+const char* SQLOutputConfig::defaultOutputFile() { return "output.odb"; }
+const char* SQLOutputConfig::defaultFormat() { return "default"; }
+
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLOutputConfig.h b/odb_api/src/odb_api/SQLOutputConfig.h
new file mode 100755
index 0000000..5ec0993
--- /dev/null
+++ b/odb_api/src/odb_api/SQLOutputConfig.h
@@ -0,0 +1,84 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLOutputConfig.h
+/// Piotr Kuchta - ECMWF Jul 2010
+
+#ifndef odb_api_SQLOutputConfig_H
+#define odb_api_SQLOutputConfig_H
+
+#include <string>
+
+namespace odb {
+namespace sql {
+
+class SQLOutputConfig {
+public:
+
+	SQLOutputConfig(const SQLOutputConfig&);
+	SQLOutputConfig& operator=(const SQLOutputConfig&);
+	SQLOutputConfig(bool cn = false,
+                    bool n = false,
+                    const std::string& d = defaultDelimiter(),
+                    const std::string& output = defaultOutputFile(),
+                    const std::string& format = defaultFormat(),
+                    bool displayBitfieldsBinary = false,
+                    bool displayBitfieldsHexadecimal = false,                    
+                    bool disableAlignmentOfColumns = false,
+                    bool fullPrecision = false);
+	bool doNotWriteColumnNames () const;
+    void doNotWriteColumnNames(bool b);
+
+	bool doNotWriteNULL () const;
+	void doNotWriteNULL (bool b);
+
+    const std::string& fieldDelimiter() const;
+    void fieldDelimiter(const std::string& d);
+
+    const std::string& outputFile () const;
+    void outputFile (const std::string& fn);
+
+    const std::string& outputFormat () const;
+    void outputFormat (const std::string& s);
+
+    bool displayBitfieldsBinary () const;
+    void displayBitfieldsBinary (bool b);
+
+    bool displayBitfieldsHexadecimal () const;
+    void displayBitfieldsHexadecimal (bool b);
+    
+    bool disableAlignmentOfColumns () const;
+    void disableAlignmentOfColumns (bool b);
+
+    bool fullPrecision () const;
+    void fullPrecision (bool);
+
+	static const SQLOutputConfig defaultConfig();
+
+private:
+	bool doNotWriteColumnNames_;
+	bool doNotWriteNULL_;
+    std::string fieldDelimiter_;
+    std::string outputFile_;          // -o
+    std::string outputFormat_;        // -f
+    bool displayBitfieldsBinary_;     // --binary
+    bool displayBitfieldsHexadecimal_; // --hex
+    bool disableAlignmentOfColumns_;  // --no_alignment
+    bool fullPrecision_;              // --full_precision
+
+    static const char* defaultDelimiter();
+    static const char* defaultOutputFile();
+    static const char* defaultFormat();
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLParser.cc b/odb_api/src/odb_api/SQLParser.cc
new file mode 100755
index 0000000..35b31c1
--- /dev/null
+++ b/odb_api/src/odb_api/SQLParser.cc
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/thread/AutoLock.h"
+#include "eckit/thread/Mutex.h"
+#include "odb_api/BitColumnExpression.h"
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/Dictionary.h"
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/NumberExpression.h"
+#include "odb_api/ParameterExpression.h"
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLExpression.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLInsertFactory.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLEmbedded.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/StringExpression.h"
+#include "odb_api/EmbeddedCodeExpression.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/EmbeddedCodeParser.h"
+#include "odb_api/FunctionMATCH.h"
+#include "odb_api/SQLAST.h"
+
+using namespace eckit;
+
+#define  YYDEBUG      1
+
+namespace SQLYacc {
+
+typedef void * odblib_scan_t;
+
+void odblib_error(odblib_scan_t, odb::sql::SQLSession*, const char* msg);
+
+using namespace odb;
+using namespace odb::sql;
+using namespace odb::sql::expression;
+using namespace odb::sql::expression::function;
+
+#include "odb_api/odblib_lex.h"
+#include "odb_api/sqly.c"
+
+void odblib_error(odblib_scan_t scanner, odb::sql::SQLSession*, const char* msg)
+{
+    std::stringstream os;
+
+    struct odblib_guts_t * odblib_g = (struct odblib_guts_t*) scanner;
+    // internally we count the lines from 0...
+    int lineNumber (1 + odblib_g->odblib_lineno_r);
+
+    os << "SQL "
+	<< (msg ? msg : "syntax error") 
+    << ", line " << lineNumber // << " of " << yypath;
+    << ". See https://software.ecmwf.int/wiki/display/ODBAPI/SQL\n";
+	throw SyntaxError(os.str()); 
+}
+
+} // namespace SQLYacc
+
+SQLYacc::Stack& includeStack(void* odblib_scanner)
+{
+    SQLYacc::Stack* stack (static_cast<SQLYacc::Stack*>(((struct SQLYacc::odblib_guts_t*) odblib_scanner)->odblib_extra_r));
+    ASSERT (stack);
+    return *stack;
+}
+
+extern "C" int odblib_wrap(void *scanner)
+{ 
+    return includeStack(scanner).pop(scanner); 
+}
+
+namespace odb {
+namespace sql {
+
+struct SessionResetter {
+    SessionResetter (SQLSession& s) : session_(s), resetSession_(true) {}
+    SessionResetter (SQLSession& s, bool r) : session_(s), resetSession_(r) {}
+    ~SessionResetter () 
+    {
+        if (! resetSession_)
+            return;
+#if YY_FLEX_MAJOR_VERSION >= 2
+#if YY_FLEX_MINOR_VERSION >= 5
+#if YY_FLEX_SUBMINOR_VERSION >=33
+    //SQLYacc::yylex_destroy(); 
+#endif
+#endif
+#endif
+        session_.selectFactory().implicitFromTableSource(0);
+        session_.selectFactory().implicitFromTableSourceStream(0);
+        session_.selectFactory().database(0);
+    }
+private:
+    SQLSession& session_;
+    bool resetSession_;
+};
+
+void SQLParser::parseString(odb::sql::SQLSession& session, const std::string& s, std::istream* is, SQLOutputConfig cfg, const std::string& csvDelimiter)
+{
+    SessionResetter ar (session);
+
+    session.selectFactory().implicitFromTableSourceStream(is);
+    session.selectFactory().config(cfg);
+    session.selectFactory().csvDelimiter(csvDelimiter);
+
+    SQLYacc::odblib_scan_t scanner;
+    SQLYacc::odblib_lex_init(&scanner);
+
+    SQLYacc::include_stack stack;
+    SQLYacc::odblib_lex_init_extra(&stack, &scanner);
+
+    stack.push(s, "", (SQLYacc::YY_BUFFER_STATE) scanner, (SQLYacc::odblib_scan_t) scanner); 
+    SQLYacc::odblib_parse(scanner, &session);
+
+    session.statement();
+    session.interactive();
+
+    SQLYacc::odblib_lex_init(&scanner); // TODO: handle unwind
+}
+
+void SQLParser::parseString(odb::sql::SQLSession& session, const std::string& s, DataHandle* dh, SQLOutputConfig cfg, bool resetSession)
+{
+    SessionResetter ar (session, resetSession);
+
+    session.selectFactory().implicitFromTableSource(dh);
+    session.selectFactory().config(cfg);
+
+    SQLYacc::odblib_scan_t scanner;
+    SQLYacc::odblib_lex_init(&scanner);
+
+    SQLYacc::include_stack stack;
+    SQLYacc::odblib_lex_init_extra(&stack, &scanner);
+
+    stack.push(s, "", (SQLYacc::YY_BUFFER_STATE) scanner, (SQLYacc::odblib_scan_t) scanner); 
+    SQLYacc::odblib_parse(scanner, &session);
+
+    session.statement();
+    session.interactive();
+}
+
+void SQLParser::parseString(odb::sql::SQLSession& session,const std::string& s, SQLDatabase& db, SQLOutputConfig cfg)
+{
+    SessionResetter ar (session);
+
+    session.currentDatabase(&db);
+    session.selectFactory().database(&db);
+    session.selectFactory().config(cfg);
+
+    SQLYacc::odblib_scan_t scanner;
+    SQLYacc::odblib_lex_init(&scanner);
+
+    SQLYacc::include_stack stack;
+    SQLYacc::odblib_lex_init_extra(&stack, &scanner);
+
+    stack.push(s, "", (SQLYacc::YY_BUFFER_STATE) scanner, (SQLYacc::odblib_scan_t) scanner); 
+    SQLYacc::odblib_parse(scanner, &session);
+
+    session.statement();
+    session.interactive();
+}
+
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLParser.h b/odb_api/src/odb_api/SQLParser.h
new file mode 100755
index 0000000..e07ca12
--- /dev/null
+++ b/odb_api/src/odb_api/SQLParser.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLParser.h
+// Baudouin Raoult - ECMWF Mar 98
+// Piotr Kuchta - ECMWF Mar 2012
+
+#ifndef odb_api_SQLParser_H
+#define odb_api_SQLParser_H
+
+#include "eckit/exception/Exceptions.h"
+#include "SQLOutputConfig.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+#define ODB_API_HAS_REENTRANT_SQL_PARSER 1
+
+namespace odb {
+namespace sql {
+
+class SQLDatabase;
+class SQLSession;
+
+class SyntaxError : public eckit::Exception {
+public:
+	SyntaxError(const std::string& s): eckit::Exception(s) {}
+};
+
+struct ParseFrame {
+    ParseFrame(const std::string& sql, const std::string& yypath);
+
+    std::string inputString_;
+    std::string yypath_;
+    char* inputText_;
+    char* inputEnd_;
+};
+
+class SQLParser {
+public:
+	static int line();
+
+	static void parseString(odb::sql::SQLSession&, const std::string&, eckit::DataHandle*, SQLOutputConfig, bool resetSession = true);
+    static void parseString(odb::sql::SQLSession&, const std::string&, std::istream*, SQLOutputConfig, const std::string& cvsDelimiter);
+	static void parseString(odb::sql::SQLSession&, const std::string&, SQLDatabase&, SQLOutputConfig);
+
+	static std::stack<ParseFrame> frames_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLReal.cc b/odb_api/src/odb_api/SQLReal.cc
new file mode 100755
index 0000000..bcc58d3
--- /dev/null
+++ b/odb_api/src/odb_api/SQLReal.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLReal.h"
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLReal::SQLReal(const std::string& name): SQLType(name) {}
+
+SQLReal::~SQLReal() {}
+
+size_t SQLReal::size() const { return sizeof(double); }
+
+void SQLReal::output(SQLOutput& o, double d, bool m) const { o.outputReal(d, m); }
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLReal.h b/odb_api/src/odb_api/SQLReal.h
new file mode 100755
index 0000000..effdfea
--- /dev/null
+++ b/odb_api/src/odb_api/SQLReal.h
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLReal.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLReal_H
+#define SQLReal_H
+
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace type {
+
+class SQLReal : public SQLType {
+public:
+
+// -- Exceptions
+	// None
+
+// -- Contructors
+
+	SQLReal(const std::string& );
+
+// -- Destructor
+
+	~SQLReal(); 
+
+// -- Convertors
+	// None
+
+// -- Operators
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	virtual void output(SQLOutput&, double, bool) const;
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+protected:
+
+// -- Members
+	// None
+
+// -- Methods
+	
+	// void print(std::ostream&) const; 	
+
+// -- Overridden methods
+	// None
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+private:
+
+// No copy allowed
+
+	SQLReal(const SQLReal&);
+	SQLReal& operator=(const SQLReal&);
+
+// -- Members
+	// None
+
+// -- Methods
+	// None
+
+// -- Overridden methods
+	// None
+
+	virtual size_t size() const;
+	virtual int getKind() const { return realType; }
+
+// -- Class members
+	// None
+
+// -- Class methods
+	// None
+
+// -- Friends
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLReal& p)
+	//	{ p.print(s); return s; }
+
+};
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLSelect.cc b/odb_api/src/odb_api/SQLSelect.cc
new file mode 100755
index 0000000..c1540fd
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSelect.cc
@@ -0,0 +1,616 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Log.h"
+#include "odb_api/ConstantExpression.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLColumn.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLOutput.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLTable.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+using namespace expression;
+
+void SQLSelect::pushFirstFrame() { env.pushFrame(sortedTables_.begin()); }
+
+SQLSelect::SQLSelect(const Expressions& columns, 
+	const std::vector<SQLTable*>& tables,
+	SQLExpression* where,
+	SQLOutput* output,
+	SQLOutputConfig cfg,
+    bool all)
+: select_(columns),
+  tables_(tables),
+  where_(where),
+  simplifiedWhere_(0),
+  output_(output),
+  total_(0),
+  skips_(0),
+  aggregate_(false),
+  mixedAggregatedAndScalar_(false),
+  outputConfig_(cfg),
+  all_(all)
+{
+	output->config(cfg);
+}
+
+SQLSelect::~SQLSelect()
+{
+	//Log::info() << "SQLSelect::~SQLSelect: " << *this << std::endl;
+	for (size_t i = 0; i < select_.size(); ++i)
+	{
+		//Log::info() << "SQLSelect::~SQLSelect: deleting " << *select_[i] << std::endl;
+		delete select_[i];
+	}
+}
+
+SQLTable* SQLSelect::findTable(const std::string& name,
+	std::string *fullName, bool *hasMissingValue, double *missingValue, bool* isBitfield, BitfieldDef* bitfieldDef) const
+{
+	std::set<SQLTable*> names;
+
+	for(std::vector<SQLTable*>::const_iterator t = tables_.begin(); 
+		t != tables_.end() ; ++t)
+	{
+		SQLTable* table = const_cast<SQLTable*>(*t);
+		if(table->hasColumn(name, fullName))
+		{
+			//if(table->hasColumn(name, fullName, hasMissingValue, missingValue, bitfieldDef))
+			names.insert(table);
+			SQLColumn& column = *table->column(name);
+			if (hasMissingValue) *hasMissingValue = column.hasMissingValue();
+			if (missingValue) *missingValue = column.missingValue();
+			if (isBitfield) *isBitfield = column.isBitfield();
+			if (bitfieldDef) *bitfieldDef = column.bitfieldDef();
+		}
+	}
+
+	if(names.size() == 0)
+		throw eckit::UserError("Can't find a table for", name);
+
+	if(names.size() != 1)
+		throw eckit::UserError("Ambiguous column name", name);
+
+	Log::debug() << "SQLSelect::findTable: name='" << name << "', fullName=" << (fullName ? (std::string("'")+ *fullName+"'") : "") << std::endl;
+
+	return *names.begin();
+}
+
+std::pair<double,bool>* SQLSelect::column(const std::string& name, SQLTable* table)
+{
+
+	if(!table) table = findTable(name);
+	SQLColumn* column = table->column(name);
+
+	std::string full = column->fullName();
+	if(values_.find(full) != values_.end())
+		return &values_[full];
+
+	allTables_.insert(table);
+
+	// Take care of aligned tables
+	SQLTable* master = table->master();
+
+	if(tablesToFetch_.find(master) == tablesToFetch_.end())
+		tablesToFetch_[master] = SelectOneTable(master);
+
+	tablesToFetch_[master].fetch_.push_back(column);
+	tablesToFetch_[master].values_.push_back(&values_[full]);
+
+	Log::debug() << "Accessing column " << full << std::endl;
+
+	return &values_[full];
+
+}
+
+const type::SQLType* SQLSelect::typeOf(const std::string& name, SQLTable* table) const
+{
+	if(!table) table = findTable(name);
+	SQLColumn* column = table->column(name);
+
+	const type::SQLType& type = column->type();
+	return type.subType(name); // This should take care of bitfields
+
+}
+
+static bool compareTables(SelectOneTable* a,SelectOneTable *b)
+{
+//#if 1
+//	if(&(a->table_->owner()) != &(b->table_->owner()))
+		return a->table_->owner().name() < b->table_->owner().name();
+//#else
+//	return a->table_->index() < b->table_->index();
+//#endif
+}
+
+inline bool SQLSelect::resultsOut(ecml::ExecutionContext* context)
+{
+	return output_->output(results_, context);
+}
+
+SQLExpression* SQLSelect::findAliasedExpression(const std::string& alias)
+{
+    for (size_t i(0); i < select_.size(); ++i)
+        if (select_[i]->title() == alias)
+            return select_[i];
+    return 0;
+}
+
+void SQLSelect::prepareExecute() {
+	reset();
+
+	for(Expressions::iterator c = select_.begin(); c != select_.end(); ++c)
+	{
+		if((*c)->isAggregate())
+		{
+			aggregated_.push_back(*c);
+			mixedResultColumnIsAggregated_.push_back(true);
+		}
+		else
+		{
+			nonAggregated_.push_back(*c);
+			mixedResultColumnIsAggregated_.push_back(false);
+		}
+
+		(*c)->prepare(*this);
+
+        Log::debug() << "SQLSelect::prepareExecute: '" << *(*c) << "'" << std::endl;
+	}
+	ASSERT(select_.size() == mixedResultColumnIsAggregated_.size());
+	ASSERT(select_.size() == aggregated_.size() + nonAggregated_.size());
+
+	output_->prepare(*this);
+	results_ = select_;
+	output_->size(results_.size());
+
+	if(aggregated_.size()) {
+		aggregate_ = true;
+		Log::debug() << "SELECT is aggregated" << std::endl;
+
+		if(aggregated_.size() != results_.size())
+		{
+			mixedAggregatedAndScalar_ = true;
+			Log::info() << "SELECT has aggregated and non-aggregated results" << std::endl;
+		}
+	}
+
+
+	SQLExpression *where = where_.get();
+	if(where) {
+		where->prepare(*this);
+		
+		bool more = true;
+		while(more) {
+			more = false;
+			SQLExpression* w = where->simplify(more);
+			if(w) where = w; // Warning, potential memory leak
+			simplifiedWhere_ = where;
+		}
+		
+		Log::debug() << "Simplified WHERE " << *where << std::endl;
+		if(where->isConstant()) {
+			bool missing = false;
+			if(where->eval(missing)) {
+				Log::info() << "WHERE condition always true, ignoring" << std::endl;
+				where = 0;
+			} else {
+				Log::info() << "WHERE condition always false" << std::endl;
+				return;
+			}
+		}
+	}
+	// Check for links
+	for(std::set<SQLTable*>::iterator j = allTables_.begin(); j != allTables_.end() ; ++j) {
+		SQLTable* table1 = *j;
+		const std::string& name1    = table1->name();
+
+		for(std::set<SQLTable*>::iterator k = allTables_.begin(); k != allTables_.end() ; ++k) {
+			SQLTable* table2 = *k;
+			SelectOneTable& x      = tablesToFetch_[table2->master()];
+
+			if(table1->hasLinkTo(*table2)) {
+				const std::string& name2    = table2->name();
+
+				// That can happen for 'aligned' tables
+				if(x.column_) {
+					ASSERT(table2->master() == x.table2_->master());
+					Log::warning() << "Ignoring link " << name1             << "->" << name2             << 
+					", using "         << x.table1_->fullName() 
+						<< "->" << x.table2_->fullName() << std::endl;
+					continue;
+				}
+				Log::info() << "Using link " << table1->fullName() 
+					<< "->" << table2->fullName() << std::endl;
+
+					//
+				std::string o                  = name2 + ".offset";
+				std::pair<double,bool>* offset = column(o,table1);
+
+				std::string l                  = name2 + ".length";
+				std::pair<double,bool>* length = column(l,table1);
+
+				// There should not be 2 tables with a link on the same table
+				
+				ASSERT(x.offset_ == 0);
+				ASSERT(x.length_ == 0);
+				ASSERT(x.column_ == 0);
+
+				x.offset_ = offset;
+				x.length_ = length;
+				x.column_ = table1->column(name2 + ".offset");
+				x.table1_ = table1;
+				x.table2_ = table2;
+			}
+		}
+	}
+
+	// ------------------------------------
+
+	for(TableMap::iterator j = tablesToFetch_.begin() ; j != tablesToFetch_.end(); ++j)
+		sortedTables_.push_back(& (*j).second);
+
+	if(where) {
+		// Try to analyse where
+		expression::Expressions e;
+		if(!where->andSplit(e))
+			e.push_back(where);
+
+		for(size_t i = 0 ; i < e.size() ; ++i) {
+			Log::debug() << "WHERE AND split " << *(e[i]) << std::endl;
+
+			// Get tables accessed
+			std::set<SQLTable*> t;
+			e[i]->tables(t);
+
+			for(std::set<SQLTable*>::iterator j = t.begin(); j != t.end(); ++j)
+				Log::debug() << "  tables -> " << (*j)->fullName() << std::endl;
+
+				
+			if(t.size() == 1) {
+				SQLTable* table = *(t.begin());
+
+				if(e[i]->useIndex()) {
+					tablesToFetch_[table].index_.push_back(e[i]);
+					Log::debug() << "INDEX on " << (*e[i]) << std::endl;
+				}
+				//else
+				{
+					tablesToFetch_[table].check_.push_back(e[i]);
+					Log::debug() << "WHERE quick check for " << table->fullName() << " " << (*e[i]) << std::endl;
+				}
+			}
+		}
+	}
+
+	// Needed, for example, if we do: select count(*) from "file.oda"
+	if (sortedTables_.size() == 0)
+		for (std::vector<SQLTable*>::iterator i = tables_.begin(); i != tables_.end(); ++i)
+			sortedTables_.push_back(new SelectOneTable(*i)); // TODO: release the objects!
+
+	std::sort(sortedTables_.begin(), sortedTables_.end(), compareTables);
+	Log::debug() << "TABLE order " << std::endl;
+	for(SortedTables::iterator k = sortedTables_.begin(); k != sortedTables_.end(); ++k) {
+		Log::debug() << (*k)->table_->fullName() << " " << (*k)->order_ << std::endl;
+
+		for(size_t i = 0; i < (*k)->check_.size(); i++)
+			Log::debug() << "    QUICK CHECK " << *((*k)->check_[i]) << std::endl;
+
+		for(size_t i = 0; i < (*k)->index_.size(); i++)
+			Log::debug() << "    INDEX CHECK " << *((*k)->index_[i]) << std::endl;
+	}
+
+
+	// Add the multi-table quick checks
+	if(where) {
+		expression::Expressions e;
+		if(!where->andSplit(e))
+			e.push_back(where);
+
+		std::set<const SQLTable*> ordered;
+		for(SortedTables::iterator k = sortedTables_.begin(); k != sortedTables_.end(); ++k) {
+			const SQLTable* table = (*k)->table_;
+			ordered.insert(table);
+
+			for(size_t i = 0; i < e.size(); ++i)
+				if(e[i]) {
+					// Get tables accessed
+					std::set<SQLTable*> t;
+					e[i]->tables(t);
+
+					if(t.size() != 1) {
+						bool ok = true;
+						for(std::set<SQLTable*>::iterator j = t.begin() ; j != t.end(); ++j)
+							if(ordered.find(*j) == ordered.end())
+								ok = false;
+
+						if(ok) {
+							(*k)->check_.push_back(e[i]);
+							Log::info() << "WHERE multi-table quick check for " << table->fullName() << " " 
+								<< (*e[i]) << std::endl;
+
+							if(e[i]->useIndex())
+							{
+								(*k)->index_.push_back(e[i]);
+								Log::info() << "WHERE multi-table INDEX" << table->fullName() << " " 
+									<< (*e[i]) << std::endl;
+							}
+
+							e[i] = 0;
+						}
+					}
+					else e[i] = 0;
+				}
+		}
+
+        // Add what's left to last table
+		for(size_t i = 0 ; i < e.size() ; ++i)
+            if(e[i]) 
+                sortedTables_.back()->check_.push_back(e[i]);
+		where = 0;
+	}
+
+	Log::debug() << "SQLSelect:prepareExecute: TABLE order:" << std::endl;
+	for(SortedTables::iterator k = sortedTables_.begin(); k != sortedTables_.end(); ++k)
+	{
+		Log::debug() << "SQLSelect:prepareExecute: TABLE order " <<  (*k)->table_->fullName() << " " <<
+			(*k)->order_ << std::endl;
+
+		for(size_t i = 0; i < (*k)->check_.size(); i++)
+			Log::debug() << "    QUICK CHECK " << *((*k)->check_[i]) << std::endl;
+	}
+}
+
+unsigned long long SQLSelect::execute(ecml::ExecutionContext* context)
+{
+	prepareExecute();
+	unsigned long long n = process(simplifiedWhere_, sortedTables_.begin(), context);
+	postExecute(context);
+	return n;
+}
+
+void SQLSelect::postExecute(ecml::ExecutionContext* context)
+{
+	if (mixedAggregatedAndScalar_)
+	{
+		for (AggregatedResults::iterator it (aggregatedResults_.begin()); it != aggregatedResults_.end(); ++it)
+		{
+            const std::vector<std::pair<double,bool> >& nonAggregatedValues (it->first);
+			const Expressions& aggregated (*(it->second)); 
+			Expressions results;
+			size_t ai(0), ni(0);
+			for (size_t i (0); i < mixedResultColumnIsAggregated_.size(); ++i)
+			{
+				if (mixedResultColumnIsAggregated_[i])
+					results.push_back(aggregated[ai++]->clone());
+				else
+				{
+					results.push_back(
+						new ConstantExpression(nonAggregatedValues[ni].first,
+								nonAggregatedValues[ni].second,
+								nonAggregated_[ni]->type()));
+					++ni;
+				}
+			}
+
+			output_->output(results, context);
+			results.release();
+		}
+	}
+	else if (aggregate_)
+	{
+		resultsOut(context);
+	}
+
+	output_->flush(context);
+	output_->cleanup(*this);
+	if(simplifiedWhere_) simplifiedWhere_->cleanup(*this);
+	
+	for(expression::Expressions::iterator c (results_.begin()); c != results_.end() ; ++c)
+		(*c)->cleanup(*this);
+
+    Log::info() << "Matching row(s): " << BigNum(output_->count()) << " out of " << BigNum(total_) << std::endl;
+    Log::info() << "Skips: " << BigNum(skips_) << std::endl;
+	reset();
+}
+
+void SQLSelect::reset()
+{
+	aggregate_ = false;
+	mixedAggregatedAndScalar_ = false;
+
+	aggregated_.clear();
+	nonAggregated_.clear();
+
+	for (AggregatedResults::iterator it = aggregatedResults_.begin(); it != aggregatedResults_.end(); ++it)
+	{
+		Expressions* exps = it->second; 
+		for (size_t i = 0; i < exps->size(); ++i)
+			delete (*exps)[i];
+		delete exps; 
+	}
+	aggregatedResults_.clear();
+
+	mixedResultColumnIsAggregated_.clear();
+
+	values_.clear();
+	results_.clear();
+
+	tablesToFetch_.clear();
+	allTables_.clear();
+
+	sortedTables_.clear();
+
+	skips_ = total_ = 0;
+
+	output_->reset();
+	count_ = 0;
+}
+
+
+
+bool SQLSelect::output(SQLExpression* where, ecml::ExecutionContext* context)
+{
+	//if (where) Log::info() << "SQLSelect::output: where: " << *where << std::endl;
+
+	bool newRow = false;
+	bool missing = false;
+    double value;
+	if ( (where == 0)
+        || (((value = where->eval(missing)) || !value) // !value for the 'WHERE 0' case, ODB-106 
+            && !missing))
+	{
+		if (! aggregate_)
+			newRow = resultsOut(context);
+		else
+		{
+			size_t n = results_.size();
+			if (! mixedAggregatedAndScalar_)
+			{
+				for(size_t i = 0; i < n; i++)
+					results_[i]->partialResult();
+			} else {
+                std::vector<std::pair<double,bool> > nonAggregatedValues;
+				for (size_t i = 0; i < nonAggregated_.size(); ++i)
+				{
+					bool missing = false;
+					double v = nonAggregated_[i]->eval(missing);
+                    nonAggregatedValues.push_back(std::make_pair(v, missing));
+				}
+	
+				AggregatedResults::iterator results = aggregatedResults_.find(nonAggregatedValues);
+				if (results == aggregatedResults_.end())
+					aggregatedResults_[nonAggregatedValues] = static_cast<Expressions*>(aggregated_.clone());
+
+				Expressions& aggregated = *aggregatedResults_[nonAggregatedValues];
+				for (size_t i = 0; i < aggregated.size(); ++i)
+					aggregated[i]->partialResult();
+			}
+		}
+	}
+	return newRow;
+}
+
+
+unsigned long long SQLSelect::process(SQLExpression* where, SortedTables::iterator j, ecml::ExecutionContext* context) {
+	simplifiedWhere_ = where;
+	env.pushFrame(j);
+
+	unsigned long long n = 0;
+	while (processOneRow(context))
+		++n;
+	return n;
+}
+
+
+bool SQLSelect::processOneRow(ecml::ExecutionContext* context) { 
+	++count_;
+	//Log::info() << "SQLSelect::processOneRow: count = " << count_ << std::endl;
+	bool recursiveCall;
+	do
+	{
+		recursiveCall = false;
+		if(sortedTables_.size() == 0 || env.tablesIterator() == sortedTables_.end())
+		{
+			bool rowProduced = output(simplifiedWhere_, context);
+			env.popFrame();
+			if (rowProduced)
+				return true;
+		}
+
+		if (env.tablePtr() == 0)
+		{
+			env.table( *(env.tablesIterator()) );
+
+			size_t n = env.table().fetch_.size();
+			for(size_t i = 0; i < n; i++) {
+				env.table().fetch_[i]->rewind();
+			}
+			env.cursor(env.table().table_->iterator(env.table().fetch_));
+			env.cursor().rewind();
+		}
+
+		while(env.cursor().next())
+		{
+			total_++;
+			size_t n = env.table().fetch_.size();
+			for(size_t i = 0; i < n; i++)
+			{
+				SQLColumn &fetchColumn = *env.table().fetch_[i];
+				//Log::info() << "SQLSelect::processOneRow: fetchColumn.name() => " << fetchColumn.name() << std::endl;
+				//Log::info() << "SQLSelect::processOneRow: fetchColumn.type() => " << fetchColumn.type() << std::endl;
+				bool &missing = env.table().values_[i]->second;
+				env.table().values_[i]->first = fetchColumn.next(missing);
+			}
+			bool ok = true;
+			n = env.table().check_.size();
+			for(size_t i = 0; i < n; i++)
+			{
+				bool missing = false;
+				if(!env.table().check_[i]->eval(missing) || missing) {
+					ok = false;
+					break;
+				} 
+			}
+			if(!ok) skips_++;
+			else {
+				SortedTables::iterator k = env.tablesIterator();
+				env.pushFrame(++k);
+				//processOneRow();
+				recursiveCall = true;
+				break;
+			}
+		}
+	} while(recursiveCall);
+	env.popFrame();
+	return false;
+}
+
+void SQLSelect::print(std::ostream& s) const
+{
+	s << "SELECT"; char sep = ' ';
+
+	//if(distinct_)
+	//	s << " DISTINCT";
+
+	for(expression::Expressions::const_iterator c = select_.begin(); c != select_.end() ; ++c)
+	{
+		s << sep << *(*c);
+		sep = ',';
+	}
+
+	s << " FROM";
+	sep = ' ';
+	for(std::vector<SQLTable*>::const_iterator t = tables_.begin(); t != tables_.end() ; ++t)
+	{
+		s << sep << (*t)->name();
+		sep = ',';
+	}
+
+	if(where_.get())
+		s << " WHERE " << *where_;
+
+	s << " " << *output_;
+}
+
+expression::Expressions SQLSelect::output() const { return select_; }
+
+vector<PathName> SQLSelect::outputFiles() const { return outputFiles_; }
+void SQLSelect::outputFiles(const vector<PathName>& files) { outputFiles_ = files; }
+
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/SQLSelect.h b/odb_api/src/odb_api/SQLSelect.h
new file mode 100755
index 0000000..0b96c68
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSelect.h
@@ -0,0 +1,138 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLSelect.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLSelect_H
+#define SQLSelect_H
+
+#include "eckit/filesystem/PathName.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/SelectOneTable.h"
+#include "odb_api/SQLOutputConfig.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/Stack.h"
+
+namespace odb {
+
+	class SelectIterator;
+
+namespace sql {
+
+namespace expression {
+    namespace function {
+        class FunctionROWNUMBER;
+        class FunctionTHIN;
+    }
+}
+
+class SQLTableIterator;
+
+class SQLSelect : public SQLStatement {
+	friend class odb::SelectIterator;
+
+public:
+	SQLSelect(const Expressions&, const std::vector<SQLTable*>&, odb::sql::expression::SQLExpression*, SQLOutput*, SQLOutputConfig, bool all);
+	~SQLSelect(); 
+
+// -- Methods
+	void prepareExecute(); //SQLExpression*& where);
+	unsigned long long process(odb::sql::expression::SQLExpression*,SortedTables::iterator, ecml::ExecutionContext*);
+	bool processOneRow() { return processOneRow(0); }
+	bool processOneRow(ecml::ExecutionContext*);
+	void postExecute() { postExecute(0); }
+	void postExecute(ecml::ExecutionContext*);
+
+	void pushFirstFrame();
+
+	bool isAggregate() { return aggregate_; }
+
+	std::pair<double,bool>* column(const std::string& name, SQLTable*);
+	const type::SQLType* typeOf(const std::string& name, SQLTable*) const;
+	// FIXME: do we really need all these optional parameters?
+	SQLTable* findTable(const std::string& name, std::string *fullName = 0, bool *hasMissingValue=0, double *missingValue=0, bool* isBitfield=0, BitfieldDef* =0) const;
+
+	virtual Expressions output() const; 
+
+	Expressions& results() { return results_; }
+
+    std::vector<eckit::PathName> outputFiles() const;
+    void outputFiles(const std::vector<eckit::PathName>& files);
+    bool all() const { return all_; }
+    std::vector<SQLTable*>& tables() { return tables_; }
+    odb::sql::expression::SQLExpression* where() { return where_.get(); }
+
+// -- Overridden methods
+	unsigned long long execute(ecml::ExecutionContext*);
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLSelect(const SQLSelect&);
+	SQLSelect& operator=(const SQLSelect&);
+
+// -- Members
+	Expressions select_;
+	std::vector<SQLTable*> tables_;
+	SortedTables sortedTables_;
+
+    std::auto_ptr<odb::sql::expression::SQLExpression> where_;
+	odb::sql::expression::SQLExpression* simplifiedWhere_;
+
+	Stack env;
+
+    std::auto_ptr<SQLOutput> output_;
+	Expressions  results_;
+
+    typedef std::map<std::vector<std::pair<double,bool> >, expression::Expressions*> AggregatedResults;
+	AggregatedResults aggregatedResults_;
+
+    std::map<std::string, std::pair<double,bool> > values_;
+	std::set<SQLTable*> allTables_;
+
+	typedef std::map<SQLTable*,SelectOneTable> TableMap;
+	TableMap tablesToFetch_;
+
+	unsigned long long count_;
+	unsigned long long total_;
+	unsigned long long skips_;
+
+	bool aggregate_;
+	bool mixedAggregatedAndScalar_;
+	Expressions aggregated_;
+	Expressions nonAggregated_;
+	std::vector<bool> mixedResultColumnIsAggregated_;
+	SQLOutputConfig outputConfig_;
+    std::vector<eckit::PathName> outputFiles_;
+    bool all_;
+
+// -- Methods
+
+	void reset();
+	bool resultsOut(ecml::ExecutionContext*);
+	bool output(odb::sql::expression::SQLExpression*, ecml::ExecutionContext*);
+    SQLExpression* findAliasedExpression(const std::string& alias);
+
+	friend class odb::sql::expression::function::FunctionROWNUMBER; // needs access to count_
+	friend class odb::sql::expression::function::FunctionTHIN; // needs access to count_
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLSelect& p)
+		{ p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLSelectFactory.cc b/odb_api/src/odb_api/SQLSelectFactory.cc
new file mode 100644
index 0000000..8fa3b05
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSelectFactory.cc
@@ -0,0 +1,357 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/utils/Translator.h"
+#include "eckit/types/Types.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/FunctionExpression.h"
+#include "odb_api/SQLDistinctOutput.h"
+#include "odb_api/SQLODAOutput.h"
+#include "odb_api/SQLOrderOutput.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/TemplateParameters.h"
+#include "odb_api/Writer.h"
+#include "odb_api/SQLCallbackOutput.h"
+#include "odb_api/EmbeddedCodeParser.h"
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLOutputConfig.h"
+#include "odb_api/BitColumnExpression.h"
+#include "odb_api/ShiftedColumnExpression.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+SQLSelectFactory::SQLSelectFactory(const odb::sql::SQLOutputConfig& config, const std::string& csvDelimiter)
+: implicitFromTableSource_(0),
+  implicitFromTableSourceStream_(0),
+  database_(0),
+  config_(config), //SQLOutputConfig::defaultConfig()),
+  maxColumnShift_(0),
+  minColumnShift_(0),
+  csvDelimiter_(csvDelimiter) //",")
+{}
+
+/*void SQLSelectFactory::reset()
+{
+    // TODO> we may need to delete things here...
+    implicitFromTableSource_ = 0;
+    implicitFromTableSourceStream_ = 0;
+    database_ = 0;
+    config_ = SQLOutputConfig::defaultConfig();
+    maxColumnShift_ = 0;
+    minColumnShift_ = 0;
+    //csvDelimiter_ = ",";
+}
+*/
+
+string SQLSelectFactory::index(const string& columnName, const SQLExpression* index)
+{
+	if (index == 0)
+		return columnName;
+
+	bool missing = false;
+	string idx = Translator<int,string>()(int(index->eval(missing)));
+	ASSERT(! missing);
+	return columnName + "_" + idx;
+}
+
+SQLExpression* SQLSelectFactory::createColumn(
+	const string& columnName,
+	const string& bitfieldName,
+	const SQLExpression* vectorIndex,
+	const Table& table,
+	const SQLExpression* pshift)
+{
+	if (! pshift->isConstant()) throw eckit::UserError("Value of shift operator must be constant");
+	bool missing = false;
+	
+	// Internally shift is an index in the cyclic buffer of old values, so the shift value is negative.
+	int shift = - pshift->eval(missing);
+
+	if (shift > maxColumnShift_) maxColumnShift_ = shift;
+	if (shift < minColumnShift_) minColumnShift_ = shift;
+
+	string expandedColumnName( index(columnName, vectorIndex) );
+    // TODO: handle .<database>
+	return bitfieldName.size()
+		? (shift == 0 ? new BitColumnExpression(expandedColumnName, bitfieldName, table.name)
+					  : new ShiftedColumnExpression<BitColumnExpression>(expandedColumnName, bitfieldName, table.name, shift, -shift))
+		: (shift == 0 ? new ColumnExpression(expandedColumnName + table.name, table.name)
+					  : new ShiftedColumnExpression<ColumnExpression>(expandedColumnName + table.name, table.name, shift, -shift));
+}
+
+SQLExpression* SQLSelectFactory::reshift(SQLExpression* e)
+{
+    if (e == 0) return 0;
+    SQLExpression* r = e;
+    ShiftedColumnExpression<BitColumnExpression>* c1 = dynamic_cast<ShiftedColumnExpression<BitColumnExpression>*>(e);
+    if (c1) {
+        int newShift = c1->shift() - minColumnShift_;
+        ASSERT(newShift >= 0);
+        r = newShift > 0
+            ? new ShiftedColumnExpression<BitColumnExpression>(*c1, newShift, c1->nominalShift())
+            : (new BitColumnExpression(*c1))->nominalShift(c1->nominalShift());
+        delete c1;
+        return r;
+    } 
+
+    ShiftedColumnExpression<ColumnExpression>* c2 = dynamic_cast<ShiftedColumnExpression<ColumnExpression>*>(e);
+    if (c2) {
+        int newShift = c2->shift() - minColumnShift_ ;
+        ASSERT(newShift >= 0);
+        r = newShift > 0
+            ? new ShiftedColumnExpression<ColumnExpression>(*c2, newShift, c2->nominalShift())
+            : (new ColumnExpression(*c2))->nominalShift(c2->nominalShift());
+        delete c2;
+        return r;
+    } 
+
+    BitColumnExpression* c3 = dynamic_cast<BitColumnExpression*>(e);
+    if(c3) {
+        r = new ShiftedColumnExpression<BitColumnExpression>(*c3, -minColumnShift_, 0);
+        delete c3;
+        return r;
+    }
+
+    ColumnExpression* c4 = dynamic_cast<ColumnExpression*>(e);
+    if(c4) {
+        r = new ShiftedColumnExpression<ColumnExpression>(*c4, -minColumnShift_, 0);
+        delete c4;
+        return r;
+    }
+    
+    odb::sql::expression::function::FunctionExpression* f = dynamic_cast<odb::sql::expression::function::FunctionExpression*>(e);
+    if (f) {
+        reshift(f->args());
+        return r;
+    }
+
+    Log::info() << "SQLSelectFactory::reshift: SKIP " << *e << endl;
+    return r;
+}
+
+void SQLSelectFactory::reshift(Expressions& select)
+{
+    ostream& L(Log::debug());
+	L << "reshift: maxColumnShift_ = " << maxColumnShift_ << endl;
+	L << "reshift: minColumnShift_ = " << minColumnShift_ << endl;
+	for (size_t i = 0; i < select.size(); ++i)
+		L << "reshift: <- select[" << i << "]=" << *select[i] << endl;
+
+	for (size_t i = 0; i < select.size(); ++i)
+        select[i] = reshift(select[i]);
+
+	L << endl;
+	for (size_t i = 0; i < select.size(); ++i)
+		L << "reshift: -> select[" << i << "]=" << *select[i] << endl;
+}
+
+//void SQLSelectFactory::resolveImplicitFrom(SQLSession& session, vector<SQLTable*>& from)
+vector<SQLTable*> SQLSelectFactory::resolveImplicitFrom(SQLSession& session, vector<Table>& from)
+{
+    ostream& L (Log::debug());
+    
+    L << "No <from> clause" << endl;
+
+    // TODO: SQLTable => string. 
+
+    SQLTable* table = implicitFromTableSource_ ? session.openDataHandle(*implicitFromTableSource_)
+        : implicitFromTableSourceStream_ ? session.openDataStream(*implicitFromTableSourceStream_, csvDelimiter_) 
+        //: database_ ? database_->table("defaultTable")
+        : database_ ? database_->defaultTable()
+        : session.currentDatabase().dualTable();
+
+    L << "Implicit FROM: " << *table << endl;
+
+    vector<SQLTable*> fromTables;
+    fromTables.push_back(table);
+    return fromTables;
+}
+
+/*
+SchemaAnalyzer& SQLSelectFactory::analyzer()
+{ return SQLSession::current().currentDatabase().schemaAnalyzer(); }
+
+MetaData SQLSelectFactory::columns(const string& tableName)
+{
+    const TableDef& tabledef ( enalyzer().findTable(tableName) );
+    const ColumnDefs& columnDefs ( tabledef.columns() );
+
+    //TODO: Convert ColumnDefs (from tabledef) into MetaData and push it into the SQLODAOutput
+    ASSERT( false ); /// @todo this code must be fixed and return
+}
+*/
+
+SQLSelect* SQLSelectFactory::create (SQLSession& session, const SelectAST& a)
+{
+    return session.selectFactory().create(session, a.distinct, a.all, a.selectList, a.into, a.from, a.where, a.groupBy, a.orderBy);
+}
+
+SQLSelect* SQLSelectFactory::create (
+    odb::sql::SQLSession& session,
+    bool distinct,
+    bool all,
+    Expressions select_list,
+    const string& into,
+    //vector<SQLTable*> from,
+    vector<Table> from,
+    SQLExpression *where,
+    Expressions group_by,
+    pair<Expressions,vector<bool> > order_by)
+{
+    ostream& L(Log::debug());
+
+	if (where) L << "SQLSelectFactory::create: where = " << *where << endl;
+
+	SQLSelect* r (0);
+	//SQLSession& session (SQLSession::current());
+
+    std::vector<SQLTable*> fromTables;
+
+	if (! from.size()) 
+    {
+        std::vector<SQLTable*> implicitTables (resolveImplicitFrom(session, from));
+        fromTables.insert( fromTables.begin(), implicitTables.begin(), implicitTables.end() );
+    }
+
+    //table : IDENT '.' IDENT { SQLSession& s  = SQLSession::current(); $$ = s.findTable($1,$3); }
+    for (size_t i(0); i < from.size(); ++i)
+    {
+        Table& t (from[i]);
+
+        if (! t.embeddedCode)
+            fromTables.push_back(session.findTable(t));
+        else
+        {
+            ecml::ExecutionContext context; // TODO: get it from session, don't pass it to getFromTables
+            std::vector<SQLTable*> computed (EmbeddedCodeParser::getFromTables(t.name, t.database, session, &context));
+            fromTables.insert(fromTables.begin(), computed.begin(), computed.end());
+        }
+    }
+
+	Expressions select;
+	for (ColumnDefs::size_type i (0); i < select_list.size(); ++i)
+	{
+		L << "expandStars: " << *select_list[i] << endl;
+		select_list[i]->expandStars(fromTables, select);
+	}
+
+	ASSERT(maxColumnShift_ >= 0);
+	ASSERT(minColumnShift_ <= 0);
+	if (minColumnShift_ < 0) 
+    {
+        L << endl << "SELECT_LIST before reshifting:" << select << endl;
+		reshift(select);
+        L << "SELECT_LIST after reshifting:" << select << endl << endl;
+
+        if (where)
+        {
+            L << endl << "WHERE before reshifting:" << *where << endl;
+            where = reshift(where);
+            L << "WHERE after reshifting:" << *where << endl << endl;
+        }
+
+        reshift(order_by.first);
+    }
+
+	maxColumnShift_ = 0;
+	minColumnShift_ = 0;
+
+	if (group_by.size())
+		Log::info() << "GROUP BY clause seen and ignored. Non aggregated values on select list will be used instead." << endl;
+
+    SQLOutput *out (createOutput(session, into, order_by.first.size()));
+
+    if(distinct)              { out = new SQLDistinctOutput(out); }
+    if(order_by.first.size()) { out = new SQLOrderOutput(out, order_by); }
+    r = new SQLSelect(select, fromTables, where, out, config_, all);
+
+	maxColumnShift_ = 0;
+	return r;
+}
+
+MetaData SQLSelectFactory::toODAColumns(odb::sql::SQLSession& session, const odb::sql::TableDef& tableDef)
+{
+    ostream& L(eckit::Log::debug());
+
+    odb::sql::ColumnDefs columnDefs (tableDef.columns());
+    MetaData md(0); 
+    for (size_t i(0); i < columnDefs.size(); ++i)
+    {
+        odb::sql::ColumnDef& c (columnDefs[i]);
+        L << "   " << c.name() << ":" << c.type() << endl; 
+
+        SchemaAnalyzer& a (session.currentDatabase().schemaAnalyzer());
+        if (a.isBitfield(c.name())) {
+            const BitfieldDef& bf ( a.getBitfieldTypeDefinition(c.name()) );
+            md.addBitfield(c.name(), bf ); 
+        }
+        else {
+            ColumnType type (Column::type(c.type()));
+            if (type == BITFIELD)
+                md.addBitfield(c.name(), c.bitfieldDef());
+            else
+                md.addColumn(c.name(), c.type());
+        }
+
+        ASSERT( &md[i]->coder() );
+    }
+    return md;
+}
+
+SQLOutput* SQLSelectFactory::createOutput (SQLSession& session, const string& into, size_t orderBySize)
+{
+    // TODO: FIXME
+    //size_t maxOpenFiles ( ! context ? 100 : atoi(context->environment().lookup("maxOpenFiles", "100", *context).c_str()));
+    size_t maxOpenFiles ( 100);
+    //TODO: pass parameter into to defaultFormat
+    SQLOutput *r (NULL);
+    //if (config_.outputFormat() == "callback") return session.defaultOutput();
+    // TODO: FIXME
+    //if (context && context->environment().lookupNoThrow("callback") )
+    //    return r = new SQLCallbackOutput(*context);
+
+    string outputFile ((config_.outputFormat() == "odb") ? config_.outputFile() : into);
+    Log::debug() << "SQLSelectFactory::createOutput: outputFile: '" << outputFile << "'" << endl;
+    if (! outputFile.size())
+        return r = session.defaultOutput();
+
+    TemplateParameters templateParameters;
+    TemplateParameters::parse(outputFile, templateParameters);
+    if (templateParameters.size())
+    {
+        r = new SQLODAOutput<DispatchingWriter>(new DispatchingWriter(outputFile, orderBySize ? 1 : maxOpenFiles));
+    } 
+    else 
+    {
+        SchemaAnalyzer& a (session.currentDatabase().schemaAnalyzer());
+        if (! a.tableKnown(outputFile)) 
+            r = new SQLODAOutput<Writer<> >(new Writer<>(outputFile));
+        else
+        {
+            Log::info() << "Table in the INTO clause known (" << outputFile << ")" << endl;
+            const odb::sql::TableDef* tableDef (&a.findTable(outputFile));
+            r = new SQLODAOutput<Writer<> >(new Writer<>(outputFile), toODAColumns(session, *tableDef));
+        } 
+    }
+    return r;
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLSelectFactory.h b/odb_api/src/odb_api/SQLSelectFactory.h
new file mode 100644
index 0000000..ad7c81f
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSelectFactory.h
@@ -0,0 +1,103 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_SQLSelectFactory_H
+#define odb_api_SQLSelectFactory_H
+
+#include "odb_api/Expressions.h"
+#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+namespace odb { namespace sql { class DataTable; } }
+namespace odb { namespace sql { class SQLDatabase; } }
+namespace odb { namespace sql { class SQLSession; } }
+
+namespace odb { 
+namespace sql {
+
+class SQLSelectFactory {
+public:
+    SQLSelectFactory(const SQLOutputConfig&, const std::string&);
+
+	SQLSelect* create(odb::sql::SQLSession&, const SelectAST&);
+
+	SQLSelect* create(
+        odb::sql::SQLSession&,
+        bool distinct,
+        bool all,
+        Expressions select_list,
+        const std::string& into,
+        std::vector<Table> from,
+        odb::sql::expression::SQLExpression *where,
+        Expressions group_by,
+        std::pair<Expressions,std::vector<bool> > order_by);
+
+	SQLExpression* createColumn(
+		const std::string& columnName,
+		const std::string& bitfieldName,
+		const SQLExpression* vectorIndex,
+		const Table& table,
+		const SQLExpression* pshift);
+
+    eckit::DataHandle* implicitFromTableSource() { return implicitFromTableSource_; }
+    void implicitFromTableSource(eckit::DataHandle* h) { implicitFromTableSource_ = h; }
+
+    std::istream* implicitFromTableSourceStream() { return implicitFromTableSourceStream_; }
+    void implicitFromTableSourceStream(std::istream* is) { implicitFromTableSourceStream_ = is; }
+
+	SQLDatabase* database() { return database_; }
+	void database(SQLDatabase* db) { database_ = db; }
+
+	SQLOutputConfig config() { return config_; }
+	void config(const SQLOutputConfig& cfg) { config_ = cfg; }
+
+	std::string csvDelimiter() { return csvDelimiter_; }
+	void csvDelimiter(const std::string& d) { csvDelimiter_ = d; }
+
+    static odb::MetaData toODAColumns(odb::sql::SQLSession&, const odb::sql::TableDef&);
+
+private:
+    // No copy allowed
+    SQLSelectFactory(const SQLSelectFactory&);
+    SQLSelectFactory& operator=(const SQLSelectFactory&);
+
+	std::string index(const std::string& columnName, const SQLExpression* index);
+
+	void reshift(Expressions&);
+
+	SQLExpression* reshift(SQLExpression*);
+
+    //void resolveImplicitFrom(SQLSession&, std::vector<SQLTable*>& from);
+    std::vector<SQLTable*> resolveImplicitFrom(SQLSession&, std::vector<Table>& from);
+
+    eckit::DataHandle* implicitFromTableSource_;
+
+    std::istream* implicitFromTableSourceStream_;
+
+    //SchemaAnalyzer& analyzer();
+    MetaData columns(const std::string& tableName);
+    SQLOutput* createOutput(SQLSession&, const std::string& into, size_t orderBySize );
+
+    SQLDatabase* database_;
+    SQLOutputConfig config_;
+    int maxColumnShift_;
+    int minColumnShift_;
+    std::string csvDelimiter_;
+
+    //friend class eckit::NewAlloc0<SQLSelectFactory>;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLSession.cc b/odb_api/src/odb_api/SQLSession.cc
new file mode 100755
index 0000000..de5c494
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSession.cc
@@ -0,0 +1,231 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Timer.h"
+#include "eckit/config/Resource.h"
+
+#include "odb_api/ODADatabase.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLNonInteractiveSession.h"
+#include "odb_api/SQLStatement.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLOutputConfig.h"
+#include "odb_api/StringTool.h"
+#include <libgen.h>
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+std::string defaultDB = "default";
+
+SQLSession::SQLSession(const odb::sql::SQLOutputConfig& config, const std::string& csvDelimiter)
+: currentDatabase_(0),
+  databases_(),
+  selectFactory_(config, csvDelimiter),
+  insertFactory_(),
+  selectAST_(),
+  gotSelectAST_(false),
+  lastExecuteResult_(),
+  config_(config),
+  csvDelimiter_(csvDelimiter)
+{
+    currentDatabase_ = new ODADatabase(".", defaultDB); 
+    currentDatabase_->open();
+    databases_[defaultDB] = currentDatabase_;
+}
+
+SQLSession::~SQLSession()
+{
+    for(std::map<std::string,SQLDatabase*>::iterator j = databases_.begin(); j != databases_.end(); ++j)
+        delete (*j).second;
+}
+
+SQLSelectFactory& SQLSession::selectFactory() { return selectFactory_; }
+SQLInsertFactory& SQLSession::insertFactory() { return insertFactory_; }
+
+SQLDatabase& SQLSession::currentDatabase(SQLDatabase *db)
+{
+    delete currentDatabase_;
+    currentDatabase_ = db;
+    currentDatabase_->open();
+    databases_[defaultDB] = currentDatabase_;
+    return *currentDatabase_;
+}
+
+SQLDatabase& SQLSession::openDatabase(const PathName& path,const std::string& name)
+{
+    std::map<std::string,SQLDatabase*>::iterator j = databases_.find(name);
+    if(j != databases_.end())
+    {
+        SQLDatabase* db = (*j).second;
+        db->close();
+        delete db;
+    }
+
+    currentDatabase_ = new ODADatabase(path,name);
+    currentDatabase_->open();
+
+    databases_[name] = currentDatabase_;
+    return *currentDatabase_;
+}
+
+void SQLSession::closeDatabase(const std::string& name)
+{
+    // TODO
+}
+
+unsigned long long SQLSession::execute(SQLStatement& sql, ecml::ExecutionContext* context)
+{
+    Timer timer("Execute");
+    ASSERT(currentDatabase_);	
+
+    unsigned long long n = sql.execute(context);
+    return lastExecuteResult_ = n;
+}
+
+SQLDatabase& SQLSession::currentDatabase() const
+{
+    ASSERT(currentDatabase_);	
+    return *currentDatabase_;
+}
+/*
+double SQLSession::getParameter(int which) const
+{
+    std::map<int,double>::const_iterator j = params_.find(which);
+    if(j == params_.end())
+        throw eckit::UserError("Undefined parameter");
+    return (*j).second;
+}
+
+void SQLSession::setParameter(int which,double value)
+{
+    params_[which] = value;
+}
+*/
+
+SQLDatabase* SQLSession::getDatabase(const std::string& name)
+{
+    std::map<std::string,SQLDatabase*>::iterator j = databases_.find(name);
+    if(j == databases_.end())
+        throw eckit::UserError("Cannot find database", name);
+    return (*j).second;
+}
+
+SQLTable* SQLSession::findTable(const odb::sql::Table& t)
+{
+    if (t.database.size())
+        return getDatabase(t.database)->table(t);
+    else
+    {
+        if (! currentDatabase_) 
+            throw eckit::UserError("No current database", t.name);
+
+        return currentDatabase_->table(t);
+    }
+}
+
+SQLTable* SQLSession::openDataStream(std::istream &is, const std::string& delimiter)
+{
+    ASSERT(currentDatabase_);
+    return currentDatabase_->openDataStream(is, delimiter);
+}
+
+SQLTable* SQLSession::openDataHandle(DataHandle &dh)
+{
+	ASSERT(currentDatabase_);
+	return currentDatabase_->openDataHandle(dh);
+}
+
+
+void SQLSession::createIndex(const std::string& column,const std::string& table)
+{
+    ASSERT(currentDatabase_);
+#if 0
+	currentDatabase_->table(table)->column(column)->createIndex();
+#endif
+}
+
+void SQLSession::statement(const SelectAST& s) 
+{ 
+    selectAST_ = s;
+    gotSelectAST_ = true;
+}
+
+const SelectAST& SQLSession::selectAST() const { return selectAST_; }
+
+bool SQLSession::gotSelectAST() const { return gotSelectAST_; }
+void SQLSession::gotSelectAST(bool b) { gotSelectAST_ = b; }
+
+
+void SQLSession::loadDefaultSchema()
+{
+    std::string schemaPathName (schemaFile());
+    if (schemaPathName.empty())
+        return;
+
+    Log::debug() << "Loading schema " << schemaPathName << std::endl;
+    
+    std::string schema (StringTool::readFile(schemaPathName));
+    SQLOutputConfig config (selectFactory().config());
+    SQLParser parser;
+    // TODO: update include path
+    parser.parseString(*this, schema, static_cast<DataHandle*>(0), config);
+}
+
+std::string SQLSession::schemaFile()
+{
+    const char* pn = 
+#ifdef ODB_API_SCHEMA_PATH
+# define STR_VALUE(s)                #s
+# define STRING(s)                   STR_VALUE(s)
+# define ODB_API_SCHEMA_PATH_STRING  STRING(ODB_API_SCHEMA_PATH) 
+    STRING(ODB_API_SCHEMA_PATH_STRING)
+#endif
+    "";
+
+    static std::string pathName (StringTool::unQuote(Resource<std::string>("$ODB_API_SCHEMA_PATH", pn)));
+    return pathName;
+}
+
+std::string SQLSession::readIncludeFile(const std::string& fileName)
+{
+    std::vector<std::string> dirs (includePaths());
+    Log::debug() << "read include: " << fileName << std::endl;
+
+    for (size_t i(0); i < dirs.size(); ++i)
+    {
+        std::string pathName (dirs[i] + fileName);
+        Log::debug() << "Looking for include file " << fileName << " in " << dirs[i] << std::endl;
+        if (! PathName(pathName).exists())
+            continue;
+        return StringTool::readFile(pathName);
+    }
+    throw eckit::UserError(std::string("Include file '") + fileName + "' not found");
+}
+
+std::vector<std::string> SQLSession::includePaths()
+{
+    std::vector<std::string> r;
+    std::string s (schemaFile());
+    char a [s.size() + 1];
+    strncpy(a, s.c_str(), s.size() + 1);
+    const std::string dir (dirname(const_cast<char *>(a)));
+
+    r.push_back("");
+    r.push_back(dir + "/");
+    r.push_back("./");
+    return r;
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLSession.h b/odb_api/src/odb_api/SQLSession.h
new file mode 100755
index 0000000..8c19c65
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSession.h
@@ -0,0 +1,117 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLSession.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef odb_sql_SQLSession_H
+#define odb_sql_SQLSession_H
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+#include "eckit/eckit.h"
+
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/SQLInsertFactory.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+class SQLDatabase;
+class SQLStatement;
+class SQLTable;
+class SQLOutputConfig;
+
+class SQLSession {
+public:
+    SQLSession(const odb::sql::SQLOutputConfig& config, const std::string& csvDelimiter);
+    virtual ~SQLSession(); 
+
+    virtual SQLDatabase& openDatabase(const eckit::PathName&,const std::string& name = "");
+    virtual void closeDatabase(const std::string& name);
+
+    virtual void createIndex(const std::string&,const std::string&);
+
+    virtual SQLDatabase* getDatabase(const std::string& name);
+
+    virtual SQLSelectFactory& selectFactory();
+    virtual SQLInsertFactory& insertFactory();
+
+    virtual SQLTable* findTable(const odb::sql::Table&);
+
+    virtual SQLTable* openDataHandle(eckit::DataHandle &);
+    virtual SQLTable* openDataStream(std::istream &, const std::string &);
+
+    virtual void statement(const SelectAST& s);
+    virtual void statement(SQLStatement*) = 0;
+    virtual SQLStatement* statement() = 0;
+    virtual SQLOutput* defaultOutput() = 0;
+
+    virtual SQLDatabase& currentDatabase() const;
+    virtual SQLDatabase& currentDatabase(SQLDatabase*);
+
+    virtual unsigned long long execute(SQLStatement&, ecml::ExecutionContext*);
+
+    virtual void interactive() {}
+
+    unsigned long long lastExecuteResult() { return lastExecuteResult_; }
+
+    bool gotSelectAST() const;
+    void gotSelectAST(bool);
+    const SelectAST& selectAST() const;
+
+    std::string csvDelimiter() { return csvDelimiter_; }
+    const odb::sql::SQLOutputConfig& outputConfig() { return config_; }
+
+    static std::string readIncludeFile(const std::string&);
+
+protected:
+    void loadDefaultSchema();
+
+private:
+// No copy allowed
+
+    SQLSession(const SQLSession&);
+    SQLSession& operator=(const SQLSession&);
+
+    static std::string schemaFile();
+    static std::vector<std::string> includePaths();
+
+    SQLDatabase* currentDatabase_;
+    //std::map<int,double> params_;
+    std::map<std::string,SQLDatabase*> databases_;
+    SQLSelectFactory selectFactory_;
+    SQLInsertFactory insertFactory_;
+
+    SelectAST selectAST_;
+    bool gotSelectAST_;
+    unsigned long long lastExecuteResult_;
+
+    const odb::sql::SQLOutputConfig config_;
+    const std::string csvDelimiter_;
+
+    friend std::ostream& operator<<(std::ostream& s, const SQLSession& p)
+    {
+        s << "[session@" << &p << ", currentDatabase: " << *(p.currentDatabase_) << " ]";
+        return s;
+    }
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLSimpleOutput.cc b/odb_api/src/odb_api/SQLSimpleOutput.cc
new file mode 100755
index 0000000..50657df
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSimpleOutput.cc
@@ -0,0 +1,162 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/Decoder.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLSimpleOutput.h"
+
+#include <limits>
+
+namespace odb {
+namespace sql {
+
+SQLSimpleOutput::SQLSimpleOutput(std::ostream& out)
+: out_(out), count_(0)
+{
+    out_ << std::fixed;
+}
+
+SQLSimpleOutput::~SQLSimpleOutput() {}
+
+void SQLSimpleOutput::print(std::ostream& s) const
+{
+	s << "SQLSimpleOutput";
+}
+
+std::ostream& SQLSimpleOutput::format(std::ostream& o, size_t i) const
+{
+	o.width(columnWidths_[i]);
+	return o << *columnAlignments_[i];
+}
+
+void SQLSimpleOutput::size(int) {}
+void SQLSimpleOutput::reset() { count_ = 0; }
+void SQLSimpleOutput::flush(ecml::ExecutionContext*) { out_ << std::flush; }
+
+bool SQLSimpleOutput::output(const expression::Expressions& results, ecml::ExecutionContext* context)
+{
+	size_t n = results.size();
+    for(size_t i = 0; i < n; i++)
+    {
+        if(i) out_ << config_.fieldDelimiter();
+		currentColumn_ = i;
+        results[i]->output(*this);
+    }
+    out_ << "\n";
+	count_++;
+	return true;
+}
+
+template <typename T> void SQLSimpleOutput::outputValue(double x, bool missing) 
+{
+    format(out_, currentColumn_);
+
+    if (missing && !config_.doNotWriteNULL())
+        out_ << "NULL";
+    else
+    {
+        if (config_.fullPrecision())
+        {
+            out_.precision(std::numeric_limits<T>::digits10 + 2);
+            out_ << std::fixed << static_cast<T>(x);
+        }
+        else
+            out_ << static_cast<T>(x);
+    }
+}
+
+void SQLSimpleOutput::outputReal(double x, bool missing) { outputValue<double>(x, missing); }
+void SQLSimpleOutput::outputDouble(double x, bool missing) { outputValue<double>(x, missing); }
+void SQLSimpleOutput::outputInt(double x, bool missing) { outputValue<long long>(x, missing); }
+void SQLSimpleOutput::outputUnsignedInt(double x, bool missing) { outputValue<unsigned long>(x, missing); }
+
+void SQLSimpleOutput::outputString(double x, bool missing)
+{
+	format(out_, currentColumn_);
+	if (missing && !config_.doNotWriteNULL())
+		out_ << "NULL";
+	else
+	{
+        std::stringstream ss;
+		ss << "'";
+		char *p = reinterpret_cast<char*>(&x);
+		for(size_t i = 0; i < sizeof(x); i++)
+			if(p[i] != ' ' && isprint(p[i]))
+				ss << p[i];
+		ss << "'";
+
+		out_ << ss.str();
+	}
+}
+
+void SQLSimpleOutput::outputBitfield(double x, bool missing)
+{
+    if (! config_.displayBitfieldsBinary())
+    {
+        outputUnsignedInt(x, missing);
+        return;
+    }
+	
+	format(out_, currentColumn_);
+	if (missing && !config_.doNotWriteNULL())
+		out_ << "NULL";
+	else
+	{
+        std::stringstream ss;
+		Decoder::printBinary(ss, static_cast<unsigned long>(x));
+		out_ << ss.str();
+	}
+}
+
+void SQLSimpleOutput::prepare(SQLSelect& sql)
+{
+    printHeader(sql);
+}
+
+void SQLSimpleOutput::printHeader(SQLSelect& sql)
+{
+    const expression::Expressions& columns(sql.output());
+    for (size_t i (0); i < columns.size(); i++)
+    {
+        const std::string& name (columns[i]->title());
+        const type::SQLType* type (columns[i]->type());
+ 
+        columnWidths_.push_back(config_.disableAlignmentOfColumns() ? 1 : std::max(type->width(), name.size()));
+        columnAlignments_.push_back(type->format());
+
+        if (! config_.doNotWriteColumnNames())
+        {
+            if(i) out_ << config_.fieldDelimiter();
+
+            format(out_, i);
+
+            if (config_.outputFormat() != "wide")
+                out_ << name;
+            else
+            {
+                std::stringstream ss;
+                ss << name << ":" << type->name();
+                out_ << ss.str();
+            }
+
+		}
+		
+	}
+    if (! config_.doNotWriteColumnNames())
+        out_ << "\n";
+}
+
+void SQLSimpleOutput::cleanup(SQLSelect& sql) {}
+unsigned long long SQLSimpleOutput::count() { return count_; }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLSimpleOutput.h b/odb_api/src/odb_api/SQLSimpleOutput.h
new file mode 100755
index 0000000..554be3c
--- /dev/null
+++ b/odb_api/src/odb_api/SQLSimpleOutput.h
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SQLSimpleOutput.h
+/// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef odb_api_SQLSimpleOutput_H
+#define odb_api_SQLSimpleOutput_H
+
+#include "odb_api/SQLOutput.h"
+
+namespace odb {
+namespace sql {
+
+class SQLSimpleOutput : public SQLOutput {
+public:
+    SQLSimpleOutput(std::ostream& = std::cout);
+	virtual ~SQLSimpleOutput(); 
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+	SQLSimpleOutput(const SQLSimpleOutput&);
+	SQLSimpleOutput& operator=(const SQLSimpleOutput&);
+
+    std::ostream& out_;
+	unsigned long long count_;
+	std::vector<size_t> columnWidths_;
+	typedef std::ios_base& (*manipulator)(std::ios_base&);
+	std::vector<manipulator> columnAlignments_;
+	size_t currentColumn_;
+
+    std::ostream& format(std::ostream&, size_t) const;
+
+	void printHeader(SQLSelect&);
+
+// -- Overridden methods
+	virtual void size(int);
+	virtual void reset();
+	virtual void flush(ecml::ExecutionContext*);
+	virtual bool output(const expression::Expressions&, ecml::ExecutionContext*);
+	virtual void prepare(SQLSelect&);
+	virtual void cleanup(SQLSelect&);
+	virtual unsigned long long count();
+
+    template <typename T> void outputValue(double x, bool missing);
+
+	virtual void outputReal(double, bool);
+	virtual void outputDouble(double, bool);
+	virtual void outputInt(double, bool);
+	virtual void outputUnsignedInt(double, bool);
+	virtual void outputString(double, bool);
+	virtual void outputBitfield(double, bool);
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLStatement.cc b/odb_api/src/odb_api/SQLStatement.cc
new file mode 100755
index 0000000..c3f3396
--- /dev/null
+++ b/odb_api/src/odb_api/SQLStatement.cc
@@ -0,0 +1,23 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLStatement.h"
+
+namespace odb {
+namespace sql {
+
+SQLStatement::SQLStatement() {}
+
+SQLStatement::~SQLStatement() {}
+
+void SQLStatement::print(std::ostream& s) const {}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLStatement.h b/odb_api/src/odb_api/SQLStatement.h
new file mode 100755
index 0000000..6b1fea3
--- /dev/null
+++ b/odb_api/src/odb_api/SQLStatement.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLStatement.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLStatement_H
+#define SQLStatement_H
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+// Forward declarations
+
+class SQLDatabase;
+
+class SQLStatement {
+public:
+	SQLStatement();
+	virtual ~SQLStatement(); 
+
+	virtual unsigned long long execute(ecml::ExecutionContext*) = 0;
+	virtual expression::Expressions output() const = 0;
+
+protected:
+	virtual void print(std::ostream&) const; 	
+
+private:
+// No copy allowed
+	SQLStatement(const SQLStatement&);
+	SQLStatement& operator=(const SQLStatement&);
+
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLStatement& p)
+		{ p.print(s); return s; }
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLString.cc b/odb_api/src/odb_api/SQLString.cc
new file mode 100644
index 0000000..c5f4b6e
--- /dev/null
+++ b/odb_api/src/odb_api/SQLString.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/SQLOutput.h"
+#include "odb_api/SQLString.h"
+
+namespace odb {
+namespace sql {
+namespace type {
+
+SQLString::SQLString(const std::string& name) : SQLType(name) {} 
+
+SQLString::~SQLString() {}
+
+size_t SQLString::size() const { return sizeof(double); }
+
+void SQLString::output(SQLOutput& o, double d, bool missing) const { o.outputString(d, missing); }
+
+SQLType::manipulator SQLString::format() const { return &std::left; }
+
+size_t SQLString::width() const { return 10; }
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SQLString.h b/odb_api/src/odb_api/SQLString.h
new file mode 100644
index 0000000..38ac151
--- /dev/null
+++ b/odb_api/src/odb_api/SQLString.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLString.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLString_H
+#define SQLString_H
+
+#include "odb_api/SQLType.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace type {
+
+class SQLString : public SQLType {
+public:
+	SQLString(const std::string& );
+	~SQLString(); 
+
+private:
+// No copy allowed
+	SQLString(const SQLString&);
+	SQLString& operator=(const SQLString&);
+
+// -- Overridden methods
+	virtual size_t size() const;
+	virtual void output(SQLOutput&, double, bool) const;
+	virtual int getKind() const { return stringType; }
+	virtual manipulator format() const;
+
+	size_t width() const;
+
+	//friend std::ostream& operator<<(std::ostream& s,const SQLString& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace type 
+} // namespace sql 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SQLTable.cc b/odb_api/src/odb_api/SQLTable.cc
new file mode 100755
index 0000000..579fc8b
--- /dev/null
+++ b/odb_api/src/odb_api/SQLTable.cc
@@ -0,0 +1,236 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/parser/Tokenizer.h"
+#include "odb_api/SQLBitColumn.h"
+#include "odb_api/SQLColumn.h"
+#include "odb_api/SQLDatabase.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SQLTable::SQLTable(SQLDatabase& owner,const std::string& path,const std::string& name):
+	path_(path),
+	name_(name),
+	owner_(owner),
+	master_(0)
+{
+	//Log::info() << "SQLTable[path=" << path_ << ",name=" << name << ",no_rows=" << no_rows_ << ",index=" << index_ << "]" << std::endl;
+}
+
+SQLTable::~SQLTable() { clearColumns(); }
+
+void SQLTable::clearColumns()
+{
+	// Don't loop on names, as we have all bitmap entries pointing to the same
+	for(std::map<int,SQLColumn*>::iterator m = columnsByIndex_.begin(); m != columnsByIndex_.end(); ++m)
+	{
+		SQLColumn* p = (*m).second;
+		delete p;
+	}
+	columnsByName_.clear();
+	columnsByIndex_.clear();
+}
+
+std::vector<std::string> SQLTable::columnNames() const
+{
+	std::vector<std::string> results;
+	for(std::map<int,SQLColumn*>::const_iterator j = columnsByIndex_.begin(); j != columnsByIndex_.end(); ++j)
+		results.push_back((*j).second->name());
+	return results;
+}
+
+FieldNames SQLTable::bitColumnNames(const std::string& name) const
+{
+	typedef std::map<std::string, FieldNames>::const_iterator I;
+	I i = bitColumnNames_.find(name);
+	if (i != bitColumnNames_.end())
+		return (*i).second;
+
+	ASSERT("name not found" && name.find("@") == std::string::npos);
+
+
+	std::string columnName;
+	FieldNames fieldNames;
+	size_t counter = 0;
+	for (I i = bitColumnNames_.begin(); i != bitColumnNames_.end(); ++i)
+	{
+		if (i->first.find(name + "@") == 0)
+		{
+			columnName = i->first;
+			fieldNames = i->second;
+			++counter;
+		}
+	}
+	if (counter == 0) throw eckit::UserError(std::string("Column '") + name + "' not found.");
+	if (counter != 1) throw eckit::UserError(std::string("Ambiguous column name: '") + name + "'");
+
+	return fieldNames;
+}
+
+
+//void SQLTable::addColumn(const std::string& name, int index, const type::SQLType& type, const FieldNames& bitmap)
+void SQLTable::addColumn(const std::string& name, int index, const type::SQLType& type, bool hasMissingValue, double missingValue, bool
+isBitfield, const BitfieldDef& bitfieldDef)
+{
+	const FieldNames& bitmap = bitfieldDef.first;
+	SQLColumn *col = isBitfield ? createSQLColumn(type, name, index, hasMissingValue, missingValue, bitfieldDef)
+                                : createSQLColumn(type, name, index, hasMissingValue, missingValue);
+
+	columnsByName_[name]   = col;
+	columnsByIndex_[index] = col;
+
+	bitColumnNames_[name] = bitmap;
+
+	std::vector<std::string> tokens;
+	Tokenizer("@")(name, tokens);
+
+	ASSERT(tokens.size() == 1 || tokens.size() == 2);
+
+	// TODO: clean up, probably no need to do this parsing as we have the whole bitfieldDef now
+	std::string tableName = (tokens.size() == 2) ? tokens[1] : "";
+	std::string columnName = tokens[0];
+
+	for(FieldNames::const_iterator j = bitmap.begin(); j != bitmap.end(); ++j)
+	{
+		std::string fieldName = *j;
+		std::string n = columnName + "." + fieldName + "@" + tableName;
+		columnsByName_[n] = col;
+
+		//Log::info() << "SQLTable::addColumn: columnsByName_[" << n << "] = " << *col << std::endl;
+	}
+}
+
+void SQLTable::addColumn(SQLColumn *col, const std::string& name, int index)
+{
+	columnsByName_[name]   = col;	
+	columnsByIndex_[index] = col;	
+}
+
+//bool SQLTable::hasColumn(const std::string& name, std::string* fullName, bool *hasMissingValue, double *missingValue, BitfieldDef* bitfieldDef)
+bool SQLTable::hasColumn(const std::string& name, std::string* fullName)
+{
+	std::map<std::string,SQLColumn*>::iterator j = columnsByName_.find(name);
+	return j != columnsByName_.end();
+}
+
+unsigned long long SQLTable::noRows() const
+{
+	std::map<std::string,SQLColumn*>::const_iterator j = columnsByName_.begin();
+	if(j != columnsByName_.end())
+		return(*j).second->noRows();
+	return 0;
+}
+
+
+SQLColumn* SQLTable::column(const std::string& name)
+{
+	std::map<std::string,SQLColumn*>::iterator j = columnsByName_.find(name);
+	if(j != columnsByName_.end())
+		return (*j).second;
+
+	std::vector<std::string> v;
+	Tokenizer(".")(name, v);
+
+	if(v.size() > 1)
+	{
+		SQLColumn* col = column(v[0]);
+		columnsByName_[name] = new SQLBitColumn(*col,v[1]);
+		return columnsByName_[name];
+	}
+
+	throw eckit::UserError("Column not found",name);
+
+}
+
+SQLPool* SQLTable::pool(int index)
+{
+	std::map<int,SQLPool*>::iterator j = pools_.find(index);
+	if(j == pools_.end())
+		return 0;
+	else
+		return (*j).second;
+}
+
+void SQLTable::addLinkFrom(const SQLTable* from)
+{
+	linksFrom_.insert(from);
+}
+
+bool SQLTable::hasLinkFrom(const SQLTable& from) const
+{
+	return linksFrom_.find(&from) != linksFrom_.end();
+}
+
+void SQLTable::addLinkTo(const SQLTable* to)
+{
+	linksTo_.insert(to);
+}
+
+bool SQLTable::hasLinkTo(const SQLTable& to) const
+{
+	return linksTo_.find(&to) != linksTo_.end();
+}
+
+bool SQLTable::isParentOf(const SQLTable& other) const
+{
+	if(hasLinkTo(other))
+		return true;
+
+	for(std::set<const SQLTable*>::const_iterator j = linksTo_.begin();
+		j != linksTo_.end(); ++j)
+			if((*j)->isParentOf(other))
+				return true;
+
+	return false;
+}
+
+SQLTable* SQLTable::master() const      
+{ 
+	return master_? master_ : const_cast<SQLTable*>(this); 
+}
+
+void SQLTable::master(SQLTable* master) 
+{ 
+//	std::cout << "MASTER of " << name() << " " << master->name() << std::endl;
+	master_ = master;            
+}
+
+std::string SQLTable::fullName() const
+{
+	return owner_.name() + "." + name_;
+}
+
+bool SQLTable::sameAs(const SQLTable& other) const
+{
+	return owner_.sameAs(other.owner_) && (name_ == other.name_);
+}
+
+bool SQLTable::sameDatabase(const SQLTable& other) const
+{
+	return &owner_ == &other.owner_;
+}
+
+void SQLTable::print(std::ostream& s) const
+{
+	s << "CREATE TABLE " << fullName() << " AS (" << std::endl;
+	for(std::map<int,SQLColumn*>::const_iterator j = columnsByIndex_.begin(); j != columnsByIndex_.end(); ++j)
+	{
+		SQLColumn *c = j->second;
+		s << "	" << c->name() << " " << c->type() << "," << std::endl;
+	}
+	s << ")" << std::endl;
+}
+
+} // namespace sql 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/SQLTable.h b/odb_api/src/odb_api/SQLTable.h
new file mode 100755
index 0000000..1e2ea41
--- /dev/null
+++ b/odb_api/src/odb_api/SQLTable.h
@@ -0,0 +1,120 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLTable.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLTable_H
+#define SQLTable_H
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/memory/NonCopyable.h"
+#include "odb_api/SQLType.h"
+#include "odb_api/Types.h"
+
+namespace odb {
+namespace sql {
+
+//class SQLFile;
+class SQLPool;
+class SQLColumn;
+class SQLDatabase;
+
+class SQLTableIterator {
+public:
+	virtual ~SQLTableIterator() {}
+	virtual void rewind() = 0;
+	virtual bool next()   = 0;
+};
+
+typedef std::vector<std::string> ColumnNames;
+
+class SQLTable : private eckit::NonCopyable {
+public:
+	SQLTable(SQLDatabase&,const std::string&,const std::string&);
+	virtual ~SQLTable(); 
+
+    void loadIOMAP(std::istream&);
+	void addColumn(const std::string&, int, const type::SQLType&, bool, double, bool, const BitfieldDef&);
+
+	void addLinkFrom(const SQLTable*);
+	bool hasLinkFrom(const SQLTable&) const;
+
+	void addLinkTo(const SQLTable*);
+	bool hasLinkTo(const SQLTable&) const;
+
+	bool isParentOf(const SQLTable&) const;
+
+	virtual SQLColumn* column(const std::string&);
+	SQLPool*   pool(int);
+
+	SQLTable* master() const;
+	void master(SQLTable* master);
+
+	//virtual bool hasColumn(const std::string&, std::string* fullName = 0, bool *hasMissingValue=0, double *missingValue=0, BitfieldDef*=0);
+	virtual bool hasColumn(const std::string&, std::string* fullName = 0);//, std::string* fullName = 0, bool *hasMissingValue=0, double *missingValue=0, BitfieldDef*=0);
+
+	unsigned long long noRows() const;
+
+	ColumnNames columnNames() const;
+	FieldNames bitColumnNames(const std::string&) const;
+
+	const std::string& name()  const { return name_; }
+
+	std::string fullName() const;
+
+	SQLDatabase& owner() const { return owner_; }
+
+	bool sameAs(const SQLTable& other) const;
+	bool sameDatabase(const SQLTable& other) const;
+
+    const std::string& path() const { return path_; }
+
+	virtual void print(std::ostream& s) const;
+
+	virtual SQLTableIterator* iterator(const std::vector<SQLColumn*>&) const = 0;
+
+protected:
+    std::string path_;
+	std::string   name_;
+
+	//std::map<int,SQLFile*> files_;
+    std::map<int, SQLPool*> pools_;
+
+    std::map<std::string, FieldNames > bitColumnNames_;
+    std::map<std::string, SQLColumn*>  columnsByName_;
+    std::map<int, SQLColumn*>     columnsByIndex_;
+
+    std::set<const SQLTable*> linksFrom_;
+    std::set<const SQLTable*> linksTo_;
+
+// -- Methods
+	void clearColumns();
+	
+	// void print(std::ostream&) const; 	
+	void addColumn(SQLColumn*, const std::string&, int);
+
+	virtual SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue, const BitfieldDef&) = 0;
+	virtual SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue) = 0;
+
+private:
+
+	SQLDatabase& owner_;
+	SQLTable* master_;
+
+	friend std::ostream& operator<<(std::ostream& s,const SQLTable& p)
+		{ p.print(s); return s; }
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SQLType.cc b/odb_api/src/odb_api/SQLType.cc
new file mode 100755
index 0000000..1b527c5
--- /dev/null
+++ b/odb_api/src/odb_api/SQLType.cc
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/thread/ThreadSingleton.h"
+#include "eckit/log/Log.h"
+#include "odb_api/SQLDouble.h"
+#include "odb_api/SQLInt.h"
+#include "odb_api/SQLReal.h"
+#include "odb_api/SQLString.h"
+#include "odb_api/SQLType.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace type {
+
+class TypeRegistry {
+public:
+    TypeRegistry();
+    static std::map<std::string,SQLType*>& typeMap();
+    std::map<std::string,SQLType*>& map();
+
+private:
+    std::map<std::string,SQLType*> map_;
+
+    SQLType* registerType(SQLType*);
+};
+
+static eckit::ThreadSingleton<TypeRegistry> typeRegistry_;
+
+TypeRegistry::TypeRegistry()
+: map_()
+{
+    registerType(new SQLInt("integer"));
+    registerType(new SQLReal("real"));
+    registerType(new SQLString("string"));
+    registerType(new SQLDouble("double"));
+}
+
+SQLType* TypeRegistry::registerType(SQLType* t)
+{
+    map_.insert(make_pair(t->name(), t));
+    return t;
+}
+
+std::map<std::string,SQLType*>& TypeRegistry::typeMap() { return typeRegistry_.instance().map(); }
+std::map<std::string,SQLType*>& TypeRegistry::map() { return map_; }
+
+SQLType* SQLType::registerType(SQLType* t)
+{
+    std::map<std::string,SQLType*>& map (TypeRegistry::typeMap());
+    //ASSERT(map.find(t->name()) == map.end());
+    map.insert(make_pair(t->name(), t));
+    return t;
+}
+
+size_t SQLType::width() const { return 14; }
+SQLType::manipulator SQLType::format() const { return &std::right; }
+
+SQLType::SQLType(const std::string& name): name_(name) {}
+
+SQLType::SQLType(const std::string& name, const std::string& shortName)
+: name_(name)
+{
+    std::map<std::string,SQLType*>& map (typeRegistry_.instance().typeMap());
+    ASSERT(map.find(name) == map.end());
+    map[shortName] = map[name_] = this;
+}
+
+SQLType::~SQLType() {}
+
+bool SQLType::exists(const std::string& name)
+{
+    std::map<std::string,SQLType*>& map (TypeRegistry::typeMap());
+    return map.find(name) != map.end();
+}
+
+const SQLType& SQLType::lookup(const std::string& name)
+{
+    std::map<std::string,SQLType*>& map (TypeRegistry::typeMap());
+    std::map<std::string,SQLType*>::iterator j (map.find(name));
+    if(j == map.end())
+        throw eckit::SeriousBug(name + ": type not defined");
+    return *(*j).second;
+}
+
+void SQLType::createAlias(const std::string& name, const std::string& alias)
+{
+    std::map<std::string,SQLType*>& map (TypeRegistry::typeMap());
+    ASSERT(SQLType::exists(name));
+    map[alias] = map[name];
+}
+
+void SQLType::print(std::ostream& s) const { s << name_; }
+
+const SQLType* SQLType::subType(const std::string&) const { return this; }
+
+
+} // namespace type
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/SQLType.h b/odb_api/src/odb_api/SQLType.h
new file mode 100755
index 0000000..350e013
--- /dev/null
+++ b/odb_api/src/odb_api/SQLType.h
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SQLType.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SQLType_H
+#define SQLType_H
+
+#include "eckit/eckit.h"
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace type {
+
+class SQLType {
+public:
+
+	enum {
+		realType    = 0,
+		integerType = 1,
+		stringType  = 2,
+		bitmapType  = 3,
+		blobType    = 4,
+		doubleType  = 5
+	};
+
+	SQLType(const std::string&);
+
+	/// Constructor used when defining a bitfield.
+	SQLType(const std::string&, const std::string&);
+
+	virtual ~SQLType(); 
+
+	const std::string& name() const { return name_; }
+
+	virtual size_t size() const = 0;
+	virtual void output(SQLOutput&, double, bool) const = 0;
+	virtual const SQLType* subType(const std::string&) const;
+
+	virtual int getKind() const = 0;
+
+	// Formating functions (used by SQLSimpleOutput)
+	virtual size_t width() const;
+	typedef std::ios_base& (*manipulator)(std::ios_base&);
+	virtual manipulator format() const;
+
+	static const SQLType& lookup(const std::string&);
+	static void createAlias(const std::string&, const std::string&);
+    static SQLType* registerType(SQLType*);
+
+protected:
+    virtual void print(std::ostream&) const;
+	static bool exists(const std::string&);
+
+private:
+// No copy allowed
+	SQLType(const SQLType&);
+	SQLType& operator=(const SQLType&);
+
+	std::string name_;
+
+    friend std::ostream& operator<<(std::ostream& s,const SQLType& p)
+		{ p.print(s); return s; }
+};
+
+class DynamicallyCreatedTypesDestroyer {
+public:
+	static SQLType* registerType(SQLType*);
+	~DynamicallyCreatedTypesDestroyer ();
+};
+
+} // namespace type 
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SchemaAnalyzer.cc b/odb_api/src/odb_api/SchemaAnalyzer.cc
new file mode 100644
index 0000000..b13f74c
--- /dev/null
+++ b/odb_api/src/odb_api/SchemaAnalyzer.cc
@@ -0,0 +1,248 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/StringTool.h"
+#include "eckit/parser/StringTools.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+SchemaAnalyzer::SchemaAnalyzer()
+{
+//desc, hdr, sat, reo3, satob, rtovs, rtovs_slev, rtovs_mlev, rtovs_pred, atovs, atovs_pred, scatt, ssmi, ssmi_slev, ssmi_mlev, body, errstat, update, rtovs_body, ssmi_body, scatt_body
+	skipTable("poolmask");
+	skipTable("bufr");
+	skipTable("ddrs");
+	skipTable("index");
+	skipTable("rtovs");
+	skipTable("rtovs_slev");
+	skipTable("rtovs_mlev");
+	skipTable("rtovs_pred");
+	skipTable("rtovs_body");
+	skipTable("ssmi");
+	skipTable("ssmi_body");
+	skipTable("ssmi_slev");
+	skipTable("ssmi_pred");
+	skipTable("ssmi_mlev");
+	skipTable("atovs");
+	skipTable("atovs_pred");
+	skipTable("scatt");
+	skipTable("scatt_body");
+	skipTable("reo3");
+	skipTable("sat");
+	skipTable("satob");
+	skipTable("update");
+	skipTable("");
+	skipTable("");
+} 
+
+SchemaAnalyzer::~SchemaAnalyzer() {}
+
+void SchemaAnalyzer::beginSchema(const std::string& name)
+{
+    if (!currentSchema_.empty())
+    {
+        std::string message = "Cannot create new schema '" + name
+            + "' - current schema '" + currentSchema_ + "' not finalized";
+        throw eckit::UserError(message);
+    }
+
+    std::pair<SchemaDefs::iterator, bool> result;
+    result = schemas_.insert(make_pair(name, SchemaDef()));
+
+    if (result.second == false)
+    {
+        std::string message = "Schema '" + name + "' already defined";
+        throw eckit::UserError(message);
+    }
+
+    currentSchema_ = name;
+}
+
+void SchemaAnalyzer::endSchema()
+{
+    currentSchema_.clear();
+}
+
+void SchemaAnalyzer::addTable(TableDef& table) 
+{
+    std::string schemaName = "";
+
+    if (StringTool::isInQuotes(table.name())) 
+        table.name(StringTool::unQuote(table.name()));
+    else {
+        size_t pos = table.name().find(".");
+        if (pos != std::string::npos)
+        {
+            schemaName = table.name().substr(0, pos);
+            table.name(table.name().substr(pos + 1));
+        }
+    }
+
+	ColumnDefs& columns (table.columns());
+	for (ColumnDefs::iterator it = columns.begin(); it != columns.end(); ++it) {
+        ColumnDef& column(*it);
+        column.name(column.name() + "@" + table.name());
+        columnTypes_[column.name()] = column.type();
+
+        if (isBitfield(column.name()))
+            column.bitfieldDef(getBitfieldTypeDefinition(column.name()));
+	}
+
+    for (int i = 0, n = table.parents().size(); i < n; i++) {
+        TableDefs::const_iterator it(tableDefs_.find(table.parents()[i]));
+
+        if (it == tableDefs_.end())
+            throw eckit::UserError(std::string("Could not find definition of parent table '")
+                     + table.parents()[i] + "' inherited by table '" + table.name() + "'");
+
+        const TableDef& parent (it->second);
+        if(! parent.parents().empty()) throw UserError("More than 1-level inheritance not supported");
+
+        for (ColumnDefs::const_iterator c (parent.columns().begin()); c != parent.columns().end(); ++c)
+            table.columns().push_back(*c);
+    }
+
+    if (currentSchema_.empty() && schemaName.empty()) {
+        std::pair<TableDefs::iterator, bool> result (tableDefs_.insert(std::pair<std::string, TableDef>(table.name(), table)));
+        if (result.second == false)
+            throw eckit::UserError(std::string ("Table '") + table.name() + "' already defined");
+    } else {
+        if (schemaName.empty())
+            schemaName = currentSchema_;
+
+        SchemaDefs::iterator it (schemas_.find(schemaName));
+        if (it == schemas_.end())
+            throw eckit::UserError(std::string("Referenced schema '") + schemaName + "' not defined '");
+
+        SchemaDef& schema (it->second);
+        TableDefs& tables (schema.tables());
+
+        std::pair<TableDefs::iterator, bool> result (tables.insert(std::pair<std::string, TableDef>(table.name(), table)));
+        if (result.second == false) 
+            throw eckit::UserError(std::string ("Table '") + table.name() + "' already defined in '" + schemaName + "' schema");
+    }
+}
+
+void SchemaAnalyzer::skipTable(std::string tableName)
+{
+	tablesToSkip_.insert(tableName);
+}
+
+std::string SchemaAnalyzer::generateSELECT() const
+{
+	std::string from = "";
+	std::string selectList = "";
+	if (tableDefs_.size() == 0)
+		return "";
+
+	for (TableDefs::const_iterator t = tableDefs_.begin(); t != tableDefs_.end(); ++t)
+	{
+		TableDef tableDef = t->second;
+		std::string tableName = tableDef.name();
+
+		if (tablesToSkip_.find(tableName) != tablesToSkip_.end())
+			continue;
+
+		from += tableName + ", ";
+		ColumnDefs columnDefs = tableDef.columns();
+		
+		for (ColumnDefs::const_iterator i = columnDefs.begin(); i != columnDefs.end(); i++)
+		{
+			const std::string typeName = i->type();
+			if (typeName == "@LINK") {
+				Log::info() << "SchemaAnalyzer::generateSELECT: Skipping " << i->name() << std::endl;
+				continue;
+			}
+			selectList += i->name() + ", ";
+		}
+		selectList += "\n";
+	}
+	return "\nSELECT\n" + selectList + "\n FROM\n" + from;
+}
+
+Definitions SchemaAnalyzer::generateDefinitions()
+{
+    return Definitions(schemas_, tableDefs_);
+}
+
+void SchemaAnalyzer::addBitfieldType(const std::string& name, const FieldNames& fields, const Sizes& sizes, const std::string& typeSignature)
+{
+	bitfieldTypes_[name] = make_pair(fields, sizes);
+}
+
+bool SchemaAnalyzer::isBitfield(const std::string& columnName) const
+{
+    for (std::map<std::string,std::string>::const_iterator it(columnTypes_.begin()); it != columnTypes_.end(); ++it)
+    {
+        if (it->first == columnName)
+        {
+            std::string columnType (it->second);
+            for (std::map<std::string, BitfieldDef>::const_iterator it(bitfieldTypes_.begin()); it != bitfieldTypes_.end(); ++it)
+                if (it->first == columnType)
+                    return true;
+        }
+    }
+    return false;
+}
+
+const BitfieldDef& SchemaAnalyzer::getBitfieldTypeDefinition(const std::string& columnName) 
+{
+	ASSERT(isBitfield(columnName));
+	std::string columnType = columnTypes_.find(columnName)->second;
+	return bitfieldTypes_[columnType];
+}
+
+void SchemaAnalyzer::updateBitfieldsDefs(MetaData &md, std::map<std::string,std::string> & truenames) const
+{
+	for (size_t i = 0; i < md.size(); i++)
+	{
+		Column &c (*md[i]);
+		if (c.type() == BITFIELD)
+			c.bitfieldDef(const_cast<SchemaAnalyzer*>(this)->getBitfieldTypeDefinition(truenames[c.name()]));
+	}
+}
+
+bool SchemaAnalyzer::tableKnown(const std::string& name) const
+{
+    return tableDefs_.find(name) != tableDefs_.end();
+}
+
+const TableDef& SchemaAnalyzer::findTable(const std::string& name) const
+{
+    for (TableDefs::const_iterator it(tableDefs_.begin()); it != tableDefs_.end(); ++it)
+        if (it->first == name)
+            return it->second;
+    throw eckit::UserError(std::string("Table '" + name + "' not found"));
+}
+
+std::string SchemaAnalyzer::findColumnType(const std::string& columnName)
+{
+    std::vector<std::string> ps (StringTools::split("@", columnName));
+    if (ps.size() != 2)
+        throw eckit::UserError(std::string("Expected fully qualified column name (<name>@<table>), got: " + columnName));
+
+    std::string name (ps[0]), table (ps[1]);
+    const TableDef& tableDef (findTable(table));
+    ColumnDefs columnDefs (tableDef.columns());
+    for (ColumnDefs::const_iterator it (columnDefs.begin()); it != columnDefs.end(); ++it)
+        if (it->name() == columnName)
+            return it->type();
+    throw eckit::UserError(std::string("Column " + columnName + " not found"));
+}
+
+} // namespace sql
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/SchemaAnalyzer.h b/odb_api/src/odb_api/SchemaAnalyzer.h
new file mode 100644
index 0000000..bb06c7b
--- /dev/null
+++ b/odb_api/src/odb_api/SchemaAnalyzer.h
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file SchemaAnalyzer.h
+/// @author Piotr Kuchta, ECMWF April 2009
+
+#ifndef odb_api_SchemaAnalyzer_H
+#define odb_api_SchemaAnalyzer_H
+
+#include "odb_api/SQLAST.h"
+
+namespace odb {
+
+class MetaData;
+
+namespace sql {
+
+class SchemaAnalyzer {
+
+public:
+
+    SchemaAnalyzer();
+    ~SchemaAnalyzer();
+
+    void beginSchema(const std::string& name);
+    void endSchema();
+    void addTable(TableDef& table);
+
+    void addBitfieldType(const std::string& name, const FieldNames& fields, const Sizes& sizes, const std::string& typeSignature);
+    bool isBitfield(const std::string& columnName) const; 
+    const BitfieldDef& getBitfieldTypeDefinition(const std::string& columnName); 
+    void updateBitfieldsDefs(MetaData &, std::map<std::string, std::string> &) const;
+    bool tableKnown(const std::string& name) const;
+    const TableDef& findTable(const std::string& name) const;
+    void skipTable(std::string tableName);
+    std::string generateSELECT() const;
+    Definitions generateDefinitions();
+    std::string findColumnType(const std::string&);
+
+private:
+    std::string currentSchema_;
+    SchemaDefs schemas_;
+    TableDefs tableDefs_;
+    BitfieldDefs bitfieldTypes_;
+    std::set<std::string> tablesToSkip_;
+    std::map<std::string,std::string> columnTypes_;
+
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
+
diff --git a/odb_api/src/odb_api/Select.cc b/odb_api/src/odb_api/Select.cc
new file mode 100644
index 0000000..ce85131
--- /dev/null
+++ b/odb_api/src/odb_api/Select.cc
@@ -0,0 +1,185 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Select.cc
+///
+/// @author Piotr Kuchta, April 2010
+
+#include "ecml/data/DataHandleFactory.h"
+#include "odb_api/Select.h"
+#include "eckit/io/DataHandle.h"
+#include "odb_api/SQLOutputConfig.h"
+
+using namespace std;
+using namespace eckit;
+using namespace ecml;
+
+namespace odb {
+
+Select::Select(const std::string& selectStatement, DataHandle &dh)
+: dataHandle_(&dh),
+  deleteDataHandle_(false),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(0),
+  ownSession_(ownSession(",")),
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement, DataHandle &dh, ecml::ExecutionContext* context)
+: dataHandle_(&dh),
+  deleteDataHandle_(false),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(context),
+  ownSession_(ownSession(",")), // TODO: get csv_delimiter from context
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement, std::istream &is, const std::string& delimiter)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(&is),
+  deleteIStream_(false),
+  selectStatement_(selectStatement),
+  delimiter_(delimiter),
+  context_(0),
+  ownSession_(ownSession(delimiter)),
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement, std::istream &is, const std::string& delimiter, ecml::ExecutionContext* context)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(&is),
+  deleteIStream_(false),
+  selectStatement_(selectStatement),
+  delimiter_(delimiter),
+  context_(context),
+  ownSession_(ownSession(delimiter)),
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(0),
+  ownSession_(ownSession(",")),
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement, odb::sql::SQLNonInteractiveSession& s)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(0),
+  ownSession_(ownSession(s.csvDelimiter())),
+  outerSession_(&s)
+{}
+
+Select::Select(const std::string& selectStatement, ecml::ExecutionContext* context)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(context),
+  ownSession_(ownSession(",")),  // TODO: get csv_delimiter from context
+  outerSession_(ownSession_)
+{}
+
+Select::Select()
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(),
+  context_(0),
+  ownSession_(ownSession(",")),
+  outerSession_(ownSession_)
+{}
+
+Select::Select(ecml::ExecutionContext* context)
+: dataHandle_(0),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(),
+  context_(context),
+  ownSession_(ownSession(",")), // TODO: get csv_delimiter from context
+  outerSession_(ownSession_)
+{}
+
+Select::Select(const std::string& selectStatement, const std::string& path)
+: dataHandle_(DataHandleFactory::openForRead(path)),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(0),
+  ownSession_(ownSession(",")),
+  outerSession_(ownSession_)
+{
+    //dataHandle_->openForRead();
+}
+
+Select::Select(const std::string& selectStatement, const std::string& path, ecml::ExecutionContext* context)
+: dataHandle_(DataHandleFactory::openForRead(path)),
+  deleteDataHandle_(true),
+  istream_(0),
+  deleteIStream_(true),
+  selectStatement_(selectStatement),
+  context_(context),
+  ownSession_(ownSession(",")),  // TODO: get csv_delimiter from context
+  outerSession_(ownSession_)
+{
+    //dataHandle_->openForRead();
+}
+
+Select::~Select()
+{
+    if (deleteDataHandle_) delete dataHandle_;
+    if (deleteIStream_) delete istream_;
+    delete ownSession_;
+}
+
+SelectIterator* Select::createSelectIterator(const std::string& sql, ecml::ExecutionContext* context)
+{
+    return new SelectIterator(*this, sql, context, *outerSession_);
+}
+
+const Select::iterator Select::end() { return iterator(0); }
+
+Select::iterator Select::begin()
+{
+    SelectIterator* it = new SelectIterator(*this, selectStatement_, context_, *outerSession_);
+    ASSERT(it);
+    it->next(context_);
+    return iterator(it);
+}
+
+odb::sql::SQLNonInteractiveSession* Select::ownSession(const std::string& delimiter)
+{ 
+    return new odb::sql::SQLNonInteractiveSession(odb::sql::SQLOutputConfig::defaultConfig(), delimiter); 
+}
+
+#ifdef SWIGPYTHON
+template odb::IteratorProxy< odb::SelectIterator,odb::Select,double const >; 
+#endif
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/Select.h b/odb_api/src/odb_api/Select.h
new file mode 100644
index 0000000..95d339f
--- /dev/null
+++ b/odb_api/src/odb_api/Select.h
@@ -0,0 +1,88 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Select.h
+///
+/// @author Piotr Kuchta, April 2010
+
+#ifndef ODBSELECT_H
+#define ODBSELECT_H
+
+#ifdef SWIGPYTHON
+#include <Python.h>
+#endif
+
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/SQLNonInteractiveSession.h"
+#include "odb_api/SQLIteratorSession.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+namespace odb {
+
+class SelectIterator;
+
+class Select
+{
+public:
+	typedef IteratorProxy<SelectIterator,Select,const double> iterator;
+	typedef iterator::Row row;
+
+	Select(const std::string& selectStatement, eckit::DataHandle &);
+	Select(const std::string& selectStatement, eckit::DataHandle &, ecml::ExecutionContext*);
+	Select(const std::string& selectStatement, std::istream &, const std::string& delimiter);
+	Select(const std::string& selectStatement, std::istream &, const std::string& delimiter, ecml::ExecutionContext*);
+    Select(const std::string& selectStatement, const std::string& path);
+    Select(const std::string& selectStatement, const std::string& path, ecml::ExecutionContext*);
+	Select(const std::string& selectStatement);
+	Select(const std::string& selectStatement, ecml::ExecutionContext*);
+	Select(const std::string& selectStatement, odb::sql::SQLNonInteractiveSession&);
+	Select();
+	Select(ecml::ExecutionContext*);
+
+	virtual ~Select();
+
+#ifdef SWIGPYTHON
+	iterator __iter__() { return iterator(createSelectIterator(selectStatement_, context_)); }
+#endif
+
+	iterator begin();
+	const iterator end();
+
+	eckit::DataHandle* dataHandle() { return dataHandle_; };
+	std::istream* dataIStream() { return istream_; }
+
+	SelectIterator* createSelectIterator(const std::string&, ecml::ExecutionContext*);
+
+private:
+    odb::sql::SQLNonInteractiveSession* ownSession(const std::string& delimiter);
+
+	friend class odb::IteratorProxy<odb::SelectIterator, odb::Select, const double>;
+
+	eckit::DataHandle* dataHandle_;
+	bool deleteDataHandle_;
+
+	std::istream* istream_;
+	bool deleteIStream_;
+
+	std::string selectStatement_;
+	std::string delimiter_;
+
+    ecml::ExecutionContext* context_;
+    odb::sql::SQLNonInteractiveSession* ownSession_;
+    odb::sql::SQLNonInteractiveSession* outerSession_;
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SelectIterator.cc b/odb_api/src/odb_api/SelectIterator.cc
new file mode 100644
index 0000000..47ca01f
--- /dev/null
+++ b/odb_api/src/odb_api/SelectIterator.cc
@@ -0,0 +1,232 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file SelectIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "odb_api/DataStream.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/SQLSelect.h"
+
+using namespace eckit;
+
+namespace odb {
+
+SelectIterator::SelectIterator(odb::Select &owner, odb::sql::SQLNonInteractiveSession& s)
+: owner_(owner),
+  select_(),
+  selectStmt_(0),
+  metaData_(0),
+  data_(0),
+  newDataset_(true),
+  noMore_(false),
+  aggregateResultRead_(false),
+  isCachingRows_(false),
+  refCount_(0),
+  session_(*this, s)
+{}
+
+SelectIterator::SelectIterator(odb::Select& owner, const std::string& select, ecml::ExecutionContext* context, odb::sql::SQLNonInteractiveSession& s)
+: owner_(owner),
+  select_(select),
+  selectStmt_(0),
+  metaData_(0),
+  data_(0),
+  newDataset_(true),
+  noMore_(false),
+  aggregateResultRead_(false),
+  isCachingRows_(false),
+  refCount_(0),
+  session_(*this, s)
+{
+    if (owner.dataIStream())
+        parse(session_, owner.dataIStream());
+    else
+        parse<DataStream<SameByteOrder, DataHandle> >(session_, owner.dataHandle());
+}
+
+template <typename DATASTREAM> 
+void SelectIterator::parse(odb::sql::SQLSession& session, typename DATASTREAM::DataHandleType *dh)
+{
+    sql::SQLParser p;
+    p.parseString(session, select_, dh, session.selectFactory().config());
+    sql::SQLStatement *stmt (session_.statement());
+
+    selectStmt_ = dynamic_cast<sql::SQLSelect*>(stmt);
+    if (! selectStmt_)
+        throw UserError(std::string("Expected SELECT, got: ") + select_);
+
+    selectStmt_->prepareExecute();
+	
+    populateMetaData<DATASTREAM>();
+
+    selectStmt_->env.pushFrame(selectStmt_->sortedTables_.begin());
+}
+
+void SelectIterator::parse(odb::sql::SQLSession& session, std::istream *is)
+{
+	sql::SQLParser p;
+	odb::sql::SQLSelectFactory& factory(session.selectFactory());
+	p.parseString(session, select_, is, factory.config(), factory.csvDelimiter());
+	sql::SQLStatement *stmt (session_.statement());
+	selectStmt_ = dynamic_cast<sql::SQLSelect*>(stmt);
+	ASSERT(selectStmt_);
+	selectStmt_->prepareExecute();
+	
+	populateMetaData<DataStream<SameByteOrder, DataHandle> >();
+
+	selectStmt_->env.pushFrame(selectStmt_->sortedTables_.begin());
+}
+
+SelectIterator::~SelectIterator()
+{
+    delete [] data_;
+    delete selectStmt_;
+    delete metaData_ ;
+}
+
+void SelectIterator::cacheRow(const Expressions& results)
+{
+    size_t n = results.size();
+    std::vector<double> v(n);
+    bool missing = false;
+    for(size_t i = 0; i < n; i++)
+        v[i] = results[i]->eval(missing = false);
+    rowCache_.push_back(v);
+}
+
+bool SelectIterator::next(ecml::ExecutionContext* context)
+{
+	newDataset_ = false;
+	if (noMore_) return false;
+
+	if (isCachingRows_)
+	{
+		if (rowCache_.size() == 0)
+		{
+			noMore_ = true;
+			return false;
+		}
+		else
+		{
+			noMore_ = false;
+			std::vector<double>& r(rowCache_.front());
+			//for (size_t i = 0; i < r.size(); ++i) data_[i] = r[i];
+			copy(r.begin(), r.end(), data_);
+	
+			rowCache_.pop_front();
+			return true;
+		}
+	}
+
+	if (aggregateResultRead_) return noMore_ = true;
+
+	bool rc = selectStmt_->processOneRow(context);
+
+	if (!rc)
+	{
+		if (selectStmt_->isAggregate())
+			aggregateResultRead_ = true;
+		else
+			noMore_ = true;
+
+		isCachingRows_ = true;
+		selectStmt_->postExecute(context);
+		//isCachingRows_ = false; // this would be needed if we reuse the same iterator for several queries. 
+		
+		if (rowCache_.size())
+		{
+			noMore_ = false;
+			std::vector<double>& r(rowCache_.front());
+			//for (size_t i = 0; i < r.size(); ++i) data_[i] = r[i];
+			copy(r.begin(), r.end(), data_);
+			rowCache_.pop_front();
+			return true;
+		}
+	}
+	
+	return rc && !aggregateResultRead_;
+}
+
+bool SelectIterator::isNewDataset() { return newDataset_; }
+
+const double* SelectIterator::data() { return data_; }
+
+double& SelectIterator::data(size_t i)
+{
+	ASSERT(i >= 0 && i < columns().size());
+	return data_[i];
+}
+
+const MetaData& SelectIterator::columns()
+{
+	ASSERT(metaData_);
+	return *metaData_;
+}
+
+template <typename DATASTREAM>
+void SelectIterator::populateMetaData()
+{
+	Expressions &results_ = selectStmt_->results_;
+	delete metaData_;
+	metaData_ = new MetaData(results_.size());
+	for (size_t i = 0; i < results_.size(); i++)
+	{
+		Column* col = new Column(*metaData_);
+		(*metaData_)[i] = col;
+		sql::expression::SQLExpression *exp = results_[i];
+		std::string title = exp->title();
+		col->name(title);
+
+		const sql::type::SQLType* sqlType = exp->type();
+		int kind = sqlType->getKind();
+		switch (kind) {
+			case sql::type::SQLType::realType:
+				col->type<DATASTREAM>(REAL, false); //FIXME
+				break;
+			case sql::type::SQLType::doubleType:
+				col->type<DATASTREAM>(DOUBLE, false); //FIXME
+				break;
+			case sql::type::SQLType::integerType:
+				col->type<DATASTREAM>(INTEGER, false); //FIXME
+				break;
+			case sql::type::SQLType::stringType:
+				col->type<DATASTREAM>(STRING, false); // FIXME
+				break;
+			case sql::type::SQLType::bitmapType:
+				col->type<DATASTREAM>(BITFIELD, false);
+				break;
+			case sql::type::SQLType::blobType:
+				NOTIMP;
+				break;
+			default:
+				Log::error() << "Unknown type: " << *sqlType << ", kind: " << kind << std::endl;
+				ASSERT(!"UnknownType");
+				break;
+		}
+
+		col->hasMissing(exp->hasMissingValue());
+		col->missingValue(exp->missingValue());
+		col->bitfieldDef(exp->bitfieldDef());
+	}
+	delete [] data_;
+	data_ = new double[metaData_->size()];
+	for (size_t i = 0; i < metaData_->size(); ++i)
+		data_[i] = (*metaData_)[i]->missingValue();
+	newDataset_ = true;
+}
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/SelectIterator.h b/odb_api/src/odb_api/SelectIterator.h
new file mode 100644
index 0000000..6a995c8
--- /dev/null
+++ b/odb_api/src/odb_api/SelectIterator.h
@@ -0,0 +1,103 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file SelectIterator.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef odb_api_SelectIterator_H
+#define odb_api_SelectIterator_H
+
+#include "odb_api/Expressions.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/SQLIteratorSession.h"
+
+extern "C" {
+	typedef void oda_select_iterator;
+	int odb_select_iterator_get_next_row(oda_select_iterator*, int, double*, int*);
+}
+
+namespace ecml { class ExecutionContext; }
+namespace odb { class Select; }
+namespace odb { class MetaData; }
+namespace odb { template <typename I, typename O, typename D> class IteratorProxy; } 
+namespace odb { namespace sql { class SQLSession; } }
+namespace odb { namespace sql { class SQLNonInteractiveSession; }}
+namespace odb { namespace sql { class SQLIteratorSession; }}
+namespace odb { namespace sql { class SQLSelect; } }
+namespace odb { namespace sql { template <typename T> class SQLIteratorOutput; }}
+
+namespace odb {
+
+class SelectIterator { 
+public:
+	
+    SelectIterator (Select &owner, const std::string&, ecml::ExecutionContext*, odb::sql::SQLNonInteractiveSession&);
+	~SelectIterator();
+
+	bool isNewDataset();
+	const double* data();
+
+	const MetaData& columns();
+	const MetaData& columns(const MetaData&) { NOTIMP; }
+    void setNumberOfColumns(size_t) { NOTIMP; }
+
+	int close() { NOTIMP; }
+    int setColumn(size_t index, std::string name, ColumnType type) { NOTIMP; }
+	void writeHeader() { NOTIMP; }
+    int setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b) { NOTIMP; }
+	void missingValue(size_t, double) { NOTIMP; }
+	
+	bool isCachingRows() { return isCachingRows_; }
+	void cacheRow(const Expressions& results);
+	double& data(size_t i);
+
+protected:
+	bool next(ecml::ExecutionContext*);
+
+private:
+// No copy allowed.
+	SelectIterator(const SelectIterator&);
+	SelectIterator& operator=(const SelectIterator&);
+
+	template <typename DATASTREAM> void populateMetaData();
+	template <typename DATASTREAM> void parse(odb::sql::SQLSession&, typename DATASTREAM::DataHandleType *);
+	void parse(odb::sql::SQLSession&, std::istream *);
+
+	Select& owner_;
+    std::string select_;
+	odb::sql::SQLSelect *selectStmt_;
+	MetaData *metaData_;
+
+	double* data_;
+	bool newDataset_;
+
+	bool noMore_;
+	bool aggregateResultRead_;
+	bool isCachingRows_;
+	std::list<std::vector<double> > rowCache_;
+	ecml::ExecutionContext* context_;
+
+protected:
+	SelectIterator (Select &owner, odb::sql::SQLNonInteractiveSession&);
+
+	int refCount_;
+	odb::sql::SQLIteratorSession session_;
+
+	friend int ::odb_select_iterator_get_next_row(::oda_select_iterator*, int, double*, int*);
+	friend class odb::Select;
+	friend class odb::sql::SQLIteratorOutput<SelectIterator>;
+	friend class odb::IteratorProxy<odb::SelectIterator, odb::Select, const double>;
+};
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/SelectOneTable.cc b/odb_api/src/odb_api/SelectOneTable.cc
new file mode 100644
index 0000000..cb65779
--- /dev/null
+++ b/odb_api/src/odb_api/SelectOneTable.cc
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file SelectOneTable.cc
+/// Piotr Kuchta - ECMWF Jul 11
+
+#include "odb_api/SelectOneTable.h"
+
+namespace odb {
+namespace sql {
+
+SelectOneTable::SelectOneTable(const SQLTable* table)
+: table_(table),
+  offset_(0),
+  length_(0),
+  column_(0),
+  table1_(0),
+  table2_(0),
+  order_(0)
+{}
+
+SelectOneTable::~SelectOneTable() {}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/SelectOneTable.h b/odb_api/src/odb_api/SelectOneTable.h
new file mode 100755
index 0000000..0caaec2
--- /dev/null
+++ b/odb_api/src/odb_api/SelectOneTable.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File SelectOneTable.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef SelectOneTable_H
+#define SelectOneTable_H
+
+#include "odb_api/Expressions.h"
+
+namespace odb {
+namespace sql {
+
+class SQLColumn;
+
+// Forward declarations
+class SQLTableIterator;
+
+struct SelectOneTable {
+	SelectOneTable(const SQLTable* table = 0);
+	~SelectOneTable();
+
+	const SQLTable*               table_;
+	std::vector<SQLColumn*>            fetch_;
+    std::vector<std::pair<double,bool>*>    values_;
+
+	Expressions check_;
+	Expressions index_;
+
+	// For links
+	std::pair<double,bool>*   offset_;
+	std::pair<double,bool>*   length_;
+	SQLColumn*           column_; // Reference column
+
+	// For checking/debugging
+	const SQLTable*    table1_;
+	const SQLTable*    table2_;
+
+	// For index
+
+	// For sorting
+	int order_;
+
+};
+
+typedef std::vector<SelectOneTable*> SortedTables;
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/SharedIterator.h b/odb_api/src/odb_api/SharedIterator.h
new file mode 100644
index 0000000..0fb013f
--- /dev/null
+++ b/odb_api/src/odb_api/SharedIterator.h
@@ -0,0 +1,177 @@
+/// @file   SharedIterator.h
+/// @author Tomas Kral
+
+#ifndef ODBLIB_SHAREDITERATOR_H_
+#define ODBLIB_SHAREDITERATOR_H_
+
+namespace odb {
+
+/*! @brief Represents a smart iterator.
+ *
+ *  The SharedIterator class uses reference counting to manage shared ownership
+ *  of an iterator object. Several instances of SharedIterator may refer to the
+ *  same iterator object. The managed iterator object is destroyed only when
+ *  the last SharedIterator pointing to it is destroyed.
+ *
+ *  @see IteratorFacade
+ *  @todo Write some test cases.
+ */ 
+template <typename T>
+class SharedIterator
+{
+public:
+    /// @brief Difference type of the managed iterator.
+    /// Provided for compatibility with C++ STL algorithms.
+
+    typedef typename T::difference_type difference_type;
+
+    /// @brief Value type of the managed iterator.
+    /// Provided for compatibility with C++ STL algorithms.
+
+    typedef typename T::value_type value_type;
+
+    /// @brief Reference type of the managed iterator.
+    /// Provided for compatibility with C++ STL algorithms.
+
+    typedef typename T::reference reference;
+
+    /// @brief Pointer type of the managed iterator.
+    /// Provided for compatibility with C++ STL algorithms.
+
+    typedef typename T::pointer pointer;
+
+    /// @brief Cagegory of the managed iterator.
+    /// Provided for compatibility with C++ STL algorithms.
+
+    typedef typename T::iterator_category iterator_category;
+
+    /*! @brief Creates a smart iterator.
+     *
+     *  This templated constructor allows to create a new SharedIterator instance
+     *  from any pointer to an iterator object @em it that is implicitly convertible
+     *  to type @em T.
+     *
+     *  @warning If the iterator object pointed to by @e it is already owned by
+     *  some other SharedIterator instance, the usage of @c this SharedIterator
+     *  will result in undefined behavior.
+     */
+    template <typename U>
+    explicit SharedIterator(U* it)
+      : it_(it),
+        useCount_(0)
+    {
+        if (it_) useCount_ = new long (1);
+    }
+
+    /*! @brief Creates a smart iterator which shares ownership of the iterator
+     *  object managed by the @em other smart iterator.
+     */
+    SharedIterator(const SharedIterator& other)
+      : it_(other.it_),
+        useCount_(other.useCount_)
+    {
+        if (it_) ++(*useCount_);
+    }
+
+    /*! @brief Creates a smart iterator which shares ownership of the iterator
+     *  object managed by the @em other smart iterator.
+     *
+     *  This templated copy-constructor allows to create new SharedIterator
+     *  from any @em other SharedIterator who's iterator type is implicitly
+     *  convertible to @em T.
+     */
+    template <typename U>
+    SharedIterator(const SharedIterator<U>& other)
+      : it_(other.it_),
+        useCount_(other.useCount_)
+    {
+        if (it_) ++(*useCount_);
+    }
+
+    /// @brief Destroys the managed iterator if there are no more SharedIterator
+    /// instances referring to it.
+    ~SharedIterator()
+    {
+        destruct();
+    }
+
+    /// @brief Replaces the wrapped iterator object with the one managed by @em other.
+    SharedIterator& operator=(const SharedIterator& other)
+    {
+        if (it_ == other.it_)
+            return *this;
+    
+        destruct();
+
+        it_ = other.it_;
+        useCount_ = other.useCount_;
+        ++(*useCount_);
+
+        return *this;
+    }
+
+    /// @brief Compares two managed iterator objects.
+    bool operator==(const SharedIterator& other)
+    {
+        return (*it_ == *other.it_);
+    }
+
+    /// @brief Compares two managed iterator objects.
+    bool operator!=(const SharedIterator& other)
+    {
+        return (*it_ != *other.it_);
+    }
+
+    /// @brief Increments the managed iterator object.
+    SharedIterator& operator++()
+    {
+        ++(*it_);
+        return *this;
+    }
+
+    /// @brief Dereferences the managed iterator object.
+    reference operator*() const { return (**it_); }
+
+    /// @brief Dereferences the managed iterator object.
+    pointer operator->() const { return &(**it_); }
+
+    /// @brief Returns pointer to the managed iterator object.
+    T* get() const { return it_; }
+
+    /*! @brief Checks if @c this is the only SharedIterator instance managing
+     *  the iterator object.
+     *
+     *  Retuns @c true if @c this is the only SharedIterator instance managing
+     *  the current iterator object (i.e. useCount() == 1), otherwise returns
+     *  @c false.
+     */
+    bool unique() const
+    {
+        return useCount_ && (*useCount_ == 1);
+    }
+
+    /*! @brief Returns the number of SharedIterator instances (including
+     *  @c this) managing the same iterator object.
+     */
+    long useCount() const
+    {
+        return useCount_ ? *useCount_ : 0;
+    }
+
+private:
+    void destruct()
+    {
+        if (it_ && (--(*useCount_) == 0))
+        {
+            delete it_;
+            delete useCount_;
+        }
+    }
+
+    T* it_;
+    long* useCount_;
+};
+
+} // namespace odb
+
+#endif // ODBLIB_SHAREDITERATOR_H_
diff --git a/odb_api/src/odb_api/ShiftedBitColumnExpression.cc b/odb_api/src/odb_api/ShiftedBitColumnExpression.cc
new file mode 100755
index 0000000..7733a2e
--- /dev/null
+++ b/odb_api/src/odb_api/ShiftedBitColumnExpression.cc
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+namespace odb {
+namespace sql {
+namespace expression {
+/*
+double ShiftedBitColumnExpression::eval(bool& missing) const
+{
+	if(value_->second) missing = true;
+	unsigned long x = static_cast<unsigned long>(value_->first);
+	return (x & mask_) >> bitShift_;
+}
+
+void ShiftedBitColumnExpression::print(std::ostream& s) const { s << columnName_ << "#" << shift_; }
+*/
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ShiftedBitColumnExpression.h b/odb_api/src/odb_api/ShiftedBitColumnExpression.h
new file mode 100755
index 0000000..ddb6662
--- /dev/null
+++ b/odb_api/src/odb_api/ShiftedBitColumnExpression.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ShiftedBitColumnExpression.h
+// Piotr Kuchta - ECMWF Dec 2012
+
+#ifndef ShiftedBitColumnExpression_H
+#define ShiftedBitColumnExpression_H
+
+#include "odb_api/BitColumnExpression.h"
+#include "odb_api/ShiftedColumnExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+typedef ShiftedColumnExpression<BitColumnExpression> ShiftedBitColumnExpression;
+
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/ShiftedColumnExpression.cc b/odb_api/src/odb_api/ShiftedColumnExpression.cc
new file mode 100755
index 0000000..8648c01
--- /dev/null
+++ b/odb_api/src/odb_api/ShiftedColumnExpression.cc
@@ -0,0 +1,138 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+template <typename T>
+void ShiftedColumnExpression<T>::allocateCircularBuffer()
+{
+	//Log::info() << "allocateCircularBuffer:" << shift_ <<  std::endl;
+
+// FIXME: we need to retrieve actual value of missing value for this column
+	double const MISSING_VALUE_REAL = -2147483647.0;
+    static std::pair<double,bool> missing_(MISSING_VALUE_REAL,true);
+
+	ASSERT(shift_ > 0);
+
+	for (size_t i = 0; i < shift_; ++i)
+		oldValues_.push_front(missing_);
+}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const T& o, int shift, int nominalShift)
+: T(o),
+  shift_(shift),
+  nominalShift_(nominalShift),
+  oldValues_()
+{}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const std::string& name, SQLTable* table, int shift, int nominalShift, int begin, int end)
+: T(name, table, begin, end),
+  shift_(shift),
+  nominalShift_(nominalShift),
+  oldValues_()
+{}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const std::string& name, const std::string& tableReference, int shift, int nominalShift, int begin, int end)
+: T(name, tableReference, begin, end),
+  shift_(shift),
+  nominalShift_(nominalShift),
+  oldValues_()
+{}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const std::string& name, const std::string& field, SQLTable* table, int shift, int nominalShift)
+: T(name, field, table),
+  shift_(shift),
+  nominalShift_(nominalShift),
+  oldValues_()
+{}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const std::string& name, const std::string& field, const std::string& tableReference, int
+shift, int nominalShift)
+: T(name, field, tableReference),
+  shift_(shift),
+  nominalShift_(nominalShift),
+  oldValues_()
+{}
+
+template <typename T>
+ShiftedColumnExpression<T>::ShiftedColumnExpression(const ShiftedColumnExpression& e)
+: T(e),
+  shift_(e.shift_),
+  nominalShift_(e.nominalShift_),
+  oldValues_(e.oldValues_)
+{}
+
+template <typename T>
+SQLExpression* ShiftedColumnExpression<T>::clone() const { NOTIMP; return 0; } // return new ShiftedColumnExpression(*this); }
+
+template <typename T>
+ShiftedColumnExpression<T>::~ShiftedColumnExpression() {}
+
+template <typename T>
+void ShiftedColumnExpression<T>::print(std::ostream& s) const
+{
+	s << this->columnName_; 
+	if (nominalShift_ != 0)
+		s << "#" << nominalShift_;
+}
+
+template <typename T>
+double ShiftedColumnExpression<T>::eval(bool& missing) const
+{
+	ShiftedColumnExpression& self(*const_cast<ShiftedColumnExpression<T>*>(this));
+	ASSERT(shift_ > 0);
+
+	if (oldValues_.size() == 0)
+		self.allocateCircularBuffer();
+
+	std::pair<double,bool> const& v(self.oldValues_.back());
+	double value = v.first;
+	bool miss = v.second;
+
+	self.oldValues_.pop_back();
+
+	std::pair<double,bool> ev;
+	ev.first = T::eval(ev.second);
+	self.oldValues_.push_front(ev);
+
+	if(miss) missing = true;
+	return value;
+}
+
+template <typename T>
+void ShiftedColumnExpression<T>::cleanup(SQLSelect& sql)
+{
+    static std::pair<double,bool> zero_(0,false);
+	this->value_ = &zero_;
+	this->type_  = 0;
+	oldValues_.clear();
+}
+
+template <typename T>
+void ShiftedColumnExpression<T>::output(SQLOutput& o) const 
+{ 
+	//Log::info() << "ShiftedColumnExpression::output:" << std::endl;
+
+	bool missing = false;
+	double v = eval(missing);
+	this->type_->output(o, v, missing); 
+}
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/ShiftedColumnExpression.h b/odb_api/src/odb_api/ShiftedColumnExpression.h
new file mode 100755
index 0000000..a340cea
--- /dev/null
+++ b/odb_api/src/odb_api/ShiftedColumnExpression.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ShiftedColumnExpression.h
+// Piotr Kuchta - ECMWF Dec 2012
+#ifndef ShiftedColumnExpression_H
+#define ShiftedColumnExpression_H
+
+namespace odb {
+namespace sql {
+
+class SQLOutput;
+
+namespace expression {
+
+template <typename T>
+class ShiftedColumnExpression : public T {
+public:
+	ShiftedColumnExpression(const std::string&, SQLTable*, int shift, int nominalShift, int begin = -1, int end = -1);
+	ShiftedColumnExpression(const std::string&, const std::string& tableReference, int shift, int nominalShift, int begin = -1, int end = -1);
+	ShiftedColumnExpression(const ShiftedColumnExpression&);
+	ShiftedColumnExpression(const T& o, int shift, int nominalShift);
+
+	// for bitfields columns
+	ShiftedColumnExpression(const std::string& name, const std::string& field, SQLTable* table, int shift, int nominalShift);
+	ShiftedColumnExpression(const std::string& name, const std::string& field, const std::string& tableReference, int shift, int nominalShift);
+
+
+	~ShiftedColumnExpression(); 
+
+	SQLTable* table() { return this->table_; }
+
+	double* current() { NOTIMP; return &(this->value_->first); }
+
+	SQLExpression* clone() const;
+
+	int shift() { return shift_; }
+	int nominalShift() { return nominalShift_; }
+
+protected:
+	int	                   shift_; // For the HASH operator
+	int	                   nominalShift_; // For the HASH operator
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void cleanup(SQLSelect& sql);
+	virtual double eval(bool& missing) const;
+	virtual void output(SQLOutput& s) const;
+
+private:
+	ShiftedColumnExpression& operator=(const ShiftedColumnExpression&);
+
+	void allocateCircularBuffer();
+    std::list<std::pair<double,bool> > oldValues_;
+
+// -- Overridden methods
+	//friend std::ostream& operator<<(std::ostream& s,const ShiftedColumnExpression& p)
+	//	{ p.print(s); return s; }
+};
+
+} // namespace expression 
+} // namespace sql
+} // namespace odb 
+
+#include "odb_api/ShiftedColumnExpression.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/Stack.cc b/odb_api/src/odb_api/Stack.cc
new file mode 100755
index 0000000..872d1ef
--- /dev/null
+++ b/odb_api/src/odb_api/Stack.cc
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/Stack.h"
+
+namespace odb {
+namespace sql {
+
+Stack::Stack() {}
+
+Stack::~Stack() { while(size()) popFrame(); }
+
+const SortedTables::iterator& Stack::tablesIterator() 
+{
+	//if (size() == 0) 
+	//	return end();
+	return top()->tablesIterator_;
+}
+
+SelectOneTable& Stack::table() { return *(top()->table()); }
+SelectOneTable* Stack::tablePtr() { return top()->table(); }
+
+void Stack::table(SelectOneTable *p) { top()->table() = p; ASSERT(top()->table()); }
+
+SQLTableIterator& Stack::cursor() { return *(top()->cursor()); }
+
+void Stack::cursor(SQLTableIterator *p) { top()->cursor() = p; }
+
+void Stack::pushFrame(const SortedTables::iterator ti) 
+{
+	push(new Environment(ti));
+}
+
+void Stack::popFrame()
+{
+	if (size())
+	{
+		delete top();
+		pop();
+	}
+}
+
+} // namespace sql 
+} // namespace odb 
diff --git a/odb_api/src/odb_api/Stack.h b/odb_api/src/odb_api/Stack.h
new file mode 100755
index 0000000..a0de44b
--- /dev/null
+++ b/odb_api/src/odb_api/Stack.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef Stack_H
+#define Stack_H
+
+#include <stack>
+
+#include "odb_api/Environment.h"
+#include "odb_api/SelectOneTable.h"
+
+namespace odb {
+namespace sql {
+
+// Forward declarations
+
+class Environment;
+
+struct Stack : private std::stack<Environment*> {
+	Stack();
+	~Stack();
+
+	void pushFrame(const SortedTables::iterator);
+	void popFrame();
+
+	void print(std::ostream& s) const;
+
+	const SortedTables::iterator& tablesIterator(); 
+
+	SelectOneTable& table();
+	SelectOneTable* tablePtr();
+	void table(SelectOneTable *);
+
+	SQLTableIterator& cursor();
+	void cursor(SQLTableIterator *);
+};
+
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Stager.cc b/odb_api/src/odb_api/Stager.cc
new file mode 100755
index 0000000..67dde4f
--- /dev/null
+++ b/odb_api/src/odb_api/Stager.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+
+#include <ctype.h>
+
+#include "eckit/config/Resource.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/core/Interpreter.h"
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/FileCollector.h"
+#include "odb_api/FileMapper.h"
+#include "odb_api/InMemoryDataHandle.h"
+#include "odb_api/ODBModule.h"
+#include "odb_api/Partition.h"
+#include "odb_api/Indexer.h"
+#include "odb_api/Partitioner.h"
+#include "odb_api/Writer.h"
+#include "odb_api/Retriever.h"
+#include "odb_api/Stager.h"
+#include "odb_api/StringTool.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+using namespace odb::tool;
+
+void Stager::prepareMapper(FileMapper&                                            mapper,
+                           const std::vector<std::string>&                        keywords,
+                           const std::map<std::string,std::vector<std::string> >& request)
+{
+    vector<string> expandedRoots, roots (eckit::StringTools::split(":", request.at("odbserverroots")[0]));
+    for (size_t i(0); i < roots.size(); ++i)
+        expandedRoots.push_back(FileCollector::expandTilde(roots[i]));
+    mapper.addRoots(expandedRoots);
+    mapper.checkRoots();
+}
+
+size_t Stager::numberOfPartitions(const std::map<std::string,std::vector<std::string> >& request)
+{
+    vector<string> nParts ( request.at("n_parts") );
+    if (nParts.size() != 1) 
+        throw UserError(string("N_PARTS should have one value"));
+    
+    size_t n (atol(nParts[0].c_str()));
+    if (n == 0)
+        throw UserError ("STAGE: N_PARTS should be a positive integer, was '" + nParts[0] + "'");
+    return n;
+}
+
+void Stager::createIndices(const std::vector<eckit::PathName>& files)
+{
+    Log::info() << "Creating indices for ";
+    for (size_t i(0); i < files.size(); ++i)
+        Log::info() << files[i] << "," << endl;
+    Log::info() << endl;
+
+    Indexer::createIndex(files); // for now for testing, indexing will be done some time earlier
+    Log::info() << "Created indices." << endl;
+}
+
+vector<PathName> Stager::writePartitionsToFiles (const Partitions& partitions, const string& pathNamePrefix, const string& fileListPath)
+{
+    Log::info() << "Writing partitions: " << partitions << endl << " to files:" << endl;
+
+    vector<PathName> ps (partitions.write(pathNamePrefix));
+
+    ofstream f;
+    f.exceptions(ofstream::badbit | ofstream::failbit);
+    f.open(fileListPath.c_str());
+    for (size_t i (0); i < ps.size(); ++i)
+    {
+        Log::info() << i << ": " << ps[i] << endl;
+        f << ps[i] << endl;
+    }
+    f.close();
+
+    Log::info() << "List of files with partitions written to: " << fileListPath << endl;
+
+    return ps;
+}
+
+void Stager::stage(eckit::MultiHandle&                                    output,
+                   const std::vector<std::string>&                        keywords,
+                   const std::map<std::string,std::vector<std::string> >& req)
+{
+    const std::map<std::string,std::vector<std::string> > request (Retriever::unquoteValues(req));
+
+    Log::info() << "STAGE: request: " << request << endl;
+
+    Retriever::checkKeywordsHaveValues(request, keywords);
+    const string partitionsInfoFile (FileCollector::expandTilde(request.at("partitionsinfo")[0]));
+
+    FileMapper mapper (request.at("odbpathnameschema")[0]);
+    prepareMapper(mapper, keywords, request);
+
+    MultiHandle devNull;
+    FileCollector fileCollector (mapper, devNull);
+    fileCollector.findFiles(keywords, request);
+
+    if(devNull.estimate() == Length(0))
+        Log::userWarning() << "Data not found" << endl;
+
+    vector<PathName> files (fileCollector.foundFilesAsPathNames());
+    createIndices (files);
+
+    Partitions partitions (Partitioner::createPartitions(files, numberOfPartitions(request)));
+    size_t requestedNumberOfPartitions (numberOfPartitions(request));
+
+    if (partitions.size() != requestedNumberOfPartitions)
+        Log::warning() << "Number of partitions (" << partitions.size() 
+            << ") different than requested: " << requestedNumberOfPartitions << std::endl;
+
+    Log::info() << "Saving partitions info to " << partitionsInfoFile << endl;
+    partitions.save(partitionsInfoFile);
+
+    vector<PathName> dataFiles (writePartitionsToFiles (partitions, "odb.partition", partitionsInfoFile + ".files"));
+
+    sendPartitionsInfo(output, partitions);
+}
+
+void Stager::sendPartitionsInfo(MultiHandle& output, const Partitions& partitions)
+{
+    Log::info() << "partitions: " << endl << partitions << endl;
+
+    InMemoryDataHandle *dh (new InMemoryDataHandle);
+    dh->openForWrite(0);
+    Writer<> out(*dh);
+    Writer<>::iterator o (out.begin());
+    MetaData md(o->columns());
+
+    md.addColumn("partition_number", "INTEGER");
+    md.addColumn("number_of_rows", "INTEGER");
+    o->columns(md);
+    o->writeHeader();
+
+    for (size_t i(0); i < partitions.size(); ++i)
+    {
+        const Partition& p(partitions[i]);
+        (*o)[0] = i;
+        (*o)[1] = p.numberOfRows();
+        ++o;
+    }
+
+    dh->openForRead();
+    output += dh;
+}
+
diff --git a/odb_api/src/odb_api/Stager.h b/odb_api/src/odb_api/Stager.h
new file mode 100755
index 0000000..aa809ca
--- /dev/null
+++ b/odb_api/src/odb_api/Stager.h
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Stager.h
+// Piotr Kuchta - ECMWF December 2015
+
+#ifndef odb_api_Stager_H
+#define odb_api_Stager_H
+
+#include "odb_api/Partitions.h"
+#include "odb_api/FileMapper.h"
+
+class Stager {
+public:
+
+    static void stage(eckit::MultiHandle&                                    output,
+                      const std::vector<std::string>&                        keywords,
+                      const std::map<std::string,std::vector<std::string> >& request);
+
+private:
+
+    static void prepareMapper(FileMapper&                                            mapper,
+                              const std::vector<std::string>&                        keywords,
+                              const std::map<std::string,std::vector<std::string> >& request);
+
+    static void createIndices(const std::vector<eckit::PathName>&);
+    static void sendPartitionsInfo(eckit::MultiHandle&, const odb::Partitions&);
+    static size_t numberOfPartitions(const std::map<std::string,std::vector<std::string> >&);
+    static std::vector<eckit::PathName> writePartitionsToFiles (const odb::Partitions&, const std::string& pathNamePrefix, const std::string& fileListPath);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/StringExpression.cc b/odb_api/src/odb_api/StringExpression.cc
new file mode 100755
index 0000000..35e4d9e
--- /dev/null
+++ b/odb_api/src/odb_api/StringExpression.cc
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/ColumnExpression.h"
+#include "odb_api/Expressions.h"
+#include "odb_api/SQLTable.h"
+#include "odb_api/StringExpression.h"
+#include "odb_api/StringTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+const odb::sql::type::SQLType* StringExpression::type() const { return &odb::sql::type::SQLType::lookup("string"); }
+
+StringExpression::StringExpression(const std::string& name)
+: name_(name)
+{
+	ASSERT(name.length() <= sizeof(value_));
+
+	char* buf = (char*)&value_;
+	memset(buf,' ',sizeof(value_));
+
+	int  off = sizeof(value_) - name.length();
+	for(size_t i = 0; i < name.length(); i++)
+		buf[off+i] = name[i];
+
+	std::string s(buf, sizeof(double));
+	//Log::info() << "StringExpression::StringExpression: '" << s << "'" << std::endl;
+}
+
+StringExpression::StringExpression(const StringExpression& o)
+: name_(o.name_), value_(o.value_)
+{}
+
+void StringExpression::expandStars(const std::vector<SQLTable*>& tables, expression::Expressions& e)
+{
+    std::ostream& L(Log::info());
+
+    //TODO: if(verbose_) {...}
+    //Log::info() << "StringExpression::expandStars: name_: '"  << name_ << "', value_: '" << value_ << "'" << std::endl;
+
+	if (! StringTool::isColumnRegex(name_))
+	{
+		e.push_back(this);
+		return;
+	}
+
+	unsigned int matched = 0;
+	for(std::vector<SQLTable*>::const_iterator j = tables.begin();  j != tables.end(); ++j)
+	{
+		SQLTable* table = (*j);
+		std::vector<std::string> names = table->columnNames();
+
+		for(size_t i = 0; i < names.size(); i++)
+		{
+			const std::string& name = names[i];
+			if (! StringTool::matchEx(name_, name))
+			{
+				L << "StringExpression::expandStars: skip '" << name << "'" << std::endl;
+				continue;
+			}
+			
+			L << "StringExpression::expandStars: adding '" << name << "'" << std::endl;
+			++matched;
+			e.push_back(new ColumnExpression(name, table));
+		}
+	}
+	if (! matched)
+		throw eckit::UserError(std::string("No columns matching regex '") + name_ + "' found.");
+	delete this;
+}
+
+SQLExpression* StringExpression::clone() const { return new StringExpression(name_); }
+
+StringExpression::~StringExpression() {}
+
+double StringExpression::eval(bool& missing) const { return value_; }
+
+void StringExpression::prepare(SQLSelect& sql) {}
+
+void StringExpression::cleanup(SQLSelect& sql) {}
+
+void StringExpression::output(std::ostream& s) const { s << name_; }
+
+void StringExpression::print(std::ostream& s) const { s << "'" << name_ << "'"; }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb 
diff --git a/odb_api/src/odb_api/StringExpression.h b/odb_api/src/odb_api/StringExpression.h
new file mode 100755
index 0000000..b566a05
--- /dev/null
+++ b/odb_api/src/odb_api/StringExpression.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File StringExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef StringExpression_H
+#define StringExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class StringExpression : public SQLExpression {
+public:
+	StringExpression(const std::string&);
+	StringExpression(const StringExpression&);
+	~StringExpression(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	StringExpression& operator=(const StringExpression&);
+
+	std::string name_;
+	double value_;
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void expandStars(const std::vector<SQLTable*>&, expression::Expressions&);
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+
+	const type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+	virtual bool isConstant() const { return true; }
+	virtual bool isNumber() const { return true; }
+	virtual void output(std::ostream& s) const;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/StringTool.cc b/odb_api/src/odb_api/StringTool.cc
new file mode 100644
index 0000000..94d50a7
--- /dev/null
+++ b/odb_api/src/odb_api/StringTool.cc
@@ -0,0 +1,215 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file StringTool.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <strings.h> // for bzero
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/utils/Regex.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/StringTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+std::vector<std::string> StringTool::readLines(const PathName fileName, bool logging)
+{
+	std::string s = readFile(fileName, logging);
+	return StringTools::split("\n", s);
+}
+
+std::string StringTool::readFile(const PathName fileName, bool logging)
+{
+	const size_t CHUNK_SIZE = 1024;
+	char buffer[CHUNK_SIZE]; 
+
+	FileHandle f(fileName);
+	Length estimated = f.openForRead();
+	
+	std::string ret;
+	size_t read, totalRead = 0;
+
+	while ( (read = f.read(buffer, sizeof(buffer) / sizeof(char))) > 0 )
+	{
+		totalRead += read;
+		ret.append(std::string(static_cast<char*>(buffer), read));
+	}
+
+	if (logging)
+		Log::info()  << "Read " << totalRead << " bytes from file " << fileName << "[" << ret << "]" << std::endl;
+
+	f.close();
+	return ret;
+}
+
+int StringTool::shell(std::string cmd, const CodeLocation& where, bool assertSuccess)
+{
+	std::string c = "/bin/sh -c \"" + cmd + "\"";
+
+	Log::info() << "Executing '" + c + "' ";
+    Log::info() << " " << where.file() << " +" << where.line();
+	Log::info() << std::endl;
+
+	int rc = system(c.c_str());
+
+	if (assertSuccess && rc != 0)
+	{
+		throw eckit::SeriousBug(std::string(" \"") + cmd + "\" failed. " + where.file() + " +" + Translator<int, std::string>()(where.line()));
+		ASSERT(rc == 0);
+	}
+	return rc;
+}
+
+bool StringTool::check(const std::string& s, ctypeFun fun)
+{
+	for (size_t i = 0; i < s.size(); ++i)
+		if (! fun(s[i]))
+			return false;
+	return true;
+}
+
+bool StringTool::isInQuotes(const std::string& value)
+{
+	return value.size() > 1
+		&& ((value[0] == '"' && value[value.size() - 1] == '"')
+	     || (value[0] == '\'' && value[value.size() - 1] == '\''));
+}
+
+std::string StringTool::unQuote(const std::string& value)
+{
+	if (! value.size())
+		return value;
+	if (isInQuotes(value))
+		return value.substr(1, value.size() - 2);
+	return value;
+}
+
+std::string StringTool::double_as_string(double m)
+{
+	char buf[sizeof(double) + 1];
+	bzero(buf, sizeof(buf));
+	memcpy(buf, reinterpret_cast<char *>(&m), sizeof(double));
+	return std::string(buf, sizeof(double));
+}
+
+double StringTool::cast_as_double(const std::string& value)
+{
+	char buf[sizeof(double)];
+	memset(buf, ' ', sizeof(double));
+
+	ASSERT(value.size() <= sizeof(double));
+
+	strncpy(buf + sizeof(double) - value.size(), value.c_str(), value.size());
+	return *reinterpret_cast<double *>(buf);
+}
+
+std::string StringTool::int_as_double2string(double v)
+{
+    std::stringstream s;
+    s.precision(0);
+    s << fixed << v;
+    return s.str();
+}
+
+double StringTool::translate(const std::string& v)
+{
+	return isInQuotes(v) ? cast_as_double(unQuote(v)) : Translator<std::string, double>()(v);
+}
+
+void StringTool::trimInPlace(std::string &str) { str = StringTools::trim(str); }
+
+bool StringTool::match(const std::string& regex, const std::string& s)
+{
+	return Regex(regex).match(s);
+}
+
+bool StringTool::matchEx(const std::string& regex, const std::string& s)
+{
+	bool negated = false;	
+	std::string rx = regex;
+	if (rx[0] == '~')
+	{
+		rx.erase(0, 0);
+		negated = true;
+	}
+
+	// TODO: remove '/' 
+
+	bool matches = Regex(rx).match(s);
+	return (negated && !matches) || (!negated && matches);
+}
+
+bool StringTool::isColumnRegex(const std::string& s)
+{
+    return ( s[0] == '/' && s[s.size() - 1] == '/' )
+        || ( s[0] == '~' && s[1] == '/' && s[s.size() - 1] == '/' );
+}
+
+bool StringTool::matchAny(const std::vector<std::string>& regs, const std::string& s)
+{
+	for (size_t i = 0; i < regs.size(); ++i)
+		if (match(regs[i], s))
+			return true;
+	return false;
+}
+
+ostream& operator<<(std::ostream& s, const std::vector<std::string>& st) 
+{
+    s << '[';
+    for (std::vector<std::string>::const_iterator it = st.begin(); it != st.end(); ++it)
+        s << *it << ",";
+    s << ']';
+    return s;
+}
+
+
+std::string StringTool::valueAsString(double d, ColumnType t)
+{
+	stringstream s;
+	switch (t) {
+	case INTEGER: return int_as_double2string(d);
+	case BITFIELD: return int_as_double2string(d); // TODO: have something to print bitfields in StringTool
+	case STRING: return double_as_string(d);
+	case DOUBLE:
+	case REAL: 
+		 s << d; return s.str();
+	case IGNORE:
+	default:
+		ASSERT(0 && "Type not known.");
+	}
+	s << d;
+	return s.str();
+}
+
+std::string StringTool::patchTimeForMars(const std::string& ss)
+{
+	std::string v = ss;
+	if (v.size() == 5) v = std::string("0") + v;
+	if (v.size() == 6)
+	{
+		std::string s = v;
+		v = v.substr(0, 4);
+		Log::debug() << "StringTool::patchTimeForMars: stripping seconds from TIME: '"
+				<< s << "' => '" << v << "'" << std::endl;
+	}
+	return v;
+}
+
+bool StringTool::isSelectStatement(const std::string& s) { return StringTool::match("select", eckit::StringTools::lower(eckit::StringTools::trim(s))); }
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/StringTool.h b/odb_api/src/odb_api/StringTool.h
new file mode 100644
index 0000000..2a54d5e
--- /dev/null
+++ b/odb_api/src/odb_api/StringTool.h
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file StringTool.h
+///
+/// @author Piotr Kuchta, ECMWF, Oct 2010
+
+#ifndef StringTool_H
+#define StringTool_H
+
+#include "eckit/eckit.h"
+//#include "odb_api/ColumnType.h"
+#include "ColumnType.h"
+
+
+namespace eckit { class PathName; class CodeLocation; }
+
+namespace odb {
+
+class StringTool {
+
+    typedef int(*ctypeFun)(int);
+
+public:
+
+    static std::string readFile(const eckit::PathName fileName, bool logging = false);
+    static std::vector<std::string> readLines(const eckit::PathName fileName, bool logging = false);
+
+    static void trimInPlace(std::string &);
+
+    static bool isColumnRegex(const std::string&);
+	static bool match(const std::string& regex, const std::string&);
+	static bool matchEx(const std::string& regex, const std::string&);
+	static bool matchAny(const std::vector<std::string>& regs, const std::string&);
+
+    static bool check(const std::string&, ctypeFun);
+    static bool isInQuotes(const std::string &);
+    static std::string unQuote(const std::string &);
+
+    static double cast_as_double(const std::string &);
+    static std::string double_as_string(double);
+	//static std::string stringAsDouble(double v)
+
+	static std::string int_as_double2string(double);
+
+    static int shell(std::string cmd, const eckit::CodeLocation &where, bool assertSuccess = true);
+
+	static double translate(const std::string& v);
+	static std::string valueAsString(double, ColumnType);
+
+	static std::string patchTimeForMars(const std::string& v);
+
+    static bool isSelectStatement(const std::string&);
+
+
+};
+
+std::ostream& operator<<(std::ostream&, const std::vector<std::string>&);
+
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/TODATable.cc b/odb_api/src/odb_api/TODATable.cc
new file mode 100644
index 0000000..f4d2878
--- /dev/null
+++ b/odb_api/src/odb_api/TODATable.cc
@@ -0,0 +1,231 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+#include "odb_api/SQLBitfield.h"
+#include "ecml/data/DataHandleFactory.h"
+
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+template <typename T>
+TODATable<T>::TODATable(SQLDatabase& owner, const std::string& path, const std::string& name)
+: SQLTable(owner, path, name),
+  data_(0),
+  oda_(path),
+  it_(oda_.begin()),
+  end_(oda_.end())
+{
+    populateMetaData();
+}
+
+template <typename T>
+TODATable<T>::~TODATable() { delete[] data_; }
+
+static const std::string nullPathName("<>");
+static const std::string inputTable("input");
+
+template <typename T>
+TODATable<T>::TODATable(SQLDatabase& owner, eckit::DataHandle &dh)
+: SQLTable(owner, nullPathName, inputTable),
+  data_(0),
+  oda_(dh),
+  it_(oda_.begin()),
+  end_(oda_.end())
+{
+    populateMetaData();
+}
+
+template <typename T>
+TODATable<T>::TODATable(SQLDatabase& owner, std::istream &is, const std::string &delimiter)
+: SQLTable(owner, nullPathName, inputTable),
+  data_(0),
+  oda_(is, delimiter),
+  it_(oda_.begin()),
+  end_(oda_.end())
+{
+    populateMetaData();
+}
+
+template <typename T>
+void TODATable<T>::populateMetaData()
+{
+    using eckit::Log;
+    
+    size_t count = it_->columns().size();
+
+    delete[] data_;
+    data_ = new double[count];
+    ASSERT(data_);
+
+    for(size_t i = 0; i < count; i++)
+    {
+        Column& column (*it_->columns()[i]);
+
+        const std::string name (column.name());
+        bool hasMissing (column.hasMissing());
+        double missing (column.missingValue());
+        BitfieldDef bitfieldDef (column.bitfieldDef());
+
+        std::string sqlType;
+        switch(column.type())
+        {
+            case INTEGER: sqlType = "integer"; break;
+            case STRING:  sqlType = "string"; break;
+            case REAL:    sqlType = "real"; break;
+            case DOUBLE:  sqlType = "double"; break;
+            case BITFIELD:
+                {
+                    std::string typeSignature = type::SQLBitfield::make("Bitfield", bitfieldDef.first, bitfieldDef.second, "DummyTypeAlias");
+                    addColumn(name, i, type::SQLType::lookup(typeSignature), hasMissing, missing, true, bitfieldDef);
+                    continue;
+                }
+                break;
+            default:
+            ASSERT("Unknown type" && 1==0);
+            break;
+        }
+        SQLColumn *c = column.type() == BITFIELD
+                ? new ODAColumn(type::SQLType::lookup(sqlType), *this, name, i, hasMissing, missing, bitfieldDef, &data_[i])
+                : new ODAColumn(type::SQLType::lookup(sqlType), *this, name, i, hasMissing, missing, &data_[i]);
+        addColumn(c, name, i);
+	}
+}
+
+template <typename T>
+void TODATable<T>::updateMetaData(const std::vector<SQLColumn*>& selected)
+{
+    using eckit::Log;
+
+	MetaData newColumns (it_->columns());
+	for(size_t i = 0; i < selected.size(); i++)
+	{
+		ODAColumn *c = dynamic_cast<ODAColumn *>(selected[i]);
+		ASSERT(c);
+		if (newColumns.size() <= c->index() || newColumns[c->index()]->name() != c->name()) 
+		{
+			Log::warning() << "Column '" << c->fullName() << "': index has changed in new dataset." << endl
+			               << "Was: " << c->index() << "." << endl;
+			bool newIndexFound = false;
+			for (size_t j = 0; j < newColumns.size(); ++j)
+			{
+				Column &other(*newColumns[j]);
+				if (other.name() == c->name() || other.name() == c->fullName())
+				{
+					newIndexFound = true;
+					Log::warning() << "New index: " << j << endl;
+					c->index(j);
+					break;
+				}
+			}
+            if (! newIndexFound)
+            {
+                // TODO: if user specified MAYBE keyword, then use a constant NULL column.
+                //if (maybe_) {
+                //    Log::warning() << "Column '" << c->name() << "' not found." << endl;
+                //    selected[i] = new NullColumn(*selected[i]);
+                //} else {
+                    stringstream ss;
+                    ss << "One of selected columns, '" << c->name() << "', does not exist in new data set.";
+                    throw eckit::UserError(ss.str());
+                //}
+            }
+		}
+		//c->value(&data_[i]);
+	}
+}
+
+template <typename T>
+SQLColumn* TODATable<T>::createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double
+missingValue)
+{
+	return new ODAColumn(type, *this, name, index, hasMissingValue, missingValue, &data_[index]);
+}
+
+template <typename T>
+SQLColumn* TODATable<T>::createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double
+missingValue, const BitfieldDef& bitfieldDef)
+{
+	return new ODAColumn(type, *this, name, index, hasMissingValue, missingValue, bitfieldDef, &data_[index]);
+}
+
+
+template <typename T>
+bool TODATable<T>::hasColumn(const std::string& name, std::string* fullName)
+{
+    using eckit::Log;
+
+    if (SQLTable::hasColumn(name))
+	{
+		if (fullName)
+			*fullName = name;
+		return true;
+	}
+
+	std::string colName (name + "@");
+
+	int n (0);
+	std::map<std::string,SQLColumn*>::iterator it (columnsByName_.begin());
+	for ( ; it != columnsByName_.end(); ++it)
+	{
+		const std::string& s (it->first);
+		if (s.find(colName) == 0)
+		{
+			n++;
+			if (fullName)
+				*fullName = s;
+		}
+	}
+
+	if (n == 0) return false;
+	if (n == 1) return true;
+
+	throw eckit::UserError(std::string("TODATable:hasColumn(\"") + name + "\"): ambiguous name");
+
+	return false;
+}
+
+template <typename T>
+SQLColumn* TODATable<T>::column(const std::string& name)
+{
+	const std::string colName (name + "@");
+
+	SQLColumn * column (0);
+	std::map<std::string,SQLColumn*>::iterator it (columnsByName_.begin());
+	for ( ; it != columnsByName_.end(); ++it)
+	{
+		const std::string s (it->first);
+		if (s.find(colName) == 0)
+		{
+			if (column)
+				throw eckit::UserError(std::string("TODATable::column: \"") + name + "\": ambiguous name.");
+			else 
+				column = it->second;
+		}
+	}
+	if (column) return column;
+
+	return SQLTable::column(name);
+}
+
+template <typename T>
+SQLTableIterator* TODATable<T>::iterator(const std::vector<SQLColumn*>& x) const
+{
+    TODATable<T>& self (*const_cast<TODATable<T>*>(this));
+    if (! (self.it_ != self.end_))
+        self.it_ = self.oda_.begin();
+
+    return new TableIterator(const_cast<TODATable&>(*this), it_, end_, const_cast<double *>(data_), x);
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/TODATable.h b/odb_api/src/odb_api/TODATable.h
new file mode 100644
index 0000000..ba17e77
--- /dev/null
+++ b/odb_api/src/odb_api/TODATable.h
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TODATable.h
+/// Piotr Kuchta - ECMWF Oct 2010
+
+#ifndef TODATable_H
+#define TODATable_H
+
+#include "odb_api/SQLTable.h"
+#include "odb_api/TODATableIterator.h"
+
+namespace odb {
+namespace sql {
+
+template <typename T>
+class TODATable : public SQLTable {
+public:
+	typedef T TReader;
+	typedef TODATableIterator<TODATable> TableIterator;
+
+	TODATable(SQLDatabase&, const std::string&, const std::string&);
+	TODATable(SQLDatabase&, eckit::DataHandle&);
+    TODATable(SQLDatabase&, std::istream&, const std::string& delimiter);
+
+	~TODATable(); 
+
+	double value(long);
+
+// -- Overridden methods
+	bool hasColumn(const std::string&, std::string* fullName = 0);
+	SQLColumn* column(const std::string&);
+
+protected:
+	// void print(std::ostream&) const;
+	SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue, const BitfieldDef&);
+	SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue);
+
+private:
+// No copy allowed
+	TODATable(const TODATable&);
+	TODATable& operator=(const TODATable&);
+
+public:
+
+	double* data_;
+	typename TODATable<T>::TReader oda_;
+	typename TODATable<T>::TReader::iterator it_;
+	typename TODATable<T>::TReader::iterator end_;
+
+// -- Methods
+
+	void populateMetaData();
+	void updateMetaData(const std::vector<SQLColumn*>&);
+
+	virtual SQLTableIterator* iterator(const std::vector<SQLColumn*>&) const;
+};
+
+} // namespace sql 
+} // namespace odb 
+
+#include "odb_api/TODATable.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/TODATableIterator.cc b/odb_api/src/odb_api/TODATableIterator.cc
new file mode 100644
index 0000000..6630624
--- /dev/null
+++ b/odb_api/src/odb_api/TODATableIterator.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include "odb_api/odb_api.h"
+
+#include "odb_api/ODAColumn.h"
+//#include "odb_api/SQLDatabase.h"
+//#include "odb_api/SQLType.h"
+//#include "odb_api/TODATable.h"
+//#include "odb_api/TODATableIterator.h"
+
+
+
+namespace odb {
+namespace sql {
+
+template <typename T>
+TODATableIterator<T>::TODATableIterator(Table &p, iterator reader, iterator end, double* data, const std::vector<odb::sql::SQLColumn*>& columns)
+: parent(p),
+  it_(reader),
+  end_(end), 
+  data_(data),
+  columns_(columns),
+  firstRow_(true)
+{
+	if (it_ != end_)
+	{
+		updateMetaData();
+		copyRow();
+	}
+}
+
+template <typename T>
+void TODATableIterator<T>::rewind() {}
+
+template <typename T>
+TODATableIterator<T>::~TODATableIterator() {}
+
+template <typename T>
+bool TODATableIterator<T>::next()
+{
+	if (firstRow_) firstRow_ = false;
+	else
+		++it_;
+
+	if (! (it_ != end_) )
+		return false;
+
+	if (it_->isNewDataset())
+		updateMetaData();
+
+	copyRow();
+	return true;
+}
+
+template <typename T>
+void TODATableIterator<T>::copyRow()
+{
+	size_t n = columns_.size();
+	for(size_t i = 0; i < n; ++i)
+		data_[i] = *static_cast<ODAColumn *>(columns_[i])->value(); //it_->data()[columns_[i]->index()];
+}
+
+template <typename T>
+void TODATableIterator<T>::updateMetaData()
+{
+	parent.updateMetaData(columns_);
+	data_ = parent.data_;
+
+	size_t n = columns_.size();
+	for(size_t i = 0; i < n; i++)
+	{
+		ODAColumn *odaColumn = dynamic_cast<ODAColumn *>(columns_[i]);
+		//ASSERT(odaColumn);
+		odaColumn->value(const_cast<double*>(it_->data()) + columns_[i]->index());
+	}
+}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/TODATableIterator.h b/odb_api/src/odb_api/TODATableIterator.h
new file mode 100644
index 0000000..4e22ea6
--- /dev/null
+++ b/odb_api/src/odb_api/TODATableIterator.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TODATableIterator_H
+#define TODATableIterator_H
+
+namespace odb {
+namespace sql {
+
+template <typename T>
+class TODATableIterator : public SQLTableIterator {
+	typedef T Table;
+	typedef typename Table::TReader::iterator iterator;
+
+public:
+	TODATableIterator(Table&, iterator, iterator, double*, const std::vector<odb::sql::SQLColumn*>&);
+	virtual ~TODATableIterator();
+	virtual void rewind();
+	virtual bool next();
+
+private:
+
+	Table &parent;
+	iterator it_;
+	iterator end_;
+	double* data_;
+	const std::vector<SQLColumn*>& columns_;
+
+	bool firstRow_;
+
+	void updateMetaData();
+	void copyRow();
+};
+
+} // namespace sql
+} // namespace odb
+
+#include "odb_api/TODATableIterator.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.cc b/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.cc
new file mode 100644
index 0000000..5e1a756
--- /dev/null
+++ b/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.cc
@@ -0,0 +1,16 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include "odb_api/TReadOnlyMemoryDataHandle.h"
+
+namespace odb {
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.h b/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.h
new file mode 100644
index 0000000..80a58cf
--- /dev/null
+++ b/odb_api/src/odb_api/TReadOnlyMemoryDataHandle.h
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TReadOnlyMemoryDataHandle.h
+/// Piotr Kuchta - ECMWF September 2010
+
+#ifndef TReadOnlyMemoryDataHandle_H
+#define TReadOnlyMemoryDataHandle_H
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+
+namespace odb {
+
+class NonVirtualBase {};
+
+template <typename T = eckit::DataHandle>
+class TReadOnlyMemoryDataHandle : public T {
+public:
+
+	friend std::ostream& operator<<(std::ostream& s, const TReadOnlyMemoryDataHandle& handle) 
+		{ handle.print(s); return s;}
+
+    TReadOnlyMemoryDataHandle() : buffer_(0), it_(0), end_(0), bufferSize_(0) {}
+
+    ~TReadOnlyMemoryDataHandle() { delete [] buffer_; }
+
+// -- Methods
+
+	unsigned char * buffer() { return buffer_; }
+
+	size_t size() const { return bufferSize_; }
+
+	void size(size_t n)
+	{
+		if (n > bufferSize_)
+		{
+			delete [] buffer_;
+			buffer_ = new unsigned char [n];
+			ASSERT(buffer_ && "Cannot allocate buffer_");
+			bufferSize_ = n;
+		}
+		it_ = buffer_;
+		end_ = buffer_ + n;
+	}
+
+	void print(std::ostream& s) const { /*TODO*/ }
+
+	bool hasSomeData() { return it_ != end_; }
+
+	/// Return estimated length.
+    virtual eckit::Length openForRead()
+	{
+		it_ = buffer_;
+		return end_ - it_;
+	}
+
+	// Receive estimated length.
+    void openForWrite(const eckit::Length&) { NOTIMP; }
+
+	// Receive estimated length
+    void openForAppend(const eckit::Length&) {}
+
+    long read(void* p, long n)
+	{
+		char *dst = reinterpret_cast<char *>(p);
+		long i = 0;
+		for ( ; i < n && it_ != end_; ++i, ++it_)
+			dst[i] = *it_;
+		return i;
+	}
+
+    long write(const void* pd, long n) { NOTIMP; }
+
+    void close() {}
+
+    void rewind()                { it_ = buffer_; }
+	eckit::Length estimate()            { return end_ - it_; }
+	eckit::Offset position()            { return it_ - buffer_; }
+
+private:
+// No copy allowed
+    TReadOnlyMemoryDataHandle(const TReadOnlyMemoryDataHandle&);
+    TReadOnlyMemoryDataHandle& operator=(const TReadOnlyMemoryDataHandle&);
+
+	unsigned char *buffer_;
+	unsigned char *it_;
+	unsigned char *end_;
+	size_t bufferSize_;
+};
+
+typedef TReadOnlyMemoryDataHandle<eckit::DataHandle> ReadOnlyMemoryDataHandle;
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/TemplateParameters.cc b/odb_api/src/odb_api/TemplateParameters.cc
new file mode 100644
index 0000000..54ca944
--- /dev/null
+++ b/odb_api/src/odb_api/TemplateParameters.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file TemplateParameters.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "odb_api/TemplateParameters.h"
+
+using namespace eckit;
+
+namespace odb {
+
+TemplateParameters::TemplateParameters()
+: std::vector<TemplateParameter*>()
+{}
+
+TemplateParameters::~TemplateParameters() { release(); }
+
+void TemplateParameters::release()
+{
+	for (size_t i = 0; i < size(); ++i)
+		delete at(i);
+	clear();
+}
+
+MetaData TemplateParameters::nullMD(0);
+
+TemplateParameters::TemplateParameters(const std::string& fileNameTemplate, const MetaData& columns)
+{
+    parse(fileNameTemplate, *this, columns);
+}
+
+TemplateParameters& TemplateParameters::parse(const std::string& fileNameTemplate, TemplateParameters& params, const MetaData& columns)
+{
+	const std::string &t = fileNameTemplate;
+
+	int start = 0;	
+	for (;;)
+	{
+		size_t l = t.find("{", start);
+		if (l == std::string::npos)
+			break;
+		start = l;
+		size_t r = t.find("}", start);
+		if (r == std::string::npos)
+			throw eckit::UserError(std::string("TemplateParameters::parse: missing '}' while parsing '") + fileNameTemplate + "'");
+		start = r;
+
+		std::string name = t.substr(l + 1, r - l - 1);
+		size_t index = &columns != &nullMD ? columns.columnIndex(name) : -1;
+
+		params.push_back(new TemplateParameter(l, r, index, name));	
+
+		Log::debug() << "TemplateParameters::parse: new param: " << l << ", " << r << ", " << index << ", " << name << std::endl;
+	}
+	return params;
+}
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/TemplateParameters.h b/odb_api/src/odb_api/TemplateParameters.h
new file mode 100644
index 0000000..366048d
--- /dev/null
+++ b/odb_api/src/odb_api/TemplateParameters.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file TemplateParameter.h
+///
+/// @author Piotr Kuchta, June 2009
+
+#ifndef TemplateParameters_H
+#define TemplateParameters_H
+
+#include <string>
+#include <stdint.h>
+
+#include "odb_api/MetaData.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+class DispatchingWriter;
+
+struct TemplateParameter {
+	TemplateParameter(size_t startPos, size_t endPos, size_t columnIndex, std::string name)
+	: startPos(startPos), endPos(endPos), columnIndex(columnIndex), name(name)
+	{}
+
+	size_t startPos;
+	size_t endPos;
+	size_t columnIndex;
+	std::string name;
+
+private:
+// No copy allowed.
+	TemplateParameter(const TemplateParameter&);
+	TemplateParameter& operator=(const TemplateParameter&);
+};
+
+class TemplateParameters : public std::vector<TemplateParameter*> {
+public:
+	TemplateParameters();
+	TemplateParameters(const std::string& fileNameTemplate, const MetaData& = nullMD);
+	~TemplateParameters();
+
+	void release();
+	static TemplateParameters& parse(const std::string& fileNameTemplate, TemplateParameters&, const MetaData& = nullMD);
+
+private:
+	TemplateParameters(const TemplateParameters&);
+	TemplateParameters& operator=(const TemplateParameters&);
+
+	static MetaData nullMD;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/TextReader.cc b/odb_api/src/odb_api/TextReader.cc
new file mode 100644
index 0000000..b9b9345
--- /dev/null
+++ b/odb_api/src/odb_api/TextReader.cc
@@ -0,0 +1,95 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODA.cc
+///
+/// @author Piotr Kuchta, Oct 2010
+
+#include "odb_api/TextReader.h"
+
+//#include "eckit/io/DataHandle.h"
+//#include "eckit/filesystem/PathName.h"
+
+#include "odb_api/TextReaderIterator.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+TextReader::TextReader(std::istream& input, const std::string& delimiter)
+: in_(&input),
+  deleteDataHandle_(false),
+  delimiter_(delimiter),
+  context_(0)
+{}
+
+TextReader::TextReader(std::istream& input, const std::string& delimiter, ecml::ExecutionContext* context)
+: in_(&input),
+  deleteDataHandle_(false),
+  delimiter_(delimiter),
+  context_(context)
+{}
+
+/*
+TextReader::TextReader()
+: in_(0),
+  deleteDataHandle_(true),
+  path_("")
+{}
+*/
+
+TextReader::TextReader(const std::string& path, const std::string& delimiter)
+: in_(new std::ifstream(path.c_str())),
+  deleteDataHandle_(true),
+  path_(path),
+  delimiter_(delimiter),
+  context_(0)
+{
+	//dataHandle_->openForRead();
+}
+
+TextReader::TextReader(const std::string& path, const std::string& delimiter, ecml::ExecutionContext* context)
+: in_(new std::ifstream(path.c_str())),
+  deleteDataHandle_(true),
+  path_(path),
+  delimiter_(delimiter),
+  context_(context)
+{
+	//dataHandle_->openForRead();
+}
+
+TextReader::~TextReader()
+{
+	///if (dataHandle_ && deleteDataHandle_)
+	if (in_ && deleteDataHandle_)
+	{
+		//dataHandle_->close();
+		//delete dataHandle_;
+		delete in_;
+	}
+}
+
+TextReaderIterator* TextReader::createReadIterator(const PathName& pathName)
+{
+	return new TextReaderIterator(*this, pathName);
+}
+
+TextReader::iterator TextReader::begin()
+{
+	TextReaderIterator * it = new TextReaderIterator(*this);
+	it->next(context_);
+	return iterator(it);
+}
+
+TextReader::iterator TextReader::end() { return iterator(0); }
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/TextReader.h b/odb_api/src/odb_api/TextReader.h
new file mode 100644
index 0000000..e8dfc44
--- /dev/null
+++ b/odb_api/src/odb_api/TextReader.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file TextReader.h
+///
+/// @author Piotr Kuchta, Oct 2010
+
+#ifndef odb_api_TextReader_H
+#define odb_api_TextReader_H
+
+#ifdef SWIGPYTHON
+#include <Python.h>
+#endif
+
+#include "odb_api/IteratorProxy.h"
+
+namespace eckit { class PathName; }
+
+namespace odb {
+
+class TextReaderIterator;
+
+class TextReader
+{
+public:
+	typedef IteratorProxy<TextReaderIterator,TextReader,double> iterator;
+	typedef iterator::Row row;
+
+	TextReader(std::istream &, const std::string& delimiter);
+	TextReader(std::istream &, const std::string& delimiter, ecml::ExecutionContext*);
+    TextReader(const std::string& path, const std::string& delimiter);
+    TextReader(const std::string& path, const std::string& delimiter, ecml::ExecutionContext*);
+
+	virtual ~TextReader();
+
+	iterator begin();
+	iterator end(); 
+
+    std::istream& stream() { return *in_; }
+	// For C API
+	TextReaderIterator* createReadIterator(const eckit::PathName&);
+
+#ifdef SWIGPYTHON
+	iterator __iter__() { return begin(); }
+#endif
+
+	const std::string& delimiter() { return delimiter_; }
+private:
+// No copy allowed
+    TextReader(const TextReader&);
+    TextReader& operator=(const TextReader&);
+	TextReader();
+
+    std::istream* in_;
+	bool deleteDataHandle_;
+	const std::string path_;
+	const std::string delimiter_;
+    ecml::ExecutionContext* context_;
+
+	friend class odb::IteratorProxy<odb::TextReaderIterator,odb::TextReader,double>;
+	friend class odb::TextReaderIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/TextReaderIterator.cc b/odb_api/src/odb_api/TextReaderIterator.cc
new file mode 100644
index 0000000..8ce780c
--- /dev/null
+++ b/odb_api/src/odb_api/TextReaderIterator.cc
@@ -0,0 +1,230 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file TextReaderIterator.cc
+///
+/// @author Piotr Kuchta, Oct 2010
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/utils/Translator.h"
+#include "eckit/types/Types.h"
+
+#include "odb_api/TextReaderIterator.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/ColumnType.h"
+
+using namespace eckit;
+
+typedef StringTools S;
+
+namespace odb {
+
+TextReaderIterator::TextReaderIterator(TextReader &owner)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  nrows_(0),
+  in_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  refCount_(0)
+{
+	in_ = &owner.stream();
+	ASSERT(in_);
+
+	parseHeader();
+}
+
+TextReaderIterator::TextReaderIterator(TextReader &owner, const PathName& pathName)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  nrows_(0),
+  in_(0),
+  newDataset_(false),
+  noMore_(false),
+  ownsF_(false),
+  refCount_(0)
+{
+	in_ = new std::ifstream(pathName.localPath());
+	ASSERT(in_);
+	ownsF_ = true;
+	parseHeader();
+}
+
+odb::BitfieldDef TextReaderIterator::parseBitfields(const std::string& c)
+{
+    //std::ostream& L( Log::debug() );
+
+    size_t leftBracket (c.find('['));
+    size_t rightBracket (c.find(']'));
+
+    if ( !(leftBracket != std::string::npos && rightBracket != std::string::npos))
+        throw UserError(std::string("Error parsing bitfield definition. Should be like: bitfield_column_name:BITFIELD[a:1;b:3] was: '") + c + "'");
+
+    std::string s(c.substr(leftBracket + 1,  rightBracket - leftBracket - 1));
+
+    //L << "TextReaderIterator::parseBitfields: s='" << s << "'" << std::endl;
+
+    odb::FieldNames names;
+    odb::Sizes      sizes;
+
+    size_t numberOfBits = 0;
+    std::vector<std::string> bs(S::split(";", s));
+
+    //L << "TextReaderIterator::parseBitfields: bs=" << bs << std::endl;
+
+    for (size_t i = 0; i < bs.size(); ++i)
+    {
+		std::vector<std::string> v(S::split(":", bs[i]));
+
+        //L << "TextReaderIterator::parseBitfields:   bs[" << i << "] = " << bs[i] << " " << v << " :  " << v.size() << std::endl;
+
+		if (v.size() != 2)
+            throw UserError("Bitfields definition parse error");
+
+        if (std::find(names.begin(), names.end(), v[0]) != names.end())
+            throw UserError("Names of fields must be unique within one bitfield");
+
+		names.push_back(v[0]);
+
+		int size = atoi(v[1].c_str());
+		
+        if ( !(v.size() > 0) )
+            throw UserError("Size of a bitfield must be positive and larger than zero");
+
+        numberOfBits += size;
+		sizes.push_back(size);
+	}
+    //L << "TextReaderIterator::parseBitfields: numberOfbits=" << numberOfBits << std::endl;
+
+    if (numberOfBits > 31)
+        throw UserError("Bitfields can have up to 31 bits only currently");
+
+	return odb::BitfieldDef(make_pair(names, sizes));
+}
+
+void TextReaderIterator::parseHeader()
+{
+    std::string header;
+    std::getline(*in_, header);
+    std::vector<std::string> columns (S::split(owner_.delimiter(), header));
+    //c->missingValue(missingValue);
+
+    std::ostream& L(Log::info());
+
+    L << "TextReaderIterator::parseHeader: columns: " << columns << std::endl;
+    L << "TextReaderIterator::parseHeader: delimiter: '" << owner_.delimiter() << "'" << std::endl;
+    L << "TextReaderIterator::parseHeader: header: '" << header << "'" << std::endl;
+
+	for (size_t i = 0; i < columns.size(); ++i)
+	{
+		Log::debug() << "TextReaderIterator::parseHeader: column " << i << " '" << columns[i] << "'" << std::endl;
+		std::vector<std::string> column (S::split(":", columns[i]));
+		if (column.size() < 2)
+			throw UserError(std::string("Column '") + columns[i] + "': format should be NAME \":\" TYPE");
+
+		const std::string columnName (S::trim(column[0]));
+		const std::string columnType (S::upper(S::join(":", std::vector<std::string>(column.begin() + 1, column.end()))));
+
+		if (! S::startsWith(columnType, "BITFIELD"))
+		{
+			Log::debug() << "TextReaderIterator::parseHeader: adding column " << columns_.size() << " '" << columnName << "' : " 
+						<< columnType << std::endl;
+			columns_.addColumn(columnName, columnType);
+		}
+		else
+		{
+			Log::debug() << "TextReaderIterator::parseHeader: adding BITFIELD " << columns_.size() << " '" << columns[i] << std::endl;
+			columns_.addBitfield(columnName, parseBitfields(columns[i]));
+		}
+	}
+	initRowBuffer();
+}
+
+TextReaderIterator::~TextReaderIterator ()
+{
+	close();
+	delete [] lastValues_;
+}
+
+
+bool TextReaderIterator::operator!=(const TextReaderIterator& other)
+{
+	return noMore_;
+}
+
+void TextReaderIterator::initRowBuffer()
+{
+	delete [] lastValues_;
+	lastValues_ = new double [columns().size()];
+	for(size_t i = 0; i < columns().size(); i++)
+		lastValues_[i] = columns()[i]->missingValue(); 
+}
+
+bool TextReaderIterator::next(ecml::ExecutionContext*)
+{
+    newDataset_ = false;
+    if (noMore_)
+        return false; 
+
+    std::string line;
+    std::getline(*in_, line);
+    StringTool::trimInPlace(line);
+    std::vector<std::string> values(S::split(owner_.delimiter(), line));
+
+    size_t nCols = values.size();
+    if (nCols == 0)
+        return ! (noMore_ = true);
+    ASSERT(nCols == columns().size());
+
+    for(size_t i = 0; i < nCols; ++i)
+    {
+        const std::string& v (S::trim(values[i]));
+        if (S::upper(v) == "NULL")
+            lastValues_[i] = columns_[i]->missingValue();
+        else 
+        {
+            odb::ColumnType typ ( columns()[i]->type() );
+            lastValues_[i] = typ == odb::STRING ? StringTool::cast_as_double(StringTool::unQuote(v))
+                           : typ == odb::REAL ? Translator<std::string, double>()(v) 
+                           : typ == odb::DOUBLE ? Translator<std::string, double>()(v) 
+                           : typ == odb::INTEGER ? Translator<std::string, int>()(v)
+                           : typ == odb::BITFIELD ? Translator<std::string, int>()(v)
+                           // TODO: signal error
+                           : columns_[i]->missingValue();
+        }
+    }
+
+    return nCols;
+}
+
+bool TextReaderIterator::isNewDataset() { return newDataset_; }
+
+const double* TextReaderIterator::data() { return lastValues_; }
+
+int TextReaderIterator::close()
+{
+	//if (ownsF_ && f) { f->close(); delete f; f = 0; }
+
+	if (ownsF_ && in_)
+	{
+		delete in_;
+		in_ = 0;
+	}
+
+	return 0;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/TextReaderIterator.h b/odb_api/src/odb_api/TextReaderIterator.h
new file mode 100644
index 0000000..3dd1363
--- /dev/null
+++ b/odb_api/src/odb_api/TextReaderIterator.h
@@ -0,0 +1,92 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file TextReaderIterator.h
+///
+/// @author Piotr Kuchta, Oct 2010
+
+#ifndef TextReaderIterator_H
+#define TextReaderIterator_H
+
+#include "odb_api/Header.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/MetaData.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace eckit { class ExecutionContext; }
+namespace odb { namespace sql { class ODATableIterator; } }
+
+namespace odb {
+
+class TextReader;
+
+class TextReaderIterator 
+{
+public:
+	TextReaderIterator (TextReader &owner);
+	TextReaderIterator (TextReader &owner, const eckit::PathName&);
+	~TextReaderIterator ();
+
+	bool isNewDataset();
+	const double* data();
+	//long integer(int i);
+
+	bool operator!=(const TextReaderIterator& other);
+
+	MetaData& columns() { return columns_; }
+
+	static odb::BitfieldDef parseBitfields(const std::string&);
+//protected:
+
+	int close();
+
+// next() is public cause it needs to be used by the C API functions - normally client code should not use it
+	bool next(ecml::ExecutionContext*);
+
+private:
+// No copy allowed.
+    TextReaderIterator(const TextReaderIterator&);
+    TextReaderIterator& operator=(const TextReaderIterator&);
+
+	void initRowBuffer();
+	void parseHeader();
+
+	TextReader& owner_;
+	MetaData columns_;
+	double* lastValues_;
+	unsigned long long nrows_;
+
+	std::istream* in_;
+	//eckit::DataHandle *f;
+	//Properties properties_;
+
+	bool newDataset_;
+public:
+	bool noMore_;
+
+	bool ownsF_;
+	ecml::ExecutionContext* context_;
+	int refCount_;
+
+protected:
+	// FIXME:
+    TextReaderIterator(): owner_(*((TextReader *) 0)), columns_(0) {}
+
+	friend class odb::TextReader;
+	friend class odb::IteratorProxy<odb::TextReaderIterator, odb::TextReader, const double>;
+	friend class odb::Header<odb::TextReaderIterator>;
+	friend class odb::sql::ODATableIterator;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Tracer.cc b/odb_api/src/odb_api/Tracer.cc
new file mode 100644
index 0000000..6303639
--- /dev/null
+++ b/odb_api/src/odb_api/Tracer.cc
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Tracer.h"
+
+Tracer::Tracer(std::ostream& o, const std::string& m)
+: out_(o), message_(m)
+{
+	//out_ << message_ << " BEGIN" <<  std::endl;
+	out_ << "BEGIN " <<  message_ << std::endl;
+}
+
+Tracer::~Tracer()
+{
+	//out_ << message_ << " END" <<  std::endl;
+	out_ << "END " <<  message_ << std::endl;
+}
diff --git a/odb_api/src/odb_api/Tracer.h b/odb_api/src/odb_api/Tracer.h
new file mode 100644
index 0000000..883b1bf
--- /dev/null
+++ b/odb_api/src/odb_api/Tracer.h
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TRACER_H
+
+#include <ostream>
+#include <string>
+
+
+class Tracer {
+public:
+	Tracer(std::ostream&, const std::string&);
+	~Tracer();
+private:
+    std::ostream& out_;
+    std::string message_;
+};
+
+#endif
+
diff --git a/odb_api/src/odb_api/Types.h b/odb_api/src/odb_api/Types.h
new file mode 100644
index 0000000..6d27365
--- /dev/null
+++ b/odb_api/src/odb_api/Types.h
@@ -0,0 +1,23 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_Types_h
+#define odb_api_Types_h
+
+namespace odb {
+
+typedef std::vector<std::string>             FieldNames;
+typedef std::vector<int32_t>                 Sizes;
+typedef std::pair<FieldNames, Sizes>         BitfieldDef;
+typedef std::map<std::string, BitfieldDef>   BitfieldDefs;
+
+}
+
+#endif 
diff --git a/odb_api/src/odb_api/UnsafeInMemoryDataHandle.h b/odb_api/src/odb_api/UnsafeInMemoryDataHandle.h
new file mode 100644
index 0000000..a739019
--- /dev/null
+++ b/odb_api/src/odb_api/UnsafeInMemoryDataHandle.h
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnsafeInMemoryDataHandle.h
+/// Piotr Kuchta - ECMWF August 2009
+
+#ifndef UnsafeInMemoryDataHandle_H
+#define UnsafeInMemoryDataHandle_H
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+
+namespace odb {
+
+class NonVirtual {};
+
+template <typename T = eckit::DataHandle>
+class UnsafeInMemoryDataHandle : public T {
+public:
+    UnsafeInMemoryDataHandle(unsigned char *p) : buf_(p), p_(p) {}
+
+    ~UnsafeInMemoryDataHandle() {}
+
+	void print(std::ostream& s) const { /*TODO*/ }
+
+	void buffer(unsigned char *p) { buf_ = p_ = p; }
+	unsigned char* buffer() { return buf_; }
+
+	/// Return estimated length.
+    eckit::Length openForRead()
+	{
+		size_t size = p_ - buf_;
+		p_ = buf_;
+		return size;
+	}
+
+	// Receive estimated length.
+    void openForWrite(const eckit::Length&) { p_ = buf_; }
+
+	// Receive estimated length
+    void openForAppend(const eckit::Length&) { NOTIMP; }
+
+    long read(void* p, long n)
+	{
+		unsigned char *dst = reinterpret_cast<unsigned char *>(p);
+		unsigned char *end = dst + n;
+		for ( ; dst != end; )
+			*dst++ = *p_++;
+		return n;
+	}
+
+    long write(const void* pd, long n)
+	{
+		const unsigned char *p = reinterpret_cast<const unsigned char*>(pd);
+		unsigned char *end = p_ + n;
+		for ( ; p_ != end; )
+			*p_++ = *p++;
+		return n;
+	}
+
+    void close() {}
+
+    void rewind()                { /*?*/ p_ = buf_; }
+	eckit::Length estimate()            { /*?*/ return p_ - buf_; }
+	eckit::Offset position()            { return p_ - buf_; }
+
+    std::string title() const { return name(); }
+    std::string name() const { return "UnsafeInMemoryDataHandle"; }
+
+private:
+    UnsafeInMemoryDataHandle();
+
+// No copy allowed
+
+    UnsafeInMemoryDataHandle(const UnsafeInMemoryDataHandle&);
+    UnsafeInMemoryDataHandle& operator=(const UnsafeInMemoryDataHandle&);
+
+	unsigned char* buf_;
+	unsigned char* p_;
+
+	friend std::ostream& operator<<(std::ostream& s, const UnsafeInMemoryDataHandle& handle) 
+		{ handle.print(s); return s;}
+};
+
+typedef UnsafeInMemoryDataHandle<NonVirtual> FastInMemoryDataHandle;
+typedef UnsafeInMemoryDataHandle<eckit::DataHandle> PrettyFastInMemoryDataHandle;
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/VariableExpression.cc b/odb_api/src/odb_api/VariableExpression.cc
new file mode 100755
index 0000000..13a7208
--- /dev/null
+++ b/odb_api/src/odb_api/VariableExpression.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+//#include "eckit/log/Log.h"
+
+//#include "odb_api/SQLAST.h"
+//#include "odb_api/SQLBitfield.h"
+//#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/VariableExpression.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLSession.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+const odb::sql::type::SQLType* VariableExpression::type() const { return &odb::sql::type::SQLType::lookup("real"); }
+
+VariableExpression::VariableExpression(const std::string& name)
+: value_(0),
+  name_(name)
+{}
+
+VariableExpression::VariableExpression(const VariableExpression& other)
+: value_(other.value_),
+  name_(other.name_)
+{}
+
+SQLExpression* VariableExpression::clone() const { return new VariableExpression(*this); }
+
+VariableExpression::~VariableExpression() {}
+
+double VariableExpression::eval(bool& missing) const { return value_->eval(missing); }
+
+void VariableExpression::prepare(SQLSelect& sql)
+{
+	SQLSession& s = SQLSession::current();
+	value_ = s.currentDatabase().getVariable(name_);
+//	std::cout << "VariableExpression " << name_ << " " << value_ << std::endl;
+}
+
+void VariableExpression::cleanup(SQLSelect& sql) { value_ = 0; }
+
+void VariableExpression::print(std::ostream& s) const
+{
+	s << name_ << " => " << *value_;
+}
+
+bool VariableExpression::isConstant() const { return value_->isConstant(); }
+
+bool VariableExpression::isVector() const { return value_->isVector(); }
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/VariableExpression.h b/odb_api/src/odb_api/VariableExpression.h
new file mode 100755
index 0000000..a849732
--- /dev/null
+++ b/odb_api/src/odb_api/VariableExpression.h
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File VariableExpression.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef VariableExpression_H
+#define VariableExpression_H
+
+#include "odb_api/SQLExpression.h"
+
+namespace odb {
+namespace sql {
+namespace expression {
+
+class VariableExpression : public SQLExpression {
+public:
+	VariableExpression(const std::string&);
+	VariableExpression(const VariableExpression&);
+	~VariableExpression(); 
+
+	SQLExpression* clone() const;
+
+private:
+// No copy allowed
+	VariableExpression& operator=(const VariableExpression&);
+
+	SQLExpression* value_;
+	std::string name_;
+
+// -- Overridden methods
+	virtual void print(std::ostream& s) const;
+	virtual void prepare(SQLSelect& sql);
+	virtual void cleanup(SQLSelect& sql);
+
+	const type::SQLType* type() const;
+	virtual double eval(bool& missing) const;
+
+	virtual bool isConstant() const;
+	virtual bool isVector() const;
+};
+
+} // namespace expression
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/VariablesTable.cc b/odb_api/src/odb_api/VariablesTable.cc
new file mode 100755
index 0000000..4f3fb20
--- /dev/null
+++ b/odb_api/src/odb_api/VariablesTable.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/VariablesTable.h"
+//#include "eckit/parser/Tokenizer.h"
+
+//#include "odb_api/SQLAST.h"
+//#include "odb_api/SQLBitColumn.h"
+//#include "odb_api/SQLBitfield.h"
+//#include "odb_api/SQLColumn.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/SQLDatabase.h"
+//#include "odb_api/VariablesTable.h"
+//#include "odb_api/SQLType.h"
+#include "odb_api/SQLColumn.h"
+//#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/SQLExpression.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace sql {
+
+class VariablesColumn : public SQLColumn {
+public:
+	VariablesColumn(const std::string& name, int i, const std::string sqlType, SQLTable& owner)  
+	: SQLColumn(type::SQLType::lookup(sqlType), owner, name, i, false, 0 /*FIXME*/)
+	{
+        missing_ = false;
+        //value_ = MISSING_VALUE_REAL;
+    }
+     
+
+    double next(bool& missing) 
+    {
+        NOTIMP;
+    }
+
+    double* value_;
+    bool  missing_;
+};
+
+VariablesTable::VariablesTable(SQLDatabase& owner, const std::string& name)
+: SQLTable(owner, "", name)
+{
+	addColumn(new VariablesColumn("name", 0, "string", *this), "name", 0);
+	addColumn(new VariablesColumn("value", 1, "real", *this), "value", 1);
+}
+
+VariablesTable::~VariablesTable() {}
+
+SQLTableIterator* VariablesTable::iterator(const std::vector<SQLColumn*>&) const
+{
+	return new VariablesTableIterator(SQLSession::current().currentDatabase().variables());
+}
+
+SQLColumn* VariablesTable::createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue)
+{ NOTIMP; }
+
+SQLColumn* VariablesTable::createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue, const BitfieldDef&)
+{ NOTIMP; }
+
+VariablesTableIterator::~VariablesTableIterator() {}
+
+void VariablesTableIterator::rewind()
+{
+    it_ = variables_.begin();
+}
+
+bool VariablesTableIterator::next()
+{
+    // TODO: populate values
+    if (it_ == variables_.end())
+        return false;
+    ++it_;
+    data_[0] = StringTool::cast_as_double(it_->first);
+    bool missing;
+    data_[1] = it_->second->eval(missing);
+    return true;
+}
+
+VariablesTableIterator::VariablesTableIterator(std::map<std::string,SQLExpression*>& vs)
+: variables_(vs)
+{
+}
+
+
+} // namespace sql 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/VariablesTable.h b/odb_api/src/odb_api/VariablesTable.h
new file mode 100755
index 0000000..6f675a1
--- /dev/null
+++ b/odb_api/src/odb_api/VariablesTable.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File VariablesTable.h
+// Baudouin Raoult - ECMWF Dec 03
+
+#ifndef VariablesTable_H
+#define VariablesTable_H
+
+#include "odb_api/SQLTable.h"
+
+namespace odb {
+namespace sql {
+
+//class SQLFile;
+class SQLPool;
+class SQLColumn;
+class SQLDatabase;
+namespace expression {
+class SQLExpression;
+}
+
+class VariablesTableIterator : public SQLTableIterator {
+    typedef std::map<std::string,expression::SQLExpression*> Variables;
+public:
+    VariablesTableIterator(std::map<std::string,expression::SQLExpression*>&);
+	~VariablesTableIterator();
+	void rewind();
+	bool next();
+private:
+	Variables& variables_;
+	Variables::iterator it_;
+    double data_[2];
+    bool missing_[2];
+};
+
+typedef std::vector<std::string> ColumnNames;
+
+class VariablesTable : public SQLTable {
+public:
+	VariablesTable(SQLDatabase&,const std::string&);
+	~VariablesTable(); 
+
+    SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue, const BitfieldDef&);
+    SQLColumn* createSQLColumn(const type::SQLType& type, const std::string& name, int index, bool hasMissingValue, double missingValue);
+
+	SQLTableIterator* iterator(const std::vector<SQLColumn*>&) const;
+
+private:
+    
+	friend std::ostream& operator<<(std::ostream& s,const VariablesTable& p)
+		{ p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/Writer.cc b/odb_api/src/odb_api/Writer.cc
new file mode 100644
index 0000000..377fdb3
--- /dev/null
+++ b/odb_api/src/odb_api/Writer.cc
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Writer.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <math.h>
+//#include <values.h>
+
+using namespace std;
+
+#include "eckit/io/DataHandle.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/FileDescHandle.h"
+#include "eckit/config/Resource.h"
+
+#include "odb_api/Codec.h"
+#include "odb_api/Column.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/CodecOptimizer.h"
+#include "odb_api/DataStream.h"
+#include "odb_api/FixedSizeWriterIterator.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/Writer.h"
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/SQLAST.h"
+
+namespace odb {
+
+
+template <typename ITERATOR>
+Writer<ITERATOR>::Writer()
+: path_(""),
+  dataHandle_(0),
+  rowsBufferSize_(eckit::Resource<long>("$ODB_ROWS_BUFFER_SIZE;-rowsBufferSize;rowsBufferSize", DEFAULT_ROWS_BUFFER_SIZE)),
+  openDataHandle_(true),
+  deleteDataHandle_(true)
+{} 
+
+template <typename ITERATOR>
+Writer<ITERATOR>::Writer(const eckit::PathName& path)
+: path_(path),
+  dataHandle_(0),
+  rowsBufferSize_(eckit::Resource<long>("$ODB_ROWS_BUFFER_SIZE;-rowsBufferSize;rowsBufferSize", DEFAULT_ROWS_BUFFER_SIZE)),
+  openDataHandle_(true),
+  deleteDataHandle_(true)
+{
+    if (path_ == "/dev/stdout" || path_ == "stdout")
+    {
+        eckit::Log::info() << "Writing to stdout" << std::endl;
+        dataHandle_ = new eckit::FileDescHandle(1);
+        openDataHandle_ = false;
+    }
+} 
+
+template <typename ITERATOR>
+Writer<ITERATOR>::Writer(eckit::DataHandle *dh, bool openDataHandle, bool deleteDataHandle)
+: path_(""),
+  dataHandle_(dh),
+  rowsBufferSize_(eckit::Resource<long>("$ODB_ROWS_BUFFER_SIZE;-rowsBufferSize;rowsBufferSize", DEFAULT_ROWS_BUFFER_SIZE)),
+  openDataHandle_(openDataHandle),
+  deleteDataHandle_(deleteDataHandle)
+{}
+
+template <typename ITERATOR>
+Writer<ITERATOR>::Writer(eckit::DataHandle &dh, bool openDataHandle)
+: path_(""),
+  dataHandle_(&dh),
+  rowsBufferSize_(eckit::Resource<long>("$ODB_ROWS_BUFFER_SIZE;-rowsBufferSize;rowsBufferSize", DEFAULT_ROWS_BUFFER_SIZE)),
+  openDataHandle_(openDataHandle),
+  deleteDataHandle_(false)
+{}
+
+template <typename ITERATOR>
+Writer<ITERATOR>::~Writer() { if (deleteDataHandle_) delete dataHandle_; }
+
+template <typename ITERATOR>
+typename Writer<ITERATOR>::iterator Writer<ITERATOR>::begin(bool openDataHandle)
+{
+	eckit::DataHandle *dh = 0;
+	if (dataHandle_ == 0)
+    {
+		dh = ODBAPISettings::instance().writeToFile(path_, eckit::Length(0), false);
+    }
+	else
+	{
+		ASSERT(dataHandle_);
+		dh = dataHandle_;
+	}
+	return typename Writer::iterator(new ITERATOR(*this, dh, openDataHandle));
+}
+
+template <typename ITERATOR>
+ITERATOR* Writer<ITERATOR>::createWriteIterator(eckit::PathName pathName, bool append)
+{
+	eckit::Length estimatedLength = 10*1024*1024;
+	eckit::DataHandle *h = append
+					? ODBAPISettings::instance().appendToFile(pathName, estimatedLength)
+					: ODBAPISettings::instance().writeToFile(pathName, estimatedLength);
+	return new ITERATOR(*this, h, false);
+}
+
+// Explicit templates' instantiations.
+
+template Writer<WriterBufferingIterator>::Writer();
+template Writer<WriterBufferingIterator>::Writer(const eckit::PathName&);
+template Writer<WriterBufferingIterator>::Writer(eckit::DataHandle&,bool);
+template Writer<WriterBufferingIterator>::Writer(eckit::DataHandle*,bool,bool);
+
+template Writer<WriterBufferingIterator>::~Writer();
+template Writer<WriterBufferingIterator>::iterator Writer<WriterBufferingIterator>::begin(bool);
+template WriterBufferingIterator * Writer<WriterBufferingIterator>::createWriteIterator(eckit::PathName,bool);
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/Writer.h b/odb_api/src/odb_api/Writer.h
new file mode 100644
index 0000000..efd89f2
--- /dev/null
+++ b/odb_api/src/odb_api/Writer.h
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file Writer.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef ODAWRITER_H
+#define ODAWRITER_H
+
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/WriterBufferingIterator.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+
+namespace odb {
+
+typedef WriterBufferingIterator DefaultWritingIterator;
+
+template <typename ITERATOR = DefaultWritingIterator>
+class Writer
+{
+	enum { DEFAULT_ROWS_BUFFER_SIZE = 10000 };
+public:
+	typedef ITERATOR iterator_class;
+	typedef IteratorProxy<ITERATOR, Writer> iterator;
+
+	Writer(const eckit::PathName& path);
+	Writer(eckit::DataHandle &, bool openDataHandle=true);
+	Writer(eckit::DataHandle *, bool openDataHandle=true, bool deleteDataHandle=false);
+	Writer();
+	virtual ~Writer();
+
+	iterator begin(bool openDataHandle=true);
+
+	eckit::DataHandle& dataHandle() { return *dataHandle_; };
+
+	ITERATOR* createWriteIterator(eckit::PathName, bool append = false);
+
+	unsigned long rowsBufferSize() { return rowsBufferSize_; }
+	Writer& rowsBufferSize(unsigned long n) { rowsBufferSize_ = n; }
+
+	const eckit::PathName path() { return path_; }
+
+private:
+// No copy allowed
+    Writer(const Writer&);
+    Writer& operator=(const Writer&);
+
+	const eckit::PathName path_;
+	eckit::DataHandle* dataHandle_;
+	unsigned long rowsBufferSize_;
+
+	bool openDataHandle_;
+	bool deleteDataHandle_;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/WriterBufferingIterator.cc b/odb_api/src/odb_api/WriterBufferingIterator.cc
new file mode 100644
index 0000000..5ad200b
--- /dev/null
+++ b/odb_api/src/odb_api/WriterBufferingIterator.cc
@@ -0,0 +1,330 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file WriterBufferingIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/Writer.h"
+
+#include "odb_api/WriterBufferingIterator.h"
+#include <arpa/inet.h>
+
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/SQLAST.h"
+
+inline size_t MEGA(size_t n) { return n*1024*1204; }
+
+using namespace eckit;
+
+namespace odb {
+
+WriterBufferingIterator::WriterBufferingIterator(Owner &owner, DataHandle *dh, bool openDataHandle, const odb::sql::TableDef* tableDef)
+: owner_(owner),
+  columns_(0),
+  lastValues_(0),
+  nextRow_(0),
+  nrows_(0),
+  f(dh),
+  encodedDataBuffer_(0),
+  refCount_(0),
+  properties_(),
+  blockBuffer_(),
+  rowsBuffer_(),
+  nextRowInBuffer_(0),
+  memoryDataHandle_(0),
+  columnsBuffer_(0),
+  rowsBufferSize_(owner.rowsBufferSize()),
+  setvBuffer_(0),
+  maxAnticipatedHeaderSize_( ODBAPISettings::instance().headerBufferSize() ),
+  tableDef_(tableDef),
+  path_(owner.path()),
+  openDataHandle_(openDataHandle)
+{
+	if (openDataHandle)	
+		open();
+}
+
+WriterBufferingIterator::~WriterBufferingIterator()
+{
+	close();
+	delete [] lastValues_;
+	delete [] nextRow_;
+	if (! openDataHandle_)	
+        delete f;
+}
+
+unsigned long WriterBufferingIterator::gatherStats(const double* values, unsigned long count)
+{
+	ASSERT(count == columns().size());
+
+	//for (size_t i = 0; i < columns_.size(); ++i) Log::info() << "gatherStats: columns_[" << i << "]=" << *columns_[i] << std::endl;
+
+	for(size_t i = 0; i < count; i++)
+		columns_[i]->coder().gatherStats(values[i]);
+
+	return 0;
+} 
+
+template <typename DATASTREAM>
+int WriterBufferingIterator::setOptimalCodecs()
+{
+	if (columnsBuffer_.size() == 0)
+	{
+		columnsBuffer_ = columns();
+		columnsBuffer_.resetStats();
+	}
+
+	return codecOptimizer_.setOptimalCodecs<DATASTREAM>(const_cast<MetaData&>(columns()));
+}
+
+void WriterBufferingIterator::allocBuffers()
+{
+	delete [] lastValues_;
+	delete [] nextRow_;
+	int32_t colSize = columns().size();
+	double* last = new double [colSize];
+	lastValues_ = last;
+	nextRow_ = new double [colSize];
+	ASSERT(last);
+
+	for (int i = 0; i < colSize; ++i)
+		nextRow_[i] = last[i] = columns_[i]->missingValue();
+
+	nrows_ = 0;
+
+	encodedDataBuffer_.size(sizeof(uint16_t) + colSize * sizeof(double));
+}
+
+void WriterBufferingIterator::allocRowsBuffer()
+{
+	size_t nCols = columns().size();
+	const size_t maxEncodedRowSize (sizeof(uint16_t) + nCols * sizeof(double));
+	blockBuffer_.size(maxAnticipatedHeaderSize_ + rowsBufferSize_ * maxEncodedRowSize);
+	rowsBuffer_.share(blockBuffer_ + maxAnticipatedHeaderSize_, rowsBufferSize_ * maxEncodedRowSize);
+
+	memoryDataHandle_.buffer(rowsBuffer_);
+	nextRowInBuffer_ = rowsBuffer_;
+}
+
+void WriterBufferingIterator::writeHeader()
+{
+	allocBuffers();
+	for (size_t i = 0; i < columns_.size(); ++i)
+		columns_[i]->coder().resetStats();
+	//for (size_t i = 0; i < columns_.size(); ++i) Log::info() << "writeHeader: columns_[" << i << "]=" << *columns_[i] << std::endl;
+}
+
+bool WriterBufferingIterator::next(ecml::ExecutionContext* context)
+{
+    return writeRow(nextRow_, columns().size()) == 0;
+}
+
+double* WriterBufferingIterator::data() { return nextRow_; }
+double& WriterBufferingIterator::data(size_t i)
+{
+	ASSERT(i >= 0 && i < columns().size());
+	return nextRow_[i];
+}
+
+int WriterBufferingIterator::writeRow(const double* data, unsigned long nCols)
+{
+	ASSERT(nCols == columns().size());
+
+	if (rowsBuffer_ == 0)
+		allocRowsBuffer();
+
+	gatherStats(data, nCols);
+
+    std::copy(data, data + nCols, reinterpret_cast<double*>(nextRowInBuffer_ + sizeof(uint16_t)));
+	nextRowInBuffer_ += sizeof(uint16_t) + nCols * sizeof(double);
+
+	ASSERT(nextRowInBuffer_ <= rowsBuffer_ + rowsBuffer_.size());
+
+	if (nextRowInBuffer_ == rowsBuffer_ + rowsBuffer_.size())
+		flush();
+
+	return 0;
+}
+
+inline bool equal(const double* const v1, const double* const v2)
+{
+	for (size_t i=0; i < sizeof(double); ++i)
+		if (reinterpret_cast<unsigned const char*>(v1)[i] != reinterpret_cast<unsigned const char*>(v2)[i])
+			return false;
+	return true;
+}
+
+unsigned char* WriterBufferingIterator::writeNumberOfRepeatedValues(unsigned char *p, uint16_t k)
+{
+	uint16_t nk (htons(k));
+	unsigned char *pk (reinterpret_cast<unsigned char *>(&nk));
+	*p++ = *pk++;
+	*p++ = *pk;
+	return p;
+}
+
+int WriterBufferingIterator::doWriteRow(const double* values, unsigned long count)
+{
+	if (lastValues_ == 0) allocBuffers();
+
+	uint16_t k (0);
+	for (; k < count && equal(&values[k], &lastValues_[k]); ++k)
+		;
+
+	unsigned char *p (encodedDataBuffer_);
+	p = writeNumberOfRepeatedValues(p, k);
+
+	for (size_t i = k; i < count; ++i) 
+	{
+		Column *col (columns_[i]);
+		p = col->coder().encode(p, values[i]);
+		lastValues_[i] = values[i];
+	}
+
+	DataStream<SameByteOrder, FastInMemoryDataHandle> f(memoryDataHandle_);
+	f.writeBytes(encodedDataBuffer_.cast<char>(), p - encodedDataBuffer_);
+
+	nrows_++;
+	return 0;
+} 
+
+int WriterBufferingIterator::open()
+{
+	//Log::debug() << "WriterBufferingIterator::open@" << this << ": Opening data handle " << f << std::endl;
+	ASSERT(f);
+
+	Length estimatedLen = MEGA(20);
+	f->openForWrite(estimatedLen);
+
+	return 0;
+}
+
+
+int WriterBufferingIterator::setColumn(size_t index, std::string name, ColumnType type)
+{
+	//Log::debug() << "WriterBufferingIterator::setColumn: " << std::endl;
+	ASSERT(index < columns().size());
+	Column* col = columns_[index];
+	ASSERT(col);
+
+	col->name(name); 
+	col->type<DataStream<SameByteOrder, FastInMemoryDataHandle> >(type, false);
+	return 0;
+}
+
+int WriterBufferingIterator::setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b)
+{
+	//Log::debug() << "WriterBufferingIterator::setBitfieldColumn: " << std::endl;
+	ASSERT(index < columns().size());
+	Column* col = columns_[index];
+	ASSERT(col);
+
+	col->name(name); 
+	col->type<DataStream<SameByteOrder, FastInMemoryDataHandle> >(type, false);
+    col->bitfieldDef(b);
+	col->missingValue(0);
+	return 0;
+}
+
+void WriterBufferingIterator::missingValue(size_t i, double missingValue)
+{
+	ASSERT(i < columns().size());
+	Column* col = columns_[i];
+	ASSERT(col);
+
+	col->missingValue(missingValue);
+}
+
+void WriterBufferingIterator::flush()
+{
+	if (nextRowInBuffer_ == rowsBuffer_)
+		return;
+    
+	setOptimalCodecs<DataStream<SameByteOrder, FastInMemoryDataHandle> >();
+
+	unsigned long rowsWritten = 0;
+	const size_t nCols = columns().size();
+	for (unsigned char *pr = rowsBuffer_; pr < nextRowInBuffer_; pr += sizeof(uint16_t) + nCols * sizeof(double), ++rowsWritten)
+		doWriteRow(reinterpret_cast<double *>(pr + sizeof(uint16_t)), nCols);
+
+	InMemoryDataHandle bufferForHeader;
+	doWriteHeader(bufferForHeader, memoryDataHandle_.position(), rowsWritten);
+
+	Log::debug() << "WriterBufferingIterator::flush: header size: " << bufferForHeader.position() << std::endl;
+
+	MemoryBlock buff(bufferForHeader.position());
+	bufferForHeader.openForRead();
+	bufferForHeader.read(buff, buff.size());
+
+	if (buff.size() < maxAnticipatedHeaderSize_)
+	{
+		Log::debug() << "WriterBufferingIterator::flush: writing header and data in one go. "
+			"Block size: " << buff.size() + memoryDataHandle_.position() << std::endl;
+
+        std::copy(static_cast<unsigned char*>(buff), static_cast<unsigned char*>(buff) + buff.size(),
+			memoryDataHandle_.buffer() - buff.size());
+		this->f->write(memoryDataHandle_.buffer() - buff.size(),
+			static_cast<size_t>(memoryDataHandle_.position()) + buff.size()); // Write encoded data
+	}
+	else
+	{
+		Log::info() << "WriterBufferingIterator::flush: writing header and data separately." << std::endl;
+
+		this->f->write(buff, buff.size()); // Write header
+		this->f->write(memoryDataHandle_.buffer(), memoryDataHandle_.position()); // Write encoded data
+	}
+
+	Log::debug() << "WriterBufferingIterator::flush: flushed " << rowsWritten << " rows." << std::endl;
+
+	memoryDataHandle_.buffer(rowsBuffer_.cast<unsigned char>());
+
+	nextRowInBuffer_ = rowsBuffer_;
+
+    MetaData& md(const_cast<MetaData&>(columns()));
+    md = columnsBuffer_;
+    md.resetStats();
+}
+
+
+template <typename T>
+void WriterBufferingIterator::doWriteHeader(T& dataHandle, size_t dataSize, size_t rowsNumber)
+{
+	//Log::debug() << "WriterBufferingIterator::doWriteHeader: dataSize=" << dataSize << ", rowsNumber=" << rowsNumber << std::endl;
+
+	allocBuffers();
+
+	//  FIXME: copy properties from input - props are not initialised now
+	serializeHeader<SameByteOrder,T>(dataHandle, dataSize, rowsNumber, properties_, columns()); 
+}
+
+int WriterBufferingIterator::close()
+{
+	flush();
+
+	if (!openDataHandle_ && f)
+	{
+		f->close();
+		f = 0;
+	}
+	return 0;
+}
+
+std::vector<eckit::PathName> WriterBufferingIterator::outputFiles()
+{
+    std::vector<eckit::PathName> r;
+    r.push_back(path_);
+    return r;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/WriterBufferingIterator.h b/odb_api/src/odb_api/WriterBufferingIterator.h
new file mode 100644
index 0000000..ee874f3
--- /dev/null
+++ b/odb_api/src/odb_api/WriterBufferingIterator.h
@@ -0,0 +1,198 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file WriterBufferingIterator.h
+///
+/// @author Piotr Kuchta, August 2009
+
+#ifndef odb_api_WriterBufferingIterator_H
+#define odb_api_WriterBufferingIterator_H
+
+#include "odb_api/Array.h"
+#include "odb_api/CodecOptimizer.h"
+#include "odb_api/Header.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/MemoryBlock.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace ecml { class ExecutionContext; }
+
+namespace odb {
+
+template <typename I> class Writer;
+namespace sql { class TableDef; }
+
+class WriterBufferingIterator 
+{
+public:
+	typedef Writer<WriterBufferingIterator> Owner;
+
+	//WriterBufferingIterator (Owner &owner, eckit::DataHandle *, bool openDataHandle=true);
+	WriterBufferingIterator (Owner &owner, eckit::DataHandle *, bool openDataHandle, const odb::sql::TableDef* tableDef=0);
+
+	~WriterBufferingIterator();
+
+	int open();
+
+	double* data();
+	double& data(size_t i);
+
+	int setColumn(size_t index, std::string name, ColumnType type);
+	int setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b);
+
+	void missingValue(size_t i, double); 
+
+	template <typename T> unsigned long pass1(T&, const T&);
+	unsigned long gatherStats(const double* values, unsigned long count);
+
+	int close();
+
+	const MetaData& columns() { return columns_; }
+	const MetaData& columns(const MetaData& md) { return columns_ = md; }
+
+    void setNumberOfColumns(size_t n) { columns_.setSize(n); }
+
+	Owner& owner() { return owner_; }
+
+	eckit::DataHandle& dataHandle() { return *f; }
+
+	void property(std::string key, std::string value) { properties_[key] = value; }
+
+//protected:
+
+	template <typename DATASTREAM> int setOptimalCodecs();
+
+	void writeHeader();
+
+	int writeRow(const double* values, unsigned long count);
+
+	size_t rowsBufferSize() { return rowsBufferSize_; }
+	void rowsBufferSize(size_t n) { rowsBufferSize_ = n; }
+
+	void flush();
+
+    std::vector<eckit::PathName> outputFiles();
+	int refCount_;
+	bool next(ecml::ExecutionContext*);
+protected:
+	Owner& owner_;
+	MetaData columns_;
+	double* lastValues_;
+	double* nextRow_;
+	unsigned long long nrows_;
+
+	eckit::DataHandle *f;
+	Array<unsigned char> encodedDataBuffer_;
+    eckit::PathName path_;
+
+	unsigned char* writeNumberOfRepeatedValues(unsigned char *, uint16_t);
+
+private:
+// No copy allowed.
+	WriterBufferingIterator(const WriterBufferingIterator&);
+	WriterBufferingIterator& operator=(const WriterBufferingIterator&);
+
+	template <typename T> void pass1init(T&, const T&);
+
+	template <typename T> void doWriteHeader(T&, size_t, size_t);
+
+
+	void allocBuffers();
+	void allocRowsBuffer();
+	void resetColumnsBuffer();
+
+	int doWriteRow(const double*, unsigned long);
+
+	Properties properties_;
+
+	Array<unsigned char> blockBuffer_;
+	Array<unsigned char> rowsBuffer_;
+	unsigned char* nextRowInBuffer_;
+	FastInMemoryDataHandle memoryDataHandle_;
+	MetaData columnsBuffer_;
+
+	size_t rowsBufferSize_;
+	MemoryBlock setvBuffer_;
+	size_t maxAnticipatedHeaderSize_;
+
+	codec::CodecOptimizer codecOptimizer_;
+
+    const odb::sql::TableDef* tableDef_;
+
+public:
+    ecml::ExecutionContext* context_;
+private:
+    bool openDataHandle_;
+
+	friend class IteratorProxy<WriterBufferingIterator, Owner>;
+	friend class Header<WriterBufferingIterator>;
+};
+
+template<typename T>
+void WriterBufferingIterator::pass1init(T& it, const T& end)
+{
+	eckit::Log::debug() << "WriterBufferingIterator::pass1init" << std::endl;
+
+	// Copy columns from the input iterator.
+	columns(columnsBuffer_ = it->columns());
+
+	columns_.resetStats();
+	columnsBuffer_.resetStats();
+	
+	size_t nCols = it->columns().size();
+	ASSERT(nCols > 0);
+
+	allocRowsBuffer();
+}
+
+template<typename T>
+unsigned long WriterBufferingIterator::pass1(T& it, const T& end)
+{
+	eckit::Log::debug() << "WriterBufferingIterator::pass1" << std::endl;
+
+	pass1init(it, end);
+
+	unsigned long nrows = 0;
+	for ( ; it != end; ++it, ++nrows)
+	{
+		if (it->isNewDataset() && it->columns() != columnsBuffer_)
+		{
+			eckit::Log::debug() << "WriterBufferingIterator::pass1: Change of input metadata." << std::endl;
+			flush();
+			pass1init(it, end);
+			writeHeader();
+		}
+
+		const double *data = it->data();
+		size_t nCols = it->columns().size();
+
+		gatherStats(data, nCols);
+
+        std::copy(data, data + nCols, reinterpret_cast<double*>(nextRowInBuffer_ + sizeof(uint16_t)));
+		nextRowInBuffer_ += sizeof(uint16_t) + nCols * sizeof(double);
+
+		ASSERT(nextRowInBuffer_ <= rowsBuffer_ + rowsBuffer_.size());
+		if (nextRowInBuffer_ == rowsBuffer_ + rowsBuffer_.size())
+			flush();
+	} 
+
+	eckit::Log::debug() << "Flushing rest of the buffer..." << std::endl;
+	flush();
+
+	eckit::Log::debug() << "WriterBufferingIterator::pass1: processed " << nrows << " row(s)." << std::endl;
+	ASSERT(close() == 0);
+	return nrows;
+}
+
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/WriterDispatchingIterator.cc b/odb_api/src/odb_api/WriterDispatchingIterator.cc
new file mode 100644
index 0000000..652d9c6
--- /dev/null
+++ b/odb_api/src/odb_api/WriterDispatchingIterator.cc
@@ -0,0 +1,515 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file WriterDispatchingIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include "odb_api/FunctionEQ.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+#include "eckit/log/Timer.h"
+#include "eckit/utils/Translator.h"
+
+namespace odb {
+
+template <typename WRITE_ITERATOR, typename OWNER>
+WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::WriterDispatchingIterator(OWNER &owner, int maxOpenFiles, bool append)
+: buffer_(0),
+  owner_(owner),
+  iteratorsOwner_(),
+  columns_(0),
+  lastValues_(0),
+  nextRow_(0),
+  nrows_(0),
+  outputFileTemplate_(owner_.outputFileTemplate()),
+  properties_(),
+  dispatchedIndexes_(),
+  values2iteratorIndex_(),
+  lastDispatch_(maxOpenFiles, -1),
+  iteratorIndex2fileName_(maxOpenFiles),
+  lastDispatchedValues_(),
+  lastIndex_(),
+  initialized_(false),
+  refCount_(0),
+  iterators_(),
+  files_(),
+  templateParameters_(),
+  maxOpenFiles_(maxOpenFiles),
+  filesCreated_(),
+  append_(append)
+{}
+
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::setColumn(size_t index, std::string name, ColumnType type)
+{
+	ASSERT(index < columns().size());
+	Column* col = columns_[index];
+	ASSERT(col);
+
+	col->name(name);
+	col->type<DataStream<SameByteOrder, FastInMemoryDataHandle> >(type, false);
+	return 0;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b)
+{
+	ASSERT(index < columns().size());
+	Column* col = columns_[index];
+	ASSERT(col);
+
+	col->name(name);
+	col->type<DataStream<SameByteOrder, FastInMemoryDataHandle> >(type, false);
+    col->bitfieldDef(b);
+	col->missingValue(0);
+	return 0;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+std::string WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::generateFileName(const double* values, unsigned long count)
+{
+    std::string fileName (outputFileTemplate_);
+    int diff (0);
+    for (TemplateParameters::iterator it (templateParameters_.begin()); it != templateParameters_.end(); ++it)
+    {
+        TemplateParameter& p (*(*it));
+
+        // TODO: if values collected can be of different type then integer,
+        // then below code must be updated:
+        // code updated for std::string [19/07/2011] AF
+        double d (values[p.columnIndex]);
+        std::string s;
+        if ( columns_[p.columnIndex]->type() == odb::STRING)
+        {
+            char* sp (reinterpret_cast<char *>(&d));
+            size_t len (0);
+            odb::sql::expression::function::FunctionEQ::trimStringInDouble(sp, len);
+            s = std::string(sp, len);
+            while (s.find("/") != std::string::npos)
+            {
+                std::string old (s);
+                size_t pos (s.find("/"));
+                s.replace(pos, pos+1, std::string("__SLASH__"));
+                //eckit::Log::info() << "WriterDispatchingIterator::generateFileName: '" << old << "' => '" << s << "'" << std::endl;
+            }
+        } else
+        {
+            s = eckit::Translator<int, std::string>()(int(d));
+        }
+
+        fileName.replace(p.startPos - diff, p.endPos - p.startPos + 1, s);
+        diff = outputFileTemplate_.size() - fileName.size();
+    }
+
+    //eckit::Log::debug() << "WriterDispatchingIterator::generateFileName: fileName = " << fileName <<  std::endl;
+    return fileName;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+WRITE_ITERATOR& WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::dispatch(const double* values, unsigned long count)
+{
+    return *iterators_[this->dispatchIndex(values, count)];
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::dispatchIndex(const double* values, unsigned long count)
+{
+    Values dispatchedValues;
+    for (size_t i (0); i < dispatchedIndexes_.size(); ++i)
+        dispatchedValues.push_back(values[dispatchedIndexes_[i]]);
+
+    if (dispatchedValues == lastDispatchedValues_)
+        return lastIndex_;
+
+    Values2IteratorIndex::iterator p (values2iteratorIndex_.find(dispatchedValues));
+    size_t iteratorIndex ((p != values2iteratorIndex_.end())
+                           ? p->second
+                           : createIterator(dispatchedValues, generateFileName(values, count), values, count));
+
+    lastDispatchedValues_ = dispatchedValues;
+    lastIndex_ = iteratorIndex;
+    lastDispatch_[iteratorIndex] = nrows_;
+    return iteratorIndex;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::createIterator(const Values& dispatchedValues, const std::string& fileName,
+const double* values, unsigned long count)
+{
+    std::ostream& L(eckit::Log::debug());
+
+    int iteratorIndex (iterators_.size());
+    if (iterators_.size() >= maxOpenFiles_)
+    {
+        ASSERT(iterators_.size());
+
+        size_t oldest (0);
+        unsigned long long oldestRow (lastDispatch_[oldest]);
+        for (size_t i = oldest; i < lastDispatch_.size(); ++i)
+        {
+            if (lastDispatch_[i] < oldestRow)
+            {
+                oldestRow = lastDispatch_[i];
+                oldest = i;
+            }
+        }
+		iteratorIndex = oldest;
+
+		L << "split writer: evicted iterator " << iteratorIndex
+			<< "' " << iteratorIndex2fileName_[iteratorIndex] << "' "
+			<< " (oldest row: " << oldestRow << "), nrows_=" << nrows_ <<  std::endl;
+
+        delete iterators_[iteratorIndex];
+        iterators_[iteratorIndex] = 0;
+
+        Values2IteratorIndex::iterator vit (values2iteratorIndex_.begin());
+        for (; vit != values2iteratorIndex_.end(); ++vit)
+            if (vit->second == iteratorIndex)
+                break;
+        values2iteratorIndex_.erase(vit);
+    }
+
+    std::string operation;
+    //bool append = false;
+    if (append_ || !eckit::PathName(fileName).exists())
+    {
+        filesCreated_[fileName] = 1;
+        operation = "creating";
+    }
+    else
+    {
+        if (filesCreated_.find(fileName) == filesCreated_.end())
+        {
+            filesCreated_[fileName] = 1; operation = "overwriting";
+        }
+        else
+        {
+            append_ = true;
+            filesCreated_[fileName]++; operation = "appending";
+        }
+    }
+
+    L << iteratorIndex << ": " << operation << " '" << fileName << "'" << std::endl;
+
+    if (iteratorIndex == iterators_.size())
+    {
+        iterators_.push_back(iteratorsOwner_.createWriteIterator(fileName, append_));
+        files_.push_back(fileName);
+    }
+    else
+    {
+        iterators_[iteratorIndex] = iteratorsOwner_.createWriteIterator(fileName, append_);
+        files_[iteratorIndex] = fileName;
+        //ASSERT(files_[iteratorIndex] == fileName);
+    }
+    values2iteratorIndex_[dispatchedValues] = iteratorIndex;
+    iteratorIndex2fileName_[iteratorIndex] = fileName;
+
+    // Prop. metadata
+    iterators_[iteratorIndex]->columns(columns());
+    //iterators_[iteratorIndex]->writeHeader();
+    //iterators_[iteratorIndex]->allocBuffers();
+    //iterators_[iteratorIndex]->gatherStats(values, count);
+
+    return iteratorIndex;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+std::vector<eckit::PathName> WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::outputFiles()
+{
+    std::vector<eckit::PathName> paths;
+    for (std::map<std::string,int>::iterator it (filesCreated_.begin()); it != filesCreated_.end(); ++it)
+        paths.push_back(it->first);
+    return paths;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::~WriterDispatchingIterator()
+{
+    //eckit::Log::debug() << "WriterDispatchingIterator<WRITE_ITERATOR>::~WriterDispatchingIterator()" << std::endl;
+    delete [] lastValues_;
+    delete [] nextRow_;
+    delete [] buffer_;
+    for (size_t i = 0; i < iterators_.size(); ++i)
+        delete iterators_[i];
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+unsigned long WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::gatherStats(const double* values, unsigned long count)
+{
+    return dispatch(values, count).gatherStats(values, count);
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+void WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::writeHeader()
+{
+	//eckit::Log::debug() << "WriterDispatchingIterator<WRITE_ITERATOR>::writeHeader" << std::endl;
+
+    delete [] lastValues_;
+    delete [] nextRow_;
+    int32_t count (columns().size());
+    double* last (lastValues_ = new double [count]);
+    nextRow_ = new double [count];
+    ASSERT(last);
+
+    for (int i (0); i < count; i++)
+        nextRow_[i] = last[i] = columns_[i]->missingValue();
+
+    nrows_ = 0;
+
+    delete [] buffer_;
+    buffer_ = new unsigned char[(count + 1) * sizeof(double)];
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+bool WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::next(eckit::ExecutionContext*)
+{
+    return writeRow(nextRow_, columns().size()) == 0;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+void WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::parseTemplateParameters()
+{
+    templateParameters_.release();
+    TemplateParameters::parse(outputFileTemplate_, templateParameters_, columns());
+    if (templateParameters_.size() == 0)
+    {
+        std::stringstream ss;
+        ss << "No parameters in output file template '" << outputFileTemplate_ << "'" << std::endl;
+        throw eckit::UserError(ss.str());
+    }
+    dispatchedIndexes_.clear();
+    for (size_t i (0); i < templateParameters_.size(); ++i)
+        dispatchedIndexes_.push_back(templateParameters_[i]->columnIndex);
+    initialized_ = true;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+double* WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::data() { return nextRow_; }
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::writeRow(const double* values, unsigned long count)
+{
+    if (!initialized_)
+        parseTemplateParameters();
+
+    WRITE_ITERATOR& wi = dispatch(values, count);
+    int rc = wi.writeRow(values, count);
+
+    if (rc == 0)
+        nrows_++;
+
+    return rc;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::open() { return 0; }
+
+/* * /
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::setColumn(size_t index, std::string name, ColumnType type)
+{
+	ASSERT(index < columns().size());
+	Column* col = columns_[index];
+	ASSERT(col);
+
+	typedef DataStream<SameByteOrder, FastInMemoryDataHandle> DS;
+
+	col->name(name);
+	col->type<DS>(type, false);
+	//col->hasMissing(hasMissing);
+	//col->missingValue(missingValue);
+
+	for (typename Iterators::iterator it = iterators_.begin(); it != iterators_.end(); ++it)
+		//(*it)->setColumn(index, name, type, hasMissing, missingValue);
+		(*it)->setColumn(index, name, type);
+
+	return 0;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b)
+{
+    //eckit::Log::info() << "WriterDispatchingIterator::setBitfieldColumn: " << std::endl;
+
+    ASSERT(index < columns().size());
+    Column* col = columns_[index];
+    ASSERT(col);
+
+    typedef DataStream<SameByteOrder, FastInMemoryDataHandle> DS;
+
+    col->name(name);
+    col->type<DS>(type, false);
+    //col->hasMissing(hasMissing);
+    //col->missingValue(missingValue);
+    col->bitfieldDef(b);
+
+    for (typename Iterators::iterator it = iterators_.begin(); it != iterators_.end(); ++it)
+        //(*it)->setBitfieldColumn(index, name, type, b, hasMissing, missingValue);
+        (*it)->setBitfieldColumn(index, name, type, b);
+
+    return 0;
+}
+/ *
+*/
+
+template <typename WRITE_ITERATOR, typename OWNER>
+const MetaData& WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::columns(const MetaData& md)
+{
+    columns_ = md;
+    return md;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+void WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::missingValue(size_t i, double missingValue)
+{
+	ASSERT(i < columns().size());
+	Column* col (columns_[i]);
+	ASSERT(col);
+
+	col->missingValue(missingValue);
+}
+
+template <>
+template <typename T>
+unsigned long WriterDispatchingIterator<WriterBufferingIterator,DispatchingWriter>::pass1(T& it, const T& end)
+{
+	if (! (it != end))
+	{
+		eckit::Log::warning() << "Split: No input data." << std::endl;
+		return 0;
+	}
+
+	// Copy columns from the input iterator.
+	columns(it->columns());
+
+	if (!initialized_) parseTemplateParameters();
+
+	size_t maxcols = columns().size();
+	ASSERT(maxcols > 0);
+
+	eckit::Log::debug() << "WriterDispatchingIterator::pass1<WriterBufferingIterator>: columns().size() => " << maxcols << std::endl;
+
+	nrows_  = 0;
+	for (; it != end; ++it)
+	{
+		if (it->isNewDataset() && columns() != it->columns() )
+		{
+			columns(it->columns());
+			parseTemplateParameters();
+
+			for (size_t i = 0; i < iterators_.size(); ++i)
+			{
+				iterators_[i]->flush();
+				iterators_[i]->columns(columns());
+				iterators_[i]->writeHeader();
+			}
+		}
+
+		const double* data (it->data());
+		size_t size (it->columns().size());
+		int rc (writeRow(data, size));
+		ASSERT(rc == 0);
+	}
+
+	eckit::Log::debug() << "Split: processed " << nrows_ << " row(s)." << std::endl;
+	return nrows_;
+}
+
+template <>
+template <typename T>
+void WriterDispatchingIterator<WriterBufferingIterator,DispatchingWriter>::verify(T& it, const T& end) {
+    using namespace eckit;
+    using namespace std;
+    Log::info() << "Verifying split..." << endl;
+    Timer timer("Split verification");
+
+    vector<Reader*> readers;
+    vector<pair<Reader::iterator, Reader::iterator> > iterators;
+    for (size_t i (0); i < files_.size(); ++i) {
+        Log::debug() << "Opening '" << files_[i] << "'" << endl;
+        Reader* reader(new Reader(files_[i]));
+        readers.push_back(reader);
+        iterators.push_back(make_pair(reader->begin(), reader->end()));
+    }
+
+    vector<size_t> rowsRead(files_.size());
+    Comparator comparator;
+    unsigned long numberOfDifferences (0);
+    long long i (0);
+    for (; it != end; ++i)
+    {
+		//if (it->isNewDataset() && columns() != it->columns() )
+		if (columns() != it->columns())
+		{
+			columns(it->columns());
+			parseTemplateParameters();
+        }
+
+        size_t fileIndex(dispatchIndex(it->data(), it->columns().size()));
+        const std::string& outFileName (files_[fileIndex]);
+
+        size_t n(columns().size());
+        typedef Reader::iterator I;
+        std::pair<I, I>& its(iterators[fileIndex]);
+        I& sIt(its.first), sEnd(its.second);
+
+        const MetaData& sMetaData (sIt->columns());
+        try {
+            ASSERT(sIt != sEnd && sMetaData == columns());
+
+            ++rowsRead[fileIndex];
+            const double* const& originalData(it->data());
+            const double* const& outputData(sIt->data());
+            comparator.compare(n, originalData, outputData, columns(), sMetaData);
+        } catch (...) {
+            ++numberOfDifferences;
+            Log::info() << "Row " << i << " of input (" << rowsRead[fileIndex] << " of " << outFileName << ") not correct." << endl << endl;
+        }
+        ++it;
+        ++sIt;
+    }
+    Log::info() << "Number of rows: " << i << ". Total number of differences: " << numberOfDifferences  << std::endl;
+    ASSERT(! (it != end));
+
+    for (size_t j = 0; j < readers.size(); ++j)
+        delete readers[j];
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+void WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::property(std::string key, std::string value)
+{
+	// TODO: save property, make sure they are propagated to iterators as they are created
+    properties_[key] = value;
+}
+
+template <typename WRITE_ITERATOR, typename OWNER>
+std::string WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::property(std::string key) { return properties_[key]; }
+
+template <typename WRITE_ITERATOR, typename OWNER>
+int WriterDispatchingIterator<WRITE_ITERATOR, OWNER>::close()
+{
+	//eckit::Log::debug() << "WriterDispatchingIterator<WRITE_ITERATOR>::close()" << std::endl;
+	int rc = 0;
+	for (typename Iterators::iterator it = iterators_.begin(); it != iterators_.end(); ++it)
+	{
+		rc |= (*it)->close();
+		delete *it;
+	}
+	iterators_.clear();
+
+	return rc;
+}
+
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/WriterDispatchingIterator.h b/odb_api/src/odb_api/WriterDispatchingIterator.h
new file mode 100644
index 0000000..9df83d4
--- /dev/null
+++ b/odb_api/src/odb_api/WriterDispatchingIterator.h
@@ -0,0 +1,136 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file WriterDispatchingIterator.h
+///
+/// @author Piotr Kuchta, June 2009
+
+#ifndef odb_api_WriterDispatchingIterator_H
+#define odb_api_WriterDispatchingIterator_H
+
+#include "eckit/eckit.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/TemplateParameters.h"
+#include "odb_api/Types.h"
+#include "odb_api/Writer.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class DataHandle; }
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+
+class TemplateParameters;
+
+template <typename WRITE_ITERATOR, typename OWNER >
+class WriterDispatchingIterator 
+{
+	typedef std::vector<double> Values;
+	typedef std::map<Values,int> Values2IteratorIndex;
+	typedef std::vector<WRITE_ITERATOR *> Iterators;
+public:
+	WriterDispatchingIterator (OWNER &owner, int maxOpenFiles, bool append = false);
+	~WriterDispatchingIterator();
+
+	int open();
+
+	double* data();
+
+	int setColumn(size_t index, std::string name, ColumnType type);
+	int setBitfieldColumn(size_t index, std::string name, ColumnType type, BitfieldDef b);
+
+	void missingValue(size_t i, double); 
+
+	template <typename T> unsigned long pass1(T&, const T&);
+	template <typename T> void verify(T&, const T&);
+	unsigned long gatherStats(const double* values, unsigned long count);
+
+	int close();
+
+	ColumnType columnType(size_t index);
+    const std::string& columnName(size_t index) const;
+    const std::string& codecName(size_t index) const;
+	double columnMissingValue(size_t index);
+
+	const MetaData& columns() { return columns_; }
+	const MetaData& columns(const MetaData& md); 
+
+	OWNER& owner() { return owner_; }
+
+	void property(std::string key, std::string value);
+	std::string property(std::string);
+
+	//std::vector<eckit::PathName> getFiles();
+    std::vector<eckit::PathName> outputFiles();
+	TemplateParameters& templateParameters() { return templateParameters_; }
+
+//protected:
+	void writeHeader();
+
+	int writeRow(const double* values, unsigned long count);
+
+protected:
+	bool next(eckit::ExecutionContext*);
+
+	/// Find iterator data should be dispatched to.
+	WRITE_ITERATOR& dispatch(const double* values, unsigned long count);
+	int dispatchIndex(const double* values, unsigned long count);
+    int createIterator(const Values& dispatchedValues, const std::string& fileName, const double* values, unsigned long count);
+
+	std::string generateFileName(const double* values, unsigned long count);
+
+	unsigned char *buffer_;
+	OWNER& owner_;
+	Writer<WRITE_ITERATOR> iteratorsOwner_;
+	MetaData columns_;
+	double* lastValues_;
+	double* nextRow_;
+	unsigned long long nrows_;
+	std::string outputFileTemplate_;
+
+	Properties properties_;
+
+	std::vector<int> dispatchedIndexes_;
+	Values2IteratorIndex values2iteratorIndex_;
+	std::vector<unsigned long long> lastDispatch_;
+	std::vector<std::string> iteratorIndex2fileName_;
+
+	Values lastDispatchedValues_;
+	int lastIndex_;
+	bool initialized_;
+	bool append_;
+    eckit::ExecutionContext* context_;
+
+private:
+// No copy allowed.
+	WriterDispatchingIterator(const WriterDispatchingIterator&);
+	WriterDispatchingIterator& operator=(const WriterDispatchingIterator&);
+
+	void parseTemplateParameters();
+
+	int refCount_;
+
+	Iterators iterators_;
+	std::vector<eckit::PathName> files_;
+
+	TemplateParameters templateParameters_;
+	int maxOpenFiles_;
+
+	std::map<std::string,int> filesCreated_;
+
+	friend class IteratorProxy<WriterDispatchingIterator<WRITE_ITERATOR,DispatchingWriter>, DispatchingWriter>;
+};
+
+} // namespace odb 
+
+#include "odb_api/WriterDispatchingIterator.cc"
+
+#endif
diff --git a/odb_api/src/odb_api/calc.sh b/odb_api/src/odb_api/calc.sh
new file mode 100755
index 0000000..bf71c95
--- /dev/null
+++ b/odb_api/src/odb_api/calc.sh
@@ -0,0 +1,11 @@
+"""
+for f in /tmp/Dropbox/work/odb_server_bundle/server/src/odbsvr/test/dhshome/data/root/20151108/12/16030.odb \
+    /tmp/Dropbox/work/odb_server_bundle/server/src/odbsvr/test/dhshome/data/root/20151108/12/16029.odb \
+    /tmp/Dropbox/work/odb_server_bundle/server/src/odbsvr/test/dhshome/data/root/20151108/12/16045.odb \
+    /tmp/Dropbox/work/odb_server_bundle/server/src/odbsvr/test/dhshome/data/root/20151108/12/16065.odb \
+     /tmp/Dropbox/work/odb_server_bundle/server/src/odbsvr/test/dhshome/data/root/20151108/12/16009.odb ; do
+     odb count $f
+ done
+ """
+ python -c 'print 119256 + 15151 + 31144 + 18800 + 93'
+
diff --git a/odb_api/src/odb_api/ecml_data/LocalHandleFactory.cc b/odb_api/src/odb_api/ecml_data/LocalHandleFactory.cc
new file mode 100644
index 0000000..ccf60e8
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/LocalHandleFactory.cc
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/MultiHandle.h"
+#include "eckit/config/Resource.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/RequestHandler.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/FileMapper.h"
+#include "odb_api/FileCollector.h"
+#include "odb_api/Archiver.h"
+#include "odb_api/Stager.h"
+#include "odb_api/Retriever.h"
+
+#include "LocalHandleFactory.h"
+
+using namespace eckit;
+using namespace ecml;
+using namespace std;
+
+LocalHandleFactory::LocalHandleFactory()
+: DataHandleFactory("local")
+{}
+
+DataHandle* LocalHandleFactory::makeHandle(const string& req) const
+{
+    const string& r (req);
+    Log::info() << "LocalHandleFactory::makeHandle: parsing [" << r << "]" << endl;
+
+    Request requests (ecml::RequestParser::parse(r));
+    Request request (requests->value());
+
+    Log::debug() << "LocalHandleFactory::makeHandle: request = " << request << endl;
+
+    if (requests->rest())
+        Log::warning() << "LocalHandleFactory: Only " << request << " used, skipped rest of " << requests << endl;
+
+    ExecutionContext context;
+    context.pushEnvironmentFrame(request->rest());
+    if (context.getValueAsList("odbpathnameschema").size() != 1)
+        throw UserError(string("\"local://\" descriptor must have one value of odbPathNameSchema: '") + r + "'");
+
+    if (context.getValueAsList("odbserverroots").size() != 1)
+        throw UserError(string("\"local://\" descriptor must have one value of odbServerRoots (string with colon separated directories): '") + r + "'");
+
+    const string odbPathNameSchema (context.getValueAsList("odbpathnameschema")[0]);
+    const string odbServerRoots (FileCollector::expandTilde(context.getValueAsList("odbserverroots")[0]));
+
+    const vector<string> keywords (FileMapper(odbPathNameSchema).keywords());
+    map<string, vector<string> > rq;
+
+    context.pushEnvironmentFrame(request);
+    for (Cell* p(request->rest()); p; p = p->rest())
+    {
+        const string keyword (StringTools::lower(p->text()));
+        if (keyword != "server_side")
+            rq[keyword] = context.getValueAsList(keyword);
+        else
+        {
+            // This needs to be a structure more complicated than vector of strings,
+            // so we need to do a trick and serialize it to a string  
+            // (it's not a hack: any ECML expression can always be properly serialized).
+            vector<string> ss;
+            Cell* server_side (context.environment().lookup(keyword));
+            ss.push_back(server_side->str());
+            rq[keyword] = ss;
+        }
+        Log::debug() << " =+= " << keyword << " = " << rq[keyword] << endl;
+    }
+
+    MultiHandle* h (new MultiHandle);
+    const string verb (StringTools::lower(request->text()));
+    if (verb == "archive")
+    {
+        rq["source"] = context.getValueAsList("source");
+        Archiver::archive(*h, keywords, rq);
+    }
+    else if (verb == "stage")
+        Stager::stage(*h, keywords, rq);
+    else
+        Retriever::retrieve(*h, keywords, rq);
+    return h;
+}
+
diff --git a/odb_api/src/odb_api/ecml_data/LocalHandleFactory.h b/odb_api/src/odb_api/ecml_data/LocalHandleFactory.h
new file mode 100644
index 0000000..4ee0cad
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/LocalHandleFactory.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, January 2016
+
+#ifndef odb_api_LocalHandleFactory_H
+#define odb_api_LocalHandleFactory_H
+
+#include "ecml/data/DataHandleFactory.h"
+
+class LocalHandleFactory : public ecml::DataHandleFactory
+{
+public:
+    LocalHandleFactory();
+
+protected:
+    eckit::DataHandle* makeHandle(const std::string&) const;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_data/Matrix.cc b/odb_api/src/odb_api/ecml_data/Matrix.cc
new file mode 100755
index 0000000..6975173
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/Matrix.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ecml_data/Matrix.h"
+
+namespace odb {
+namespace sql {
+
+Matrix::Matrix()
+: rows_(),
+  missing_()
+{}
+
+void Matrix::append(std::vector<double>& values, std::vector<bool>& missing)
+{
+    rows_.push_back(values);
+    missing_.push_back(missing);
+}
+
+Matrix::~Matrix() {}
+
+size_t Matrix::numberOfRows() const
+{
+    return rows_.size();
+}
+
+size_t Matrix::numberOfColumns() const
+{
+    return rows_[0].size();
+}
+
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/ecml_data/Matrix.h b/odb_api/src/odb_api/ecml_data/Matrix.h
new file mode 100755
index 0000000..dcaf3ee
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/Matrix.h
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Matrix.h
+// Piotr Kuchta - ECMWF June 2015
+
+#ifndef odb_sql_Matrix_H
+#define odb_sql_Matrix_H
+
+#include <vector>
+#include <ostream>
+
+namespace odb {
+namespace sql {
+
+class Matrix {
+public:
+    Matrix();
+    ~Matrix(); 
+
+    void append(std::vector<double>&, std::vector<bool>&);
+
+    std::vector<std::vector<double> >& rows() { return rows_; }
+
+    size_t numberOfRows() const;
+    size_t numberOfColumns() const;
+
+private:
+// No copy allowed
+    Matrix(const Matrix&);
+    Matrix& operator=(const Matrix&);
+
+    void print(std::ostream&) const;
+
+// -- Friends
+    friend std::ostream& operator<<(std::ostream& s, const Matrix& p)
+        { p.print(s); return s; }
+
+    std::vector<std::vector<double> > rows_;
+    std::vector<std::vector<bool> > missing_;
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_data/ResultSet.cc b/odb_api/src/odb_api/ecml_data/ResultSet.cc
new file mode 100755
index 0000000..03a78f6
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/ResultSet.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ecml_data/ResultSet.h"
+
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+ResultSet::ResultSet() {}
+
+void ResultSet::append(std::vector<double>& values, std::vector<bool>& missing)
+{
+    matrix_.append(values, missing);
+}
+
+void ResultSet::print(std::ostream& s) const
+{
+    s << "result_set,at=" << this 
+        << ",number_of_rows=" << matrix_.numberOfRows() 
+        << ",number_of_columns=" << matrix_.numberOfColumns() 
+        << endl;
+}
+
+ResultSet::~ResultSet() {}
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/ecml_data/ResultSet.h b/odb_api/src/odb_api/ecml_data/ResultSet.h
new file mode 100755
index 0000000..9a29050
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/ResultSet.h
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ResultSet.h
+// Piotr Kuchta - ECMWF June 2015
+
+#ifndef ResultSet_H
+#define ResultSet_H
+
+#include <vector>
+#include <ostream>
+
+#include "odb_api/ecml_data/Matrix.h"
+
+namespace odb {
+namespace sql {
+
+class ResultSet {
+public:
+    ResultSet();
+    ~ResultSet(); 
+
+    void append(std::vector<double>&, std::vector<bool>&);
+
+    std::vector<std::vector<double> >& rows() { return matrix_.rows(); }
+
+private:
+// No copy allowed
+    ResultSet(const ResultSet&);
+    ResultSet& operator=(const ResultSet&);
+
+    void print(std::ostream&) const;
+
+    Matrix matrix_;
+
+// -- Friends
+    friend std::ostream& operator<<(std::ostream& s, const ResultSet& p)
+        { p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_data/ResultSetStore.cc b/odb_api/src/odb_api/ecml_data/ResultSetStore.cc
new file mode 100755
index 0000000..339dfc0
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/ResultSetStore.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/ecml_data/ResultSetStore.h"
+
+using namespace std;
+
+namespace odb {
+namespace sql {
+
+std::map<std::string, ResultSet*> ResultSetStore::resultSets_ = std::map<std::string, ResultSet*>();
+
+ResultSetStore::ResultSetStore() {}
+ResultSetStore::~ResultSetStore() {}
+
+ResultSet& ResultSetStore::get(const std::string& id)
+{
+    return *resultSets_[id];
+}
+
+void ResultSetStore::put(const std::string& id, ResultSet& rs)
+{
+    resultSets_[id] = &rs;
+}
+
+void ResultSetStore::remove(const std::string& id)
+{
+    resultSets_.erase(id);
+}
+
+//void ResultSetStore::print(std::ostream& s) const { }
+
+} // namespace sql
+} // namespace odb
diff --git a/odb_api/src/odb_api/ecml_data/ResultSetStore.h b/odb_api/src/odb_api/ecml_data/ResultSetStore.h
new file mode 100755
index 0000000..cdca317
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_data/ResultSetStore.h
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ResultSetStore.h
+// Piotr Kuchta - ECMWF June 2015
+
+#ifndef odb_sql_ResultSetStore_H
+#define odb_sql_ResultSetStore_H
+
+#include <map>
+#include <ostream>
+
+#include "odb_api/ecml_data/ResultSet.h"
+
+namespace odb {
+namespace sql {
+
+//TODO: ExecutionContext will have an instance of this class, its static functions will become member ones
+
+class ResultSetStore {
+public:
+    ResultSetStore();
+    ~ResultSetStore(); 
+
+    static void put(const std::string&, ResultSet&);
+    static ResultSet& get(const std::string&);
+    static void remove(const std::string&);
+
+private:
+// No copy allowed
+    ResultSetStore(const ResultSetStore&);
+    ResultSetStore& operator=(const ResultSetStore&);
+
+    //void print(std::ostream&) const;
+
+    static std::map<std::string, ResultSet*> resultSets_;
+
+// -- Friends
+    //friend std::ostream& operator<<(std::ostream& s, const ResultSetStore& p) { p.print(s); return s; }
+};
+
+} // namespace sql
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.cc b/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.cc
new file mode 100644
index 0000000..2a87e86
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.cc
@@ -0,0 +1,149 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+#include <algorithm>
+#include <string>
+
+#include "eckit/io/FileHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "ArchiveHandler.h"
+
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/Archiver.h"
+
+using namespace std;
+using namespace ecml;
+
+namespace odb {
+
+const char * cfg_ = "let, CLASS=class, DATE=andate, TIME=antime, TYPE=type, OBSGROUP=groupid, REPORTYPE=reportype, STREAM=stream, EXPVER=expver";
+
+ArchiveHandler::ArchiveHandler(const string& name) : RequestHandler(name) {}
+
+Values ArchiveHandler::handle(ExecutionContext& context)
+{
+    const string host (database(context));
+    const string protocol (host == "local" ? "local://" : "mars://");
+
+    vector<string> sources (context.getValueAsList("source"));
+    if (! sources.size())
+        throw eckit::UserError("You must specify file(s) to be archived using the SOURCE keyword");
+
+    Values r(0);
+    List list(r);
+    for (size_t i(0); i < sources.size(); ++i)
+    {
+        const string source (sources[i]);
+
+        Request generatedRequest (generateRequest(source));
+
+        generatedRequest->value("database", host);
+        generatedRequest->value("odbpathnameschema", context.getValueAsList("odbpathnameschema")[0]);
+        generatedRequest->value("odbserverroots", context.getValueAsList("odbserverroots")[0]);
+        generatedRequest->value("source", source);
+
+        ASSERT(generatedRequest->text() == "RETRIEVE");
+        generatedRequest->text("ARCHIVE");
+
+        checkRequestMatchesFilesMetaData(context, generatedRequest);
+ 
+        eckit::Log::info() << "Request generated for file " << source << ":" << endl
+                    << generatedRequest << endl;
+
+        archive(source, host, generatedRequest, protocol);
+
+        generatedRequest->text("RETRIEVE");
+        list.append(protocol + generatedRequest->str());
+    }
+
+    return r;
+}
+
+void ArchiveHandler::checkRequestMatchesFilesMetaData(const ExecutionContext& context, const Request generatedRequest)
+{
+    // TODO
+    /*
+    for (Request::const_iterator it(generatedRequest.begin()); it != generatedRequest.end(); ++it)
+    {
+        string key (it->first);
+        Values values (it->second);
+
+        Log::info() << "check if request matches files metadata: key=" << key << " value=" << values << endl;
+
+        if (request.find(key) == request.end() || ! request.at(key).size())
+        {
+            string message("keyword '" + key + "' was not set in user's request");
+            //throw UserError(message);
+            Log::warning() << message << endl;
+        }
+        else
+        {
+            // TODO: check values in range
+        }
+    }
+    */
+}
+
+Request ArchiveHandler::generateRequest(const string& source)
+{
+    odb::FastODA2Request<odb::ODA2RequestClientTraits> o2r;
+    o2r.parseConfig(cfg_);
+
+    eckit::OffsetList offsets;
+    eckit::LengthList lengths;
+    std::vector<odb::ODAHandle*> handles;
+    bool rc (o2r.scanFile(source, offsets, lengths, handles));
+    for (size_t i (0); i < handles.size(); ++i)
+        delete handles[i];
+    handles.clear();
+    if (! rc)
+        throw eckit::UserError(string("Cannot archive file ") + source);
+    eckit::Log::info() << "File " << source << " has " << o2r.rowsNumber() << " rows." << endl;
+
+    ASSERT(lengths.size() && lengths.size() == offsets.size());
+
+    string r ( o2r.genRequest() );
+    std::replace(r.begin(), r.end(), '\n', ' ');
+
+    r.erase(std::remove(r.begin(), r.end(), ' '), r.end());
+    
+    r = "RETRIEVE," + r;
+    Request requests (RequestParser::parse(r));
+    //ASSERT(requests.size() == 1);
+    //return requests.front();
+    ASSERT(requests->tag() == "_requests");
+
+    //requests->showGraph(string("generateRequest: ") + requests->str() );
+    
+    return requests->value();
+}
+
+void ArchiveHandler::archive(const eckit::PathName& source, const string& host, const Request request, const string& protocol)
+{
+    eckit::Log::info() << "ARCHIVE " << source << " on " << host << endl;
+    eckit::Log::info() << "ARCHIVE request: " << request << endl;
+
+    eckit::FileHandle input(source);
+
+    stringstream ss;
+    ss << protocol << request;
+    auto_ptr<eckit::DataHandle> mars (DataHandleFactory::openForWrite(ss.str(), eckit::Length(Archiver::fileSize(source))));
+
+    input.saveInto(*mars);
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.h b/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.h
new file mode 100644
index 0000000..d8a8118
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ArchiveHandler.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_ArchiveHandler_H
+#define odb_api_ArchiveHandler_H
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class ArchiveHandler : public ecml::RequestHandler {
+public:
+    ArchiveHandler(const std::string&);
+
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+
+    static ecml::Request generateRequest(const std::string&);
+
+private:
+    void archive(const eckit::PathName& source, const std::string& host, const ecml::Request request, const std::string& protocol);
+    static void checkRequestMatchesFilesMetaData(const ecml::ExecutionContext&, const ecml::Request);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/ChunkHandler.cc b/odb_api/src/odb_api/ecml_verbs/ChunkHandler.cc
new file mode 100644
index 0000000..1d87c4d
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ChunkHandler.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "ChunkHandler.h"
+
+#include "odb_api/Comparator.h"
+
+#include "eckit/types/Types.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+namespace odb {
+
+ChunkHandler::ChunkHandler(const string& name) : RequestHandler(name) {}
+
+ecml::Values ChunkHandler::handle(ecml::ExecutionContext& context)
+{
+    vector<string> source (context.environment().lookupList("source", context));
+
+    typedef odb::MetaDataReader<odb::MetaDataReaderIterator> MDReader;
+
+    string fileName (source[0]); // TODO: process rest of files
+
+    Log::info() << "chunk: chunking file " << fileName << std::endl;
+
+    MDReader oda (fileName);
+    MDReader::iterator r(oda.begin());
+    MDReader::iterator end(oda.end());
+
+    if (! (r != end))
+        throw UserError(string("chunk: file ") + fileName + " appears to be empty");
+
+    odb::MetaData metaData(r->columns());
+    ecml::List l;
+    for(; r != end; ++r)
+    {
+        ASSERT (r->isNewDataset());
+        Offset offset ((**r).blockStartOffset());
+        Length length ((**r).blockEndOffset() - (**r).blockStartOffset());
+        stringstream ss;
+        ss << "partfile://" << fileName << ":" << offset << "," << length;
+
+        l.append(ss.str());
+    }
+    return l;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/ChunkHandler.h b/odb_api/src/odb_api/ecml_verbs/ChunkHandler.h
new file mode 100644
index 0000000..58ed944
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ChunkHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_ChunkHandler_H
+#define odb_api_ChunkHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class ChunkHandler : public ecml::RequestHandler {
+public:
+    ChunkHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/CompareHandler.cc b/odb_api/src/odb_api/ecml_verbs/CompareHandler.cc
new file mode 100644
index 0000000..c16b908
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CompareHandler.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "CompareHandler.h"
+
+#include "odb_api/Comparator.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "eckit/types/Types.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core//ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+using namespace std;
+using namespace eckit;
+using namespace ecml;
+using namespace odb;
+
+namespace odb {
+
+CompareHandler::CompareHandler(const string& name) : RequestHandler(name) {}
+
+ecml::Values CompareHandler::handle(ExecutionContext& context)
+{
+    vector<string> left (context.environment().lookupList("left", context));
+    vector<string> right (context.environment().lookupList("right", context));
+
+    Log::info() << "left: " << left << endl;
+    Log::info() << "right: " << right  << endl;
+
+
+    MultiHandle leftH, rightH;
+    DataHandleFactory::buildMultiHandle(leftH, left);
+    DataHandleFactory::buildMultiHandle(rightH, right);
+
+    leftH.openForRead();
+    rightH.openForRead();
+
+    Comparator comparator;
+    comparator.compare(leftH, rightH);
+
+    Log::info() << "No difference found" << endl;
+
+    List list;
+    return list;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/CompareHandler.h b/odb_api/src/odb_api/ecml_verbs/CompareHandler.h
new file mode 100644
index 0000000..9730f3f
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CompareHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_CompareHandler_H
+#define odb_api_CompareHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class CompareHandler : public ecml::RequestHandler {
+public:
+    CompareHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.cc b/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.cc
new file mode 100644
index 0000000..d79e938
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "CreateIndexHandler.h"
+
+#include "odb_api/Comparator.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "eckit/types/Types.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core//ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "odb_api/Indexer.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+namespace odb {
+
+CreateIndexHandler::CreateIndexHandler(const string& name) : RequestHandler(name) {}
+
+ecml::Values CreateIndexHandler::handle(ecml::ExecutionContext& context)
+{
+    vector<string> fs (context.environment().lookupList("files", context));
+
+    vector<PathName> files;
+    for (size_t i(0); i < fs.size(); ++i)
+        files.push_back(fs[i]);
+
+    vector<PathName> indices (odb::Indexer::createIndex(files));
+
+    ecml::List l;
+    for (size_t i(0); i < indices.size(); ++i)
+        l.append(indices[i]);
+
+    return l;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.h b/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.h
new file mode 100644
index 0000000..1e6fb8b
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CreateIndexHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, November 2015
+
+#ifndef odb_api_CreateIndexHandler_H
+#define odb_api_CreateIndexHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class CreateIndexHandler : public ecml::RequestHandler {
+public:
+    CreateIndexHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.cc b/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.cc
new file mode 100644
index 0000000..a2d7ad4
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.cc
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/types/Types.h"
+#include "eckit/parser/StringTools.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core//ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "CreatePartitionsHandler.h"
+#include "odb_api/Partitioner.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+namespace odb {
+
+CreatePartitionsHandler::CreatePartitionsHandler(const string& name) : RequestHandler(name) {}
+
+/// verb create_partitions
+/// parameters:
+///             files             ODB files
+///             n                 desired number of partitions
+///             target            file with partitions information; default: partitions_info.txt 
+///             write_files       if set to true create one data file for each partition; default: false
+///             partition_prefix  prefix of file names if writing data; default: "part.odb" 
+///
+/// return value: descriptors of partitions
+
+ecml::Values CreatePartitionsHandler::handle(ecml::ExecutionContext& context)
+{
+    string target (context.environment().lookup("target", "partitions_info.txt", context));
+
+    Log::info() << "create_partitions: saving partitions info to '" << target << "'" << endl;
+
+    const string numberOfPartitions (context.environment().lookup("n", "", context));
+    size_t n (atol(numberOfPartitions.c_str()));
+
+    if (n <= 0)
+        throw UserError("create_partitions: parameter 'n' must be a positive integer");
+
+    string writeFiles (context.environment().lookup("write_files", "0", context));
+    if (StringTools::lower(writeFiles) == "true")
+        writeFiles = "1";
+    bool doWriteFiles (atoi(writeFiles.c_str()));
+
+    const string prefix (context.environment().lookup("partition_prefix", "part.odb", context));
+
+    vector<string> fs (context.environment().lookupList("files", context));
+    vector<PathName> files;
+    for (size_t i(0); i < fs.size(); ++i)
+        files.push_back(fs[i]);
+
+    Partitions partitions (odb::Partitioner::createPartitions(files, n));
+    partitions.save(target);
+
+    ecml::List l;
+
+    if (! doWriteFiles)
+        l.append(target);
+    else
+    {
+        vector<PathName> ps (partitions.write(prefix));
+        for (size_t i(0); i < ps.size(); ++i)
+            l.append(ps[i]);
+    }
+
+    return l;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.h b/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.h
new file mode 100644
index 0000000..450179f
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/CreatePartitionsHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, November 2015
+
+#ifndef odb_api_CreatePartitionsHandler_H
+#define odb_api_CreatePartitionsHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class CreatePartitionsHandler : public ecml::RequestHandler {
+public:
+    CreatePartitionsHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.cc b/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.cc
new file mode 100644
index 0000000..2de29c9
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.cc
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/tools/ImportTool.h"
+#include "odb_api/SQLInteractiveSession.h"
+
+#include "ImportTextHandler.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+ImportTextHandler::ImportTextHandler(const string& name)
+: RequestHandler(name)
+{}
+
+unsigned long long ImportTextHandler::importText(const PathName& pathName, const string& csv)
+{
+    const string delimiter(",");
+    odb::sql::SQLInteractiveSession session;
+	session.selectFactory().csvDelimiter(delimiter);
+	stringstream fs(csv);
+	odb::Select input ("select *;", fs, delimiter);
+	odb::Writer<> writer (pathName);
+	odb::Writer<>::iterator output (writer.begin());
+
+	unsigned long long n (output->pass1(input.begin(), input.end()));
+    return n;
+}
+
+/// Accepted parameters: text, source, target
+ecml::Request ImportTextHandler::handle(ecml::ExecutionContext& context)
+{
+    ecml::Environment& e (context.environment());
+
+    //const string source (e.lookup("source", "", context));
+    const PathName target (e.lookup("target", "", context));
+    vector<string> texts (context.environment().lookupList("text", context));
+
+    if (! texts.size())
+        throw UserError("import_text: missing parameter TEXT");
+
+    if (texts.size() != 1)
+        throw UserError("import_text: multiple values of TEXT not yet supported");
+
+    if (! string(target).size())
+        throw UserError("import_text: obligatory parameter TARGET must be a single path name");
+
+    for (size_t i(0); i < texts.size(); ++i)
+    {
+        const string text (texts[i]);
+        importText(target, text);
+    }
+
+    ecml::List l;
+    l.append(target);
+
+    return l;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.h b/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.h
new file mode 100644
index 0000000..90f6fcc
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/ImportTextHandler.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, November 2015
+
+#ifndef odb_api_ImportTextHandler_H
+#define odb_api_ImportTextHandler_H
+
+#include "eckit/filesystem/PathName.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class ImportTextHandler : public ecml::RequestHandler {
+public:
+    ImportTextHandler(const std::string&);
+
+    static unsigned long long importText(const eckit::PathName& pathName, const std::string& csv);
+
+private:
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.cc b/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.cc
new file mode 100644
index 0000000..dd53752
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.cc
@@ -0,0 +1,114 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "RetrieveHandler.h"
+#include "SQLHandler.h"
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/config/Resource.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "odb_api/FileCollector.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/TemplateParameters.h"
+
+using namespace std;
+using namespace eckit;
+using namespace ecml;
+using namespace odb;
+
+namespace odb {
+
+RetrieveHandler::RetrieveHandler(const string& name, bool local) 
+: RequestHandler(name), 
+  local_(local) 
+{}
+
+std::string RetrieveHandler::odbPathNameSchema(ecml::ExecutionContext& context) 
+{ 
+    return FileCollector::expandTilde(valueInContextOrResource(context, "odbpathnameschema")); 
+}
+
+std::string RetrieveHandler::odbServerRoots(ecml::ExecutionContext& context) 
+{ 
+    return FileCollector::expandTilde(valueInContextOrResource(context, "odbserverroots")); 
+} 
+
+std::string RetrieveHandler::valueInContextOrResource(ecml::ExecutionContext& context, const string& keyword, bool required)
+{
+    string r (context.environment().lookup(keyword, eckit::Resource<std::string>(keyword, ""), context));
+    if (required && ! r.size())
+        throw UserError(string("Value of ") + keyword + " not found in neither ECML context nor config resource.");
+    return r;
+}
+
+ecml::Values RetrieveHandler::handle(ecml::ExecutionContext& context)
+{
+    ecml::Request request (ecml::Cell::clone(context.environment().currentFrame())); // TODO: delete later
+    request->text("retrieve"); // TODO: we should not need to do this
+
+    Log::info() << "RetrieveHandler::handle: request: " << request << endl;
+ 
+    const string host (database(context)),
+                 target (context.environment().lookup("target", "", context)),
+                 filter (context.environment().lookup("filter", "", context));
+
+    const string protocol (host == "local" ? "local://" : "mars://");
+
+    if (! target.size())
+        throw UserError("You must specify TARGET explicitly");
+
+    Log::info() << "RETRIEVE from " << host << " into " << target << endl;
+
+    stringstream ss;
+    ss << (local_ ? "local://" : protocol) << request;
+    if (local_)
+        ss << ",odbpathnameschema=\"" << odbPathNameSchema(context) << "\""
+           << ",odbserverroots=\"" << odbServerRoots(context) << "\"";
+
+    eckit::MultiHandle input;
+    ecml::DataHandleFactory::buildMultiHandle(input, ss.str());
+    Log::info() << "RETRIEVE input " << input << endl;
+
+    TemplateParameters templateParameters;
+    TemplateParameters::parse(target, templateParameters);
+
+    vector<PathName> r;
+    if (filter.size() || templateParameters.size())
+    {
+        try { 
+            r = SQLHandler::executeSelect(filter, input, target, &context);
+        } catch (eckit::ReadError e) {
+            Log::warning() << "No data received from " << host << endl;
+        }
+    }
+    else
+    {
+        FileHandle p(target);
+        input.saveInto(p);
+        r.push_back(target);
+    }
+
+    ASSERT(r.size());
+    List list;
+    for (size_t i(0); i < r.size(); ++i)
+        list.append(r[i]);
+    return list;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.h b/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.h
new file mode 100644
index 0000000..8456192
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/RetrieveHandler.h
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_RetrieveHandler_H
+#define odb_api_RetrieveHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class RetrieveHandler : public ecml::RequestHandler {
+public:
+    RetrieveHandler(const std::string&, bool local);
+
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+
+    static std::string odbPathNameSchema(ecml::ExecutionContext&);
+    static std::string odbServerRoots(ecml::ExecutionContext&);
+
+    static std::string valueInContextOrResource(ecml::ExecutionContext&, const std::string& keyword, bool required = true);
+
+private:
+    bool local_;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/SQLHandler.cc b/odb_api/src/odb_api/ecml_verbs/SQLHandler.cc
new file mode 100644
index 0000000..27b2ccf
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/SQLHandler.cc
@@ -0,0 +1,151 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, February 2015
+
+#include <sstream>
+
+#include "SQLHandler.h"
+
+#include "eckit/io/MultiHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/SQLNonInteractiveSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/SQLSelect.h"
+
+#include "ecml/data/DataHandleFactory.h"
+
+using namespace std;
+using namespace eckit;
+using namespace ecml;
+using namespace odb;
+using namespace odb::sql;
+
+namespace odb {
+
+SQLHandler::SQLHandler(const string& name) : RequestHandler(name) {}
+
+Values SQLHandler::handle(ecml::ExecutionContext& context)
+{
+    string target (context.environment().lookup("target", "", context)),
+           include (cleanUpSQLText(context.environment().lookup("include", "", context)));
+           //callback (context.environment().lookup("callback", "", context));
+
+    vector<string> filters (context.getValueAsList("filter"));
+    vector<string> sources (context.getValueAsList("source"));
+    if (sources.empty())
+        throw UserError("sql: SOURCE is empty");
+
+    string filter;
+    for (size_t i(0); i < filters.size(); ++i)
+        filter += cleanUpSQLText(filters[i]) + "\n";
+
+    MultiHandle input;
+    DataHandleFactory::buildMultiHandle(input, sources);
+
+    Log::debug() << "SQLHandler:" << " target: " << target << ", input : " << input << ", filter: " << filter << endl;
+
+    vector<string> ps( pathNamesToStrings(executeSelect(filter, include, input, target, &context)) );
+
+    List result;
+    for (size_t i(0); i < ps.size(); ++i)
+        result.append(ps[i]);
+    return result;
+}
+
+vector<PathName> SQLHandler::executeSelect(const string& select, DataHandle& input, const string& into, ExecutionContext* context)
+{
+    return executeSelect(select, "", input, into, context);
+}
+
+class WithOpenForRead {
+public:
+    WithOpenForRead (eckit::DataHandle& h) : h_(h) { h_.openForRead(); }
+    ~WithOpenForRead () { h_.close(); }
+private:
+    DataHandle& h_;
+};
+
+vector<PathName> SQLHandler::executeSelect(const string& select, const string& inc, DataHandle& input, const string& into, ExecutionContext* context)
+{
+    // We don't call saveInto here so have to open the handle explicitly:
+    WithOpenForRead scope (input);
+
+    string sql(select);
+    if (! sql.size())
+    {
+        Log::info() << "No SQL, using default 'select *;'" << endl;
+        sql = "select *;";
+    }
+
+    sql = cleanUpSQLText(sql);
+    Log::debug() << "Executing '" << sql << "'" << endl;
+
+    SQLNonInteractiveSession session(odb::sql::SQLOutputConfig::defaultConfig(), ",");
+
+    SQLOutputConfig config(session.selectFactory().config());
+    config.outputFormat("odb");
+    config.outputFile(into);
+
+    SQLParser parser;
+    if (inc.size())
+        parser.parseString(session, inc, &input, config);
+    parser.parseString(session, sql, &input, config);
+
+    SQLStatement* statement (session.statement());
+    if (! statement)
+    {
+        //throw UserError("sql: No SELECT parsed");
+        Log::info()  << "sql: No SELECT parsed" << endl;
+        return vector<PathName> ();
+    }
+
+    SQLSelect* sqlSelect(dynamic_cast<SQLSelect*>(statement));
+    if (! sqlSelect) 
+    {
+        stringstream ss;
+        ss << "sql: Parsed statement " << *statement << " is not SELECT";
+        throw UserError(ss.str());
+    }
+
+    long long numberOfRows (sqlSelect->execute(context));
+    vector<PathName> r(sqlSelect->outputFiles());
+
+    delete sqlSelect;
+
+    Log::debug() << "Processed " << numberOfRows << " row(s)." << endl;
+
+    return r;
+}
+
+string SQLHandler::cleanUpSQLText(const string& sql)
+{
+    if (sql.size() == 0)
+        return sql;
+
+    string s(sql);
+    StringTool::trimInPlace(s);
+    s = StringTool::isInQuotes(s) ? StringTool::unQuote(s) : s;
+    StringTool::trimInPlace(s);
+    if (s[s.size() - 1] != ';')
+        s.append(";");
+    return s;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/SQLHandler.h b/odb_api/src/odb_api/ecml_verbs/SQLHandler.h
new file mode 100644
index 0000000..327225f
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/SQLHandler.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_SQLHandler_H
+#define odb_api_SQLHandler_H
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace odb {
+
+class SQLHandler : public ecml::RequestHandler {
+public:
+    SQLHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+
+    static std::vector<eckit::PathName> executeSelect(const std::string&, eckit::DataHandle&, const std::string&, ecml::ExecutionContext* context);
+
+    static std::vector<eckit::PathName> executeSelect(const std::string&, const std::string&, eckit::DataHandle&, const std::string&, ecml::ExecutionContext* context);
+
+    static std::string cleanUpSQLText(const std::string&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.cc b/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.cc
new file mode 100644
index 0000000..0825776
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.cc
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/io/FileHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/tools/ImportTool.h"
+#include "odb_api/SQLInteractiveSession.h"
+
+#include "SQLTestHandler.h"
+#include "ImportTextHandler.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+
+SQLTestHandler::SQLTestHandler(const string& name)
+: RequestHandler(name)
+{
+}
+
+PathName SQLTestHandler::generatePathName(const string& testLabel, const string& parameterName)
+{
+    // TODO: remove spaces and funny characters from label and use it as a prefix of the file name
+   //return testLabel + "_" + parameterName + ".odb";
+   return parameterName + ".odb";
+}
+
+
+PathName SQLTestHandler::write(const string& testLabel, const string& parameterName, const string& csv)
+{
+    const PathName fn (generatePathName(testLabel, parameterName));
+    ImportTextHandler::importText(fn, csv);
+    return fn;
+}
+
+void SQLTestHandler::createInputTables(ecml::Cell* dictionary)
+{
+    if (! dictionary) return;
+    dictionary = dictionary->value();
+    if (! dictionary) return;
+
+    for (ecml::Cell* p (dictionary->rest()); p; p = p->rest())
+    {
+        string tableName (p->text());
+        ecml::Cell* csv (p->value()->value());
+        const string text (csv->text());
+
+        Log::info() << "createInputTables: tableName: " << tableName << endl;
+        Log::info() << "createInputTables: text: " << text << endl;
+
+        ImportTextHandler::importText(PathName(tableName + ".odb"), text);
+    }
+}
+
+/// Accepted parameters: label, input, expect, sql
+ecml::Request SQLTestHandler::handle(ecml::ExecutionContext& context)
+{
+    ecml::Environment& e (context.environment());
+
+    const string label (e.lookup("label", "", context));
+    const string input (e.lookup("input", "", context));
+    const string expect (e.lookup("expect", "", context));
+    const string sql (e.lookup("sql", "", context));
+
+    ecml::Cell * inputTables (e.lookupNoThrow("input_tables"));
+    createInputTables (inputTables);
+
+    if (! (expect.size() && sql.size()))
+        throw UserError("sql_test: you need to pass parameters sql, expect and input or input_tables");
+
+    PathName expectedOutput (write(label, "expect", expect));
+    PathName actualOutput (generatePathName(label, "actual_output"));
+
+    odb::Writer<> writer (actualOutput);
+    odb::Writer<>::iterator output (writer.begin());
+
+    if (input.size())
+    {
+        PathName in (write(label, "input", input));
+        odb::Select select (sql, in);
+        unsigned long long rowsProduced (output->pass1(select.begin(), select.end()));
+    } 
+    else 
+    {
+        odb::Select select (sql);
+        unsigned long long rowsProduced (output->pass1(select.begin(), select.end()));
+    }
+
+    FileHandle l (expectedOutput);
+    FileHandle r (actualOutput);
+    l.openForRead();
+    r.openForRead();
+
+    odb::Comparator comparator;
+    comparator.compare (l, r);
+
+    ecml::List result;
+    result.append(actualOutput);
+
+    return result;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.h b/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.h
new file mode 100644
index 0000000..64776f3
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/SQLTestHandler.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, October 2015
+
+#ifndef odb_api_SQLTestHandler_H
+#define odb_api_SQLTestHandler_H
+
+#include "eckit/filesystem/PathName.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class SQLTestHandler : public ecml::RequestHandler {
+public:
+    SQLTestHandler(const std::string&);
+
+private:
+    eckit::PathName generatePathName(const std::string& testLabel, const std::string& parameterName);
+    eckit::PathName write(const std::string& testLabel, const std::string& parameterName, const std::string& csv);
+    void createInputTables(ecml::Cell*);
+
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/StageHandler.cc b/odb_api/src/odb_api/ecml_verbs/StageHandler.cc
new file mode 100644
index 0000000..16b9516
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/StageHandler.cc
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "StageHandler.h"
+#include "SQLHandler.h"
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/config/Resource.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+
+#include "RetrieveHandler.h"
+#include "odb_api/Stager.h"
+#include "odb_api/FileMapper.h"
+
+using namespace std;
+using namespace ecml;
+using namespace eckit;
+using namespace odb;
+
+namespace odb {
+
+StageHandler::StageHandler(const string& name, bool local) 
+: RequestHandler(name),
+  local_(local) 
+{}
+
+Values StageHandler::handle(ecml::ExecutionContext& context)
+{
+    Request request (Cell::clone(context.environment().currentFrame())); // TODO: delete later
+    request->text("stage"); // it could be let if stage is called via apply
+
+    Log::info() << "STAGE: " << request << endl;
+
+    const string target (context.environment().lookup("target", "", context));
+    if (! target.size()) 
+        throw UserError("You must specify TARGET explicitly");
+
+    MultiHandle input;
+    if (! local_)
+    {
+        const string host (database(context)); 
+        const string protocol (host == "local" ? "local://" : "mars://");
+        Log::info() << "STAGE on " << host << " save partitions info to '" << target << "'" << endl;
+        DataHandleFactory::buildMultiHandle(input, protocol + request->str());
+    }
+    else
+    {
+        const string odbPathNameSchema (RetrieveHandler::odbPathNameSchema(context));
+
+        FileMapper mapper (odbPathNameSchema);
+        vector<string> keywords (mapper.keywords());
+        keywords.push_back("n_parts");
+
+        map<string,vector<string> > rq;
+        for (size_t i(0); i < keywords.size(); ++i)
+        {
+            const string& key (keywords[i]);
+
+            rq [key] = context.getValueAsList(key);
+
+            if (! rq[key].size() )
+                throw UserError("LOCAL_STAGE: " + key + " must be set" );
+
+            Log::info() << "STAGE: " << key << " = " << rq [ key ] << endl;
+        }
+
+        const string defaultPartitionsInfo ("partitions_info.txt");
+
+        rq["odbPathNameSchema"].push_back(odbPathNameSchema);
+        rq["odbServerRoots"].push_back(RetrieveHandler::odbServerRoots(context));
+        rq["partitionsInfo"].push_back(defaultPartitionsInfo);
+
+        Stager::stage(input, keywords, rq);
+    }
+
+    vector<PathName> r;
+
+    FileHandle p(target);
+    input.saveInto(p);
+    r.push_back(target);
+
+    ASSERT(r.size());
+    List list;
+    for (size_t i(0); i < r.size(); ++i)
+        list.append(r[i]);
+    return list;
+}
+
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/ecml_verbs/StageHandler.h b/odb_api/src/odb_api/ecml_verbs/StageHandler.h
new file mode 100644
index 0000000..cdf5651
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/StageHandler.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, November 2015
+
+#ifndef odb_api_StageHandler_H
+#define odb_api_StageHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+namespace odb {
+
+class StageHandler : public ecml::RequestHandler {
+public:
+    StageHandler(const std::string&, bool local);
+
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+
+private:
+    bool local_;
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/odb_server_management.ecml b/odb_api/src/odb_api/ecml_verbs/tests/odb_server_management.ecml
new file mode 100644
index 0000000..5658074
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/odb_server_management.ecml
@@ -0,0 +1,43 @@
+### Helper functions
+
+function, of = _, path = (join_strings, separator = "/", _ = (_))
+
+function, of = _, sh = (
+    let, cmd = (join_strings, separator = "", _ = (_))
+
+    println, values = "sh: executing '" / (value, of = cmd) / "'",
+             separator = ""
+
+    test, label = (value, of = cmd), # TODO: test doesn't evaluate label at the moment
+    do = (
+        let, rc = (system, values = (value, of = cmd))
+        println, values = (value, of = cmd) / "=>" / (value, of = rc)
+        value, of = rc
+    ),
+    expect = 0
+)
+
+#################### the ODB Server management functions ###############
+
+println, values = "odb_server_management.ecml: define start_server, stop_server"
+
+function, start_server = (
+    let, server = (path, _ = (getenv, values = MARS_INSTALLATION_DIRECTORY)/bin/mars)
+
+    println, values = "Trying to start ODB Server:" / (server)
+
+    stop_server
+    sh, _ = "ls -l " / (server) 
+    sh, _ = "rm -rf " / (getenv, values = TEST_DHSHOME) / "/txn/*"
+    sh, _ = "PIPE_DEBUG=1 DHSHOME=" / (getenv, values = TEST_DHSHOME) / " " / (server) / " &"
+    #sh, _ = "ps auxw | grep $(basename " / (server) / ")"
+)
+
+function, stop_server = (
+    println, values = "Trying to kill ODB Server..."
+    sh, _ = "killall -s 9 mars mars.t queue.t || true"
+    println, values = "Done"
+)
+
+#function, stop_server = ( println, values = "(DUMMY) Trying to kill ODB Server..." )
+#function, start_server = ( println, values = "(DUMMY)Trying to start ODB Server" ) 
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/odbgov_defs.ecml b/odb_api/src/odb_api/ecml_verbs/tests/odbgov_defs.ecml
new file mode 100644
index 0000000..a3c4c0f
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/odbgov_defs.ecml
@@ -0,0 +1,415 @@
+let, report_type=(let,
+16001 = (let,description='Automatic Land SYNOP',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='3',subtype='Automatic Land SYNOP',obstype_id='1',obstype='SYNOP',codetype_id='14',codetype='Automatic Land SYNOP'),
+16002 = (let,description='Manual Land SYNOP',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='1',subtype='Manual Land SYNOP',obstype_id='1',obstype='SYNOP',codetype_id='11',codetype='Manual Land SYNOP'),
+16003 = (let,description='Abbreviated SYNOP',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='9',subtype='Abbreviated SYNOP',obstype_id='1',obstype='SYNOP',codetype_id='11',codetype='Manual Land SYNOP'),
+16004 = (let,description='METAR',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='140',subtype='METAR',obstype_id='1',obstype='SYNOP',codetype_id='140',codetype='METAR'),
+16065 = (let,description='Automatic METAR',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='147',subtype='AUTO METAR',obstype_id='1',obstype='SYNOP',codetype_id='140',codetype='METAR'),
+16025 = (let,description='Additional Land Surface Data',group_id='17',group='CONV',bufrtype_id='0',bufrtype='Land Surface',subtype_id='28',subtype='SNOW DEPTH',obstype_id='1',obstype='SYNOP',codetype_id='17',codetype='Additional Land Surface Data'),
+16005 = (let,description='DRIBU',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='21',subtype='DRIBU',obstype_id='4',obstype='BUOY',codetype_id='165',codetype='DRIBU'),
+16006 = (let,description='Automatic SHIP',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='13',subtype='Automatic SHIP',obstype_id='1',obstype='SYNOP',codetype_id='24',codetype='Automatic SHIP'),
+16007 = (let,description='Reduced SHIP',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='19',subtype='Reduced SHIP',obstype_id='1',obstype='SYNOP',codetype_id='23',codetype='Reduced SHIP'),
+16008 = (let,description='SHIP',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='11',subtype='SHIP',obstype_id='1',obstype='SYNOP',codetype_id='21',codetype='SHIP'),
+16009 = (let,description='Abbreviated SHIP',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='9',subtype='Abbreviated SYNOP',obstype_id='1',obstype='SYNOP',codetype_id='21',codetype='SHIP'),
+16010 = (let,description='DRIBU-BATHY',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='22',subtype='DRIBU-BATHY',obstype_id='4',obstype='BUOY',codetype_id='63',codetype='DRIBU-BATHY'),
+16011 = (let,description='DRIBU-TESAC',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='23',subtype='DRIBU-TESAC',obstype_id='4',obstype='BUOY',codetype_id='64',codetype='DRIBU-TESAC'),
+16012 = (let,description='Ground-Based GPS',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='110',subtype='Ground-based GPS',obstype_id='1',obstype='SYNOP',codetype_id='110',codetype='Ground-based GPS'),
+16084 = (let,description='BUFR DRIFTING BUOYS',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='182',subtype='BUFR DRIFTING BUOYS',obstype_id='4',obstype='BUOY',codetype_id='189',codetype='BUFR DRIFTING BUOYS'),
+16083 = (let,description='BUFR MOORED BUOYS',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='181',subtype='BUFR MOORED BUOYS',obstype_id='4',obstype='BUOY',codetype_id='188',codetype='BUFR MOORED BUOYS'),
+16074 = (let,description='BUFR SHIP SYNOP',group_id='17',group='CONV',bufrtype_id='1',bufrtype='Sea Surface',subtype_id='183',subtype='BUFR SHIP SYNOP 2',obstype_id='1',obstype='SYNOP',codetype_id='182',codetype='BUFR SHIP SYNOP'),
+16013 = (let,description='Land PILOT',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='91',subtype='PILOT',obstype_id='6',obstype='PILOT',codetype_id='32',codetype='Land PILOT'),
+16014 = (let,description='PILOT SHIP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='92',subtype='PILOT SHIP',obstype_id='6',obstype='PILOT',codetype_id='33',codetype='PILOT SHIP'),
+16015 = (let,description='American Wind Profiler 1',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='95',subtype='American Wind Profiler 1',obstype_id='6',obstype='PILOT',codetype_id='34',codetype='American Wind Profiler'),
+16016 = (let,description='American Wind Profiler 2',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='96',subtype='Wind Profiler',obstype_id='6',obstype='PILOT',codetype_id='34',codetype='American Wind Profiler'),
+16017 = (let,description='European Wind Profiler',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='96',subtype='Wind Profiler',obstype_id='6',obstype='PILOT',codetype_id='134',codetype='European Wind Profiler'),
+16018 = (let,description='Japanese Wind Profiler',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='96',subtype='Wind Profiler',obstype_id='6',obstype='PILOT',codetype_id='131',codetype='Japanese Wind Profiler'),
+16019 = (let,description='TEMP SHIP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='102',subtype='TEMP SHIP',obstype_id='5',obstype='TEMP',codetype_id='36',codetype='TEMP SHIP'),
+16020 = (let,description='DROP Sonde',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='103',subtype='DROP Sonde',obstype_id='5',obstype='TEMP',codetype_id='135',codetype='DROP Sonde'),
+16021 = (let,description='Mobile TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='106',subtype='MOBILE TEMP',obstype_id='5',obstype='TEMP',codetype_id='37',codetype='MOBILE TEMP'),
+16022 = (let,description='Land TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='101',subtype='Land TEMP',obstype_id='5',obstype='TEMP',codetype_id='35',codetype='Land TEMP'),
+16023 = (let,description='ROCOB TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='104',subtype='ROCOB TEMP',obstype_id='5',obstype='TEMP',codetype_id='39',codetype='ROCOB TEMP'),
+16024 = (let,description='SHIP ROCOB TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='105',subtype='SHIP ROCOB TEMP',obstype_id='5',obstype='TEMP',codetype_id='40',codetype='SHIP ROCOB TEMP'),
+16069 = (let,description='BUFR SHIP PILOT',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='113',subtype='BUFR SHIP PILOT',obstype_id='6',obstype='PILOT',codetype_id='113',codetype='BUFR SHIP PILOT'),
+16068 = (let,description='BUFR LAND PILOT',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='112',subtype='BUFR LAND PILOT',obstype_id='6',obstype='PILOT',codetype_id='112',codetype='BUFR LAND PILOT'),
+16045 = (let,description='BUFR LAND TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='109',subtype='Land High Resolution Temp',obstype_id='5',obstype='TEMP',codetype_id='109',codetype='Land High Resolution Temp'),
+16046 = (let,description='BUFR SHIP TEMP',group_id='17',group='CONV',bufrtype_id='2',bufrtype='Upper Air Sounding',subtype_id='111',subtype='SHIP High Resolution TEMP',obstype_id='5',obstype='TEMP',codetype_id='111',codetype='SHIP High Resolution TEMP'),
+16032 = (let,description='PAOB via Sat code',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='161',subtype='PAOB',obstype_id='8',obstype='PAOB',codetype_id='180',codetype='PAOB'),
+16037 = (let,description='SATEM 500km Low Level Temperature',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='61',subtype='500km Low Level Temperature',obstype_id='7',obstype='SATEM',codetype_id='86',codetype='SATEM'),
+16038 = (let,description='SATEM 500km PWC',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='62',subtype='500km PWC',obstype_id='7',obstype='SATEM',codetype_id='86',codetype='SATEM'),
+16039 = (let,description='SATEM 500km High Level Temperature',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='63',subtype='500km High Level Temperature',obstype_id='7',obstype='SATEM',codetype_id='86',codetype='SATEM'),
+16040 = (let,description='SATEM 500km Merged',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='65',subtype='500km Merged',obstype_id='7',obstype='SATEM',codetype_id='86',codetype='SATEM'),
+16041 = (let,description='SATEM 250km Low Level Temperature',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='71',subtype='250km Low Level Temperature',obstype_id='7',obstype='SATEM',codetype_id='186',codetype='SATEM'),
+16042 = (let,description='SATEM 250km PWC',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='72',subtype='250km PWC',obstype_id='7',obstype='SATEM',codetype_id='186',codetype='SATEM'),
+16043 = (let,description='SATEM 250km High Level Temperature',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='73',subtype='250km High Level Temperature',obstype_id='7',obstype='SATEM',codetype_id='186',codetype='SATEM'),
+16044 = (let,description='SATEM 250km Merged',group_id='17',group='CONV',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='75',subtype='250km Merged',obstype_id='7',obstype='SATEM',codetype_id='186',codetype='SATEM'),
+16026 = (let,description='AIREP',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='142',subtype='AIREP',obstype_id='2',obstype='AIREP',codetype_id='141',codetype='AIREP'),
+16027 = (let,description='CODAR',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='141',subtype='AIREP',obstype_id='2',obstype='AIREP',codetype_id='41',codetype='CODAR'),
+16028 = (let,description='COLBA',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='143',subtype='COLBA',obstype_id='2',obstype='AIREP',codetype_id='241',codetype='COLBA'),
+16029 = (let,description='AMDAR',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='144',subtype='AMDAR',obstype_id='2',obstype='AIREP',codetype_id='144',codetype='AMDAR'),
+16030 = (let,description='ACARS',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='145',subtype='ACARS',obstype_id='2',obstype='AIREP',codetype_id='145',codetype='ACARS'),
+16082 = (let,description='WIGOS AMDAR',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='146',subtype='WIGOS AMDAR',obstype_id='2',obstype='AIREP',codetype_id='146',codetype='WIGOS AMDAR'),
+16066 = (let,description='TAMDAR',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='148',subtype='TAMDAR',obstype_id='2',obstype='AIREP',codetype_id='148',codetype='TAMDAR'),
+16067 = (let,description='ACARs with mixing ratio',group_id='17',group='CONV',bufrtype_id='4',bufrtype='AIREP',subtype_id='149',subtype='ACARs with mixing ratio',obstype_id='2',obstype='AIREP',codetype_id='149',codetype='ACARs with mixing ratio'),
+25001 = (let,description='Ground-Based Radar',group_id='26',group='GBRAD',bufrtype_id='6',bufrtype='Ground-based radar',subtype_id='125',subtype='Radar rain rate',obstype_id='14',obstype='Ground-based weather radar',codetype_id='3',codetype='Radar Rain Rates'),
+16031 = (let,description='PAOB',group_id='17',group='CONV',bufrtype_id='253',bufrtype='PAOB',subtype_id='164',subtype='PAOB',obstype_id='8',obstype='PAOB',codetype_id='180',codetype='PAOB'),
+9001 = (let,description='Old ERS 1 Scatterometer',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='8',subtype='ERS-1',obstype_id='9',obstype='Scatterometer',codetype_id='8',codetype='Old ERS 1',sensor_id='150',sensor='Scatterometer',satellite_instrument_id='150',satellite_instrument='SCATTEROMETER',satellite_identifier_id='1',satellite_identifier='ERS 1'),
+9002 = (let,description='New ERS 1 Scatterometer',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='122',subtype='ERS',obstype_id='9',obstype='Scatterometer',codetype_id='122',codetype='ERS',sensor_id='150',sensor='Scatterometer',satellite_instrument_id='150',satellite_instrument='SCATTEROMETER',satellite_identifier_id='1',satellite_identifier='ERS 1'),
+5001 = (let,description='ERS 2 GOME O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='170',sensor='GOME',satellite_instrument_id='170',satellite_instrument='GOME',satellite_identifier_id='2',satellite_identifier='ERS 2'),
+9003 = (let,description='ERS 2 Scatterometer',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='122',subtype='ERS',obstype_id='9',obstype='Scatterometer',codetype_id='122',codetype='ERS',sensor_id='150',sensor='Scatterometer',satellite_instrument_id='150',satellite_instrument='SCATTEROMETER',satellite_identifier_id='2',satellite_identifier='ERS 2'),
+35002 = (let,description='ERS-2 GOME layer integrated mass density with averaging kernels',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='209',subtype='Layer integrated mass density with averaging kernels',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='170',sensor='GOME',satellite_instrument_id='170',satellite_instrument='GOME',satellite_identifier_id='2',satellite_identifi [...]
+5025 = (let,description='ERS 2 GOME MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='170',sensor='GOME',satellite_instrument_id='170',satellite_instrument='GOME',satellite_identifier_id='2',satellite_identifier='ERS 2'),
+5050 = (let,description='ERS-2 GOME Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='170',sensor='GOME',satellite_instrument_id='170',satellite_instrument='GOME',satellite_identifier_id='2',satellite_identifier='ERS 2'),
+9007 = (let,description='METOP-B ASCAT',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='139',subtype='ASCAT',obstype_id='9',obstype='Scatterometer',codetype_id='139',codetype='ASCAT',sensor_id='190',sensor='ASCAT',satellite_instrument_id='190',satellite_instrument='ASCAT',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+25 = (let,description='METOP-B HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='607',satellite_instrument='HIRS/4',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+1009 = (let,description='METOP-B AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+3004 = (let,description='METOP-B MHS Radiances',group_id='4',group='MHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+7013 = (let,description='METOP-B GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='202',sensor='GRAS',satellite_instrument_id='202',satellite_instrument='GRAS',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+11002 = (let,description='METOP-B IASI Radiances',group_id='12',group='IASI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='240',subtype='IASI',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='16',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+21009 = (let,description='METOP-B AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+5057 = (let,description='METOP-B GOME-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+35011 = (let,description='METOP-B IASI layer integrated mass density with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='229',subtype='Layer integrated mass density with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='221',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',sa [...]
+44004 = (let,description='MHS METOP-B MHS Radiances All-sky',group_id='45',group='MHS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+5061 = (let,description='METOP-B GOME-2 Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+35012 = (let,description='METOP-B GOME-2 layer integrated mass density with averaging kernels',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='209',subtype='Layer integrated mass density with averaging kernels',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='3',satellite_ [...]
+35013 = (let,description='METOP-B GOME-2 layer integrated mass density with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='229',subtype='Layer integrated mass density with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME [...]
+5062 = (let,description='METOP-B GOME-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+5072 = (let,description='METOP-B GOME-2 PMAP',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='207',subtype='MODIS aerosol',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+15 = (let,description='METOP-A HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='607',satellite_instrument='HIRS/4',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+1007 = (let,description='METOP-A AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+3002 = (let,description='METOP-A MHS Radiances',group_id='4',group='MHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+5013 = (let,description='METOP-A GOME-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+7001 = (let,description='METOP-A GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='202',sensor='GRAS',satellite_instrument_id='202',satellite_instrument='GRAS',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+9005 = (let,description='METOP-A ASCAT',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='139',subtype='ASCAT',obstype_id='9',obstype='Scatterometer',codetype_id='139',codetype='ASCAT',sensor_id='190',sensor='ASCAT',satellite_instrument_id='190',satellite_instrument='ASCAT',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+11001 = (let,description='METOP-A IASI Radiances',group_id='12',group='IASI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='240',subtype='IASI',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='16',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+21007 = (let,description='METOP-A AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+5024 = (let,description='METOP-A IASI Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='221',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+35004 = (let,description='METOP-A GOME-2 layer integrated mass density with averaging kernels',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='209',subtype='Layer integrated mass density with averaging kernels',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='4',satellite_ [...]
+35005 = (let,description='METOP-A IASI layer volumetric mixing ratio with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='228',subtype='Layer volumetric mixing ratio with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='221',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',sa [...]
+35007 = (let,description='METOP-A IASI layer integrated mass density with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='229',subtype='Layer integrated mass density with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='221',sensor='IASI',satellite_instrument_id='221',satellite_instrument='IASI',sa [...]
+5037 = (let,description='METOP-A GOME-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+5047 = (let,description='METOP-A GOME-2 Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+35010 = (let,description='METOP-A GOME-2 layer integrated mass density with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='229',subtype='Layer integrated mass density with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME [...]
+44002 = (let,description='MHS METOP-A MHS Radiances All-sky',group_id='45',group='MHS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+5071 = (let,description='METOP-A GOME-2 PMAP',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='207',subtype='MODIS aerosol',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='220',sensor='GOME-2',satellite_instrument_id='220',satellite_instrument='GOME-2',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+7002 = (let,description='CHAMP GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='41',satellite_identifier='CHAMP'),
+7010 = (let,description='TerraSAR-X GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='103',sensor='IGOR',satellite_instrument_id='103',satellite_instrument='IGOR',satellite_identifier_id='42',satellite_identifier='TerraSAR-X'),
+7015 = (let,description='TanDEM-X GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='103',sensor='IGOR',satellite_instrument_id='103',satellite_instrument='IGOR',satellite_identifier_id='43',satellite_identifier='TanDEM-X'),
+18001 = (let,description='SMOS',group_id='19',group='SMOS',bufrtype_id='30',bufrtype='SMOS',subtype_id='203',subtype='SMOS',obstype_id='7',obstype='SATEM',codetype_id='400',codetype='SMOS',sensor_id='18',sensor='MIRAS',satellite_instrument_id='176',satellite_instrument='MIRAS',satellite_identifier_id='46',satellite_identifier='SMOS'),
+4020 = (let,description='METEOSAT 4 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='20',sensor='MVIRI',satellite_instrument_id='205',satellite_instrument='MVIRI',satellite_identifier_id='51',satellite_identifier='METEOSAT 4'),
+4017 = (let,description='METEOSAT 5 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='20',sensor='MVIRI',satellite_instrument_id='205',satellite_instrument='MVIRI',satellite_identifier_id='52',satellite_identifier='METEOSAT 5'),
+4021 = (let,description='METEOSAT 6 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='20',sensor='MVIRI',satellite_instrument_id='205',satellite_instrument='MVIRI',satellite_identifier_id='53',satellite_identifier='METEOSAT 6'),
+4007 = (let,description='METEOSAT 7 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='20',sensor='MVIRI',satellite_instrument_id='205',satellite_instrument='MVIRI',satellite_identifier_id='54',satellite_identifier='METEOSAT 7'),
+4008 = (let,description='METEOSAT 8 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='189',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='55',satellite_identifier='METEOSAT 8'),
+5002 = (let,description='METEOSAT 8 SEVIRI O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='55',satellite_identifier='METEOSAT 8'),
+5026 = (let,description='METEOSAT 8 SEVIRI MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='55',satellite_identifier='METEOSAT 8'),
+4018 = (let,description='METEOSAT 8 GEOS Allsky Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='190',subtype='GEOS Allsky Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='55',satellite_identifier='METEOSAT 8'),
+4009 = (let,description='METEOSAT 9 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='189',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+5003 = (let,description='METEOSAT 9 SEVIRI O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+4013 = (let,description='METEOSAT 9 GEOS Allsky Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='190',subtype='GEOS Allsky Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+5027 = (let,description='METEOSAT 9 SEVIRI MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+5063 = (let,description='METEOSAT-9 SEVIRI aerosol optical depth',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='223',subtype='Satellite aerosol optical depth',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+4016 = (let,description='METEOSAT 10 GEOS Allsky Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='190',subtype='GEOS Allsky Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='57',satellite_identifier='METEOSAT 10'),
+5058 = (let,description='METEOSAT 10 SEVIRI O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='57',satellite_identifier='METEOSAT 10'),
+5064 = (let,description='METEOSAT-10 SEVIRI aerosol optical depth',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='223',subtype='Satellite aerosol optical depth',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='207',sensor='SEVIRI',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='57',satellite_identifier='METEOSAT 10'),
+5014 = (let,description='ENVISAT SCIAMACHY O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument='SCIAMACHY',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5015 = (let,description='ENVISAT GOMOS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='172',sensor='GOMOS',satellite_instrument_id='172',satellite_instrument='GOMOS',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5016 = (let,description='ENVISAT MIPAS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='161',sensor='MIPAS',satellite_instrument_id='161',satellite_instrument='MIPAS',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+6001 = (let,description='ENVISAT MERIS TCWV',group_id='7',group='MERIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='212',subtype='MERIS',obstype_id='7',obstype='SATEM',codetype_id='214',codetype='TCWC',sensor_id='174',sensor='MERIS',satellite_instrument_id='174',satellite_instrument='MERIS',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+35003 = (let,description='ENVISAT SCIAMACHY layer integrated mass density with averaging kernels',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='209',subtype='Layer integrated mass density with averaging kernels',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument='SCIAMACHY',satellite_identifier_id='60', [...]
+35006 = (let,description='ENVISAT SCIAMACHY layer volumetric mixing ratio with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='228',subtype='Layer volumetric mixing ratio with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument [...]
+5038 = (let,description='ENVISAT SCIAMACHY MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument='SCIAMACHY',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5039 = (let,description='ENVISAT GOMOS MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='172',sensor='GOMOS',satellite_instrument_id='172',satellite_instrument='GOMOS',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5040 = (let,description='ENVISAT MIPAS MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='161',sensor='MIPAS',satellite_instrument_id='161',satellite_instrument='MIPAS',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5045 = (let,description='ENVISAT SCIAMACHY Layer volumetric mixing ratio',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='215',subtype='Layer volumetric mixing ratio',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument='SCIAMACHY',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5046 = (let,description='ENVISAT SCIAMACHY Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='175',sensor='SCIAMACHY',satellite_instrument_id='175',satellite_instrument='SCIAMACHY',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5049 = (let,description='TERRA MOPITT Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='90',sensor='MOPITT',satellite_instrument_id='90',satellite_instrument='MOPITT',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+5067 = (let,description='ENVISAT AATSR Aerosol',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='207',subtype='MODIS aerosol',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='10',sensor='SSMIS',satellite_instrument_id='10',satellite_instrument='AATSR',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+4023 = (let,description='METEOSAT 11 GEOS Allsky Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='190',subtype='GEOS Allsky Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='21',sensor='SEVIRI RADIANCES',satellite_instrument_id='207',satellite_instrument='SEVIRI',satellite_identifier_id='70',satellite_identifier='METEOSAT 11'),
+5069 = (let,description='TOMS ADEOS-I',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='120',satellite_identifier='ADEOS'),
+41001 = (let,description='GCOM-W1 AMSR-2 Radiances All-sky',group_id='42',group='AMSR-2 All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='60',subtype='AMSR-2',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='63',sensor='AMSR-2',satellite_instrument_id='478',satellite_instrument='AMSR2',satellite_identifier_id='122',satellite_identifier='GCOM-W1'),
+5021 = (let,description='GOSAT TANSO-FTS Layer volumetric mixing ratio',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='215',subtype='Layer volumetric mixing ratio',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='516',sensor='TANSO-FTS',satellite_instrument_id='516',satellite_instrument='TANSO-FTS',satellite_identifier_id='140',satellite_identifier='GOSAT'),
+35009 = (let,description='GOSAT TANSO-FTS layer volumetric mixing ratio with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='228',subtype='Layer volumetric mixing ratio with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='516',sensor='TANSO-FTS',satellite_instrument_id='516',satellite_instrument=' [...]
+4010 = (let,description='MTSAT-1R GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='189',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='24',sensor='MTSATIMG',satellite_instrument_id='294',satellite_instrument='JAMI',satellite_identifier_id='171',satellite_identifier='MTSAT-1R'),
+4012 = (let,description='MTSAT-2 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='189',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='24',sensor='MTSATIMG',satellite_instrument_id='294',satellite_instrument='JAMI',satellite_identifier_id='172',satellite_identifier='MTSAT-2'),
+4022 = (let,description='Himawari 8 GEOS radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='189',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='56',sensor='AHI',satellite_instrument_id='297',satellite_instrument='AHI',satellite_identifier_id='173',satellite_identifier='Himawari-8'),
+4 = (let,description='NOAA 8 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+30004 = (let,description='NOAA 8 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+31004 = (let,description='NOAA 8 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+16 = (let,description='NOAA 8 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+30010 = (let,description='NOAA 8 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+31009 = (let,description='NOAA 8 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='200',satellite_identifier='NOAA 8'),
+5 = (let,description='NOAA 9 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+5006 = (let,description='NOAA 9 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+30005 = (let,description='NOAA 9 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+31005 = (let,description='NOAA 9 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+17 = (let,description='NOAA 9 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+31010 = (let,description='NOAA 9 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+5030 = (let,description='NOAA 9 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+30017 = (let,description='NOAA 9 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+6 = (let,description='NOAA 10 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='202',satellite_identifier='NOAA 10'),
+30006 = (let,description='NOAA 10 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='202',satellite_identifier='NOAA 10'),
+18 = (let,description='NOAA 10 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='202',satellite_identifier='NOAA 10'),
+30011 = (let,description='NOAA 10 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='202',satellite_identifier='NOAA 10'),
+7 = (let,description='NOAA 11 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+5007 = (let,description='NOAA 11 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+30007 = (let,description='NOAA 11 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+31006 = (let,description='NOAA 11 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+19 = (let,description='NOAA 11 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+30012 = (let,description='NOAA 11 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+31011 = (let,description='NOAA 11 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+5031 = (let,description='NOAA 11 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+8 = (let,description='NOAA 12 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+23 = (let,description='NOAA 12 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+30008 = (let,description='NOAA 12 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+31007 = (let,description='NOAA 12 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+30018 = (let,description='NOAA 12 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+31016 = (let,description='NOAA 12 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+9 = (let,description='NOAA 14 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+5008 = (let,description='NOAA 14 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+24 = (let,description='NOAA 14 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+30009 = (let,description='NOAA 14 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+31008 = (let,description='NOAA 14 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+5032 = (let,description='NOAA 14 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+31015 = (let,description='NOAA 14 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+30016 = (let,description='NOAA 14 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+10 = (let,description='NOAA 15 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='606',satellite_instrument='HIRS/3',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+1001 = (let,description='NOAA 15 AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+2001 = (let,description='NOAA 15 AMSUB Radiances',group_id='3',group='AMSUB',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+21001 = (let,description='NOAA 15 AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+43001 = (let,description='AMSUB NOAA 15 AMSUB Radiances All-sky',group_id='44',group='AMSUB All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+11 = (let,description='NOAA 16 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='606',satellite_instrument='HIRS/3',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+1002 = (let,description='NOAA 16 AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+2002 = (let,description='NOAA 16 AMSUB Radiances',group_id='3',group='AMSUB',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+5009 = (let,description='NOAA 16 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+21002 = (let,description='NOAA 16 AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+5033 = (let,description='NOAA 16 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+43002 = (let,description='AMSUB NOAA 16 AMSUB Radiances All-sky',group_id='44',group='AMSUB All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+12 = (let,description='NOAA 17 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='606',satellite_instrument='HIRS/3',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+1003 = (let,description='NOAA 17 AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+2003 = (let,description='NOAA 17 AMSUB Radiances',group_id='3',group='AMSUB',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+5010 = (let,description='NOAA 17 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+21003 = (let,description='NOAA 17 AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+5034 = (let,description='NOAA 17 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+43003 = (let,description='AMSUB NOAA 17 AMSUB Radiances All-sky',group_id='44',group='AMSUB All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+13 = (let,description='NOAA 18 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='607',satellite_instrument='HIRS/4',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+1004 = (let,description='NOAA 18 AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+2004 = (let,description='NOAA 18 AMSUB Radiances',group_id='3',group='AMSUB',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+5011 = (let,description='NOAA 18 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+21004 = (let,description='NOAA 18 AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+3003 = (let,description='NOAA 18 MHS Radiances',group_id='4',group='MHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+5035 = (let,description='NOAA 18 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+43004 = (let,description='AMSUB NOAA 18 AMSUB Radiances All-sky',group_id='44',group='AMSUB All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='4',sensor='AMSUB',satellite_instrument_id='574',satellite_instrument='AMSU-B',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+44003 = (let,description='MHS NOAA 18 MHS Radiances All-sky',group_id='45',group='MHS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+14 = (let,description='NOAA 19 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='607',satellite_instrument='HIRS/4',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+1005 = (let,description='NOAA 19 AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+3001 = (let,description='NOAA 19 MHS Radiances',group_id='4',group='MHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+5012 = (let,description='NOAA 19 SBUV-2 O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+21005 = (let,description='NOAA 19 AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+5036 = (let,description='NOAA 19 SBUV-2 MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+44001 = (let,description='MHS NOAA 19 MHS Radiances All-sky',group_id='45',group='MHS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='15',sensor='MHS',satellite_instrument_id='203',satellite_instrument='MHS',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+34001 = (let,description='NPP ATMS Radiances',group_id='35',group='ATMS',bufrtype_id='21',bufrtype='Radiances    (Satellite Measured) ',subtype_id='201',subtype='ATMS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='19',sensor='ATMS',satellite_instrument_id='621',satellite_instrument='ATMS',satellite_identifier_id='224',satellite_identifier='NPP'),
+36001 = (let,description='NPP CRIS Radiances',group_id='37',group='CRIS',bufrtype_id='21',bufrtype='Radiances    (Satellite Measured) ',subtype_id='202',subtype='CRIS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='27',sensor='CRIS',satellite_instrument_id='620',satellite_instrument='CrIRS/NP',satellite_identifier_id='224',satellite_identifier='NPP'),
+5065 = (let,description='NPP VIIRS aerosol optical thickness',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='219',subtype='VIIRS aerosol optical thickness',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='616',sensor='VIIRS',satellite_instrument_id='375',satellite_instrument='VIRS',satellite_identifier_id='224',satellite_identifier='NPP'),
+5068 = (let,description='NPP OMPS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='947',sensor='OMPS',satellite_instrument_id='947',satellite_instrument='OMPS',satellite_identifier_id='224',satellite_identifier='NPP'),
+49001 = (let,description='NPP ATMS Radiances All-sky',group_id='50',group='ATMS All-sky',bufrtype_id='21',bufrtype='Radiances    (Satellite Measured) ',subtype_id='201',subtype='ATMS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='19',sensor='ATMS',satellite_instrument_id='621',satellite_instrument='ATMS',satellite_identifier_id='224',satellite_identifier='NPP'),
+10001 = (let,description='DMSP 7 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='240',satellite_identifier='DMSP 7'),
+10002 = (let,description='DMSP 8 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='241',satellite_identifier='DMSP 8'),
+20001 = (let,description='DMSP 8 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='241',satellite_identifier='DMSP 8'),
+56001 = (let,description='DMSP 8 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='241',satellite_identifier='DMSP 8'),
+10009 = (let,description='DMSP 8 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='241',satellite_identifier='DMSP 8'),
+10003 = (let,description='DMSP 9 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='242',satellite_identifier='DMSP 9'),
+20002 = (let,description='DMSP 9 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='242',satellite_identifier='DMSP 9'),
+10004 = (let,description='DMSP 10 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='243',satellite_identifier='DMSP 10'),
+20003 = (let,description='DMSP 10 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='243',satellite_identifier='DMSP 10'),
+56002 = (let,description='DMSP 10 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='243',satellite_identifier='DMSP 10'),
+10010 = (let,description='DMSP 10 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='243',satellite_identifier='DMSP 10'),
+10005 = (let,description='DMSP 11 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='244',satellite_identifier='DMSP 11'),
+20004 = (let,description='DMSP 11 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='244',satellite_identifier='DMSP 11'),
+56003 = (let,description='DMSP 11 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='244',satellite_identifier='DMSP 11'),
+58001 = (let,description='DMSP 11 SSMT2 Radiances',group_id='59',group='SSMT2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='33',sensor='SSM/T-2',satellite_instrument_id='907',satellite_instrument='SSM/T-2',satellite_identifier_id='244',satellite_identifier='DMSP 11'),
+10011 = (let,description='DMSP 11 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='244',satellite_identifier='DMSP 11'),
+58002 = (let,description='DMSP 12 SSMT2 Radiances',group_id='59',group='SSMT2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='33',sensor='SSM/T-2',satellite_instrument_id='907',satellite_instrument='SSM/T-2',satellite_identifier_id='245',satellite_identifier='DMSP 12'),
+10006 = (let,description='DMSP 13 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='246',satellite_identifier='DMSP 13'),
+20005 = (let,description='DMSP 13 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='246',satellite_identifier='DMSP 13'),
+56004 = (let,description='DMSP 13 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='246',satellite_identifier='DMSP 13'),
+10012 = (let,description='DMSP 13 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='246',satellite_identifier='DMSP 13'),
+10007 = (let,description='DMSP 14 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='247',satellite_identifier='DMSP 14'),
+20006 = (let,description='DMSP 14 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='247',satellite_identifier='DMSP 14'),
+56005 = (let,description='DMSP 14 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='247',satellite_identifier='DMSP 14'),
+58003 = (let,description='DMSP 14 SSMT2 Radiances',group_id='59',group='SSMT2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='33',sensor='SSM/T-2',satellite_instrument_id='907',satellite_instrument='SSM/T-2',satellite_identifier_id='247',satellite_identifier='DMSP 14'),
+10013 = (let,description='DMSP 14 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='247',satellite_identifier='DMSP 14'),
+10008 = (let,description='DMSP 15 SSMI All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='248',satellite_identifier='DMSP 15'),
+20007 = (let,description='DMSP 15 SSMI',group_id='21',group='SSMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='248',satellite_identifier='DMSP 15'),
+56006 = (let,description='DMSP 15 SSMI 1DVAR TCWV Cloudy-Sky',group_id='57',group='SSMI 1DVAR TCWV Cloudy-Sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='127',subtype='SSMI',obstype_id='7',obstype='SATEM',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='248',satellite_identifier='DMSP 15'),
+58004 = (let,description='DMSP 15 SSMT2 Radiances',group_id='59',group='SSMT2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='33',sensor='SSM/T-2',satellite_instrument_id='907',satellite_instrument='SSM/T-2',satellite_identifier_id='248',satellite_identifier='DMSP 15'),
+10014 = (let,description='DMSP 15 SSMI CMSAF All-sky',group_id='11',group='SSMI All-sky',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='128',subtype='SSMI CMSAF',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='6',sensor='SSM/I',satellite_instrument_id='905',satellite_instrument='SSM/I',satellite_identifier_id='248',satellite_identifier='DMSP 15'),
+13001 = (let,description='DMSP 16 SSMIS Radiances All-sky',group_id='14',group='SSMIS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='249',satellite_identifier='DMSP 16'),
+24001 = (let,description='DMSP 16 SSMIS Radiances',group_id='25',group='SSMIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='249',satellite_identifier='DMSP 16'),
+4002 = (let,description='GOES 8 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='252',satellite_identifier='GOES 8'),
+4003 = (let,description='GOES 9 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='253',satellite_identifier='GOES 9'),
+4004 = (let,description='GOES 10 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='254',satellite_identifier='GOES 10'),
+4005 = (let,description='GOES 11 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='255',satellite_identifier='GOES 11'),
+4006 = (let,description='GOES 12 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='256',satellite_identifier='GOES 12'),
+4011 = (let,description='GOES 13 GEOS Radiances',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='257',satellite_identifier='GOES 13'),
+4014 = (let,description='GOES 14 GEOS Radiances ',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='258',satellite_identifier='GOES 14'),
+4015 = (let,description='GOES 15 GEOS Radiances ',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='22',sensor='IMAGER',satellite_instrument_id='615',satellite_instrument='IMAGER',satellite_identifier_id='259',satellite_identifier='GOES 15'),
+9004 = (let,description='QuickSCAT SeaWind',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='137',subtype='QSCAT',obstype_id='9',obstype='Scatterometer',codetype_id='301',codetype='QSCAT',sensor_id='313',sensor='SeaWinds',satellite_instrument_id='313',satellite_instrument='SeaWinds',satellite_identifier_id='281',satellite_identifier='QUIKSCAT'),
+14001 = (let,description='TRMM TMI Radiances All-sky',group_id='15',group='TMI All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='129',subtype='TRMM',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='9',sensor='TMI',satellite_instrument_id='365',satellite_instrument='TMI',satellite_identifier_id='282',satellite_identifier='TRMM'),
+23001 = (let,description='TRMM TMI Radiances',group_id='24',group='TMI',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='129',subtype='TRMM',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='9',sensor='TMI',satellite_instrument_id='365',satellite_instrument='TMI',satellite_identifier_id='282',satellite_identifier='TRMM'),
+19001 = (let,description='CORIOLIS  WINDSAT All-sky',group_id='20',group='WINDSAT All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='156',subtype='WINDSAT',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='30',sensor='WINDSAT',satellite_instrument_id='953',satellite_instrument='MWHS-2 (or WINDSAT for data from Coriolis satellite)',satellite_identifier_id='283',satellite_identifier='CORIOLIS'),
+13002 = (let,description='DMSP 17 SSMIS Radiances All-sky',group_id='14',group='SSMIS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='285',satellite_identifier='DMSP17'),
+24002 = (let,description='DMSP 17 SSMIS Radiances',group_id='25',group='SSMIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='285',satellite_identifier='DMSP17'),
+13003 = (let,description='DMSP 18 SSMIS Radiances All-sky',group_id='14',group='SSMIS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='286',satellite_identifier='DMSP18'),
+24003 = (let,description='DMSP 18 SSMIS Radiances',group_id='25',group='SSMIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='286',satellite_identifier='DMSP18'),
+5017 = (let,description='METEOR 3-5 TOMS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='321',satellite_identifier='METEOR 3-5'),
+5041 = (let,description='METEOR 3-5 TOMS MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='321',satellite_identifier='METEOR 3-5'),
+9006 = (let,description='OceanSat-2 Scatterometer',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='135',subtype='OSCAT',obstype_id='9',obstype='Scatterometer',codetype_id='302',codetype='OSCAT',sensor_id='288',sensor='OSCAT',satellite_instrument_id='288',satellite_instrument='SCAT',satellite_identifier_id='421',satellite_identifier='Oceansat-2'),
+42001 = (let,description='SAPHIR All-sky',group_id='43',group='SAPHIR All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='211',subtype='SAPHIR',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='34',sensor='SAPHIR',satellite_instrument_id='941',satellite_instrument='SAPHIR',satellite_identifier_id='440',satellite_identifier='Megha-Tropiques'),
+26001 = (let,description='FY-3A MWHS Radiances',group_id='27',group='MWHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='41',sensor='MWHS',satellite_instrument_id='936',satellite_instrument='MWHS',satellite_identifier_id='520',satellite_identifier='FY-3A'),
+27001 = (let,description='FY-3A MWTS Radiances',group_id='28',group='MWTS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='40',sensor='MWTS',satellite_instrument_id='934',satellite_instrument='MWAS',satellite_identifier_id='520',satellite_identifier='FY-3A'),
+28001 = (let,description='FY-3A MWRI  Radiances All-sky',group_id='29',group='MWRI All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='153',subtype='Micro Wave Radiation Imager (MWRI) FY-3A',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='43',sensor='MWRI',satellite_instrument_id='938',satellite_instrument='MWRI',satellite_identifier_id='520',satellite_identifier='FY-3A'),
+29001 = (let,description='FY-3A IRAS Radiances',group_id='30',group='IRAS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='42',sensor='IRAS',satellite_instrument_id='933',satellite_instrument='IRAS',satellite_identifier_id='520',satellite_identifier='FY-3A'),
+29002 = (let,description='FY-3B IRAS Radiances',group_id='30',group='IRAS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='42',sensor='IRAS',satellite_instrument_id='933',satellite_instrument='IRAS',satellite_identifier_id='521',satellite_identifier='FY-3B'),
+28002 = (let,description='FY-3B MWRI  Radiances All-sky',group_id='29',group='MWRI All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='153',subtype='Micro Wave Radiation Imager (MWRI) FY-3A',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='43',sensor='MWRI',satellite_instrument_id='938',satellite_instrument='MWRI',satellite_identifier_id='521',satellite_identifier='FY-3B'),
+27002 = (let,description='FY-3B MWTS Radiances',group_id='28',group='MWTS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='40',sensor='MWTS',satellite_instrument_id='934',satellite_instrument='MWAS',satellite_identifier_id='521',satellite_identifier='FY-3B'),
+26002 = (let,description='FY-3B MWHS Radiances',group_id='27',group='MWHS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='41',sensor='MWHS',satellite_instrument_id='936',satellite_instrument='MWHS',satellite_identifier_id='521',satellite_identifier='FY-3B'),
+32001 = (let,description='NOAA 2 VTPR1 Radiances',group_id='33',group='VTPR1',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='7',sensor='VTPR1',satellite_instrument_id='990',satellite_instrument='VTPR1',satellite_identifier_id='702',satellite_identifier='NOAA 2'),
+33001 = (let,description='NOAA 2 VTPR2 Radiances',group_id='34',group='VTPR2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='8',sensor='VTPR2',satellite_instrument_id='991',satellite_instrument='VTPR2',satellite_identifier_id='702',satellite_identifier='NOAA 2'),
+32002 = (let,description='NOAA 3 VTPR1 Radiances',group_id='33',group='VTPR1',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='7',sensor='VTPR1',satellite_instrument_id='990',satellite_instrument='VTPR1',satellite_identifier_id='703',satellite_identifier='NOAA 3'),
+33002 = (let,description='NOAA 3 VTPR2 Radiances',group_id='34',group='VTPR2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='8',sensor='VTPR2',satellite_instrument_id='991',satellite_instrument='VTPR2',satellite_identifier_id='703',satellite_identifier='NOAA 3'),
+32003 = (let,description='NOAA 4 VTPR1 Radiances',group_id='33',group='VTPR1',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='7',sensor='VTPR1',satellite_instrument_id='990',satellite_instrument='VTPR1',satellite_identifier_id='704',satellite_identifier='NOAA 4'),
+33003 = (let,description='NOAA 4 VTPR2 Radiances',group_id='34',group='VTPR2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='8',sensor='VTPR2',satellite_instrument_id='991',satellite_instrument='VTPR2',satellite_identifier_id='704',satellite_identifier='NOAA 4'),
+32004 = (let,description='NOAA 5 VTPR1 Radiances',group_id='33',group='VTPR1',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='7',sensor='VTPR1',satellite_instrument_id='990',satellite_instrument='VTPR1',satellite_identifier_id='705',satellite_identifier='NOAA 5'),
+33004 = (let,description='NOAA 5 VTPR2 Radiances',group_id='34',group='VTPR2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='8',sensor='VTPR2',satellite_instrument_id='991',satellite_instrument='VTPR2',satellite_identifier_id='705',satellite_identifier='NOAA 5'),
+2 = (let,description='NOAA 6 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+30002 = (let,description='NOAA 6 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+31002 = (let,description='NOAA 6 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+20 = (let,description='NOAA 6 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+30013 = (let,description='NOAA 6 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+31012 = (let,description='NOAA 6 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='706',satellite_identifier='NOAA 6'),
+3 = (let,description='NOAA 7 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+30003 = (let,description='NOAA 7 MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+31003 = (let,description='NOAA 7 SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+21 = (let,description='NOAA 7 HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+30014 = (let,description='NOAA 7 MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+31013 = (let,description='NOAA 7 SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+1 = (let,description='TIROS-N HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+30001 = (let,description='TIROS-N MSU Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+31001 = (let,description='TIROS-N SSU Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+22 = (let,description='TIROS-N HIRS Radiances',group_id='1',group='HIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='0',sensor='HIRS',satellite_instrument_id='605',satellite_instrument='HIRS/2',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+30015 = (let,description='TIROS-N MSU TOVS1B Radiances',group_id='31',group='MSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='1',sensor='MSU',satellite_instrument_id='623',satellite_instrument='MSU',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+31014 = (let,description='TIROS-N SSU TOVS1B Radiances',group_id='32',group='SSU',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='2',sensor='SSU',satellite_instrument_id='627',satellite_instrument='SSU',satellite_identifier_id='708',satellite_identifier='TIROS-N'),
+7003 = (let,description='GRACE A GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='722',satellite_identifier='GRACE A'),
+7014 = (let,description='GRACE B GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='723',satellite_identifier='GRACE B'),
+7004 = (let,description='COSMIC-1 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='740',satellite_identifier='COSMIC-1'),
+7005 = (let,description='COSMIC-2 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='741',satellite_identifier='COSMIC-2'),
+7006 = (let,description='COSMIC-3 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='742',satellite_identifier='COSMIC-3'),
+7007 = (let,description='COSMIC-4 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='743',satellite_identifier='COSMIC-4'),
+7008 = (let,description='COSMIC-5 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='744',satellite_identifier='COSMIC-5'),
+7009 = (let,description='COSMIC-6 GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='745',satellite_identifier='COSMIC-6'),
+46001 = (let,description='IRIS Nimbus-3 Radiances',group_id='47',group='IRIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='69',sensor='IRIS',satellite_instrument_id='404',satellite_instrument='IRIS',satellite_identifier_id='763',satellite_identifier='NIMBUS 3'),
+46002 = (let,description='IRIS Nimbus-4 Radiances',group_id='47',group='IRIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='54',subtype='TOVS1B',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='69',sensor='IRIS',satellite_instrument_id='404',satellite_instrument='IRIS',satellite_identifier_id='764',satellite_identifier='NIMBUS 4'),
+5070 = (let,description='NIMBUS 4 BUV',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='334',satellite_instrument='BUV',satellite_identifier_id='764',satellite_identifier='NIMBUS 4'),
+5018 = (let,description='NIMBUS 7 SBUV-2  O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='767',satellite_identifier='NIMBUS 7'),
+5022 = (let,description='NIMBUS 7 TOMS  O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='767',satellite_identifier='NIMBUS 7'),
+5042 = (let,description='NIMBUS 7 SBUV-2  MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='624',sensor='SBUV-2',satellite_instrument_id='624',satellite_instrument='SBUV/2',satellite_identifier_id='767',satellite_identifier='NIMBUS 7'),
+5043 = (let,description='NIMBUS 7 TOMS  MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='767',satellite_identifier='NIMBUS 7'),
+5023 = (let,description='EARTH PROBE TOMS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='782',satellite_identifier='EARTH PROBE'),
+5044 = (let,description='EARTH PROBE TOMS MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='431',sensor='TOMS',satellite_instrument_id='431',satellite_instrument='TOMS',satellite_identifier_id='782',satellite_identifier='EARTH PROBE'),
+5020 = (let,description='TERRA MODIS Aerosol',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='207',subtype='MODIS aerosol',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='389',sensor='MODIS',satellite_instrument_id='389',satellite_instrument='MODIS',satellite_identifier_id='783',satellite_identifier='TERRA'),
+35001 = (let,description='TERRA MOPITT layer integrated mass density with averaging kernels and a priori profile',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='229',subtype='Layer integrated mass density with averaging kernels and a priori profile',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='90',sensor='MOPITT',satellite_instrument_id='90',satellite_instrument='MOPITT', [...]
+1008 = (let,description='AQUA AMSUA Radiances',group_id='2',group='AMSUA',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='784',satellite_identifier='AQUA'),
+22001 = (let,description='AQUA AMSRE Radiances',group_id='23',group='AMSRE',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='59',subtype='AMSRE',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='17',sensor='AMSRE',satellite_instrument_id='479',satellite_instrument='AMSR-E',satellite_identifier_id='784',satellite_identifier='AQUA'),
+12001 = (let,description='AQUA AIRS Radiances',group_id='13',group='AIRS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='57',subtype='AIRS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='11',sensor='AIRS',satellite_instrument_id='420',satellite_instrument='AIRS',satellite_identifier_id='784',satellite_identifier='AQUA'),
+15001 = (let,description='AQUA AMSRE Radiances All-sky',group_id='16',group='AMSRE All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='59',subtype='AMSRE',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='17',sensor='AMSRE',satellite_instrument_id='479',satellite_instrument='AMSR-E',satellite_identifier_id='784',satellite_identifier='AQUA'),
+21008 = (let,description='AQUA AMSUA Radiances All-sky',group_id='22',group='AMSUA All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='55',subtype='ATOVS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='3',sensor='AMSUA',satellite_instrument_id='570',satellite_instrument='AMSU-A',satellite_identifier_id='784',satellite_identifier='AQUA'),
+5019 = (let,description='AQUA MODIS Aerosol',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='207',subtype='MODIS aerosol',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='389',sensor='MODIS',satellite_instrument_id='389',satellite_instrument='MODIS',satellite_identifier_id='784',satellite_identifier='AQUA'),
+5004 = (let,description='AURA MLS O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='387',sensor='MLS',satellite_instrument_id='387',satellite_instrument='MLS (EOS-Aura)',satellite_identifier_id='785',satellite_identifier='AURA'),
+5005 = (let,description='AURA OMI O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='206',subtype='Retrieved O3 Layers',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='394',sensor='OMI',satellite_instrument_id='394',satellite_instrument='OMI',satellite_identifier_id='785',satellite_identifier='AURA'),
+35008 = (let,description='AURA OMI layer integrated mass density with averaging kernels',group_id='36',group='RESAT Averaging Kernels',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='209',subtype='Layer integrated mass density with averaging kernels',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='394',sensor='OMI',satellite_instrument_id='394',satellite_instrument='OMI',satellite_identifier_id='785',satellite_identifier [...]
+5028 = (let,description='AURA MLS MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='387',sensor='MLS',satellite_instrument_id='387',satellite_instrument='MLS (EOS-Aura)',satellite_identifier_id='785',satellite_identifier='AURA'),
+5029 = (let,description='AURA OMI MACC O3',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='205',subtype='Retrieved O3 Layers for MACC ozone observation operator',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='394',sensor='OMI',satellite_instrument_id='394',satellite_instrument='OMI',satellite_identifier_id='785',satellite_identifier='AURA'),
+5048 = (let,description='AURA OMI Layer integrated mass density',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='208',subtype='Layer integrated mass density',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='394',sensor='OMI',satellite_instrument_id='394',satellite_instrument='OMI',satellite_identifier_id='785',satellite_identifier='AURA'),
+7011 = (let,description='CNOFS GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='786',satellite_identifier='C/NOFS'),
+5066 = (let,description='CALIPSO attenuated aerosol lidar backscatter',group_id='6',group='RESAT',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='221',subtype='CALIPSO attenuated aerosol lidar backscatter',obstype_id='7',obstype='SATEM',codetype_id='206',codetype='Retrieved Atmospheric Constituents',sensor_id='303',sensor='CALIOP',satellite_instrument_id='303',satellite_instrument='CALIOP',satellite_identifier_id='787',satellite_identifier='CALIPSO'),
+4019 = (let,description='COMS-1 GEOS Radiances ',group_id='5',group='GEOS',bufrtype_id='5',bufrtype='SATOB',subtype_id='89',subtype='GEOS Radiances',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='45',sensor='COMS MI',satellite_instrument_id='2047',satellite_instrument='Missing value',satellite_identifier_id='810',satellite_identifier='COMS-1'),
+7012 = (let,description='SAC-C GPSRO',group_id='8',group='GPSRO',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='250',subtype='GPS Radio Occultation',obstype_id='10',obstype='Limb',codetype_id='250',codetype='GPS Radio Occultation',sensor_id='102',sensor='GPS',satellite_instrument_id='102',satellite_instrument='CHAMP GPS sounder',satellite_identifier_id='820',satellite_identifier='SAC-C'),
+50001 = (let,description='GPM GMI Radiances All-sky',group_id='51',group='GMI All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='224',subtype='GPM',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='71',sensor='GMI',satellite_instrument_id='519',satellite_instrument='GMI',satellite_identifier_id='288',satellite_identifier='GPM'),
+55001 = (let,description='FY-3C MWTS2 Radiances',group_id='56',group='MWTS2',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='72',sensor='MWTS2',satellite_instrument_id='954',satellite_instrument='MWTS2',satellite_identifier_id='522',satellite_identifier='FY-3C'),
+28003 = (let,description='FY-3C MWRI Radiances All-sky',group_id='29',group='MWRI All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='153',subtype='Micro Wave Radiation Imager (MWRI) FY-3A',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='43',sensor='MWRI',satellite_instrument_id='938',satellite_instrument='MWRI',satellite_identifier_id='522',satellite_identifier='FY-3C'),
+57001 = (let,description='FY-3C MWHS2 Radiances All-sky',group_id='58',group='MWHS2 All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='73',sensor='MWHS2',satellite_instrument_id='953',satellite_instrument='MWHS-2 (or WINDSAT for data from Coriolis satellite)',satellite_identifier_id='522',satellite_identifier='FY-3C'),
+29003 = (let,description='FY-3C IRAS Radiances',group_id='30',group='IRAS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='154',subtype='VASS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='42',sensor='IRAS',satellite_instrument_id='933',satellite_instrument='IRAS',satellite_identifier_id='522',satellite_identifier='FY-3C'),
+9008 = (let,description='ISS RapidScat',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='138',subtype='RSCAT',obstype_id='9',obstype='Scatterometer',codetype_id='302',codetype='OSCAT',sensor_id='314',sensor='RSCAT',satellite_instrument_id='314',satellite_instrument='RSCAT',satellite_identifier_id='801',satellite_identifier='ISS'),
+9009 = (let,description='HY-2A HSCAT',group_id='10',group='SCATT',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='138',subtype='RSCAT',obstype_id='9',obstype='Scatterometer',codetype_id='302',codetype='OSCAT',sensor_id='686',sensor='HSCAT',satellite_instrument_id='686',satellite_instrument='HSCAT',satellite_identifier_id='502',satellite_identifier='HY-2a'),
+59001 = (let,description='SMAP',group_id='60',group='SMAP',bufrtype_id='30',bufrtype='SMOS',subtype_id='204',subtype='SMAP',obstype_id='7',obstype='SATEM',codetype_id='401',codetype='SMAP',sensor_id='432',sensor='SMAP',satellite_instrument_id='432',satellite_instrument='SMAP',satellite_identifier_id='789',satellite_identifier='SMAP'),
+13004 = (let,description='DMSP 19 SSMIS Radiances All-sky',group_id='14',group='SSMIS All-sky',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='16',obstype='ALLSKY',codetype_id='215',codetype='SSMI/TRMM',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='287',satellite_identifier='DMSP 19'),
+24004 = (let,description='DMSP 19 SSMIS Radiances',group_id='25',group='SSMIS',bufrtype_id='3',bufrtype='Satellite Sounding',subtype_id='49',subtype='SSMIS',obstype_id='7',obstype='SATEM',codetype_id='210',codetype='Radiances',sensor_id='10',sensor='SSMIS',satellite_instrument_id='908',satellite_instrument='SSMIS',satellite_identifier_id='287',satellite_identifier='DMSP 19'),
+37004 = (let,description='ERS 1 RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='123',subtype='RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='1',satellite_identifier='ERS 1'),
+37005 = (let,description='ERS 2 RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='123',subtype='RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='2',satellite_identifier='ERS 2'),
+8030 = (let,description='METOP-B AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='3',satellite_identifier='METOP-1 (Metop-B)'),
+8027 = (let,description='METOP-A AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='4',satellite_identifier='METOP-2 (Metop-A)'),
+37006 = (let,description='CRYOSAT 2 RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='217',subtype='CRYOSAT-2 RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='47',satellite_identifier='CryoSat-2'),
+8002 = (let,description='METEOSAT 3 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='50',satellite_identifier='METEOSAT 3'),
+8003 = (let,description='METEOSAT 4 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='51',satellite_identifier='METEOSAT 4'),
+8004 = (let,description='METEOSAT 5 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='52',satellite_identifier='METEOSAT 5'),
+8005 = (let,description='METEOSAT 6 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='53',satellite_identifier='METEOSAT 6'),
+8006 = (let,description='METEOSAT 7 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='54',satellite_identifier='METEOSAT 7'),
+8007 = (let,description='METEOSAT 8 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='55',satellite_identifier='METEOSAT 8'),
+8008 = (let,description='METEOSAT 9 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='56',satellite_identifier='METEOSAT 9'),
+8031 = (let,description='METEOSAT 10 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='57',satellite_identifier='METEOSAT 10'),
+8034 = (let,description='Meteosat-1 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='82',subtype='SATOB AMV',obstype_id='3',obstype='SATOB',codetype_id='88',codetype='SATOB AMV',satellite_identifier_id='58',satellite_identifier='METEOSAT 1'),
+8001 = (let,description='METEOSAT 2 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='59',satellite_identifier='METEOSAT 2'),
+37001 = (let,description='ENVISAT RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='213',subtype='ENVISAT RADAR ALTIMETER ',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='60',satellite_identifier='ENVISAT'),
+8056 = (let,description='METEOSAT 11 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='70',satellite_identifier='METEOSAT 11'),
+8043 = (let,description='GMS-3 SATOB-AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='82',subtype='SATOB AMV',obstype_id='3',obstype='SATOB',codetype_id='88',codetype='SATOB AMV',satellite_identifier_id='150',satellite_identifier='GMS 3'),
+8032 = (let,description='GMS-3 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='150',satellite_identifier='GMS 3'),
+8033 = (let,description='GMS-4 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='151',satellite_identifier='GMS 4'),
+8009 = (let,description='GMS 5 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='152',satellite_identifier='GMS 5'),
+8010 = (let,description='MTSAT-1R AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='171',satellite_identifier='MTSAT-1R'),
+8026 = (let,description='MTSAT-2 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='172',satellite_identifier='MTSAT-2'),
+8053 = (let,description='Himawari 8 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='173',satellite_identifier='Himawari-8'),
+8036 = (let,description='NOAA 9 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='201',satellite_identifier='NOAA 9'),
+8037 = (let,description='NOAA 10 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='202',satellite_identifier='NOAA 10'),
+8038 = (let,description='NOAA 11 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='203',satellite_identifier='NOAA 11'),
+8039 = (let,description='NOAA 12 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='204',satellite_identifier='NOAA 12'),
+8040 = (let,description='NOAA 14 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='205',satellite_identifier='NOAA 14'),
+8015 = (let,description='NOAA 15 AVHRR IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='206',satellite_identifier='NOAA 15'),
+8016 = (let,description='NOAA 16 AVHRR IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='207',satellite_identifier='NOAA 16'),
+8017 = (let,description='NOAA 17 AVHRR IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='208',satellite_identifier='NOAA 17'),
+8018 = (let,description='NOAA 18 AVHRR IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='209',satellite_identifier='NOAA 18'),
+8019 = (let,description='NOAA 19 AVHRR IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='223',satellite_identifier='NOAA 19'),
+8050 = (let,description='NPP AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='224',satellite_identifier='NPP'),
+8051 = (let,description='GOES 6 AMV subtype 82',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='82',subtype='SATOB AMV',obstype_id='3',obstype='SATOB',codetype_id='88',codetype='SATOB AMV',satellite_identifier_id='250',satellite_identifier='GOES 6'),
+8052 = (let,description='GOES 6 AMV subtype 83',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='83',subtype='GOES-6 AMV subtype 83',obstype_id='3',obstype='SATOB',codetype_id='88',codetype='SATOB AMV',satellite_identifier_id='250',satellite_identifier='GOES 6'),
+8042 = (let,description='GOES 8 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='252',satellite_identifier='GOES 8'),
+8011 = (let,description='GOES 9 WV AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='253',satellite_identifier='GOES 9'),
+8012 = (let,description='GOES 10 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='254',satellite_identifier='GOES 10'),
+8013 = (let,description='GOES 11 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='255',satellite_identifier='GOES 11'),
+8014 = (let,description='GOES 12 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='256',satellite_identifier='GOES 12'),
+8025 = (let,description='GOES 13 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='257',satellite_identifier='GOES 13'),
+8028 = (let,description='GOES 14 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='258',satellite_identifier='GOES 14'),
+8029 = (let,description='GOES 15 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='259',satellite_identifier='GOES 15'),
+37002 = (let,description='JASON 1 RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='214',subtype='JASON RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='260',satellite_identifier='JASON 1'),
+37003 = (let,description='JASON 2 RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='216',subtype='JASON-2 RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='261',satellite_identifier='JASON 2'),
+8046 = (let,description='KALPANA-1 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='410',satellite_identifier='KALPANA-1'),
+37007 = (let,description='SARAL RALT WAVE',group_id='38',group='WAVE integrated Parameters',bufrtype_id='12',bufrtype='ERS-SSMI-SCAT',subtype_id='218',subtype='SARAL RADAR ALTIMETER',obstype_id='12',obstype='WAVE',codetype_id='123',codetype='RALT',satellite_identifier_id='441',satellite_identifier='SARAL'),
+8049 = (let,description='INSAT-3D AMVs',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='471',satellite_identifier='INSAT 3D'),
+8022 = (let,description='FY-2C IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='513',satellite_identifier='FY-2C'),
+8023 = (let,description='FY-2D IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='514',satellite_identifier='FY-2D'),
+8024 = (let,description='FY-2E IR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='515',satellite_identifier='FY-2E'),
+8035 = (let,description='NOAA 7 AVHRR AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='707',satellite_identifier='NOAA 7'),
+8020 = (let,description='TERRA MODIS AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='783',satellite_identifier='TERRA'),
+8021 = (let,description='AQUA MODIS AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='784',satellite_identifier='AQUA'),
+8045 = (let,description='COMS-1 AMV ',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='810',satellite_identifier='COMS-1'),
+8048 = (let,description='Dual-Metop AMVs',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='852',satellite_identifier='Combination of Metop-1 to Metop-3'),
+8047 = (let,description='Leo-Geo AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='854',satellite_identifier='Non specific mixture of geostationary and low earth orbiting satellites'),
+8044 = (let,description='GMS-1 AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='153',satellite_identifier='GMS'),
+8041 = (let,description='GMS-2 SATOB-AMV',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='82',subtype='SATOB AMV',obstype_id='3',obstype='SATOB',codetype_id='88',codetype='SATOB AMV',satellite_identifier_id='154',satellite_identifier='GMS 2'),
+8054 = (let,description='FY-2G AMVs',group_id='9',group='SATOB',bufrtype_id='5',bufrtype='SATOB',subtype_id='87',subtype='AMV',obstype_id='3',obstype='SATOB',codetype_id='90',codetype='AMV',satellite_identifier_id='517',satellite_identifier='FY-2G'),
+39001 = (let,description='1h rain gauge accumulation',group_id='40',group='RAINGG',bufrtype_id='0',bufrtype='Land Surface',subtype_id='126',subtype='Rain Gauge',accumulation_length='3600'),
+39002 = (let,description='3h rain gauge accumulation',group_id='40',group='RAINGG',bufrtype_id='0',bufrtype='Land Surface',subtype_id='126',subtype='Rain Gauge',accumulation_length='10800'),
+39003 = (let,description='6h rain gauge accumulation',group_id='40',group='RAINGG',bufrtype_id='0',bufrtype='Land Surface',subtype_id='126',subtype='Rain Gauge',accumulation_length='21600'),
+39004 = (let,description='12h rain gauge accumulation',group_id='40',group='RAINGG',bufrtype_id='0',bufrtype='Land Surface',subtype_id='126',subtype='Rain Gauge',accumulation_length='43200'),
+39005 = (let,description='24h rain gauge accumulation',group_id='40',group='RAINGG',bufrtype_id='0',bufrtype='Land Surface',subtype_id='126',subtype='Rain Gauge',accumulation_length='86400'))
+let, report_type_ids=16001/16002/16003/16004/16065/16025/16005/16006/16007/16008/16009/16010/16011/16012/16084/16083/16074/16013/16014/16015/16016/16017/16018/16019/16020/16021/16022/16023/16024/16069/16068/16045/16046/16032/16037/16038/16039/16040/16041/16042/16043/16044/16026/16027/16028/16029/16030/16082/16066/16067/25001/16031/9001/9002/5001/9003/35002/5025/5050/9007/25/1009/3004/7013/11002/21009/5057/35011/44004/5061/35012/35013/5062/5072/15/1007/3002/5013/7001/9005/11001/21007/5024 [...]
+let, group_ids=1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/40/42/43/44/45/47/50/51/56/57/58/59/60
+let, group=(let,1='HIRS',2='AMSUA',3='AMSUB',4='MHS',5='GEOS',6='RESAT',7='MERIS',8='GPSRO',9='SATOB',10='SCATT',11='SSMI All-sky',12='IASI',13='AIRS',14='SSMIS All-sky',15='TMI All-sky',16='AMSRE All-sky',17='CONV',19='SMOS',20='WINDSAT All-sky',21='SSMI',22='AMSUA All-sky',23='AMSRE',24='TMI',25='SSMIS',26='GBRAD',27='MWHS',28='MWTS',29='MWRI All-sky',30='IRAS',31='MSU',32='SSU',33='VTPR1',34='VTPR2',35='ATMS',36='RESAT Averaging Kernels',37='CRIS',38='WAVE integrated Parameters',40='R [...]
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/split_and_archive.ecml b/odb_api/src/odb_api/ecml_verbs/tests/split_and_archive.ecml
new file mode 100644
index 0000000..c2912f9
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/split_and_archive.ecml
@@ -0,0 +1,42 @@
+# Function split_and_archive:
+#
+# - prepares ODB data for archiving by splitting it into several files 
+#   with data homogenious with regards to analysis date, time and report type,
+#   suitable for indexing on MARS Server / ODB Server.
+#
+# - archives the files,
+#
+# - verifies the archiving was successfull by retrieving archived files
+#   and comparing them to the originals
+
+# Function split_and_archive accepts 3 parameters:
+function,of = filter         # SQL to be applied to input files.
+            / source         # List of files to be procesed.
+            / output_schema, # Template of output files with ODB columns to split 
+                             # the data by in curly braces,
+                             # e.g. "/odb_root/{andate}/{antime}/{reportype}.odb"
+
+         split_and_archive = (
+
+            println, values = "******** About to archive" / ($,_ = source) / "*******" 
+
+            let, files = (split,
+                            source = ($,_ = source),
+                            target = ($,_ = output_schema),
+                            filter = ($,_ = filter))
+
+            println,values = "******** Files produced by split:" / ($,_ = files)
+
+            let, mars_handles = (archive,
+                                    database = localhost,
+                                    source = ($,_ = files))
+
+            # Retrieve each file using list of "mars://retrieve,..." 
+            # URLs produced by archive, and compare them to 
+            # original files as produced by split
+
+            compare, left = ($,_ = files), right = ($,_ = mars_handles)
+
+            $,_ = mars_handles
+         )
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/start_server.ecml b/odb_api/src/odb_api/ecml_verbs/tests/start_server.ecml
new file mode 100644
index 0000000..a0d3fd8
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/start_server.ecml
@@ -0,0 +1,2 @@
+# depends on definitions from odb_server_management.ecml
+start_server
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/stop_server.ecml b/odb_api/src/odb_api/ecml_verbs/tests/stop_server.ecml
new file mode 100644
index 0000000..184667c
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/stop_server.ecml
@@ -0,0 +1,2 @@
+# depends on definitions from odb_server_management.ecml
+stop_server
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_chunk.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_chunk.ecml
new file mode 100644
index 0000000..cf0cc79
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_chunk.ecml
@@ -0,0 +1,17 @@
+test, label = Check verb chunk works,
+do = (
+    let, input = "2000010106.odb"
+    let, chunks = (chunk, source = (value, of = input))
+
+    #println, values = (value, of = chunks)
+
+    compare, left = (sql, source = (value, of = chunks),
+                          filter = "select count(*)",
+                          target = (temporary_file) ),
+             right = (sql, source = (value, of = input),
+                           filter = "select count(*)",
+                           target = (temporary_file) )
+
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_chunk2.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_chunk2.ecml
new file mode 100644
index 0000000..b9c8480
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_chunk2.ecml
@@ -0,0 +1,26 @@
+test, label = Check verb chunk works,
+do = (
+    let, input = "2000010106.odb"
+    let, chunks = (chunk, source = (input))
+
+    let, left  = (sql, filter = "select varno,sum(varno_count) as varno_count",
+                       source = (for, c = (chunks),
+                                      do = (sql,
+                                              source = (c),
+                                              filter = "select varno,count(*) as varno_count",
+                                              target = (temporary_file))),
+                       target = (temporary_file))
+
+    println, values = " **** left: " / (left)
+
+    let, right = (sql, source = (input),
+                       filter = "select varno, count(*) as varno_count",
+                       target = (temporary_file) )
+
+    println, values = " *** right: " / (right)
+
+    compare, left = (left), right = (right)
+
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.cc b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.cc
new file mode 100644
index 0000000..4a4008b
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file test_client_lib.cc
+///
+/// This file contains example showing how to retrieve 
+/// some ODB data from ODB Server using traditional ODB API iterator API.
+///
+///  Note: MarsClient lib must be linked to executable.
+///
+/// @author Piotr Kuchta, ECMWF, November 2015
+
+#include <string>
+#include <iostream>
+#include <vector>
+
+#include "odb_api/odb_api.h"
+
+void test_retrieve_with_iterator_api()
+{
+    const std::string url ("local://retrieve,class=OD,"
+                                           "date=20150218,"
+                                           "time=1200,"
+                                           "type=OFB,"
+                                           "obsgroup=conv,"
+                                           "reportype=16058,"
+                                           "stream=oper,"
+                                           "expver=qu12,"
+                                           "odbPathNameSchema = '{date}/{time}/{reportype}.odb',",
+                                           "database=localhost");
+    odb::Select select("select varno,min(varno),max(varno);", url);
+
+    unsigned long long rowNumber (0);
+    for (odb::Select::iterator it (select.begin()),
+                               end (select.end());
+         it != end;
+         ++it, ++rowNumber)
+    {
+        double r0 = (*it)[0], r1 = (*it)[1], r2 = (*it)[2];
+
+        std::cout << r0 << ", " << r1 << ", " << r2 << std::endl;
+    }
+
+    eckit::Log::info() << "test_retrieve_with_iterator_api: retrieved " 
+                << rowNumber  << " rows "
+                << "from '" << url << "'" << std::endl;
+
+    ASSERT(rowNumber == 1000);
+}
+
+int main(int argc, char** argv) 
+{ 
+    try {
+        test_retrieve_with_iterator_api(); 
+    } 
+    catch (std::exception e) 
+    {
+        eckit::Log::info() << "test_retrieve_with_iterator_api: exception caught: " << e.what() << std::endl;
+        return 1;
+    }
+    return 0;
+}
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.ecml
new file mode 100644
index 0000000..777cfb9
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_cpp.ecml
@@ -0,0 +1,7 @@
+test, label = Execute test_client_lib_cpp,
+do = (
+    sh, _ = (join_strings, separator="/", _ = (getenv, values = MARS_INSTALLATION_DIRECTORY) /bin/test_client_lib_cpp )
+
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.ecml
new file mode 100644
index 0000000..9570eeb
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.ecml
@@ -0,0 +1,6 @@
+test, label = Execute test_client_lib_fortran,
+do = (
+    sh, _ = (getenv, values = MARS_INSTALLATION_DIRECTORY)/"/bin/test_client_lib_fortran"
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.f90 b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.f90
new file mode 100644
index 0000000..653f9ed
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran.f90
@@ -0,0 +1,91 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+program test_client_lib_fortran
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  implicit none
+
+  integer, parameter :: max_varlen = 128
+  integer(kind=4)    :: ncolumns 
+
+  write(0,*) "Calling odb_start..."
+  call odb_start()
+
+  call example_fortran_api_retrieve()
+
+contains
+
+subroutine example_fortran_api_retrieve
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i, ci
+
+ character(kind=C_CHAR, len=2048)              :: sql='select * from &
+ & "mars://retrieve,class=OD,date=20150218,time=1200,type=OFB,&
+ &obsgroup=conv,reportype=16058,stream=oper,expver=qu12,database=localhost";'//achar(0)
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=65, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=8)                              :: tmp_str
+
+ write(0,*) 'example_fortran_api_retrieve: ', sql
+
+ odb_handle = odb_select_new(config, cerr)
+ if (cerr /=0) STOP 1
+
+ odb_it = odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr /=0) STOP 1
+ 
+ cerr = odb_select_get_no_of_columns(odb_it, ncolumns)
+ if (cerr /=0) STOP 1
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve: number of columns: ', ncolumns
+ if (c_ncolumns /= ncolumns) STOP 2
+ 
+ do ci=0, ncolumns - 1
+     cerr = odb_select_get_column_name(odb_it, ci, ptr_colname, size_name)
+     if (cerr /=0) STOP 1
+     call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+     do i=1, size_name 
+        colname(i:i)  = f_ptr_colname(i)
+     end do
+     write(0,*) ' : ', colname(1:i)
+ end do
+
+ allocate(one_row(c_ncolumns))
+ cerr=0
+ i = 0
+ do 
+   cerr = odb_select_get_next_row(odb_it, c_ncolumns, one_row, newdataset)
+   if ( cerr /= 0) exit
+   !tmp_str = transfer(one_row(4), tmp_str)
+   !write(0,*) i, ":",  one_row(1), one_row(2), one_row(3), tmp_str
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve: number of rows: ', i
+ if (i /= 9380) STOP 22
+
+ cerr = odb_select_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+
+end subroutine example_fortran_api_retrieve
+end program test_client_lib_fortran
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_local.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_local.ecml
new file mode 100644
index 0000000..6f56629
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_local.ecml
@@ -0,0 +1,6 @@
+test, label = Execute test_client_lib_fortran_local,
+do = (
+    sh, _ = (getenv, values = MARS_INSTALLATION_DIRECTORY)/"/bin/test_client_lib_fortran_local"
+    sequence, values = OK
+),
+expect = OK
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_server_side_ecml.f90 b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_server_side_ecml.f90
new file mode 100644
index 0000000..0e65052
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_client_lib_fortran_server_side_ecml.f90
@@ -0,0 +1,95 @@
+! (C) Copyright 1996-2012 ECMWF.
+! 
+! This software is licensed under the terms of the Apache Licence Version 2.0
+! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+! In applying this licence, ECMWF does not waive the privileges and immunities 
+! granted to it by virtue of its status as an intergovernmental organisation nor
+! does it submit to any jurisdiction.
+!
+
+program test_client_lib_fortran_server_side_ecml
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  implicit none
+
+  integer, parameter :: max_varlen = 128
+  integer(kind=4)    :: ncolumns 
+
+  write(0,*) "Calling odb_start..."
+  call odb_start()
+
+  call example_fortran_api_retrieve_server_side()
+
+contains
+
+subroutine example_fortran_api_retrieve_server_side
+ implicit none
+ type(C_PTR)                                   :: odb_handle, odb_it
+ integer(kind=C_INT)                           :: cerr
+ character(kind=C_CHAR, len=64)                :: config = C_NULL_CHAR
+ type(C_PTR)                                   :: ptr_colname
+ type(C_PTR)                                   :: ptr_bitfield_names
+ type(C_PTR)                                   :: ptr_bitfield_sizes
+ character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_names
+ character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_bitfield_sizes
+ character(len=max_varlen)                     :: colname
+ character(len=max_varlen)                     :: bitfield_names
+ character(len=max_varlen)                     :: bitfield_sizes
+ integer(kind=4)                               :: i, ci, row_count
+
+ character(kind=C_CHAR, len=2048)              :: sql='select * from &
+ & "mars://retrieve,class=OD,date=20150218,time=1200,type=OFB,&
+ & obsgroup=conv,reportype=16058,stream=oper,expver=qu12,&
+ & server_side=(function,of=source,_=&
+ &  (sql,filter=\"select count(*)\",target=(temporary_file))),&
+ & database=localhost";'//achar(0)
+ integer(kind=C_INT)                           :: itype, newdataset, c_ncolumns=1, size_name 
+ integer(kind=C_INT)                           :: bitfield_names_size, bitfield_sizes_size
+ real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+ character(len=8)                              :: tmp_str
+
+ write(0,*) 'example_fortran_api_retrieve: ', sql
+
+ odb_handle = odb_select_new(config, cerr)
+ if (cerr /=0) STOP 1
+
+ odb_it = odb_select_iterator_new(odb_handle, sql, cerr);
+ if (cerr /=0) STOP 1
+ 
+ cerr = odb_select_get_no_of_columns(odb_it, ncolumns)
+ if (cerr /=0) STOP 1
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve: number of columns: ', ncolumns
+ if (c_ncolumns /= ncolumns) STOP 2
+ 
+ do ci=0, ncolumns - 1
+     cerr = odb_select_get_column_name(odb_it, ci, ptr_colname, size_name)
+     if (cerr /=0) STOP 1
+     call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+     do i=1, size_name 
+        colname(i:i)  = f_ptr_colname(i)
+     end do
+     write(0,*) ' : ', colname(1:i)
+ end do
+
+ allocate(one_row(c_ncolumns))
+ cerr=0
+ i = 0
+ do 
+   cerr = odb_select_get_next_row(odb_it, c_ncolumns, one_row, newdataset)
+   if ( cerr /= 0) exit
+   row_count = one_row(1)
+   i = i + 1
+ enddo
+ deallocate(one_row)
+
+ if (i /= 1) STOP 3
+
+ write(0,*) '-=-=-=-=-= example_fortran_api_retrieve: number of rows calculated on the server: ', row_count
+ if (row_count /= 9380) STOP 33
+
+ cerr = odb_select_iterator_delete(odb_it)
+ cerr = odb_read_delete(odb_handle)
+
+end subroutine example_fortran_api_retrieve_server_side
+end program test_client_lib_fortran_server_side_ecml
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_cope_archiving.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_cope_archiving.ecml
new file mode 100644
index 0000000..b516d25
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_cope_archiving.ecml
@@ -0,0 +1,16 @@
+# ec:/dipl/cope/demo_sample/odb2/{airs|amsua|*}.odb
+
+test, label = "Check archiving and retrieving from server works (COPE)",
+do = (
+    for, obsgroup = airs/amsua/amsua_allsky/atms/conv/cris/gbrad/geos/gmi/gpsro/hirs/iasi/ims/iras/mhs/mhs_allsky/mwhs/mwhs2/mwts2/ralt/resat/satob/scatt/ssmis/tmi/windsat,
+         do = ( 
+                split,
+                    source = (join_strings, _ = "/tmp/cope_demo_sample/" / (obsgroup) /  ".odb"),
+                    filter = "select *",
+                    #output_schema = (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"odb_root/{andate}/{antime}/{reportype}.odb"),
+                    target = "/tmp/odb_root/{andate}/{antime}/{groupid}/{reportype}.odb"
+         )
+    println, values = OK
+
+), expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_create_partitions.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_create_partitions.ecml
new file mode 100644
index 0000000..3522b08
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_create_partitions.ecml
@@ -0,0 +1,35 @@
+
+let, files = (import_text, target = 'test_create_partitions_1.input.odb', text = 
+'seqno:INTEGER,a:INTEGER
+1,1
+1,1
+1,1
+2,2
+2,2
+2,2
+') / (import_text, target = 'test_create_partitions_2.input.odb', text = 
+'seqno:INTEGER,a:INTEGER
+3,3,
+3,3,
+3,3,
+4,4,
+4,4,
+4,4,
+')
+
+function, of = files / number_of_partitions, partition = 
+(
+    println, values = Create indices for files / ($, _ = files)
+    create_index, files = ($, _ = files)
+    println, values = Create partitions for files / ($, _ = files)
+    let, partitions = (create_partitions, n = ($, _ = number_of_partitions), 
+                                          write_files = true, 
+                                          files = ($, _ = files))
+    compare, left = ($, _ = files), 
+             right = ($, _ = partitions) 
+)
+
+
+
+
+partition, files = '2000010106.odb', number_of_partitions = 5
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_ec_archiving.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_ec_archiving.ecml
new file mode 100644
index 0000000..e535693
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_ec_archiving.ecml
@@ -0,0 +1,61 @@
+function, of = _, path = (join_strings, separator = "/", _ = (_))
+
+# Function split_and_archive:
+#
+# - prepares ODB data for archiving by splitting it into several files 
+#   with data homogenious with regards to analysis date, time and report type,
+#   suitable for indexing on MARS Server / ODB Server.
+#
+# - archives the files,
+#
+# - verifies the archiving was successfull by retrieving archived files
+#   and comparing them to the originals
+
+# Function split_and_archive accepts 3 parameters:
+function,of = filter         # SQL to be applied to input files.
+            / source         # List of files to be procesed.
+            / output_schema, # Template of output files with ODB columns to split 
+                             # the data by in curly braces,
+                             # e.g. "/odb_root/{andate}/{antime}/{reportype}.odb"
+
+         split_and_archive = (
+
+            println, values = "******** About to archive" / ($,_ = source) / "*******" 
+
+            let, files = (split,
+                            source = ($,_ = source),
+                            target = ($,_ = output_schema),
+                            filter = ($,_ = filter))
+
+            println,values = "******** Files produced by split:" / ($,_ = files)
+
+            let, mars_handles = (archive,
+                                    database = local,
+                                    odbpathnameschema = "{date}/{time}/{reportype}.odb",
+                                    odbserverroots = "~/data/root",
+                                    source = ($,_ = files))
+
+            # Retrieve each file using list of "local://retrieve,..." 
+            # URLs produced by archive, and compare them to 
+            # original files as produced by split
+
+            compare, left = ($,_ = files), right = ($,_ = mars_handles)
+
+            $,_ = mars_handles
+         )
+
+let, sql_filter = "select *"
+
+test, label = "Check archiving and retrieving from server works (ECMWF)",
+do = (
+    system, values = "rm -rf " / (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"odb_root/*")
+
+    split_and_archive,
+        filter = ($,_ = sql_filter),
+        source = (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"conv_mfb_20151108_12.odb"),
+        output_schema = (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"odb_root/{andate}/{antime}/{reportype}.odb")
+
+    println, values = OK
+
+), expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_embedded_ecml_in_from_clause.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_embedded_ecml_in_from_clause.ecml
new file mode 100644
index 0000000..a7d4260
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_embedded_ecml_in_from_clause.ecml
@@ -0,0 +1,9 @@
+# This stuff is experimental.
+# The test is a bit silly, as embedded ECML in FROM clause of a SQL embedded in ECML makes little sense
+# (the SQL verb requires SOURCE, which should be used as implicit FROM)
+# but it's OK as a test for now, at least we know this can be parsed.
+
+sql,
+filter = "select count(*) from { println, values = \"file://2000010106.odb\" }",
+source = 2000010106.odb,
+target = 2000010106_count.odb
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_local_stage.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_local_stage.ecml
new file mode 100644
index 0000000..4a818c9
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_local_stage.ecml
@@ -0,0 +1,76 @@
+test, label = check STAGE works,
+
+do = (
+    run, source = "test_config_local.ecml"
+
+    let, 
+        number_of_partitions = 3,
+        #rts = 16030
+        rts = 16030/16002/16029/16045/16065/16009
+    let,
+        data_description = (let,
+                                class = OD, 
+                                date = 20151108, 
+                                time = 1200, 
+                                type = MFB, 
+                                obsgroup = conv, 
+                                groupid = 17, # TODO: we need to translate obsgroup to groupid in local_stage
+                                reportype = ($,_=rts), 
+                                stream = oper, 
+                                expver = qu12,
+                                database = localhost)
+    
+    # STAGE the data described by data_description so given number_of_partitions is ready
+    # for subsequent parallel RETRIEVE requests can fetch each of the partitions.
+
+    # TODO (?): The data saved by STAGE in TARGET file should contain detailed information on
+    # the partitions (rows and reports numbers, etc.) 
+
+    apply, function  = ($,_ = local_stage),
+        args         = (update, _        = ($, _ = data_description),
+                                n_parts  = ($,_ = number_of_partitions),
+                                target   = 20150218_0_16001_parts_info.odb)
+
+    #throw,what=STOP
+
+    # retrieve_part is a helper function which retrieves partition number n
+    # using the data_description and number_of_partitions defined earlier
+
+    function, of = n, capture = data_description / number_of_partitions,
+        retrieve_part = (apply, function = ($,_ = local_retrieve),
+                            args         = (update, _           = ($, _ = data_description),
+                                                    n_parts     = ($,_ = number_of_partitions),
+                                                    part_number = ($,_ = n),
+                                                    target      = (temporary_file)))
+
+    println, values = OK
+
+    # RETRIEVE all data described by data_description to a temporary file,
+    # set variable all to path of that file
+
+    let, all = (apply, 
+                   function = ($, _ = local_retrieve),
+                   args     = (update, _      = ($, _ = data_description),
+                                       target = (temporary_file)))
+
+    let, parts = (
+        for, i = (range, from = 0, below = ($, _ = number_of_partitions)),
+             do = (
+                 println, values = " **** Retrieving part " / ($,_=i)
+                 retrieve_part, n = ($,_=i)
+             )
+    )
+
+    println,values = "**** Compare concatenation of partitions: " / ($,_ = parts)
+    println,values = "**** to the whole dataset: " / ($,_ = all)
+
+    compare, 
+        left = ($,_ = parts), 
+        right = ($,_ = all)
+
+    #system, values = "rm -rf " / ($,_=parts) / ($,_=all) 
+
+    println, values = OK
+),
+expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_mo_archiving.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_mo_archiving.ecml
new file mode 100644
index 0000000..dfb84e9
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_mo_archiving.ecml
@@ -0,0 +1,86 @@
+function, of = _, path = (join_strings, separator = "/", _ = (_))
+
+# Function split_and_archive:
+#
+# - prepares ODB data for archiving by splitting it into several files 
+#   with data homogenious with regards to analysis date, time and report type,
+#   suitable for indexing on MARS Server / ODB Server.
+#
+# - archives the files,
+#
+# - verifies the archiving was successfull by retrieving archived files
+#   and comparing them to the originals
+
+# Function split_and_archive accepts 3 parameters:
+function,of = filter         # SQL to be applied to input files.
+            / source         # List of files to be procesed.
+            / output_schema, # Template of output files with ODB columns to split 
+                             # the data by in curly braces,
+                             # e.g. "/odb_root/{andate}/{antime}/{reportype}.odb"
+
+         split_and_archive = (
+
+            println, values = "******** About to archive" / ($,_ = source) / "*******" 
+
+            let, files = (split,
+                            source = ($,_ = source),
+                            target = ($,_ = output_schema),
+                            filter = ($,_ = filter))
+
+            println,values = "******** Files produced by split:" / ($,_ = files)
+
+            let, mars_handles = (archive,
+                                    database = local,
+                                    odbpathnameschema = "{date}/{time}/{reportype}.odb",
+                                    odbserverroots = "~/data/root",
+                                    source = ($,_ = files))
+
+            # Retrieve each file using list of "local://retrieve,..." 
+            # URLs produced by archive, and compare them to 
+            # original files as produced by split
+
+            compare, left = ($,_ = files), right = ($,_ = mars_handles)
+
+            $,_ = mars_handles
+         )
+
+
+let, sql_filter = "
+ select 1 as class,    -- od, see /usr/local/apps/odb_api/codes/class.table
+        1025 as stream, -- oper, see /usr/local/apps/odb_api/codes/stream.table
+        263 as type,    -- ofb, currently hardcoded! 
+        17 as groupid,  -- conv, see /usr/local/apps/odb_api/codes/group.txt
+        reporttype at hdr as reportype,
+        ops_obsgroup at hdr, ops_subtype at hdr, fg_depar at body, an_depar at body, 
+        corvalue at body, datum_status at body, datum_event1 at body, derived_value at body,
+        obsvalue at body, obs_error at errstat, final_obs_error at errstat, fg_error at errstat,
+        pges_buddy at errstat, pges_final at errstat, vertco_reference_1 at body,
+        vertco_reference_2 at body, vertco_type at body, date at hdr, time at hdr, 
+        lat at hdr, lon at hdr, ops_datum_flags at body, ops_obstype at hdr, orography at modsurf,
+        lsm at modsurf, pstar at modsurf, statid at hdr, stalt at hdr, ident at hdr, 
+        report_pge at hdr, report_status at hdr, report_event1 at hdr, hawson_corr at conv, 
+        codetype at hdr, used_in_runid at hdr,
+        -- reporttype at hdr,
+        andate at desc as andate, antime at desc, expver at desc, model at desc, 
+        back_frct_length at desc, ops_report_flags at hdr, solar_zenith at hdr, varno at body,
+        receipt_date at hdr, receipt_time at hdr, station_type at conv, 
+        pressure_sensor_alt at conv, buoy_identifier at conv, ob_practice at conv,
+        sst_measurement_method at conv, road_site_identifier at conv,
+        wmo_block_number at conv, wmo_region_number at conv, wmo_station_number at conv,
+        position_accuracy_flag at conv, buoy_speed at conv, buoy_direction at conv,
+        hours_since_last_position at conv, bogus_identity at conv, bgvalue at body"
+
+
+test, label = "Check archiving and retrieving from server works (Met Office)",
+do = (
+    system, values = "rm -rf " / (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"odb_root/*")
+
+    split_and_archive,
+        filter = ($,_ = sql_filter),
+        source = (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"20150218_glu_surface_odb2"),
+        output_schema = (path, _ = (getenv, values = TEST_DATA_DIRECTORY)/"odb_root/{andate}/{antime}/{reportype}.odb")
+
+    println, values = OK
+
+), expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_multithreaded_sql.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_multithreaded_sql.ecml
new file mode 100644
index 0000000..c3cd0f2
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_multithreaded_sql.ecml
@@ -0,0 +1,15 @@
+test, label = check SQL is thread safe,
+do = ( 
+
+  let, results = 
+    (for, task = (let, q = "select lat", target = lat.odb)
+                / (let, q = "select lon", target = lon.odb)
+                / (let, q = "select obsvalue", target = obsvalue.odb),
+          do = (sql,
+                   filter = ($, _ = task/q),
+                   target = ($, _ = task/target),
+                   source = 2000010106.odb))
+
+    results
+),
+expect = lat.odb / lon.odb / obsvalue.odb
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_odb_governance.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_odb_governance.ecml
new file mode 100644
index 0000000..7c21a02
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_odb_governance.ecml
@@ -0,0 +1,59 @@
+test, label = Check report_type definitions are in the environment set by module odb,
+do = (
+    for, rt = 16001/16002/16003,
+         do = (println,values = ($,_=report_type/(rt)/description))
+),
+expect = Automatic Land SYNOP 
+       / Manual Land SYNOP 
+       / Abbreviated SYNOP
+
+
+test, label = Check list of report types defined in ODB Governance is available in variable report_type_ids,
+do = (first, of = (report_type_ids)),
+expect = 16001
+
+
+test, label = Check group definitions are in the environment set by module odb,
+do = (
+    for, g = (range, from = 1, below = 61),
+         do = (try, do = (println,values = (g) / ($,_ = group/(g))),
+                    catch = (println,values = (g) / NOT AVAILABLE))
+),
+expect = 
+1 / HIRS / 
+2 / AMSUA / 
+3 / AMSUB / 
+4 / MHS / 
+5 / GEOS / 
+6 / RESAT / 
+7 / MERIS / 
+8 / GPSRO / 
+9 / SATOB / 
+10 / SCATT / 11 / SSMI All-sky / 
+12 / IASI / 
+13 / AIRS / 
+14 / SSMIS All-sky / 
+15 / TMI All-sky / 
+16 / AMSRE All-sky / 
+17 / CONV / 
+18 / NOT AVAILABLE / 
+19 / SMOS / 
+20 / WINDSAT All-sky / 
+21 / SSMI / 
+22 / AMSUA All-sky / 
+23 / AMSRE / 
+24 / TMI / 
+25 / SSMIS / 
+26 / GBRAD / 
+27 / MWHS / 
+28 / MWTS / 
+29 / MWRI All-sky / 
+30 / IRAS / 31 / MSU / 32 / SSU / 33 / VTPR1 / 34 / VTPR2 / 35 / ATMS / 36 / RESAT Averaging Kernels / 37 / CRIS / 38 / WAVE integrated Parameters / 39 / NOT AVAILABLE / 40 / RAINGG / 41 / NOT AVAILABLE / 42 / AMSR-2 All-sky / 43 / SAPHIR All-sky / 44 / AMSUB All-sky / 45 / MHS All-sky / 46 / NOT AVAILABLE / 47 / IRIS / 48 / NOT AVAILABLE / 49 / NOT AVAILABLE / 50 / ATMS All-sky / 51 / GMI All-sky / 52 / NOT AVAILABLE / 53 / NOT AVAILABLE / 54 / NOT AVAILABLE / 55 / NOT AVAILABLE / 56 /  [...]
+
+
+test, label = Check list of groups defined in ODB Governance is available in variable group_ids,
+do = (first, of = (for, g = (group_ids),
+                        do = (println,values = (g) / ($,_ = group/(g))))),
+expect = 1
+
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_parallel_sql.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_parallel_sql.ecml
new file mode 100644
index 0000000..92fdf29
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_parallel_sql.ecml
@@ -0,0 +1,42 @@
+#let, selects = (for, q = (glob,_="/tmp/ddl/*sql"), do = (read_text_file,_ = ($,_ = q)))
+
+#let, vars = (read_text_file,_="/tmp/42r1_sql/ddl/obstype.h")
+
+let, 
+    vars = (read_text_file, _ = "obstype.h"),
+    queries = (for, f = (glob, _ = "/tmp/ddl/*sql"),
+                    do = (let, 
+                            path = (f),
+                            select = (read_text_file,_ = (f)))),
+    data = (retrieve, 
+                type = mfb, 
+                date = 20151108, 
+                time = 12, 
+                database = marsod,
+                target="20151108_12_reportype_16002_type_mfb.odb",
+                reportype = 16002)
+
+function, of = queries, process_queries =
+    (for, 
+        q = (queries),
+        do = (
+            #println, values = Process / ($,_=q/path) / "::: " / ($,_=q/select)
+            try,
+                do = (sql, source = (data),
+                           filter = ($,_ = q/select),
+                           include = (vars)
+                      let, 
+                           path = ($,_ = q/path), 
+                           exception = NONE),
+
+                catch = (let, 
+                           path = ($,_ = q/path),
+                           exception = (current_exception) )))
+
+#println,values = "files:" / (for, f = ($,_=queries), do = ($,_=f/path))
+
+for, l = (process_queries),
+    do = (println, values = ""
+                    / ($,_ = l/exception) / ": "
+                    / ($,_ = l/path) 
+                    )
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_server_side_processing.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_server_side_processing.ecml
new file mode 100644
index 0000000..c6446b2
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_server_side_processing.ecml
@@ -0,0 +1,59 @@
+function, server_side_test = (
+    let, processed_on_server = 
+            (retrieve,
+                class = OD,
+                date = 20150218,
+                time = 1200,
+                type = OFB,
+                obsgroup = conv,
+                reportype = 16058,
+                stream = oper,
+                expver = qu12,
+                server_side = (function, of = source, _ = (sql, filter = "select varno,count(*) order by varno", target = (temporary_file))),
+                target = 20150218_0_16001_stats_server_side.odb,
+                odbpathnameschema = "{date}/{time}/{reportype}.odb",
+                odbserverroots = "~/data/root",
+                database = local)
+
+    println, values = XXXXXXXXXX Retrieved / (processed_on_server)
+
+    let, processed_on_client = 
+            (retrieve,
+                class = OD,
+                date = 20150218,
+                time = 1200,
+                type = OFB,
+                obsgroup = conv,
+                reportype = 16058,
+                stream = oper,
+                expver = qu12,
+                filter = "select varno,count(*) order by varno",
+                target = 20150218_0_16001_stats.odb,
+                odbpathnameschema = "{date}/{time}/{reportype}.odb",
+                odbserverroots = "~/data/root",
+                database = local)
+
+    println, values = XXXXXXXXXX Retrieved / (processed_on_client)
+
+    compare, left = (processed_on_server),
+            right = (processed_on_client)
+)
+
+test, label = check server side processing works,
+do = (
+    try, 
+    do = (
+        server_side_test
+
+        sequence,values = OK
+    ),
+    catch = (
+        test, label = check exception is caused by disabled support for SERVER_SIDE,
+        do = (current_exception),
+        expect = "UserError: SERVER_SIDE Server side processing disabled at compile time"
+
+        sequence,values = OK
+    )
+),
+expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_sql_like.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_like.ecml
new file mode 100644
index 0000000..8a7f361
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_like.ecml
@@ -0,0 +1,17 @@
+sql_test, 
+    label = "Check operator LIKE, ODB-75",
+
+    input = "a:STRING,b:INTEGER
+            'abra',1
+            'cadabra',2
+            'czary',3
+            'mary',4
+            ",
+
+    sql = "select a,b where a like 'ra';",
+
+    expect = "a:STRING,b:INTEGER
+            'abra',1
+            'cadabra',2
+            "
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_sql_match_in.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_match_in.ecml
new file mode 100644
index 0000000..73fc069
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_match_in.ecml
@@ -0,0 +1,30 @@
+sql_test, 
+    label = "Check operator MATCH (..) IN QUERY (SELECT ...) ODB-146",
+
+    input_tables = (let,
+                        test_match_in_query_input = "a:INTEGER,b:INTEGER
+                                                        1,2
+                                                        3,4
+                                                        5,6
+                                                        7,8
+                                                        9,0
+                                                    ",
+                        test_match_in_query_input2 = "a:INTEGER,b:INTEGER
+                                                        1,2
+                                                        3,4
+                                                        5,6
+                                                      "
+                                                      ),
+
+    sql = "select b 
+           from 'test_match_in_query_input.odb'
+           where match (a) in query (select a 
+                                     from 'test_match_in_query_input2.odb')
+           ;
+          ",
+
+    expect = "b:INTEGER
+            2
+            4
+            6
+            "
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_sql_splitting.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_splitting.ecml
new file mode 100644
index 0000000..3639a2f
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_splitting.ecml
@@ -0,0 +1,10 @@
+test, label = check splitting functionality of verb SQL,
+do = (
+    println,values = Current working directory is
+    system,values = pwd
+    sql,
+        source = 2000010106.odb,
+        filter = "select lat,lon,varno,obsvalue",
+        target = "2000010106_varno_{varno}.odb"
+),
+expect = 2000010106_varno_1.odb / 2000010106_varno_110.odb / 2000010106_varno_112.odb / 2000010106_varno_119.odb / 2000010106_varno_123.odb / 2000010106_varno_2.odb / 2000010106_varno_206.odb / 2000010106_varno_29.odb / 2000010106_varno_3.odb / 2000010106_varno_39.odb / 2000010106_varno_4.odb / 2000010106_varno_41.odb / 2000010106_varno_42.odb / 2000010106_varno_58.odb / 2000010106_varno_7.odb / 2000010106_varno_9.odb
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_sql_variables.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_variables.ecml
new file mode 100644
index 0000000..348a416
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_sql_variables.ecml
@@ -0,0 +1,15 @@
+sql_test, 
+    label = "Check variables defined in schema can be used in queries , ODB-127",
+
+    input = "varno:INTEGER,obsvalue:REAL
+             1,0.1
+             2,0.2
+             3,0.3
+            ",
+
+    sql = "select * where varno = $z;",
+
+    expect = "varno:INTEGER,obsvalue:REAL
+             1,0.1
+            "
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_stage.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_stage.ecml
new file mode 100644
index 0000000..62d58e7
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_stage.ecml
@@ -0,0 +1,72 @@
+test, label = check STAGE works,
+
+do = (
+    let, 
+        number_of_partitions = 3,
+        rts = 16030/16002/16029/16045/16065/16009
+        #rts = 16030
+    let,
+        data_description = (let,
+                                class = OD, 
+                                date = 20151108, 
+                                time = 1200, 
+                                type = MFB, 
+                                obsgroup = conv, 
+                                reportype = (rts), 
+                                stream = oper, 
+                                expver = qu12,
+                                odbpathnameschema = "{date}/{time}/{reportype}.odb",
+                                odbserverroots = "~/data/root",
+                                database = local)
+    # STAGE the data described by data_description so given number_of_partitions is ready
+    # for subsequent parallel RETRIEVE requests can fetch each of the partitions
+    # The data saved by STAGE in TARGET file should contain detailed information on
+    # the partitions (rows and reports numbers, etc. TODO) 
+
+    apply, function  = ($,_ = stage),
+        args         = (update, _        = (data_description),
+                                n_parts  = (number_of_partitions),
+                                partitionsinfo = "~/data/partitions_info_test_stage.txt",
+                                target   = 20150218_0_16001_parts_info.odb)
+
+    # retrieve_part is a helper function which retrieves partition number n
+    # using the data_description and number_of_partitions defined earlier
+
+    function, of = n, capture = data_description / number_of_partitions,
+        retrieve_part = (apply, 
+                            function     = ($,_ = retrieve),
+                            args         = (update, _           = (data_description),
+                                                    n_parts     = (number_of_partitions),
+                                                    partitionsinfo = "~/data/partitions_info_test_stage.txt",
+                                                    part_number = (n),
+                                                    target      = (temporary_file)))
+
+    # RETRIEVE all data described by data_description to a temporary file,
+    # set variable all to path of that file
+
+    let, all = (apply, 
+                    function = ($,_ = retrieve),
+                    args     = (update, _      = (data_description),
+                                        target = (temporary_file)))
+
+    # RETRIEVE each partition separately, 
+    # Assign list of retrieved files to variable parts.
+
+    let, parts = (
+        for, i = (range, from = 0, below = (number_of_partitions)),
+             do = (retrieve_part, n = (i))
+    )
+
+    println,values = Compare concatenation of partitions / (parts)
+    println,values = to the whole dataset / (all)
+
+    compare, 
+        left = (parts), 
+        right = (all)
+
+    system, values = "rm -rf " / (parts) / (all) 
+
+    println, values = OK
+),
+expect = OK
+
diff --git a/odb_api/src/odb_api/ecml_verbs/tests/test_temporary_file.ecml b/odb_api/src/odb_api/ecml_verbs/tests/test_temporary_file.ecml
new file mode 100644
index 0000000..f832276
--- /dev/null
+++ b/odb_api/src/odb_api/ecml_verbs/tests/test_temporary_file.ecml
@@ -0,0 +1,6 @@
+test, label = check temporary_file works,
+do = (
+    println, values = Generated file name is / (temporary_file)
+    println, values = OK
+
+), expect = OK
diff --git a/odb_api/src/odb_api/fwrap.py b/odb_api/src/odb_api/fwrap.py
new file mode 100755
index 0000000..ad25aee
--- /dev/null
+++ b/odb_api/src/odb_api/fwrap.py
@@ -0,0 +1,410 @@
+#!/usr/bin/env python 
+
+import re
+
+PARAM_TYPE_COLUMN = 43
+CONSTANTS = {}
+
+def formatParameter(typ, name):
+    global PARAM_TYPE_COLUMN 
+    space = ' ' * (PARAM_TYPE_COLUMN - len(typ)) 
+    return typ + space + ':: ' + name
+
+def declarations(source_cc = 'odbql.cc'):
+    decls = [line for line in [l.strip() for l in open(source_cc).read().splitlines()]
+         if line.find('odbql_') <> -1 
+             and not line.startswith('//')
+             and line.find('return') == -1
+             and line.find('virtual') == -1
+             and line.find('::') == -1
+             and line.find('typedef') == -1]
+    return decls
+
+def constants(source_h = 'odbql.h'):
+
+    lines = [line.strip() for line in open(source_h).read().splitlines()
+            if line.startswith('#define ODBQL_') 
+               and len(line.split()) > 2]
+
+    defs = [line.split(None, 2)[1:] for line in lines]
+    return defs 
+
+def evaluated_expression(s):
+    try: 
+        e = s
+        for c in CONSTANTS:
+            e = e.replace(c, CONSTANTS[c])
+        return ' [' + str(eval(e)) + ']'
+    except: return ''
+
+def translate_value_and_comment(value_and_possibly_comment):
+    value = value_and_possibly_comment.split('/*')[0].strip()
+    comment = ''
+
+    if len(value_and_possibly_comment.split('/*')) > 1:
+        comment = value_and_possibly_comment.split('/*')[1].split('*/')[0]
+
+    if value.find('|') <> -1:
+        comment = value + ' ' + comment + evaluated_expression(value_and_possibly_comment)
+        l,r = [x.strip(' ()') for x in value.split('|')]
+        i, shift = [x.strip() for x in r.split('<<')]
+        value = 'IOR(%s, LSHIFT(%s,%s))' % (l, i, shift)
+    
+    if value.startswith('0x'):
+        comment = value + ' ' + comment
+        value = eval(value)
+
+    return value, '! ' + comment 
+
+def generateParameter(define):
+    global CONSTANTS
+
+    name, value_and_possibly_comment = define
+
+    typ = 'integer'
+    if value_and_possibly_comment.find('"') <> -1:
+        typ = 'character(len=*)'
+
+    if name == 'ODBQL_TRANSIENT':
+        value, comment = '-1', " ! ((odbql_destructor_type)-1)"
+    elif name == 'ODBQL_STATIC':
+        value, comment = '0', ' ! ((odbql_destructor_type)0)'
+    else:
+        value, comment = translate_value_and_comment(value_and_possibly_comment) 
+
+    CONSTANTS[name] = value
+
+    return '%s, parameter :: %s = %s %s' % (typ, name, value, comment)
+
+def generateParameters(defs):
+    return """
+module odbql_constants
+  implicit none
+
+""" + '\n'.join(generateParameter(d) for d in defs) + """
+
+end module odbql_constants
+""" 
+
+def normalize_type(t): return re.sub(' [*]', '*', t)
+
+def parseParam(p):
+    p = p.strip()
+
+    # Hack for the destructor type, used in sqlite3 to let user pass
+    # a function that will release memory occupied by strings and blobs'
+    # No idea how to handle this in Fortran at this point, not sure
+    # if we immediately need this functionality anyway.
+    ## 
+    if p == 'void(*d)(void*)': return 'void(*)(void*)', 'd'
+
+    param_name = re.findall(r"[\w']+", p)[-1]
+    param_type = p[:len(p) - len(param_name)].strip()
+    param_type = normalize_type(param_type)
+
+    return param_type, param_name
+    
+
+def parseDeclaration(decl):
+    ret_and_fun_name = decl.split('(', 1)[0].strip()
+    fun_name = re.findall(r"[\w']+", ret_and_fun_name)[-1]
+    return_type = ret_and_fun_name[:-len(fun_name)].strip()
+    return_type = normalize_type(return_type)
+
+    params = decl.split('(',1)[1].strip().rsplit(')', 1)[0].strip()
+    params = list([parseParam(p) for p in params.split(',')])
+
+    # filter out void from parameterless functions e.g: const char * odbql_libversion(void)
+    if len(params) == 1 and (len(params[0][0]) == 0 and params[0][1] == 'void'):
+        params = []
+    return (decl, (return_type, fun_name, params))
+
+def translate_type_for_binding(t):
+    #print 'translate_type_for_binding:', t
+    if t == 'const char*':           return 'character(kind=C_CHAR), dimension(*)'
+    if t == 'const char**':          return 'character(kind=C_CHAR), dimension(*)' # TODO
+    if t == 'double':                return 'real(kind=C_DOUBLE), value'
+    if t == 'int':                   return 'integer(kind=C_INT), value'
+    if t == 'odbql*':                return 'type(C_PTR), VALUE'
+    if t == 'odbql**':               return 'type(C_PTR)'
+    if t == 'odbql_stmt*':           return 'type(C_PTR), VALUE'
+    if t == 'odbql_stmt**':          return 'type(C_PTR)'
+    if t == 'odbql_value*':          return 'type(C_PTR), VALUE'
+    if t == 'odbql_value**':         return 'type(C_PTR)'
+    if t == 'void(*)(void*)':        return 'type(C_PTR), VALUE'
+
+    raise Exception("Don't know how to translate '" + t + "'")
+
+
+def translate_type_for_binding_return(t):
+    #print 'translate_type_for_binding_return:', t
+    if t == 'const char*':           return 'type(C_PTR)'
+    if t == 'const unsigned char*':  return 'type(C_PTR)'
+    if t == 'int':                   return 'integer(kind=C_INT)'
+    if t == 'double':                return 'real(kind=C_DOUBLE)'
+    if t == 'odbql_value*':          return 'type(C_PTR)'
+    if t == 'error_code_t':          return 'integer(kind=C_INT)'
+
+    raise Exception("Don't know how to translate '" + t + "'")
+
+
+def translate_type_for_fortran(t):
+    #print 'translate_type_for_fortran:', t
+    if t == 'const char*':           return 'character(len=*), intent(in)'
+    if t == 'const char**':          return 'character(len=*), intent(out)'
+    if t == 'double':                return 'real(kind=C_DOUBLE), value'
+    if t == 'int':                   return 'integer(kind=C_INT), value'
+    if t == 'odbql*':                return 'type(odbql), value'
+    if t == 'odbql**':               return 'type(odbql)'
+    if t == 'odbql_stmt*':           return 'type(odbql_stmt), value'
+    if t == 'odbql_stmt**':          return 'type(odbql_stmt)'
+    if t == 'odbql_value*':          return 'type(odbql_value), value'
+    if t == 'odbql_value**':         return 'type(odbql_value)'
+    if t == 'void(*)(void*)':        return 'type(C_PTR), value'
+
+    raise Exception("Don't know how to translate '" + t + "'")
+
+
+def translate_type_for_fortran_return(t):
+    #print 'translate_type_for_fortran_return:', t
+    if t == 'const char*':           return 'character(len=*), intent(out)'
+    if t == 'const unsigned char*':  return 'character(len=*), intent(out)' # TODO: think about it
+    if t == 'int':                   return 'integer(kind=C_INT)'
+    if t == 'double':                return 'real(kind=C_DOUBLE)'
+    if t == 'odbql_value*':          return 'type(odbql_value)'
+    if t == 'error_code_t':          return 'integer(kind=C_INT), intent(out), optional'
+
+    raise Exception("Don't know how to translate '" + t + "'")
+
+def fortranParamTypeDeclaration(p, translate_type = translate_type_for_binding):
+    typ, parameter_name = p
+    fortran_type = translate_type(typ)
+    return formatParameter(fortran_type, parameter_name)
+
+nl_indent = '\n     '
+
+helper_functions = """
+
+!> Helper routine to convert C '\\0' terminated strings to Fortran strings
+
+    subroutine C_to_F_string(c_string_pointer, out_string)
+
+      use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char
+
+      type(c_ptr), intent(in)                       :: c_string_pointer
+      character(len=*), intent(out)                 :: out_string
+      character(kind=c_char), dimension(:), pointer :: char_array_pointer
+      integer                                       :: i,length
+
+      char_array_pointer => null()
+      call c_f_pointer(c_string_pointer,char_array_pointer,[255])
+
+      if (.not.associated(char_array_pointer)) then
+          out_string = "NULL"
+          return
+      end if
+
+      out_string = " "
+      do i = 1, len(out_string)
+        if (char_array_pointer(i) == c_null_char) exit
+        out_string(i:i) = char_array_pointer(i)
+      end do
+
+    end subroutine
+
+
+"""
+
+status_handling_code = """
+if (present(status)) then
+    status = rc ! let user handle the error
+else
+    if (rc /= ODBQL_OK .and. rc /= ODBQL_ROW .and. rc /= ODBQL_DONE .and. rc /= ODBQL_METADATA_CHANGED) then
+        write (0,*) 'Error in %(function_name)s'
+        stop
+    end if
+end if
+"""
+status_handling_code = nl_indent.join (status_handling_code.splitlines())
+
+def parameter_type(p): return p[0]
+def parameter_name(p): return p[1]
+
+def actual_parameter(p):
+    if p[1] == 'iCol': return p[1] + '-1'
+    if p[0] == 'const char*': return p[1] + '_tmp'
+    if p[0] == 'odbql*': return p[1] + '%this'
+    if p[0] == 'odbql**': return p[1] + '%this'
+    if p[0] == 'odbql_stmt*': return p[1] + '%this'
+    if p[0] == 'odbql_stmt**': return p[1] + '%this'
+    if p[0] == 'odbql_value*': return p[1] + '%this'
+    if p[0] == 'odbql_value**': return p[1] + '%this'
+    return p[1]
+
+def generateWrapper(signature, comment, template):
+
+    print 'generateWrapper:', signature, comment, template
+    global status_handling_code
+
+    return_type, function_name, params = signature
+    procedure_keyword = 'function'
+
+    output_parameter = function_name
+
+    # filter out user supplied destructor functions (not supported now)
+    fortran_params = [p for p in params if not parameter_type(p) == 'void(*)(void*)']
+    fortran_params_excluding_return_parameter = fortran_params[:]
+
+    binding_parameter_list = '(' + ','.join([p[1] for p in params]) + ')'
+    actual_binding_parameter_list = '(' + ','.join([actual_parameter(p) for p in params]) + ')'
+
+    temporary_variables_declarations = nl_indent.join(
+        [formatParameter('character(len=len_trim('+p[1]+')+1)', p[1] + '_tmp') for p in params if p[0] == 'const char*']
+        + [formatParameter('type(C_PTR)', p[1]) for p in params if p[0] == 'void(*)(void*)'])
+    temporary_variables_assignments   = nl_indent.join(p[1] + '_tmp = ' + p[1] + '//achar(0)'
+                                                      for p in params if p[0] == 'const char*')
+    call_binding = function_name + '_c' + actual_binding_parameter_list
+
+    error_handling = ''
+    return_value_tmp = None
+
+    if return_type == 'odbql_value*': 
+        return_value_tmp = output_parameter + "%this"
+
+
+    if return_type == 'error_code_t':
+        procedure_keyword = 'subroutine'
+        output_parameter = 'status'
+        return_value_tmp = 'rc'
+        temporary_variables_declarations = nl_indent.join(
+            [temporary_variables_declarations, 
+             formatParameter('integer(kind=c_int)', 'rc')])
+        fortran_params.append( (return_type, output_parameter) )
+        error_handling = status_handling_code % locals()
+
+    if return_type == 'const char*' or return_type == 'const unsigned char*':
+        procedure_keyword = 'subroutine'
+        output_parameter = 'return_value'
+        fortran_params.append( (return_type, output_parameter) )
+
+        # For C to F string, we call a subroutine rather than make an assignment
+        return_value_assignment = "call C_to_F_string(%s, %s)" % (call_binding, (return_value_tmp or output_parameter))
+
+    else:
+
+        # Normal assignment
+        return_value_assignment = (return_value_tmp or output_parameter) + ' = ' + call_binding
+
+    binding_parameters_declarations = nl_indent.join([fortranParamTypeDeclaration(p) for p in params])
+    binding_return_type_declaration = formatParameter(translate_type_for_binding_return(return_type), function_name + '_c')
+
+    fortran_parameter_list = '(' + ','.join([p[1] for p in fortran_params]) + ')'
+    fortran_parameters_declarations = nl_indent.join(
+        [fortranParamTypeDeclaration(p, translate_type = translate_type_for_fortran)
+         for p in fortran_params_excluding_return_parameter])
+    fortran_return_type_declaration = formatParameter(translate_type_for_fortran_return(return_type), output_parameter)
+
+    use_intrinsic = formatParameter('use, intrinsic', 'iso_c_binding')
+
+    return template % locals()
+
+def generateWrappers(decls, header, footer, template):
+    s = header
+    for original, ast in [parseDeclaration(decl) for decl in decls]: 
+        s += generateWrapper(ast, original, template) + '\n'
+
+    s += footer
+    return s
+
+
+
+def generateBindings(source_cc = '../odb_api/odbql.cc',
+                     source_h = '../odb_api/odbql.h',
+                     binding_f90 = '../fortran/odbql_binding.f90',
+                     wrappers_f90 = '../fortran/odbql_wrappers.f90',
+                     constants_f90 = '../fortran/odbql_constants.f90'):
+
+    decls = declarations(source_cc)
+    defs = constants(source_h)
+    with open(constants_f90, 'w') as f: 
+        f.write(generateParameters(defs))
+
+    ########## odbql_binding.f90: declarations of ISO C bindings for the new ODB API
+    with open(binding_f90, 'w') as f: 
+        f.write(generateWrappers(decls, header = """
+
+!!!!! THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY !!!!!
+!!    See fwrap.py
+
+module odbql_binding
+  use iso_c_binding
+  use, intrinsic :: iso_c_binding
+  implicit none
+interface
+""", footer = """
+end interface
+end module odbql_binding
+""", template = """
+!> %(comment)s
+
+    function %(function_name)s_c %(binding_parameter_list)s bind(C, name="%(function_name)s")
+     %(use_intrinsic)s
+     %(binding_parameters_declarations)s
+     %(binding_return_type_declaration)s
+    end function %(function_name)s_c
+
+
+    """))
+
+    ############ odbql_wrappers.f90  The Fortran user interface
+
+    with open(wrappers_f90, 'w') as f: 
+        f.write(generateWrappers(decls, header = """
+
+!!!!! THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY !!!!!
+!!    See fwrap.py
+
+module odbql_wrappers
+  use odbql_binding
+  use odbql_constants
+  implicit none
+  
+  type odbql
+    type(c_ptr) :: this
+  end type
+
+  type odbql_stmt
+    type(c_ptr) :: this
+  end type
+
+  type odbql_value
+    type(c_ptr) :: this
+  end type
+
+contains
+
+%(helper_functions)s
+
+""" % globals(), footer = """
+end module odbql_wrappers
+""", template = """
+!> %(comment)s
+
+    %(procedure_keyword)s %(function_name)s %(fortran_parameter_list)s 
+     use odbql_binding
+     %(use_intrinsic)s
+     %(fortran_parameters_declarations)s
+     %(fortran_return_type_declaration)s
+
+     %(temporary_variables_declarations)s
+
+     %(temporary_variables_assignments)s
+     %(return_value_assignment)s
+     %(error_handling)s
+
+    end %(procedure_keyword)s %(function_name)s
+
+    """ 
+    ))
+
+if __name__ == '__main__': generateBindings()
diff --git a/odb_api/src/odb_api/md5_hash.c b/odb_api/src/odb_api/md5_hash.c
new file mode 100644
index 0000000..c44bda4
--- /dev/null
+++ b/odb_api/src/odb_api/md5_hash.c
@@ -0,0 +1,306 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/** 
+ * @file md5.c
+ * @author Baudouin Raoult
+ */
+
+#include "md5_hash.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+/* Cray C compiler should not try to optimize this code */
+#if _CRAYC
+    #pragma _CRI noopt
+#endif
+
+static unsigned long r[] = {
+    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
+    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
+
+static unsigned long k[] = {
+    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
+
+static const unsigned long t = 32;
+
+//static unsigned long rotate(unsigned long x,unsigned long c) { return (x << c) | (x >> (t-c)); }
+
+
+//static unsigned long F(unsigned long x,unsigned long y,unsigned long z) { return (x&y)|((~x)&z); }
+//static unsigned long G(unsigned long x,unsigned long y,unsigned long z) { return (x&z)|(y&(~z)); }
+//static unsigned long H(unsigned long x,unsigned long y,unsigned long z) { return x^y^z;          }
+//static unsigned long I(unsigned long x,unsigned long y,unsigned long z) { return y^(x|(~z));     }
+
+
+#define ROT(x,c) ((x << c) | (x >> (32-c)))
+
+#define _F(x,y,z) ((x&y)|((~x)&z))
+#define _G(x,y,z) ((x&z)|(y&(~z)))
+#define _H(x,y,z) (x^y^z)
+#define _I(x,y,z) (y^(x|(~z)))
+
+#define F_(A,B,C,D,g,i) A += _F(B,C,D)+w[g]+k[i]; A &= 0xffffffff; A = ROT(A, r[i]); A+=B;
+#define G_(A,B,C,D,g,i) A += _G(B,C,D)+w[g]+k[i]; A &= 0xffffffff; A = ROT(A, r[i]); A+=B;
+#define H_(A,B,C,D,g,i) A += _H(B,C,D)+w[g]+k[i]; A &= 0xffffffff; A = ROT(A, r[i]); A+=B;
+#define I_(A,B,C,D,g,i) A += _I(B,C,D)+w[g]+k[i]; A &= 0xffffffff; A = ROT(A, r[i]); A+=B;
+
+static void md5_flush(md5_state* s)
+{
+
+//    unsigned long i, g;
+    unsigned long a = s->h0;
+    unsigned long b = s->h1;
+    unsigned long c = s->h2;
+    unsigned long d = s->h3;
+//    unsigned long f;
+//    unsigned long temp;
+    unsigned long *w = &s->words[0];
+//    unsigned long h;
+
+#if 1
+    F_(a,b,c,d, 0, 0);
+    F_(d,a,b,c, 1, 1);
+    F_(c,d,a,b, 2, 2);
+    F_(b,c,d,a, 3, 3);
+
+    F_(a,b,c,d, 4, 4);
+    F_(d,a,b,c, 5, 5);
+    F_(c,d,a,b, 6, 6);
+    F_(b,c,d,a, 7, 7);
+
+    F_(a,b,c,d, 8, 8);
+    F_(d,a,b,c, 9, 9);
+    F_(c,d,a,b,10,10);
+    F_(b,c,d,a,11,11);
+
+    F_(a,b,c,d,12,12);
+    F_(d,a,b,c,13,13);
+    F_(c,d,a,b,14,14);
+    F_(b,c,d,a,15,15);
+
+    G_(a,b,c,d, 1,16);
+    G_(d,a,b,c, 6,17);
+    G_(c,d,a,b,11,18);
+    G_(b,c,d,a, 0,19);
+
+    G_(a,b,c,d, 5,20);
+    G_(d,a,b,c,10,21);
+    G_(c,d,a,b,15,22);
+    G_(b,c,d,a, 4,23);
+
+    G_(a,b,c,d, 9,24);
+    G_(d,a,b,c,14,25);
+    G_(c,d,a,b, 3,26);
+    G_(b,c,d,a, 8,27);
+
+    G_(a,b,c,d,13,28);
+    G_(d,a,b,c, 2,29);
+    G_(c,d,a,b, 7,30);
+    G_(b,c,d,a,12,31);
+
+    H_(a,b,c,d, 5,32);
+    H_(d,a,b,c, 8,33);
+    H_(c,d,a,b,11,34);
+    H_(b,c,d,a,14,35);
+
+    H_(a,b,c,d, 1,36);
+    H_(d,a,b,c, 4,37);
+    H_(c,d,a,b, 7,38);
+    H_(b,c,d,a,10,39);
+
+    H_(a,b,c,d,13,40);
+    H_(d,a,b,c, 0,41);
+    H_(c,d,a,b, 3,42);
+    H_(b,c,d,a, 6,43);
+
+    H_(a,b,c,d, 9,44);
+    H_(d,a,b,c,12,45);
+    H_(c,d,a,b,15,46);
+    H_(b,c,d,a, 2,47);
+
+    I_(a,b,c,d, 0,48);
+    I_(d,a,b,c, 7,49);
+    I_(c,d,a,b,14,50);
+    I_(b,c,d,a, 5,51);
+
+    I_(a,b,c,d,12,52);
+    I_(d,a,b,c, 3,53);
+    I_(c,d,a,b,10,54);
+    I_(b,c,d,a, 1,55);
+
+    I_(a,b,c,d, 8,56);
+    I_(d,a,b,c,15,57);
+    I_(c,d,a,b, 6,58);
+    I_(b,c,d,a,13,59);
+
+    I_(a,b,c,d, 4,60);
+    I_(d,a,b,c,11,61);
+    I_(c,d,a,b, 2,62);
+    I_(b,c,d,a, 9,63);
+
+#else
+    for(i=0; i< 16; i++) {
+        f = F(b,c,d);
+        g = i;
+        temp = d;
+        d = c;
+        c = b;
+        h = a + f + k[i] + w[g];
+        b = b + rotate(h , r[i]);
+        a = temp;
+    }
+
+    for(i=16; i< 32; i++) {
+        f = G(b,c,d);
+        g = (5*i + 1) % 16;
+        temp = d;
+        d = c;
+        c = b;
+        h = a + f + k[i] + w[g];
+        b = b + rotate(h , r[i]);
+        a = temp;
+    }
+    for(i=32; i< 48; i++) {
+        f = H(b,c,d);
+        g = (3*i + 5) % 16;
+        temp = d;
+        d = c;
+        c = b;
+        h = a + f + k[i] + w[g];
+        b = b + rotate(h , r[i]);
+        a = temp;
+    }
+    for(i=48; i< 64; i++) {
+        f = I(b,c,d);
+        g = (7*i) % 16;
+        temp = d;
+        d = c;
+        c = b;
+        h = a + f + k[i] + w[g];
+        b = b + rotate(h, r[i]);
+        a = temp;
+    }
+
+#endif
+
+    s->h0 += a;
+    s->h1 += b;
+    s->h2 += c;
+    s->h3 += d;
+
+    s->word_count = 0;
+}
+
+void md5_init(md5_state* s) {
+    memset(s,0,sizeof(md5_state));
+    s->h0 = 0x67452301;
+    s->h1 = 0xefcdab89;
+    s->h2 = 0x98badcfe;
+    s->h3 = 0x10325476;
+
+}
+
+void md5_add(md5_state* s,const void* data,size_t len) {
+
+    unsigned char* p = (unsigned char*)data;
+    s->size += len;
+
+    while(len-- > 0) {
+        s->bytes[s->byte_count++] = *p++;
+
+        if(s->byte_count == 4) {
+            s->words[s->word_count++] = (s->bytes[3]<<24)|(s->bytes[2]<<16)|(s->bytes[1]<<8)|(s->bytes[0]);
+            s->byte_count = 0;
+
+            if(s->word_count == 16)
+                md5_flush(s);
+        }
+
+    }
+}
+
+void md5_end(md5_state* s, char *digest)
+{
+    uint64_t h = 8;
+    uint64_t bits, leng = s->size * h;
+    unsigned char c = 0x80;
+    int i;
+
+
+    md5_add(s,&c,1);
+
+    bits = s->size * h;
+    c = 0;
+    while( (bits % 512) != 448) 
+    {
+        md5_add(s,&c,1);
+        bits = s->size * h;
+    }
+
+
+    for(i = 0; i < 8 ; i++) {
+        c =  leng & 0xff;
+        leng >>= 8;
+        md5_add(s,&c,1);
+    }
+
+	typedef unsigned int ui;
+    sprintf(digest, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+            (ui) (s->h0 & 0xff), (ui) ((s->h0 >> 8) & 0xff), (ui) ((s->h0 >> 16) & 0xff), (ui) ((s->h0 >> 24) & 0xff),
+            (ui) (s->h1 & 0xff), (ui) ((s->h1 >> 8) & 0xff), (ui) ((s->h1 >> 16) & 0xff), (ui) ((s->h1 >> 24) & 0xff),
+            (ui) (s->h2 & 0xff), (ui) ((s->h2 >> 8) & 0xff), (ui) ((s->h2 >> 16) & 0xff), (ui) ((s->h2 >> 24) & 0xff),
+            (ui) (s->h3 & 0xff), (ui) ((s->h3 >> 8) & 0xff), (ui) ((s->h3 >> 16) & 0xff), (ui) ((s->h3 >> 24) & 0xff));
+}
+
+#if 0
+
+main(int argc, char **argv)
+{
+    char digest[1024];
+
+    const char* p = "The quick brown fox jumps over the lazy dog";
+    md5_state s;
+    md5_init(&s);
+    if(argc>1) {
+        char buffer[10240];
+        long len = 0;
+        FILE* f = ::fopen(argv[1],"r");
+        if(!f) {
+            perror(argv[1]);
+            exit(1);
+        }
+        while((len = fread(buffer, 1, sizeof(buffer), f)) > 0) {
+            md5_add(&s,buffer,len);
+        }
+        fclose(f);
+    }
+    else
+    {
+        md5_add(&s,p,strlen(p));
+    }
+    md5_end(&s, digest);
+
+    printf("%s\n",digest);
+}
+
+#endif
diff --git a/odb_api/src/odb_api/md5_hash.h b/odb_api/src/odb_api/md5_hash.h
new file mode 100644
index 0000000..d50b358
--- /dev/null
+++ b/odb_api/src/odb_api/md5_hash.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/* File md5.h
+ * Baudouin Raoult - (c) ECMWF Sep 11
+ */
+
+#ifndef md5_H
+#define md5_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+
+typedef struct md5_state {
+    uint64_t size;
+
+    unsigned long words[64];
+    unsigned long word_count;
+
+    unsigned char bytes[4];
+    unsigned long byte_count;
+
+    unsigned long h0;
+    unsigned long h1;
+    unsigned long h2;
+    unsigned long h3;
+
+} md5_state;
+
+void md5_init(md5_state* s);
+void md5_add(md5_state* s,const void* data,size_t len);
+void md5_end(md5_state* s, char *digest);
+
+
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/2oda b/odb_api/src/odb_api/migrator/2oda
new file mode 100755
index 0000000..9e1e5e7
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/2oda
@@ -0,0 +1,49 @@
+#!/bin/ksh
+
+OBSGROUPS="gpsro mhs"
+OBSGROUPS="amsua amsub geos gpsro hirs meris mhs reo3"
+DATES="2009072812 "
+DATES="2009072800 2009072812 2009072900 2009072912 2009073000 2009073012"
+
+for DATE in $DATES;
+do
+	echo `date` ------ Processing data from $DATE
+	echo
+
+	for OBSGROUP in $OBSGROUPS;
+	do
+		echo `date` --- Importing data from /hugetmp/data/$DATE/ECMA.$OBSGROUP
+
+		./odb2oda -addcolumns reptype=0,class="'od'",stream="'oper'",type="'oda'" \
+		 -obsgroup $OBSGROUP \
+		 -reptypecfg $OBSGROUP.cfg \
+		/hugetmp/data/$DATE/ECMA.$OBSGROUP sql_$OBSGROUP /hugetmp/data/oda/ECMA.$OBSGROUP.$DATE.{reptype}.oda || exit 1
+
+		echo `date` --- DONE
+	done
+	#continue
+
+	for OBSGROUP in $OBSGROUPS;
+	do
+		for f in /hugetmp/data/oda/ECMA.$OBSGROUP.$DATE.*.oda;
+		do
+			echo `date` --- Creating request for $f
+
+			oda oda2request $f $OBSGROUP $f.arch.request;
+
+			echo `date` --- DONE
+		done 
+	done
+
+	for OBSGROUP in $OBSGROUPS;
+	do
+		for f in /hugetmp/data/oda/ECMA.$OBSGROUP.$DATE.*.oda.arch.request;
+		do
+			echo `date` --- Archiving $f on MARS 
+
+			time mars -t < $f;
+
+			echo `date` --- DONE
+		done
+	done
+done
diff --git a/odb_api/src/odb_api/migrator/CMakeLists.txt b/odb_api/src/odb_api/migrator/CMakeLists.txt
new file mode 100644
index 0000000..9118e1e
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/CMakeLists.txt
@@ -0,0 +1,84 @@
+list( APPEND migrator_srcs
+FakeODBIterator.cc
+FakeODBIterator.h
+ImportODBTool.cc
+ImportODBTool.h
+ODB2ODATool.cc
+ODB2ODATool.h
+MigratorTool.h
+MigratorTool.cc
+ODBIterator.cc
+ODBIterator.h
+OldODBReader.h
+ReptypeGenIterator.cc
+ReptypeGenIterator.h
+TSQLReader.cc
+TSQLReader.h
+ODBMigratorModule.h
+ODBMigratorModule.cc
+MigrateHandler.h
+MigrateHandler.cc
+migrator_api.cc
+migrator_api.h
+)
+
+    ecbuild_add_library( TARGET Odbmigrator
+                         SOURCES    ${migrator_srcs}
+                         CONDITION
+                            HAVE_MIGRATOR AND ODB_FOUND AND DEFINED FORTRAN_LIBRARIES
+                         TEMPLATES
+                            ImportODBTool.cc TSQLReader.cc
+                         PRIVATE_INCLUDES   ${ODB_INCLUDE_DIRS}
+                         INSTALL_HEADERS LISTED
+                         HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api/migrator
+                         LIBS       Odb odbtools ${ODB_LIBRARIES} ${FORTRAN_LIBRARIES} ${DL_LIBRARIES} ${EXTRA_ODB_MIGRATOR_LIBS}) #FIXME: add gomp for gfortran 
+                        
+    ecbuild_add_executable( TARGET     odb_migrator
+                        CONDITION
+                            HAVE_MIGRATOR AND ODB_FOUND AND DEFINED FORTRAN_LIBRARIES
+                        SOURCES    odb2oda.cc
+                        INCLUDES   ${ODB_INCLUDE_DIRS}
+                        # LINKER_LANGUAGE Fortran
+                        LIBS       Odbmigrator odbtools ${ODB_LIBRARIES} ${FORTRAN_LIBRARIES} ${DL_LIBRARIES} )
+
+    set( test_environment
+      ODB_API_CODES=${PROJECT_SOURCE_DIR}/etc
+      ODB_API_HOME=${PROJECT_SOURCE_DIR}
+      ODB_API_TEST_DATA_PATH=${CMAKE_CURRENT_BINARY_DIR}
+      PATH=${CMAKE_BINARY_DIR}/bin:$ENV{PATH}
+      ODB_RTABLE_PATH=${PROJECT_SOURCE_DIR}/etc )
+
+    if( HAVE_MIGRATOR AND ODB_FOUND AND DEFINED FORTRAN_LIBRARIES )
+
+      get_target_property( migrator_bin odb_migrator LOCATION)
+
+      list( APPEND odb_migrator_data_files  2000010106.old.ECMA.tar.gz )
+
+      ecbuild_get_test_multidata( TARGET get_odb_migrator_test_data
+                                  DIRNAME odb_api/tests 
+                                  NAMES ${odb_migrator_data_files} 
+                                  NOCHECK )
+
+      #ecbuild_add_test( TARGET     test_migrator.ecml
+      #                COMMAND      ${migrator_bin} 
+      #                ARGS         ecml ${CMAKE_CURRENT_SOURCE_DIR}/test_migrator.ecml
+      #                CONDITION    HAVE_MIGRATOR
+      #                ENVIRONMENT  ${test_environment}
+      #                LABELS       odb_api odb_api_ecml
+      #                TEST_DEPENDS get_odb_migrator_test_data
+      #                )
+    endif()
+
+  if( HAVE_MIGRATOR AND HAVE_PYTHON AND SWIG_FOUND AND PYTHONLIBS_FOUND )
+        set(CMAKE_SWIG_FLAGS "")
+        include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${PYTHON_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../odb_api )
+        set_source_files_properties( pyodbdump.i PROPERTIES CPLUSPLUS ON )
+        # set_source_files_properties( pyodbapi.i PROPERTIES SWIG_FLAGS "-includeall")
+        swig_add_module( pyodbdump python pyodbdump.i )
+        swig_link_libraries( pyodbdump Odbmigrator Odb ${ODB_LIBRARIES} ${FORTRAN_LIBRARIES} ${DL_LIBRARIES} ${PYTHON_LIBRARIES} )
+        
+        set(PYTHON_SITE "${INSTALL_LIB_DIR}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages" )
+        set(PYTHON_DEST "${PYTHON_SITE}" )
+        install(TARGETS _pyodbdump DESTINATION ${PYTHON_DEST} )
+        install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyodbdump.py  DESTINATION ${PYTHON_DEST})
+    endif()
diff --git a/odb_api/src/odb_api/migrator/FakeODBIterator.cc b/odb_api/src/odb_api/migrator/FakeODBIterator.cc
new file mode 100644
index 0000000..52038e0
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/FakeODBIterator.cc
@@ -0,0 +1,160 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file FakeODBIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <strings.h>
+
+extern "C" {
+#include "odbdump.h"
+}
+
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/utils/Translator.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+
+#include "odb_api/migrator/FakeODBIterator.h"
+#include "odb_api/migrator/ODBIterator.h"
+
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+FakeODBIterator::ConstParameters FakeODBIterator::ConstParameters::instance_ = FakeODBIterator::ConstParameters();
+
+Assignments::Assignments(const std::string& s)
+{
+    Tokenizer splitAssignments(",");
+    std::vector<std::string> assignments;
+    splitAssignments(s, assignments);
+	
+    Tokenizer splitEq("=");
+	for (size_t i = 0; i < assignments.size(); ++i)
+	{
+		std::vector<std::string> assignment;
+		splitEq(assignments[i], assignment);
+		ASSERT(assignment.size() == 2);
+
+		push_back(make_pair(assignment[0], assignment[1]));
+	}
+}
+
+void FakeODBIterator::ConstParameters::add(const Assignments& ass)
+{
+	Log::debug() << "FakeODBIterator::ConstParameters::add(const Assignments& ass)" << std::endl;
+	for (size_t i = 0; i < ass.size(); ++i)
+	{
+		const Assignment &as = ass[i];
+		std::string columnName = as.first;
+		std::string value = as.second;
+
+		ASSERT(value.size() > 0);
+
+		if (Tool::isInQuotes(value))
+			addString(columnName, Tool::unQuote(value));
+		else if (value.find('.') == std::string::npos)
+			addInteger(columnName, Translator<std::string, long>()(value));
+		else
+			addReal(columnName, Translator<std::string, double>()(value));
+	}
+}
+
+void FakeODBIterator::ConstParameters::addInteger(const std::string& name, long v)
+{
+	Log::info() << "FakeODBIterator::ConstParameters::addInteger: " << name << " = " << v << std::endl;
+	push_back(ConstParameter(name, v, odb::INTEGER));
+}
+
+void FakeODBIterator::ConstParameters::addReal(const std::string& name, double v)
+{
+	Log::info() << "FakeODBIterator::ConstParameters::addReal: " << name << " = " << v << std::endl;
+	push_back(ConstParameter(name, v, odb::REAL));
+}
+
+void FakeODBIterator::ConstParameters::addString(const std::string& name, std::string v)
+{
+	Log::info() << "FakeODBIterator::ConstParameters::addString: " << name << " = '" << v << "'" << std::endl;
+	push_back(ConstParameter(name, Tool::cast_as_double(v), odb::STRING));
+}
+
+FakeODBIterator::FakeODBIterator(const PathName& db, const std::string& sql)
+: iterator_(db, sql),
+  columns_(0),
+  data_(0),
+  constParameters_(FakeODBIterator::ConstParameters::instance())
+{}
+
+FakeODBIterator::~FakeODBIterator()
+{
+	delete [] data_;
+}
+
+odb::MetaData& FakeODBIterator::columns() {
+	if (columns_.size() == 0)
+	{
+		columns_ = iterator_.columns();
+
+		size_t i = constParameters_.size();
+		while (i-- > 0)
+		{
+			std::string name = constParameters_[i].name;
+			odb::ColumnType type = constParameters_[i].type;
+
+			Log::debug() << "FakeODBIterator::columns: i = " << i << ", name=" << name << std::endl;
+
+			odb::Column* col = new odb::Column(columns_);
+			col->name(name);
+			col->type<DataStream<SameByteOrder, DataHandle> >(type, false);
+
+			columns_.insert(columns_.begin(), col);
+		}
+
+	}
+	return columns_;
+}
+
+double* FakeODBIterator::data()
+{
+	if (data_ == 0)
+		data_ = new double[columns_.size()];
+
+	double* trueData = iterator_.data();
+
+	size_t count = constParameters_.size();
+	memcpy(data_ + count, trueData, (columns_.size() - count) * sizeof(double));
+
+	for (size_t i = 0; i < count; ++i)
+		data_[i] = constParameters_[i].value;
+	
+	return data_;
+}
+
+bool FakeODBIterator::isNewDataset()
+{
+	return iterator_.isNewDataset();
+}
+
+bool FakeODBIterator::next(eckit::ExecutionContext* context)
+{
+	bool r = iterator_.next(context);
+	noMore_ = !r;
+	return r;
+}
+
+} // namespace tool 
+} //namespace odb 
+
diff --git a/odb_api/src/odb_api/migrator/FakeODBIterator.h b/odb_api/src/odb_api/migrator/FakeODBIterator.h
new file mode 100644
index 0000000..dacb882
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/FakeODBIterator.h
@@ -0,0 +1,96 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file FakeODBIterator.h
+/// @author Piotr Kuchta, ECMWF, March 2009
+
+#ifndef FakeODBIterator_H
+#define FakeODBIterator_H
+
+
+#include <eckit/eckit.h>
+#include "odb_api/MetaData.h"
+#include "odb_api/migrator/ODBIterator.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+namespace tool {
+
+class ODBIterator;
+
+typedef std::pair<std::string, std::string> Assignment;
+typedef std::vector<Assignment> AssignmentsBase;
+
+struct Assignments : public AssignmentsBase {
+	Assignments(const std::string&);
+};
+
+struct ConstParameter {
+	ConstParameter(std::string name, double value, odb::ColumnType type)
+	: name(name), value(value), type(type)
+	{}
+
+	std::string name;
+	double      value;
+	odb::ColumnType  type;
+};
+
+class FakeODBIterator //: public odb::RowsReaderIterator
+{
+public:
+    struct ConstParameters : public std::vector<ConstParameter>
+	{
+		// Not thread safe.
+		static ConstParameters& instance() { return instance_; }
+
+		void addInteger(const std::string& name, long);
+		void addReal(const std::string& name, double);
+		void addString(const std::string& name, std::string value);
+
+		void add(const Assignments&);
+	private:
+		static ConstParameters instance_;
+	};
+
+
+	FakeODBIterator(const eckit::PathName& db, const std::string& sql); 
+	~FakeODBIterator ();
+
+	const FakeODBIterator& end() { return *reinterpret_cast<FakeODBIterator*>(0); }
+	//bool operator!=(const FakeODBIterator& o) { ASSERT(&o == 0); return iterator_ != iterator_.end(); }
+	//FakeODBIterator& operator++() { next(); return *this; }
+
+	odb::MetaData& columns();
+
+	virtual bool isNewDataset();
+	virtual double* data();
+
+//protected:
+	virtual bool next(eckit::ExecutionContext*);
+
+	int refCount_;
+	bool noMore_;
+    eckit::ExecutionContext* context_;
+
+private:
+	ODBIterator iterator_;
+
+	odb::MetaData columns_;
+	double* data_;
+
+	ConstParameters& constParameters_;
+};
+
+} // namespace tool 
+} //namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/ImportODBTool.cc b/odb_api/src/odb_api/migrator/ImportODBTool.cc
new file mode 100644
index 0000000..0bd0886
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ImportODBTool.cc
@@ -0,0 +1,207 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <iostream>
+#include <fstream>
+
+#include "eckit/filesystem/LocalPathName.h"
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/BigNum.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/migrator/ImportODBTool.h"
+#include "odb_api/migrator/ReptypeGenIterator.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/RowsCounter.h"
+#include "odb_api/tools/ODA2RequestTool.h"
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/ToolFactory.h"
+
+extern "C" {
+#include "odbdump.h"
+}
+
+#include "ODBIterator.h"
+#include "FakeODBIterator.h"
+
+namespace odb {
+namespace tool {
+
+typedef std::string str;   // string is a typedef in an ODB header.
+
+template <typename IN>
+ImportODBTool<IN>::ImportODBTool (int argc, char *argv[])
+: Tool(argc, argv),
+  noVerification_ (optionIsSet("-no_verification"))
+{}
+
+template <typename IN>
+ImportODBTool<IN>::ImportODBTool (const CommandLineParser& clp)
+: Tool(clp),
+  noVerification_ (optionIsSet("-no_verification"))
+{}
+
+template <typename IN>
+std::pair<unsigned long long, const std::vector<eckit::PathName> > ImportODBTool<IN>::importDispatching(const eckit::PathName& db, const std::string& sql, const std::string& dumpFile)
+{    
+    using namespace eckit;
+
+    Timer importingAndDipatching("Importing and dipatching");
+
+    Log::info() << "Importing data from '" << db
+        << "', query is '" << sql << "', into '"
+        << dumpFile << "' template." << std::endl;
+
+    odb::DispatchingWriter writer(dumpFile);
+    odb::DispatchingWriter::iterator w = writer.begin();
+
+    unsigned long long inRowsNumber = saveData<>(w, db, sql);
+
+    std::vector<eckit::PathName> files = (**w).outputFiles();
+    return std::make_pair(inRowsNumber, files);
+}
+
+template <typename IN>
+void ImportODBTool<IN>::validate(const eckit::PathName &db, const std::string& sql, const eckit::PathName& file)
+{
+    eckit::Timer verification("Validating dispatched output");
+
+    odb::Reader odaReader(file);
+    odb::Reader::iterator r(odaReader.begin());
+
+    IN reader(db, sql);
+    typename IN::iterator begin (reader.begin());
+    const typename IN::iterator end (reader.end());
+
+    // don't check missing value as the old ODB is not giving correct info for that some times (e.g.  sortbox)
+    odb::Comparator(false)
+        .compare(begin, end, r, odaReader.end(),
+            std::string("ODB input ") + db, std::string("converted output ") + file);
+}
+
+
+template <typename IN>
+void ImportODBTool<IN>::validateRowsNumber(unsigned long long inRowsNumber, const std::vector<eckit::PathName>& files)
+{
+    using namespace eckit;
+    
+	Timer verification("Validating dispatched output");
+
+	Log::info() << "ImportODBTool::validateRowsNumber: Validating output. " << std::endl;
+	Log::info() << "ImportODBTool::validateRowsNumber: input rows number: " << BigNum(inRowsNumber) << std::endl;
+
+	unsigned long long outRowsNumber (0);
+	unsigned long long outFilesSize (0);
+	for (size_t i = 0; i < files.size(); ++i)
+	{
+		const PathName &fn (files[i]);
+		unsigned long long n (RowsCounter::rowCount(fn));
+		unsigned long long fileSize (fn.size());
+
+		outRowsNumber += n;
+		outFilesSize += fileSize;
+
+		Log::info() << "ImportODBTool::validateRowsNumber: " << fn << ": " << BigNum(n) << " rows, file size: " << BigNum(fileSize) << "." << std::endl;
+	}
+	Log::info() << "ImportODBTool::validateRowsNumber: sum of output rows number: " << BigNum(outRowsNumber) << ", sum of file sizes: " << BigNum(outFilesSize) << std::endl;
+	ASSERT(inRowsNumber == outRowsNumber);
+}
+
+
+template <typename IN>
+void ImportODBTool<IN>::run()
+{
+    using namespace eckit;
+    
+    ASSERT("Wrong number of parameters. odb2oda.cc:main should check this."
+        && !(parameters().size() < 2 || parameters().size() > 4));
+
+    str db (parameters(1));
+    str sql (parameters().size() > 2 && parameters(2) != "." ? readFile(parameters(2)) : "");
+
+    Log::info() << "ImportODBTool::run: sql='" << sql << "'" << std::endl;
+
+    str dumpFile ((parameters().size() > 3) ? parameters(3) : (db + ".odb"));
+    if (dumpFile.substr(dumpFile.size() - 4) != ".odb")
+        dumpFile.append(".odb");
+
+    Log::info() << "Importing data from '" << db << "', query is '" << sql << "', into '" << dumpFile << "'." << std::endl;
+
+    odb::TemplateParameters params;
+    if (odb::TemplateParameters::parse(dumpFile, params).size() != 0)
+    {
+        DispatchResult r (importDispatching(db, sql, dumpFile));
+
+        unsigned long long importedRowsNumber (r.first);
+        const std::vector<eckit::PathName>& outFiles (r.second);
+
+        if (! noVerification_ )
+        {
+            Timer verification("Verification");
+            validateRowsNumber(importedRowsNumber, outFiles);
+        }
+    }
+    else
+    {
+        odb::Writer<> writer(dumpFile);
+        odb::Writer<>::iterator w (writer.begin());
+
+        unsigned long long importedRowsNumber (saveData<>(w, db, sql)); 
+        Log::info() << "Imported " << BigNum(importedRowsNumber) << " row(s)." << std::endl;
+
+        Timer verification("Verification");
+        Log::info() << "Verifying." << std::endl;
+        Log::info() << "Comparing data from: 1) ODB, and 2) ODA" << std::endl;
+
+        if (importedRowsNumber && ! noVerification_) 
+            validate(db, sql, dumpFile);
+    }
+    Log::info() << "ImportODBTool: Finished OK" << std::endl;
+}
+
+template <typename IN>
+template <typename OUT_ITERATOR>
+unsigned long long ImportODBTool<IN>::saveData(OUT_ITERATOR w, eckit::PathName odb, str sql) //, const SchemaAnalyzer &schema)
+{
+    using namespace eckit;
+    
+    Log::info() << "ImportODBTool<IN>::saveData: odb='" << odb << "', sql='" << sql << "'" << std::endl;
+    unsigned long long n (0);
+	try {
+        (**w).property("ODB_DATABASE", odb);
+        IN reader (odb, sql);
+        typename IN::iterator begin (reader.begin());
+        const typename IN::iterator end (reader.end());
+		
+        if (begin->columns().empty())
+        {
+            Log::warning() << "ImportODBTool<IN>::saveData: empty input data set." << std::endl;
+            return 0;
+        }
+
+        n = w->pass1(begin, end);
+        //w->close();
+    } catch (...) {
+        shell("[ -f odbdump.stderr ] && cat odbdump.stderr && cp odbdump.stderr " + odb + ".odb.log || echo odbdump.stderr not found", Here()); 
+        throw;
+    }
+    return n;
+}
+
+
+} // namespace tool 
+} // namespace odb 
+
+
diff --git a/odb_api/src/odb_api/migrator/ImportODBTool.h b/odb_api/src/odb_api/migrator/ImportODBTool.h
new file mode 100644
index 0000000..b444983
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ImportODBTool.h
@@ -0,0 +1,66 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_ImportODBTool_H
+#define odb_api_ImportODBTool_H
+
+#include <eckit/eckit.h>
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/migrator/OldODBReader.h"
+#include "odb_api/tools/Tool.h"
+
+
+class ODBIterator;
+class CommandLineParser;
+
+
+namespace odb {
+namespace tool {
+
+template <typename IN = odb::tool::OldODBReader>
+class ImportODBTool : public Tool {
+public:
+	ImportODBTool (int argc, char *argv[]); 
+	ImportODBTool (const CommandLineParser&); 
+
+	void run(); 
+
+protected:
+
+	template <typename OUT_ITERATOR>
+	unsigned long long saveData(OUT_ITERATOR w, eckit::PathName odb, std::string sql);
+	
+    typedef std::pair<unsigned long long, const std::vector<eckit::PathName> > DispatchResult;
+
+	DispatchResult importDispatching(const eckit::PathName& db, const std::string& sql, const std::string& dumpFile);
+
+    void validate(const eckit::PathName& db, const std::string& sql, const eckit::PathName& file);
+    void validateRowsNumber(unsigned long long, const std::vector<eckit::PathName>&);
+
+    void archiveFiles(const std::vector<eckit::PathName>&);
+private:
+
+// No copy allowed
+    ImportODBTool(const ImportODBTool&);
+    ImportODBTool& operator=(const ImportODBTool&);
+
+	bool isECFSPathName(const eckit::PathName fileName);
+	eckit::PathName readFromECFS(const eckit::PathName fileName);
+
+    bool noVerification_;
+};
+
+} // namespace tool 
+} //namespace odb 
+
+#include "ImportODBTool.cc"
+
+#endif 
diff --git a/odb_api/src/odb_api/migrator/Makefile b/odb_api/src/odb_api/migrator/Makefile
new file mode 100755
index 0000000..317bc1f
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/Makefile
@@ -0,0 +1,2 @@
+all: 
+	m
diff --git a/odb_api/src/odb_api/migrator/Makefile.old b/odb_api/src/odb_api/migrator/Makefile.old
new file mode 100755
index 0000000..ed78228
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/Makefile.old
@@ -0,0 +1,60 @@
+CC=$(ODB_CPLUSPLUS) $(CPPFLAGS)
+FC=pgf
+
+INSTALLLIBDIR=/var/tmp/tmpdir/stf/p4w/od/oda/src/lib
+INSTALLLIBDIR=../lib
+
+DEPLIB= $(INSTALLLIBDIR)/libEc.a $(INSTALLLIBDIR)/libOdb.a
+INCLUDE=-I../eclib -I../odalib -I../oda $(ODB_INCLUDE)
+
+# I put '-lifsaux'  in front of other libs required by ODB cause it helps to link on Suse91
+# $ uname -a
+# Linux drn05 2.6.5-7.308-smp #1 SMP Mon Dec 10 11:36:40 UTC 2007 x86_64 x86_64 x86_64 GNU/Linux
+# 
+# g++ (GCC) 4.2.1
+# 
+BINLIB=-L$(INSTALLLIBDIR) -lEc -lOdb  -lifsaux $(ODB_LIBS)
+LIBLIB=-L$(INSTALLLIBDIR) -lEc -lOdb  $(ODB_LIBS)
+
+PROGRAM.TARGET=odb2oda
+
+SRC.cc= \
+ODB2ODATool.cc \
+ODBIterator.cc \
+FakeODBIterator.cc \
+ReptypeGenIterator.cc \
+ksh.cc \
+TestImportODB.cc \
+
+
+SRC.c=odbdump_sami.c
+PERSIST=
+LINK=
+
+
+all :: program
+#all :: sharedlib 
+
+
+include ../make.mars
+
+install:
+	cp -p -f $(PROGRAM.TARGET) $(INSTALLBINDIR)
+	chmod -R a+rx $(INSTALLBINDIR)
+
+clean::
+	rm -f sqll.c sqly.c
+
+%.cc : junk/%.cc
+	rsh leda "echo 'cd /ccvobs/mars/mars/src/sql;cleartool mv junk/$@ .' | cleartool setview mars_server_linux"
+%.h : junk/%.h
+	rsh leda "echo 'cd /ccvobs/mars/mars/src/sql;cleartool mv junk/$@ .' | cleartool setview mars_server_linux"
+
+test::
+	time echo ./odb2oda \
+		-addcolumns reptype=0,class="'od'",stream="'oper'",type="'oda'" \
+		-genreptype sensor at hdr,satname_1 at hdr,satname_2 at hdr,satname_3 at hdr,satname_4 at hdr,bufrtype at hdr,subtype at hdr,obstype at hdr,codetype at hdr \
+		/hugetmp/data/2009072800/ECMA.conv sql_conv /hugetmp/data/2009072800/ECMA.conv.{reptype}.oda
+	#for f in /hugetmp/data/2009072800/ECMA.conv.*.oda; do echo oda oda2request $$f $$f.archive.request; oda oda2request $$f $$f.archive.request; done
+	#cd /tmp/p4/mars/client/dev/odb_dump/src && 
+	for f in /hugetmp/data/2009072800/ECMA.conv.*.oda.archive.request; do pwd;echo $$f;cat $$f|mars -t;echo; done
diff --git a/odb_api/src/odb_api/migrator/MigrateHandler.cc b/odb_api/src/odb_api/migrator/MigrateHandler.cc
new file mode 100644
index 0000000..8af50c9
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/MigrateHandler.cc
@@ -0,0 +1,80 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "eckit/filesystem/TmpFile.h"
+#include "eckit/io/MultiHandle.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/StringTool.h"
+
+#include "ecml/data/DataHandleFactory.h"
+
+#include "MigrateHandler.h"
+#include "migrator_api.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+using namespace odb::sql;
+
+MigrateHandler::MigrateHandler(const string& name) : RequestHandler(name) {}
+
+ecml::Values MigrateHandler::handle(ecml::ExecutionContext& context)
+{
+    string target (context.environment().lookup("target", "", context)),
+           filter (cleanUpSQLText(context.environment().lookup("filter", "", context))),
+           source (context.environment().lookup("source", "", context));
+
+    if (! source.size()) throw UserError("SOURCE is obligatory");
+    if (! target.size()) throw UserError("TARGET is obligatory");
+
+    Log::debug() << "MigrateHandler:" << " target: " << target << ", source: " << source << ", filter: " << filter << endl;
+
+
+    TmpFile sqlFile;
+    ofstream f (sqlFile.asString().c_str());
+    f << filter;
+    f.close();
+
+    int rc = 0;
+    if ((rc = odb::tool::import_odb_with_sql_in_file(source.c_str(),
+                                          sqlFile.asString().c_str(),
+                                          target.c_str())))
+    {
+        throw UserError("migrator failed");
+    }
+
+    ecml::List r;
+    r.append(target);
+
+    return r;
+}
+
+string MigrateHandler::cleanUpSQLText(const string& sql)
+{
+    if (sql.size() == 0)
+        return sql;
+
+    string s(sql);
+    StringTool::trimInPlace(s);
+    s = StringTool::isInQuotes(s) ? StringTool::unQuote(s) : s;
+    StringTool::trimInPlace(s);
+    if (s[s.size() - 1] != ';')
+        s.append(";");
+    return s;
+}
+
diff --git a/odb_api/src/odb_api/migrator/MigrateHandler.h b/odb_api/src/odb_api/migrator/MigrateHandler.h
new file mode 100644
index 0000000..c05420a
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/MigrateHandler.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef eckit_ecml_MigrateHandler_H
+#define eckit_ecml_MigrateHandler_H
+
+#include <string>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+class MigrateHandler : public ecml::RequestHandler {
+public:
+    MigrateHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+private:
+    std::string cleanUpSQLText(const std::string&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/MigratorTool.cc b/odb_api/src/odb_api/migrator/MigratorTool.cc
new file mode 100644
index 0000000..31baefb
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/MigratorTool.cc
@@ -0,0 +1,144 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file MigratorTool.cc
+///
+/// @author Piotr Kuchta, ECMWF, July 2009
+///
+
+#include <unistd.h>
+#include <iostream>
+#include <fstream>
+
+#include "eckit/parser//StringTools.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/migrator/FakeODBIterator.h"
+#include "odb_api/migrator/ImportODBTool.h"
+#include "odb_api/migrator/MigratorTool.h"
+#include "odb_api/migrator/ODB2ODATool.h"
+#include "odb_api/migrator/ODBIterator.h"
+#include "odb_api/migrator/OldODBReader.h"
+#include "odb_api/migrator/ReptypeGenIterator.h"
+#include "odb_api/MDI.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/ODBModule.h"
+
+#include "ODBMigratorModule.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace tool {
+
+
+int gdb(const std::vector<std::string>& params)
+{
+    std::cout << "gdb: params: " << params << std::endl;
+	str cmd(params[0]);
+	str args;
+	for (size_t i = 1; i < params.size(); ++i)
+		args += str(" ") + params[i];
+
+    eckit::PathName scriptFile = str(".gdb_") + params[2];
+	if (! scriptFile.exists())
+	{
+		str s = str("file ") + cmd + "\nbreak main\nrun " + args + "\n";
+        s += "catch throw\n";
+        eckit::FileHandle f(scriptFile);
+		f.openForWrite(1024);
+		f.write(s.c_str(), s.size());
+		f.close();
+	}
+	str vi = str("vi ") + scriptFile;
+	std::cout << "Executing '" << vi << "'" << std::endl;
+	system(vi.c_str());
+
+	str gdbCmd = str("gdb -x ") + scriptFile;
+	std::cout << "Executing '" << gdbCmd << "'" << std::endl;
+	return system(gdbCmd.c_str());
+}
+
+// valgrind --log-file=v.log --show-reachable=yes --leak-check=full ./oda test 
+int valgrind(const std::vector<std::string>& params)
+{
+    std::cout << "valgrind: params: " << params << std::endl;
+	str cmd(params[0]);
+	str args;
+	for (size_t i = 1; i < params.size(); ++i)
+		args += str(" ") + params[i];
+
+	str logFile = str("vg.log");
+	str vg = str("valgrind --log-file=") + logFile + " --show-reachable=yes --leak-check=full " + cmd + " " + args;
+	std::cout << "Executing '" << vg << "'" << std::endl;
+	return system(vg.c_str());
+}
+
+//MigratorTool::MigratorTool (int argc, char *argv[]) : Tool(argc, argv) { } 
+
+MigratorTool::MigratorTool (const CommandLineParser &clp) : Tool(clp) { } 
+
+void MigratorTool::runECML()
+{
+    ecml::ExecutionContext context;
+    ODBModule odbModule;
+    ODBMigratorModule migratorModule;
+    context.import(odbModule);
+    context.import(migratorModule);
+
+    std::vector<std::string> params(parameters());
+    params.erase(params.begin());
+    params.erase(params.begin());
+    for (size_t i (0); i < params.size(); ++i)
+    {
+        Log::info() << "*** Executing " << params[i] << endl;
+        context.executeScriptFile(params[i]);
+    }
+
+}
+
+void MigratorTool::run()
+{
+    if (parameters().size() > 1)
+    {
+        if (parameters(1) == "g" || parameters(1) == "vg")
+        {
+            std::vector<std::string> params;
+            for (size_t i(0); i < parameters().size(); ++i)
+                if (i != 1)
+                    params.push_back(parameters()[i]);
+
+            if (parameters(1) == "g")
+                gdb(params);
+            else valgrind(params);
+
+            return;
+        }
+        if (parameters(1) == "ecml")
+            return runECML();
+
+        if (parameters(1) == "test")
+        {
+            //odb::tool::test::TestRunnerApplication(argc(), argv()).start();
+            return; // TODO: Retrieve a status from the test runner
+        }
+    }
+
+    ODB2ODATool odb2oda(*this);
+    odb2oda.run();
+}
+
+} // namespace tool 
+} //namespace odb 
+
diff --git a/odb_api/src/odb_api/migrator/MigratorTool.h b/odb_api/src/odb_api/migrator/MigratorTool.h
new file mode 100644
index 0000000..0f76510
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/MigratorTool.h
@@ -0,0 +1,39 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MigratorTool_H
+#define MigratorTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class MigratorTool : public Tool {
+public:
+	MigratorTool (int argc, char *argv[]); 
+    MigratorTool (const CommandLineParser &);
+
+    void resetMDI(const std::string&);
+
+	void run(); 
+
+private:
+// No copy allowed
+    MigratorTool(const MigratorTool&);
+    MigratorTool& operator=(const MigratorTool&);
+
+    void runECML();
+};
+
+} // namespace tool 
+} //namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/migrator/ODB2ODATool.cc b/odb_api/src/odb_api/migrator/ODB2ODATool.cc
new file mode 100644
index 0000000..c82c1f8
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODB2ODATool.cc
@@ -0,0 +1,139 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ODB2ODATool.cc
+///
+/// @author Piotr Kuchta, ECMWF, July 2009
+///
+
+#include <iostream>
+#include <fstream>
+
+#include "eckit/parser/StringTools.h"
+#include "odb_api/migrator/FakeODBIterator.h"
+#include "odb_api/migrator/ImportODBTool.h"
+#include "odb_api/migrator/ODB2ODATool.h"
+#include "odb_api/migrator/ODBIterator.h"
+#include "odb_api/migrator/OldODBReader.h"
+#include "odb_api/migrator/ReptypeGenIterator.h"
+#include "odb_api/MDI.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/ToolFactory.h"
+
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+ODB2ODATool::ODB2ODATool (int argc, char *argv[])
+: Tool(argc, argv)
+{}
+
+ODB2ODATool::ODB2ODATool (const CommandLineParser &clp)
+: Tool(clp)
+{}
+
+
+/// -mdi <type1:MDI1,type2:MDI2,...>
+void ODB2ODATool::resetMDI(const std::string& s)
+{
+    typedef eckit::StringTools S;
+    std::vector<std::string> columns(S::split(",", s));
+    for (size_t i = 0; i < columns.size(); ++i)
+    {
+        std::vector<std::string> ass(S::split(":", columns[i]));
+
+        if (ass.size() != 2)
+            throw UserError("Error parsing option -mdi");
+
+        const std::string typeName(S::upper(ass[0]));
+        double value(StringTool::translate(ass[1]));
+
+        Log::info() << "  typeName: " << typeName << " value: " << value << std::endl;
+
+        if (typeName == "REAL")
+            odb::MDI::realMDI(value);
+        else if (typeName == "INTEGER" || typeName == "INT")
+            odb::MDI::integerMDI(value);
+        else
+            throw UserError("Changing MDI of types different than INTEGER or REAL not supported yet.");
+    }
+}
+
+void ODB2ODATool::run()
+{
+	if (parameters().size() < 2 || parameters().size() > 4)
+	{
+        std::cerr << "Usage:" << std::endl
+			<< "	" << parameters(0)
+
+            << " [<options>] <odb_database> [<file-with-select-statement-defining-dump> [<output.odb>]]" << std::endl
+
+            << "Options: " << std::endl << std::endl
+
+            << "\t[-genreptype <list-of-columns>]" << std::endl
+            << "\t[-reptypecfg <reptype-generation-config-file>]" << std::endl
+            << "\t[-addcolumns <list-of-assignments>]" << std::endl
+            << "\t[-mdi <type1:MDI1,type2:MDI2,...>]              Provide values of missing data indicators, e.g.: -mdi REAL:2147483647,INTEGER:2147483647" << std::endl
+            << "\t[-no_verification]                              Do not verify the conversion" << std::endl
+
+			<< std::endl;
+		return;
+	}
+
+    std::string nonDefaultMDIS(optionArgument<std::string>("-mdi", ""));
+    if (nonDefaultMDIS.size())
+        Log::info() << "Using non default missing data indicators: " << nonDefaultMDIS << std::endl;
+
+	bool addColumns (optionIsSet("-addcolumns"));
+	if (addColumns)
+		FakeODBIterator::ConstParameters::instance().add(Assignments(optionArgument<std::string>("-addcolumns", "")));
+ 
+	bool genReptype (optionIsSet("-genreptype"));
+	bool reptypeCfg (optionIsSet("-reptypecfg"));
+    if (optionIsSet("-mdi"))
+        resetMDI(optionArgument<std::string>("-mdi", ""));
+
+	ASSERT("Only one of -genreptype and -reptypecfg can be choosen at a time." && !(genReptype && reptypeCfg));
+
+	if (genReptype) {
+        std::vector<std::string> columns = StringTools::split(",", optionArgument<std::string>("-genreptype", ""));
+		ReptypeTableConfig::addColumns(columns.begin(), columns.end());
+	}
+
+	if (reptypeCfg) ReptypeTableConfig::load(optionArgument<std::string>("-reptypecfg", ""));
+
+	if (addColumns && (genReptype || reptypeCfg)) {
+		typedef odb::tool::TSQLReader<ReptypeGenIterator<FakeODBIterator> > R;
+		ImportODBTool<R>(*this).run();
+		return;
+	}
+
+	if (addColumns) {
+		ImportODBTool<odb::tool::TSQLReader<FakeODBIterator> >(*this).run();
+		return;
+	}
+
+	if (genReptype || reptypeCfg) {
+		ImportODBTool<odb::tool::TSQLReader<ReptypeGenIterator<ODBIterator> > >(*this).run();
+		return;
+	}
+
+	{	
+		ImportODBTool<odb::tool::OldODBReader>(*this).run();
+		Log::info() << "ImportODBTool<ODBIterator> finished OK" << std::endl;
+	}
+}
+
+} // namespace tool 
+} //namespace odb 
+
diff --git a/odb_api/src/odb_api/migrator/ODB2ODATool.h b/odb_api/src/odb_api/migrator/ODB2ODATool.h
new file mode 100644
index 0000000..7ba2976
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODB2ODATool.h
@@ -0,0 +1,37 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODB2ODATool_H
+#define ODB2ODATool_H
+
+
+
+namespace odb {
+namespace tool {
+
+class ODB2ODATool : public Tool {
+public:
+	ODB2ODATool (int argc, char *argv[]); 
+    ODB2ODATool (const CommandLineParser &);
+
+    void resetMDI(const std::string&);
+
+	void run(); 
+
+private:
+// No copy allowed
+    ODB2ODATool(const ODB2ODATool&);
+    ODB2ODATool& operator=(const ODB2ODATool&);
+};
+
+} // namespace tool 
+} //namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/migrator/ODBIterator.cc b/odb_api/src/odb_api/migrator/ODBIterator.cc
new file mode 100644
index 0000000..23243cd
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODBIterator.cc
@@ -0,0 +1,250 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ODBIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <map>
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/migrator/ODBIterator.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+
+
+namespace odb { namespace sql { class SQLInteractiveSession; } }
+
+extern "C" {
+#include "odbdump.h"
+}
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+//ODBIterator::ODBIterator(const PathName& db, const std::string& sql)
+ODBIterator::ODBIterator(const std::string& db, const std::string& sql)
+: db_(db),
+  odbHandle_(0),
+  noOfColumns_(0),
+  ci_(0),
+  columns_(new odb::MetaData(0, (odb::Column *) 0)),
+  data_(0),
+  nd_(0),
+  schemaParsed_(false),
+  noMore_(true)
+{
+	Log::info() << "ODBIterator::ODBIterator: @" << this << " db=" << db << std::endl;
+
+	const std::string odbDirectory = db; //.asString();
+	Log::info() << "Opening ODB in '" << odbDirectory << "'" << std::endl;
+	if (! PathName(odbDirectory).exists())
+        throw CantOpenFile(odbDirectory);
+	
+	std::string select = sql.size() ? sql : defaultSQL(db);
+
+	ASSERT(select.size() != 0 && select != "");
+
+	const std::string dbPath = db; //.asString();
+	const char *db_path = dbPath.c_str();
+	const char *sql_select = select.c_str();
+
+	Log::info() << "ODBIterator::ODBIterator: Calling odbdump_open(\"" << db_path << "\",\"" << sql_select << "\", NULL, NULL, NULL, &" << nd_ << ")" << std::endl;
+
+	odbHandle_ = odbdump_open(db_path, sql_select, NULL, NULL, NULL, &nd_);
+	ASSERT("odbdump_open returned NULL" && odbHandle_);
+	ASSERT("odbdump_open returned nd_ <= 0" && nd_ > 0);
+
+	data_ = new double[nd_];
+
+	//next();
+	//if (noMore_) Log::warning() << "ODBIterator::ODBIterator: result set empty, no data." << std::endl;
+}
+
+bool ODBIterator::next(eckit::ExecutionContext*)
+{
+	newDataset_ = false;
+	noOfColumns_ = odbdump_nextrow(odbHandle_, data_, nd_, &newDataset_);
+	if (noOfColumns_ == 0)
+	{
+		return !(noMore_ = true);
+	}
+
+	if (newDataset_)
+	{
+		Log::info() << "ODBIterator::readRow: new data set" << std::endl;
+		createColumns();
+	}
+
+	ASSERT(noOfColumns_ <= nd_);
+    // FIXME: read the missing values for a given constant from somewhere
+	// This is because sometime ODB has MISSING_VALUE_REAL in INTEGER columns...
+	// for example station_type at hdr in ECMA.conv
+	for (int i = 0; i < noOfColumns_; ++i)
+		if ((*columns_)[i]->type() == odb::INTEGER && data_[i] == odb::MDI::realMDI())
+			data_[i] = odb::MDI::integerMDI();
+
+	return !(noMore_ = false);
+}
+
+void ODBIterator::createColumns()
+{
+	Log::debug() << " => ODBIterator::createColumns: " << std::endl;
+
+	delete columns_;
+	columns_ = new odb::MetaData(noOfColumns_, (odb::Column *) 0);
+
+	bool preservePrecision = Resource<bool>("$ODB2ODA_PRESERVE_PRECISION", false);
+	
+	ci_ = (colinfo_t *) odbdump_destroy_colinfo( (colinfo_t *) ci_, noOfColumns_); 
+	int nci = 0;
+	ci_ = (colinfo_t *) odbdump_create_colinfo(odbHandle_, &nci); 
+
+    std::map<std::string, std::string> truenames;
+	for (int i = 0; i < noOfColumns_; i++)
+	{
+		colinfo_t *pci = &((colinfo_t *) ci_)[i];
+		std::string name = pci->nickname ? pci->nickname : pci->name;
+        truenames[name] = pci->name;
+
+		odb::ColumnType type = odb::REAL;
+		double missing = odb::MDI::integerMDI(); 
+
+		switch(pci->dtnum)
+		{
+			case DATATYPE_REAL4:
+				type = odb::REAL;
+				missing = odb::MDI::realMDI(); 
+				break;
+
+			case DATATYPE_REAL8:
+				type = preservePrecision ? odb::DOUBLE : odb::REAL;
+				missing = odb::MDI::realMDI(); 
+				break;
+
+			case DATATYPE_STRING:
+				type = odb::STRING;
+				break;
+
+			case DATATYPE_INT4:
+			case DATATYPE_YYYYMMDD:
+			case DATATYPE_HHMMSS:
+				type = odb::INTEGER;
+				break;
+
+			case DATATYPE_BITFIELD:
+				type = odb::BITFIELD;
+				break;
+
+			default:
+				Log::error() << "Unsupported type: [" << pci->type_name << "] " << name
+							<< std::endl;
+				break;
+		}
+		setColumn(i, name, type, missing);
+	}
+	getSchema(db_).updateBitfieldsDefs(columns(), truenames);
+	Log::debug() << " <= ODBIterator::createColumns: " << std::endl;
+}
+
+void ODBIterator::destroy()
+{
+	Log::debug() << "ODBIterator::destroy: @" << this << std::endl;
+	odbdump_destroy_colinfo( (colinfo_t *) ci_, noOfColumns_); 
+	odbdump_close(odbHandle_);
+	delete columns_;
+	delete [] data_;
+}
+
+ODBIterator::~ODBIterator ()
+{
+	Log::info() << "ODBIterator::~ODBIterator: @" << this << std::endl;
+	destroy();
+}
+
+odb::MetaData& ODBIterator::columns() { return *columns_; }
+
+double* ODBIterator::data() { return data_; }
+
+bool ODBIterator::isNewDataset() { return newDataset_; }
+
+int ODBIterator::setColumn(unsigned long index, std::string& name, odb::ColumnType type, double missingValue)
+{
+	//Log::debug() << "ODBIterator::setColumn: " << index << ", " << name << ", " << columnTypeName(type) << ", " << missingValue << std::endl;
+
+	ASSERT(int(index) < noOfColumns_);
+	ASSERT(columns_);
+	odb::Column* col = (*columns_)[index];
+	delete col;
+	col = new odb::Column(*columns_);
+	ASSERT(col);
+
+	col->name(name); 
+	col->type<DataStream<SameByteOrder, DataHandle> >(type, false);
+	col->missingValue(missingValue);
+
+	(*columns_)[index] = col;
+	return 0;
+}
+
+
+//const char* defaultSQL = "select {!/LINK/} from hdr,body,errstat";
+PathName ODBIterator::schemaFile(const PathName db)
+{
+    Log::info() << "ODBIterator::schemaFile: db=" << db << std::endl;
+
+	std::string d = db;
+
+	if (d[d.size() - 1] != '/')
+		d += "//";
+
+	std::string s = StringTools::split(".", StringTools::split("//", d).back())[0];
+
+	return d + s + ".sch";
+}
+
+const odb::sql::SchemaAnalyzer& ODBIterator::getSchema(const PathName& db)
+{
+	//odb::sql::SQLSelectFactory::instance().config(); //odb::sql::SQLOutputConfig());
+	// FIXME: this is not good cause when you call the method several times with different db it will fail...
+	// It's not used like that at the moment, anyway. So perhaps I should make db a member (db_).
+	if (!schemaParsed_)
+	{
+		PathName schemaFile = this->schemaFile(db);
+		Log::info() << "ImportODBTool::getSchema: parsing '" << schemaFile << "'" << std::endl;
+	
+		odb::sql::SQLParser p;
+		p.parseString(session_, StringTool::readFile(schemaFile), static_cast<DataHandle*>(0), session_.selectFactory().config());
+		schemaParsed_ = true;
+	}
+
+	return session_
+			.currentDatabase()
+			.schemaAnalyzer();
+}
+
+std::string ODBIterator::defaultSQL(const PathName& db)
+{
+	return getSchema(db).generateSELECT();
+}
+
+} // namespace tool 
+} //namespace odb 
+
diff --git a/odb_api/src/odb_api/migrator/ODBIterator.h b/odb_api/src/odb_api/migrator/ODBIterator.h
new file mode 100644
index 0000000..b6dfdda
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODBIterator.h
@@ -0,0 +1,86 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODBIterator_H
+#define ODBIterator_H
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/ColumnType.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class ExecutionContext; }
+
+namespace odb { namespace sql { class SchemaAnalyzer; class SQLInteractiveSession; } }
+
+namespace odb {
+
+class MetaData;
+
+namespace tool {
+
+class ODBIterator 
+{
+public:
+    ODBIterator(const std::string& db, const std::string& sql); 
+    ~ODBIterator ();
+
+    void destroy();
+
+    const ODBIterator& end() { return *reinterpret_cast<ODBIterator*>(0); }
+
+    bool operator!=(const ODBIterator& o) { return hasNext_; }
+
+    ODBIterator& operator++() { next(context_); return *this; }
+
+    odb::MetaData& columns();
+
+    virtual bool isNewDataset();
+    virtual double* data();
+
+    static eckit::PathName schemaFile(const eckit::PathName db);
+
+    virtual bool next(eckit::ExecutionContext*);
+
+protected:
+    int setColumn(unsigned long index, std::string& name, odb::ColumnType type, double missingValue);
+
+private:
+    void createColumns();
+
+    eckit::PathName db_;
+
+    void *odbHandle_;
+    int noOfColumns_;
+    //colinfo_t *ci_;
+    void *ci_;
+    odb::MetaData *columns_;
+    int newDataset_;
+    double* data_;
+    int nd_;
+    unsigned long long count_;
+    bool hasNext_;
+
+    std::string defaultSQL(const eckit::PathName& db);
+    const odb::sql::SchemaAnalyzer& getSchema(const eckit::PathName& db);
+    bool schemaParsed_;
+    odb::sql::SQLInteractiveSession session_;
+
+    friend class FakeODBIterator;
+public:
+    int refCount_;
+    bool noMore_;
+    eckit::ExecutionContext* context_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/ODBMigratorModule.cc b/odb_api/src/odb_api/migrator/ODBMigratorModule.cc
new file mode 100644
index 0000000..4f720b9
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODBMigratorModule.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ODBMigratorModule.cc
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#include <string>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+
+#include "ODBMigratorModule.h"
+#include "MigrateHandler.h"
+
+namespace odb {
+
+using namespace std;
+using namespace eckit;
+using namespace ecml;
+
+ODBMigratorModule::ODBMigratorModule() {}
+ODBMigratorModule::~ODBMigratorModule() {}
+
+static Request native(const string& name) { return new Cell("_native", name, 0, 0); }
+
+void ODBMigratorModule::importInto(ExecutionContext& context)
+{
+    static MigrateHandler migrate("odb.migrate");
+    context.registerHandler("migrate", migrate);
+}
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/migrator/ODBMigratorModule.h b/odb_api/src/odb_api/migrator/ODBMigratorModule.h
new file mode 100644
index 0000000..22730ff
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ODBMigratorModule.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File ODBMigratorModule.h
+// Piotr Kuchta - (c) ECMWF August 2015
+
+#ifndef eckit_ecml_ODBMigratorModule_H
+#define eckit_ecml_ODBMigratorModule_H
+
+#include "ecml/core/Module.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace odb {
+
+class ODBMigratorModule : public ecml::Module {
+public:
+    ODBMigratorModule();
+    ~ODBMigratorModule();
+    void importInto(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/Odb2_to_odb1_era.f90 b/odb_api/src/odb_api/migrator/Odb2_to_odb1_era.f90
new file mode 100644
index 0000000..b9040e8
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/Odb2_to_odb1_era.f90
@@ -0,0 +1,432 @@
+
+program odb2_to_odb1_era
+
+  use odb1
+  use odb2
+  implicit none
+
+  integer,parameter     :: nmaxsopt=1000 ! Quite generous, but please adapt if required
+  integer               :: ioptval,getopt
+  character*120         :: carg
+  character             :: options*14,copt
+
+  integer            :: ipool,npools,iproc,nproc,iodb2,nodb2,nsetval,isetval,iseqoff,iseqfirst
+  integer,allocatable:: ioff(:)
+  character(len=128) :: table,croot,dbname
+  character(len=128) :: odb2file(nmaxsopt),csetval(nmaxsopt)
+  type (todb1)       :: sch1,sch2       ! odb-1 schema files
+  type (todb2)       :: db2             ! odb-2 
+
+  integer :: itable
+
+! 1. Crack options
+! ----------------
+
+  data options/'i:r:l:s:w:f:o:'/
+
+! Set default values
+  npools=1; nsetval=0
+  nodb2=0 ; odb2file(:)="" ; croot="" ; csetval(:)=""; dbname="ECMA"
+  iseqoff=0 ; iseqfirst=0
+  do
+    ioptval=getopt(options,carg)
+    carg=trim(carg)
+    copt=char(ioptval)
+ 
+    if (ioptval <=0) exit
+    if (copt == 's') read(carg,*)npools
+    if (copt == 'f') read(carg,*)iseqfirst
+    if (copt == 'o') read(carg,*)iseqoff
+    if (copt == 'i') nodb2            = nodb2+1
+    if (copt == 'w') nsetval          = nsetval+1
+                     if (max(nodb2,nsetval)>nmaxsopt) then
+                         write(6,*)'Please increase nmaxsopt:',nmaxsopt 
+                         call abort
+                     endif
+    if (copt == 'i') odb2file(nodb2)  = trim(carg)
+    if (copt == 'w') csetval(nsetval) = trim(carg)
+    if (copt == 'r') croot            = trim(carg)
+    if (copt == 'l') dbname           = trim(carg)
+  enddo
+
+! Add "@" to croot in case not present
+  if((croot/="").and.(index(croot,"@"))==0)croot="@"//trim(croot)
+
+  if (nodb2==0) then
+     write(6,*)'Usage: odb2_to_odb1_era -i odb2file [-l dbname -r @mytableroot -s npools -o iseqno_offset -w column at table=my_value ]'
+     return
+  endif
+
+  write(6,*)       "Welcome to odb2_to_odb1_era:"
+  do iodb2=1,nodb2
+     write(6,'(2A)')  "ODB-2 input file:",trim(odb2file(iodb2)) 
+  enddo
+  write(6,'(2A)')  "ODB-1   database:",trim(dbname) 
+  write(6,'(2A)')  "ODB-1 root table:",trim(croot) 
+  write(6,'(A,I4)')"Number of pools :",npools
+
+
+! 2. Process
+! ----------
+
+! Open ODB-1, read schema and place its contents in sch1
+  call odb1_open(sch1,dbname,npools,iproc,nproc)
+  write(6,'(A,I4 )')"Number of procs :",nproc
+  write(6,'(A,I4 )')"Index  of proc  :",iproc
+  write(6,'(A,I12)')"First Seqno     :",iseqfirst
+  write(6,'(A,I12)')"Seqno  ioffset  :",iseqoff
+
+! Setup ODB-2 libraries
+  call odb_start()
+
+! Do the work
+  allocate(ioff(npools))
+  do ipool=iproc,npools,nproc           ! my pools only
+     ioff(ipool)= iseqfirst + (ipool-1)*iseqoff
+     do iodb2=ipool,nodb2,npools        ! my files only, if any
+
+     ! Read ODB-2 file and place contents in db2
+       call odb2_read_all(db2,odb2file(iodb2))
+     ! call odb2_header(db2)  ! Print summary
+
+       call odb22odb1(sch2,db2,sch1,croot)
+     ! call odb1_header(sch2)  ! Print summary
+
+       call odb1_merge(sch2,sch1)
+
+     ! Overwrite some columns, if any
+       do isetval=1,nsetval
+           call odb1_set_value(sch1,csetval(isetval))
+       enddo
+       call odb1_reassign_seqno(sch1,ioff(ipool))
+
+       call odb1_write(sch1,ipool)
+     enddo
+
+  enddo
+
+! Close the ODB-1 database
+  call odb1_close(sch1)
+  deallocate(ioff)
+
+contains
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb22odb1(db1,db2,template1,croot)
+! convert odb2 db2 database to odb1 db1, using the odb1 template
+  implicit none
+  type (todb2) :: db2
+  type (todb1) :: template1,db1
+
+  character(len=128)             :: croot
+
+  call odb12_dress_columns(db2,template1)          ! add any missing @table info from template1 in db2
+  call create_odb1_schema(db1,db2,template1,croot) ! Figure out ODB-1 table structure
+  call odb12_compress(db1,db2)                     ! Compress ODB-2 table to ODB-1 tables
+
+end subroutine odb22odb1
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb12_dress_columns(db2,db1)
+! In case missing: add @table to db2 columns, from info in db1 schema
+  implicit none
+  type (todb2) :: db2
+  type (todb1) :: db1
+
+  type (odb1_table), pointer :: tbl
+  character(len=128)         :: col2,col1
+  integer                    :: i1,i2,j,itable
+
+  do i2=1,db2%ncol
+     col2=""; write(col2,'(A)')trim(db2%cname(i2))
+     if(index(col2,"@")/=0) cycle                        ! it's o.k.
+
+     do itable=1,db1%ntable
+        tbl => db1%tbl(itable)
+        do i1=1,tbl%ncol
+           col1="" ; write(col1,'(A)')trim(tbl%cname(i1))
+           j=index(col1,"@")-1
+           if ( trim(col2)==trim(col1(1:j)) ) then
+              write(*,*)"Dress table: ",trim(col2)," --> ",trim(col1)
+              col2="" ; write(col2,'(A)')trim(col1)
+              exit
+           endif
+        enddo
+     enddo
+     db2%cname(i2)="" ; write(db2%cname(i2),'(A)')trim(col2)
+  enddo
+
+end subroutine odb12_dress_columns
+
+!-----------------------------------------------------------------------------------------
+
+subroutine create_odb1_schema(db1,db2,template1,croot)
+
+  implicit none
+  type (todb2)        :: db2
+  type (todb1)        :: template1,db1
+  character(len=128) :: croot
+
+  type (odb1_table), pointer     :: tbl,dbl
+  logical,allocatable            :: ltable(:),ltall(:)
+  integer,allocatable            :: iparent(:),itree(:),imap(:)
+  character(len=128),allocatable :: tname(:),cname(:),ctype(:)
+  integer                        :: i,j,k,l,it,ip,nt,ntable,ncol,iroot
+  character*128                  :: tab,parent
+  logical                        :: lkeep
+
+  nt=template1%ntable
+  allocate(ltall  (0:nt)) ; ltall  (0:nt)=.false.
+  allocate(ltable (0:nt)) ; ltable (0:nt)=.false.
+  allocate(iparent(0:nt)) ; iparent(0:nt)=0
+  allocate(itree  (0:nt)) ; itree  (0:nt)=0
+
+  iroot=odb1_find_index(template1,croot)
+  if(trim(croot)=="")iroot=-1
+
+! Find all tables, plus missing link tables up to "@"
+  do i=1,db2%ncol
+     j=index(db2%cname(i),"@")
+     tab=""; write(tab,'(A)')trim(db2%cname(i)(j:))
+
+     it=odb1_find_index(template1,tab)
+     if (ltable(it)) cycle
+         ltable(it)=.true.
+     do
+       ltall(it)=.true.
+       if(trim(tab)=="@") exit ! we've reached root
+       ip=odb1_find_parent(template1,tab,parent)
+       iparent(it)=ip
+       it=ip
+       tab="";write(tab,'(A)')trim(parent)
+     enddo
+  enddo
+
+! Make a selection, depending on the contents of croot
+  do i=0,nt  ! disregard tables that are ancestor from croot
+    if(.not.ltable(i)) cycle
+    if(iroot<0) cycle   ! take everything on board
+    j=i
+    lkeep=(j==iroot)
+    do
+      j=iparent(j)
+      if (j==iroot) lkeep=.true. 
+      if (j==0) exit
+    enddo
+    ltable(i)=lkeep
+  enddo
+
+  ntable=0
+  do i=1,nt ! check whether any ancestors belong to the ODB2 table
+    if(.not.ltall(i)) cycle
+    j=i
+    lkeep=ltable(j)
+    do
+      j=iparent(j)
+      if (j==0) exit
+      if (ltable(j)) lkeep=.true.
+      itree(i)=itree(i)+1
+    enddo
+    ltall(i)=lkeep
+    if(.not.lkeep )cycle
+    ntable=ntable+1
+  enddo
+
+! Unsorted Table names ; only used to find childs later on
+  allocate(tname(ntable)) ; tname(:)=""
+  k=0
+  do i=1,nt ; if(.not.ltall(i)) cycle
+     k=k+1
+     tbl => template1%tbl(i)
+     write(tname(k),'(A)')trim(tbl%tname)
+  enddo
+
+! Sort Tables, such that parent(i) = table(j) ==> i>j
+  allocate(imap(0:nt)); imap(:)=0
+  allocate(cname(db2%ncol),ctype(db2%ncol)) ! Safe upper limit
+
+  db1%ntable=ntable
+  allocate(db1%tbl(0:ntable))
+
+  write(db1%tbl(0)%tname,'(A)')"@" ; db1%tbl(0)%ncol=0
+  k=0
+  do j=0,maxval(itree) 
+     do i=1,nt
+        if(.not.ltall(i)) cycle
+        if(itree(i)/=j) cycle
+        k=k+1
+
+        tbl => template1%tbl(i)
+        tab=""; write(tab,'(A)')trim(tbl%tname)
+        imap(i)=k
+        ip=imap(iparent(i))
+
+        ncol=0 ; cname(:)="" ; ctype(:)=""
+        do l=1,db2%ncol                 ! Find all regular columns
+           it=index(db2%cname(l),"@")
+           if( trim(db2%cname(l)(it:))/=trim(tab) ) cycle
+           ncol=ncol+1
+           write(cname(ncol),'(A)')trim(db2%cname(l))
+           call odb1_find_type(template1,cname(ncol),ctype(ncol))
+        enddo
+
+        do l=1,ntable                   ! Add links to children
+           if(odb1_child_linkindex(tbl,tname(l))==0)cycle
+
+           ncol=ncol+1
+           write(ctype(ncol),'(A)')trim(tloffset)
+           write(cname(ncol),'(A,"(",A,")",A)')&
+           &    trim(cloffset),trim(tname(l)(2:)),trim(tab)
+           ncol=ncol+1
+           write(ctype(ncol),'(A)')trim(tlinklen)
+           write(cname(ncol),'(A,"(",A,")",A)')&
+           &    trim(clinklen),trim(tname(l)(2:)),trim(tab)
+        enddo 
+
+        call odb1_create_table(db1%tbl(k),tab,ip,ncol,cname(1:ncol),ctype(1:ncol))
+     enddo
+  enddo
+  deallocate(ltall,ltable,iparent,itree,imap,tname,cname,ctype)
+
+end subroutine create_odb1_schema
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb12_compress(db1,db2)
+! Fill and compress data contents of ODB2 db2, in schema-matched ODB1 db1
+! group aligned tables, and compress if contents of a 'super' row has not changed
+
+  use aligned
+  implicit none
+  type (todb1) :: db1
+  type (todb2) :: db2
+
+  type (odb1_table), pointer :: tbl,tbp
+  integer                    :: k,i,it,ic,i1,i2,irow,j,ntable,ncol,ncol2,nrow2,nrowc,nrowp,kp,nalign
+  integer,allocatable        :: icmp(:,:),icol(:),ialign(:)
+  real*8 ,allocatable        :: data(:,:)
+  character(len=128)         :: tab
+  
+  ntable=db1%ntable
+  nrow2 =db2%nrow
+  ncol2 =db2%ncol
+ 
+  allocate(icol(ncol2))              ! map between odb2 column and odb1 column
+  allocate(ialign(ntable))           ! list of tables that are aligned
+  allocate(icmp(0:nrow2+1,0:ntable)) ! index of compressed table;
+           icmp(0:nrow2+1,0:ntable)=-1
+
+  write(*,*)'ODB-2 data compression:'
+  do k=1,ntable
+     tbl => db1%tbl(k)
+
+     tab="" ; write(tab,'(A)')trim(tbl%tname)
+     kp=tbl%iparent
+
+     if (icmp(1,k)<0) then    ! If it's 0 we've already dome this column
+        ialign(:)=0 ; nalign=0
+        ncol=0 ; icol(:)=0
+        do i=k,ntable
+           tbp => db1%tbl(i)
+           if(.not.odb1_aligned(tab,tbp%tname)) cycle
+           nalign=nalign+1 ; ialign(nalign)=i
+
+          ! Find odb-2 columns that relate with table tbp
+            do j=1,db2%ncol
+               it=index(db2%cname(j),"@")
+               if( trim(db2%cname(j)(it:))/=trim(tbp%tname) ) cycle
+               ncol=ncol+1 ; icol(ncol)=j
+            enddo
+
+        enddo
+
+      ! Determine the level of compression for each table
+      ! Info ends up in the icmp(nrow2,ntable) array
+        allocate(data(2,0:ncol))
+        i1=1; 
+        nrowc=0 
+        do j=1,nalign
+           icmp(1,ialign(j))=0 ! Always take the first row
+        enddo
+
+        do i=1,nrow2
+           i2=1+mod(i1,2)
+           data(i2,0)=icmp(i,kp)        ! to check whether parent changed
+           do j=1,ncol
+              data(i2,j)=db2%data(icol(j),i)
+           enddo
+           if(i>1) then                 ! contents changed?
+              do j=0,ncol
+                  if(data(i2,j)/=data(i1,j)) exit
+              enddo
+              if(j/=ncol+1) nrowc=nrowc+1
+              do j=1,nalign
+                 icmp(i,ialign(j))=nrowc
+              enddo
+           endif
+           i1=i2
+        enddo
+        do j=1,nalign
+           icmp(nrow2+1,ialign(j))=nrowc+1 ! convenient extension to calculate differences
+        enddo
+        deallocate(data)
+     endif
+
+   ! Now we know the # of rows, allocate space and fill
+     nrowc=icmp(nrow2+1,k)
+     call odb1_alloc_data(tbl,nrowc)
+
+   ! Find odb-2 columns that relate with table tbl
+     ncol=0 ; icol(:)=0
+     do j=1,db2%ncol
+        it=index(db2%cname(j),"@")
+        if( trim(db2%cname(j)(it:))/=trim(tab) ) cycle
+        ncol=ncol+1 ; icol(ncol)=j
+     enddo
+
+     allocate(data(2,ncol))
+     irow=0
+     do i=1,nrow2
+        if (icmp(i,k)==icmp(i-1,k)) cycle  ! new compressed row?
+        do j=1,ncol
+          data(1,j)=db2%data(icol(j),i)
+        enddo
+        irow=irow+1
+        call odb1_add_block(tbl,data(1:1,1:ncol),irow,1,1,ncol)
+     enddo
+     deallocate(data)
+
+     write(*,*)'# Columns/Children/Rows ',ncol,(tbl%ncol-ncol)/2,nrowc,trim(tab)
+
+   ! Sort out the links to its parent
+     if(kp==0) cycle
+     tbp => db1%tbl(kp)
+
+     nrowp=tbp%nrow
+     allocate(data(nrowp+1,2))
+
+     irow=0
+     do i=1,nrow2+1    
+        if (icmp(i,kp)==icmp(i-1,kp)) cycle
+        irow=irow+1 
+        data(irow,1)=icmp(i,k)                   ! Offset
+     enddo
+
+     do irow=1,nrowp
+        data(irow,2)=data(irow+1,1)-data(irow,1) ! Length
+     enddo
+
+     ic=odb1_child_linkindex(tbp,tab)            ! index of link table
+     call odb1_add_block(tbp,data(1:nrowp,1:2),1,ic,nrowp,2)
+
+     deallocate(data)
+
+  enddo
+  deallocate(icol,icmp)
+end subroutine odb12_compress
+
+!-----------------------------------------------------------------------------------------
+
+end program odb2_to_odb1_era
diff --git a/odb_api/src/odb_api/migrator/OldODBReader.h b/odb_api/src/odb_api/migrator/OldODBReader.h
new file mode 100644
index 0000000..d1355bc
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/OldODBReader.h
@@ -0,0 +1,25 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_OldODBReader_H
+#define odb_api_OldODBReader_H
+
+#include "odb_api/migrator/TSQLReader.h"
+#include "odb_api/migrator/ODBIterator.h"
+
+namespace odb {
+namespace tool {
+
+typedef odb::tool::TSQLReader<ODBIterator> OldODBReader;
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/ReptypeGenIterator.cc b/odb_api/src/odb_api/migrator/ReptypeGenIterator.cc
new file mode 100644
index 0000000..05e3c4b
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ReptypeGenIterator.cc
@@ -0,0 +1,234 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file ReptypeGenIterator.cc
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#include <strings.h>
+
+extern "C" {
+#include "odbdump.h"
+}
+
+#include "eckit/parser/StringTools.h"
+#include "eckit/parser/Tokenizer.h"
+#include "eckit/types/Types.h"
+#include "eckit/utils/Translator.h"
+#include "odb_api/migrator/FakeODBIterator.h"
+#include "odb_api/migrator/ODBIterator.h"
+#include "odb_api/migrator/ReptypeGenIterator.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+std::ostream& operator<<(std::ostream& s, const ReptypeTable& m)
+{
+	s << "{";
+	for (ReptypeTable::const_iterator it = m.begin(); it != m.end(); ++it)
+	{
+		s << "[";
+		const Values& vals = it->first;
+		for (Values::const_iterator i = vals.begin(); i != vals.end(); ++i)
+			s << *i;
+		s << "]";
+
+		s << " : " << it->second << "," << std::endl;
+	}
+	s << "}";
+	return s;
+}
+
+/*
+Hi Peter,
+
+most of the columns given by Manuel do not exist yet.
+
+As a first attempt, you can use
+sensor at hdr,
+satname_1 at hdr,
+satname_2 at hdr,
+satname_3 at hdr,
+satname_4 at hdr,
+bufrtype at hdr,
+subtype at hdr,
+obstype at hdr,
+codetype at hdr
+(when sat.len > 0 you can add satid at sat).
+
+Anne. 
+*/
+
+std::vector<std::string> ReptypeTableConfig::columns_ = std::vector<std::string>();
+ReptypeTable ReptypeTableConfig::reptypeTable_ = ReptypeTable();
+
+void ReptypeTableConfig::load(const PathName& fileName)
+{
+	std::string s = Tool::readFile(fileName, false);
+	Log::debug() << "ReptypeTableConfig::load(fileName = '" << fileName << "')" << std::endl;
+	Log::debug() << "ReptypeTableConfig::load(fileName = '" << fileName << "')" << "'" << s << "'" << std::endl;
+
+    std::vector<std::string> lines = StringTools::split("\n", s);
+
+	size_t i = 0;
+	while (lines[i] == "")
+		++i;
+
+    std::vector<std::string> firstLine = StringTools::split(":", lines[i]);
+	ASSERT(firstLine[0] == "reptype");
+
+    std::vector<std::string> columnNames = StringTools::split(",", firstLine[1]);
+	std::for_each(columnNames.begin(), columnNames.end(), Tool::trimInPlace);
+
+	columns_.insert(columns_.end(), columnNames.begin(), columnNames.end());
+	++i;
+
+    for (; i < lines.size(); ++i)
+    {
+        std::vector<std::string> lr;
+        Tokenizer(":")(lines[i], lr);
+
+        // Skip empty lines.
+        if (lr.size() == 0) continue;
+
+		ASSERT(lr.size() == 2);
+
+		int reptype_value = Translator<std::string, int>()(lr[0]);
+
+        std::vector<std::string> rvalues = StringTools::split(",", lr[1]);
+		//Log::debug() << "ReptypeTableConfig::load: rvalues = " << rvalues << std::endl;
+
+		ASSERT("Number of values must be equal to number of column names (first line)"
+			&& columnNames.size() == rvalues.size());
+
+		std::for_each(rvalues.begin(), rvalues.end(), Tool::trimInPlace);
+		Values vals;
+		Log::debug() << "ReptypeTableConfig::load: " << reptype_value << " = ";
+		for (size_t j = 0; j < rvalues.size(); ++j)
+		{
+			std::string &vs(rvalues[j]);
+
+			Log::debug() << "{" << vs << ":" << Tool::isInQuotes(vs) << "}";
+			double v = Tool::isInQuotes(vs)
+				? Tool::cast_as_double(Tool::unQuote(vs))
+				: Translator<std::string, double>()(vs);
+			vals.push_back(v);
+			Log::debug() << "[" << rvalues[j] << "] '" << Tool::double_as_string(v) << "', " << std::endl;
+		}
+		Log::debug() << std::endl;
+		//at(vals) = reptype_value;
+		reptypeTable_[vals] = reptype_value;
+	}
+
+	//Log::debug() << "ReptypeTableConfig::load: columns_ = " << columns_ << std::endl; 
+	//Log::debug() << "ReptypeTableConfig::load: reptypeTable_ = " << reptypeTable_ << std::endl;
+}
+
+template<typename ITERATOR, typename CONFIG>
+ReptypeGenIterator<ITERATOR, CONFIG>::ReptypeGenIterator(const PathName& db, const std::string& sql)
+: iterator_(db, sql),
+  data_(0),
+  reptypeTable_(CONFIG::reptypeTable())
+{
+	odb::MetaData &md = iterator_.columns();
+	data_ = new double[md.size()];
+	reptypeIndex_ = md.columnIndex("reptype");
+
+	const std::vector<std::string>& columnNames = CONFIG::columns();
+	for (std::vector<std::string>::const_iterator i = columnNames.begin(); i != columnNames.end(); ++i)
+	{
+		std::string name = *i;
+
+		Log::debug() << "ReptypeGenIterator<ITERATOR>::ctor: " << name << std::endl;
+
+		indices_.push_back(iterator_.columns().columnIndex(name));
+		values_.push_back(0);
+	}
+	Log::debug() << "ReptypeGenIterator::ReptypeGenIterator: Reptype table:" << std::endl;
+	Log::debug() << "reptypeTable_ = " << reptypeTable_ << std::endl;
+}
+
+template<typename ITERATOR, typename CONFIG>
+ReptypeGenIterator<ITERATOR, CONFIG>::~ReptypeGenIterator()
+{
+	Log::debug() << "ReptypeGenIterator::~ReptypeGenIterator: Reptype table:" << std::endl;
+	Log::debug() << "reptypeTable_.size() = " << reptypeTable_.size() << std::endl;
+	Log::debug() << "reptypeTable_ =" << reptypeTable_ << std::endl;
+	delete [] data_;
+}
+
+template<typename ITERATOR, typename CONFIG>
+odb::MetaData& ReptypeGenIterator<ITERATOR, CONFIG>::columns()
+{
+	return iterator_.columns();
+}
+
+template<typename ITERATOR, typename CONFIG>
+double* ReptypeGenIterator<ITERATOR, CONFIG>::data()
+{
+	return data_;
+}
+
+template<typename ITERATOR, typename CONFIG>
+bool ReptypeGenIterator<ITERATOR, CONFIG>::isNewDataset()
+{
+	return iterator_.isNewDataset();
+}
+
+template<typename ITERATOR, typename CONFIG>
+bool ReptypeGenIterator<ITERATOR, CONFIG>::next(eckit::ExecutionContext* context)
+{
+	bool r = iterator_.next(context);
+	if (r)
+	{
+		double* trueData = iterator_.data();
+
+        std::copy(trueData, trueData + iterator_.columns().size(), data_);
+
+		for (size_t i = 0; i < indices_.size(); ++i)
+			values_[i] = data_[indices_[i]];
+
+		ReptypeTable::const_iterator it = reptypeTable_.find(values_); 
+		if (it != reptypeTable_.end())
+			data_[reptypeIndex_] = it->second;
+		else
+		{
+			// TODO: rethink!
+			Log::info() << "ReptypeGenIterator::next(): No matching report type, creating new one." << std::endl;
+
+			size_t newRT = reptypeTable_.size();
+			for (ReptypeTable::const_iterator i = reptypeTable_.begin(); i != reptypeTable_.end(); ++i)
+			{
+				size_t rt = i->second;
+				if (newRT <= rt)
+					newRT = rt + 1;
+			}
+
+			Log::info() << "ReptypeGenIterator::next(): New report type: " << newRT << std::endl;
+
+			data_[reptypeIndex_] = reptypeTable_[values_] = newRT;
+		}
+	}
+	noMore_ = !r;
+	return r;
+}
+
+template class ReptypeGenIterator<>;
+template class ReptypeGenIterator<FakeODBIterator>;
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/migrator/ReptypeGenIterator.h b/odb_api/src/odb_api/migrator/ReptypeGenIterator.h
new file mode 100644
index 0000000..369011e
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/ReptypeGenIterator.h
@@ -0,0 +1,91 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ReptypeGenIterator_H
+#define ReptypeGenIterator_H
+
+#include "ODBIterator.h"
+
+namespace eckit { class PathName; }
+namespace eckit { class ExecutionContext; }
+
+namespace odb {
+namespace tool {
+
+class ODBIterator;
+
+typedef std::vector<size_t> Indices;
+typedef std::vector<double> Values;
+
+typedef std::map<Values, int> ReptypeTableBase;
+
+class ReptypeTable : public ReptypeTableBase
+{
+};
+
+class ReptypeTableConfig {
+public:
+	static void load(const eckit::PathName&);
+
+	template <typename I>
+	static void addColumns(I begin, I end) { columns_.insert(columns_.end(), begin, end); }
+
+    static const std::vector<std::string> columns() { return columns_; }
+	static const ReptypeTable& reptypeTable() { return reptypeTable_; }
+
+	static void reptypeTable(const ReptypeTable &rt) { reptypeTable_ = rt; }
+private:
+    static std::vector<std::string> columns_;
+	static ReptypeTable reptypeTable_;
+};
+
+template<typename ITERATOR = ODBIterator, typename CONFIG = ReptypeTableConfig>
+class ReptypeGenIterator //: public odb::RowsReaderIterator 
+{
+public:
+
+	ReptypeGenIterator(const eckit::PathName& db, const std::string& sql); 
+	~ReptypeGenIterator ();
+
+	odb::MetaData& columns();
+
+	virtual bool isNewDataset();
+	virtual double* data();
+
+	const ReptypeGenIterator<ITERATOR,CONFIG>& end() { return *reinterpret_cast<ReptypeGenIterator<ITERATOR,CONFIG>*>(0); }
+	//bool operator!=(const ReptypeGenIterator<ITERATOR,CONFIG>& o) { ASSERT(&o == 0); return iterator_ != iterator_.end(); }
+	//ReptypeGenIterator<ITERATOR,CONFIG>& operator++() { next(); return *this; }
+
+	int refCount_;
+	bool noMore_;
+    eckit::ExecutionContext* context_;
+
+//protected:
+	virtual bool next(eckit::ExecutionContext*);
+	//void addColumn(std::string name);
+
+private:
+	ITERATOR iterator_;
+
+	double* data_;
+
+	size_t reptypeIndex_;
+
+	Indices indices_;
+	Values values_;
+	ReptypeTable reptypeTable_;
+
+	int lastIndex_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/migrator/TSQLReader.cc b/odb_api/src/odb_api/migrator/TSQLReader.cc
new file mode 100644
index 0000000..9dc7900
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/TSQLReader.cc
@@ -0,0 +1,14 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "TSQLReader.h"
+namespace odb {
+
+} // namespace odb
diff --git a/odb_api/src/odb_api/migrator/TSQLReader.h b/odb_api/src/odb_api/migrator/TSQLReader.h
new file mode 100644
index 0000000..f55ba70
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/TSQLReader.h
@@ -0,0 +1,60 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TSQLReader_H
+#define TSQLReader_H
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/IteratorProxy.h"
+
+namespace odb {
+namespace tool {
+
+template <typename T>
+class TSQLReader
+{
+public:
+	typedef T iterator_class;
+	typedef typename odb::IteratorProxy<iterator_class, TSQLReader, const double> iterator;
+
+	TSQLReader(const std::string& pathName, const std::string& sql)
+	: pathName_(pathName), sql_(sql)
+	{}
+
+	~TSQLReader() {}
+
+	iterator begin()
+    { 
+        iterator_class* it = new iterator_class(pathName_, sql_);
+        it->next(it->context_);
+        if (it->noMore_)
+            eckit::Log::warning() << "ODBIterator::ODBIterator: result set empty, no data." << std::endl;
+        return iterator(it);
+    }
+
+	const iterator end() { return iterator(0); }
+
+#ifdef SWIGPYTHON
+    // FIXME: add createReadIterator as in odb::Reader
+    iterator __iter__() { return iterator(new iterator_class(pathName_, sql_)); }
+#endif
+
+private:
+	const eckit::PathName pathName_;
+	const std::string sql_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#include "TSQLReader.cc"
+
+#endif
+
diff --git a/odb_api/src/odb_api/migrator/all b/odb_api/src/odb_api/migrator/all
new file mode 100755
index 0000000..66b553a
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/all
@@ -0,0 +1,36 @@
+
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat,atovs, atovs_pred, atovs_body, update_1, update_2, update_3 >sql
+
+echo select \"~/\(LINK\)/\" from desc, sat, timeslot_index, index, hdr, body,errstat, update_1, update_2, update_3 >sql_amsre
+echo select \"~/\(LINK\)/\" from desc, timeslot_index, index, hdr, body, errstat, update_1, update_2, update_3 >sql_conv
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat,update_1, update_2, update_3 >sql_gpsro
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat, update_1, update_2, update_3 >sql_meris
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat, update_1, update_2, update_3 >sql_reo3
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat, update_1, update_2, update_3 >sql_satob
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat, update_1, update_2, update_3 >sql_scatt
+echo select \"~/\(LINK\)/\" from desc,sat,timeslot_index,index, hdr, body,errstat, update_1, update_2, update_3 >sql_ssmi
+
+echo select station_type at hdr from desc, timeslot_index, index, hdr, body, errstat, update_1, update_2, update_3 >sql_conv_debug
+echo select \"~/\(LINK\)/\" from desc, sat, index, hdr, body, errstat, atovs, atovs_pred >sql_old
+echo select \"~/\(LINK\)/\" from desc, sat, index, hdr, body, errstat, atovs, atovs_pred where maxcount\(10\) >sql_old_short
+
+./odb2oda /hugetmp/data/2009072800/ECMA.airs sql
+./odb2oda /hugetmp/data/2009072800/ECMA.amsre sql_amsre
+./odb2oda /hugetmp/data/2009072800/ECMA.amsua sql
+./odb2oda /hugetmp/data/2009072800/ECMA.amsub sql
+./odb2oda /hugetmp/data/2009072800/ECMA.audit sql
+./odb2oda /hugetmp/data/2009072800/ECMA.conv sql_conv
+./odb2oda /hugetmp/data/2009072800/ECMA.geos sql
+./odb2oda /hugetmp/data/2009072800/ECMA.gpsro sql_gpsro
+./odb2oda /hugetmp/data/2009072800/ECMA.hirs sql
+./odb2oda /hugetmp/data/2009072800/ECMA.iasi sql
+./odb2oda /hugetmp/data/2009072800/ECMA.meris sql_meris
+./odb2oda /hugetmp/data/2009072800/ECMA.mhs sql
+./odb2oda /hugetmp/data/2009072800/ECMA.reo3 sql_reo3
+./odb2oda /hugetmp/data/2009072800/ECMA.satob sql_satob
+./odb2oda /hugetmp/data/2009072800/ECMA.scatt sql_scatt
+./odb2oda /hugetmp/data/2009072800/ECMA.ssmi sql_ssmi
+./odb2oda /hugetmp/data/2009072800/ECMA.ssmis sql
+./odb2oda /hugetmp/data/2009072800/ECMA.tmi sql
+
+
diff --git a/odb_api/src/odb_api/migrator/diurnal.f90 b/odb_api/src/odb_api/migrator/diurnal.f90
new file mode 100644
index 0000000..3ec42d7
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/diurnal.f90
@@ -0,0 +1,76 @@
+SUBROUTINE DIURNAL(K_MM,K_DD,K_HH,P_DLAT,P_DLON,P_ANGLE)
+
+!**** *DIURNAL*
+
+!       PURPOSE.
+!      ----------
+
+!           COMPUTE SOLAR ELEVATION ANGLE
+
+!       INTERFACE.
+!      ------------
+
+!         CALL DIURNAL(MM,DD,HH,DLAT,DLON,ANGLE)
+
+!        INPUT
+!         MM     -  MONTH
+!         DD     -  DAY OF MONTH
+!         HH     -  HOUR
+!         DLAT   -  LATITUDE
+!         DLON   -  LONGITUDE
+
+!        OUTPUT
+!         ANGLE  -  SOLAR ELEVATION
+
+!       EXTERNALS.
+!      -----------
+
+!         NONE
+
+!       REFERENCE.
+!      ------------
+
+!         NONE
+
+!       AUTHOR.
+!      ---------
+
+!         ANTTI LANGE,  ECMWF,  25 JULY 1984
+
+!       MODIFICATIONS.
+!      ----------------
+!        M.Hamrud      01-Oct-2003 CY28 Cleaning
+
+!         NONE
+
+IMPLICIT NONE
+INTEGER,INTENT(IN)    :: K_MM 
+INTEGER,INTENT(IN)    :: K_DD 
+INTEGER,INTENT(IN)    :: K_HH 
+REAL   ,INTENT(IN)    :: P_DLAT 
+REAL   ,INTENT(IN)    :: P_DLON 
+REAL   ,INTENT(OUT)   :: P_ANGLE 
+REAL :: Z_PI, Z_TWOPI, Z_RADCON, Z_DAY, Z_SEASON,Z_COSSSN,&
+ & Z_DECL, Z_RLAT, Z_SINLAT, Z_COSLAT, Z_RLON, Z_RLHH, Z_COSLHH, Z_SINE  
+
+Z_PI=2.0*ASIN(1.0)
+Z_TWOPI=2.0*Z_PI
+Z_RADCON=Z_TWOPI/360.0
+
+!*** SINUS OF SOLAR ELEVATION ANGLE WILL BE APPROXIMATED ************
+
+Z_DAY=REAL(K_MM)*30.44+REAL(K_DD)-31.44
+Z_SEASON=Z_TWOPI*(Z_DAY+9.5)/365.25
+Z_COSSSN=COS(Z_SEASON)
+Z_DECL=-Z_RADCON*23.5*Z_COSSSN
+Z_RLAT=Z_RADCON*P_DLAT
+Z_SINLAT=SIN(Z_RLAT)
+Z_COSLAT=COS(Z_RLAT)
+Z_RLON=Z_RADCON*P_DLON
+Z_RLHH=Z_TWOPI*K_HH/24.00+Z_RLON-Z_PI
+Z_COSLHH=COS(Z_RLHH)
+Z_SINE=SIN(Z_DECL)*Z_SINLAT+COS(Z_DECL)*Z_COSLAT*Z_COSLHH
+
+P_ANGLE = ASIN(Z_SINE) * 180. / Z_PI
+
+END SUBROUTINE DIURNAL
diff --git a/odb_api/src/odb_api/migrator/migrator_api.cc b/odb_api/src/odb_api/migrator/migrator_api.cc
new file mode 100644
index 0000000..cd92930
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/migrator_api.cc
@@ -0,0 +1,38 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/migrator/ImportODBTool.h"
+#include "odb_api/migrator/migrator_api.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/odbcapi.h"
+#include "odb_api/tools/Tool.h"
+
+
+namespace odb {
+namespace tool {
+
+int import_odb_with_sql_in_file(const char* odb_database, const char* sql_file, const char* output_file)
+{
+    try {
+        const char *argv[] = {"importodb",  odb_database, sql_file, output_file, 0 };
+        odb_start_with_args(1, const_cast<char **>(argv));
+        ImportODBTool<> importer(4, const_cast<char **>(argv));
+        importer.run();
+        return 0;
+    } catch (...) {
+        return 1;
+    }
+}
+
+
+} // namespace tool 
+} //namespace odb 
+
+
diff --git a/odb_api/src/odb_api/migrator/migrator_api.h b/odb_api/src/odb_api/migrator/migrator_api.h
new file mode 100644
index 0000000..86ae9fe
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/migrator_api.h
@@ -0,0 +1,22 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef migrator_api_H
+#define migrator_api_H
+
+namespace odb {
+namespace tool {
+
+int import_odb_with_sql_in_file(const char* odb_database, const char* sql_file, const char* output_file);
+
+} // namespace tool 
+} //namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/migrator/odb1.f90 b/odb_api/src/odb_api/migrator/odb1.f90
new file mode 100644
index 0000000..11f19de
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/odb1.f90
@@ -0,0 +1,561 @@
+!-----------------------------------------------------------------------------------------
+! 
+!  Some routines to read/manipulate 'old-style' odb-1 data
+!  Hans Hersbach, ECMWF, October 2011
+! 
+!-----------------------------------------------------------------------------------------
+
+module odb1
+!! Contains information of an odb-1 database
+   implicit none
+
+   character(len= 8), parameter :: cblank8  = '        '
+   character(len=12),parameter  :: cloffset = 'LINKOFFSET', tloffset = "linkoffset_t"
+   character(len=12),parameter  :: clinklen = 'LINKLEN'   , tlinklen = "linklen_t"
+
+   type odb1_table                ! Contents of one table
+     character(len=128)          :: tname                ! Table name
+     integer                     :: iparent              ! index of parent table
+     integer                     :: ncol                 ! Number of columns
+     integer                     :: nrow                 ! Number of rows
+     character(len=128), pointer :: cname(: ) => NULL()  ! column names
+     character(len=128), pointer :: ctype(:)  => NULL()  ! column data type
+     real*8            , pointer :: data(:,:) => NULL()  ! data
+   end type odb1_table
+
+   type todb1                     ! Contents of odb1 file for database dbname
+     integer                     :: iundef
+     real*8                      :: rundef
+     integer                     :: handle
+     integer                     :: npools
+     character(len=64)           :: dbname
+     integer                     :: ntable
+     type (odb1_table) , pointer :: tbl(:)   => NULL()  ! table
+   end type todb1
+
+contains
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_delete_odb1(db1)
+  implicit none
+
+  type (todb1)              :: db1
+  integer                    :: i   
+
+  do i=1,db1%ntable
+     call odb1_delete_table(db1%tbl(i))
+  enddo
+
+  db1%handle =-1
+  db1%dbname =""
+  db1%ntable =0
+  if(associated(db1%tbl))deallocate(db1%tbl)
+
+end subroutine odb1_delete_odb1
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_delete_table(tbl)
+  implicit none
+
+  type (odb1_table) :: tbl
+
+  tbl%iparent=0
+  tbl%ncol   =0
+  tbl%nrow   =0
+  tbl%tname  =""
+  if (associated(tbl%cname)) deallocate(tbl%cname)
+  if (associated(tbl%ctype)) deallocate(tbl%ctype)
+  if (associated(tbl%data )) deallocate(tbl%data )
+
+end subroutine odb1_delete_table
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_create_table(tbl,tname,iparent,ncol,cname,ctype)
+  implicit none
+
+  type (odb1_table)  :: tbl
+  integer            :: ncol, iparent
+  character(len=128) :: tname,cname(ncol),ctype(ncol)
+
+  integer :: i
+
+  call odb1_delete_table(tbl)
+  write(tbl%tname,'(A)')trim(tname)
+
+  tbl%iparent=iparent
+  tbl%ncol   =ncol
+  allocate(tbl%cname(ncol))
+  allocate(tbl%ctype(ncol))
+
+  do i=1,ncol
+     write(tbl%cname(i),'(A)')trim(cname(i))
+     write(tbl%ctype(i),'(A)')trim(ctype(i))
+  enddo
+
+end subroutine odb1_create_table
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_alloc_data(tbl,nrow)
+  implicit none
+
+  type (odb1_table) :: tbl
+  integer           :: nrow,j
+
+  real*8 :: rblank8
+  rblank8 = transfer(cblank8,rblank8)
+
+  if (tbl%ncol<=0) call abort() 
+      tbl%nrow=nrow
+  if (associated(tbl%data)) deallocate(tbl%data)
+     allocate(tbl%data(nrow,0:tbl%ncol))
+
+  tbl%data(:,:)=0
+  do j=1,tbl%ncol
+     if(trim(tbl%ctype(j))=='string') tbl%data(:,j)=rblank8
+  enddo
+
+end subroutine odb1_alloc_data
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_add_block(tbl,data,irow,icol,nrow,ncol)
+  implicit none
+
+  type (odb1_table) :: tbl
+  real*8            :: data(nrow,ncol)
+  integer           :: irow,icol,ncol,nrow
+
+  integer           :: irowe,icole
+
+  irowe=irow+nrow-1 ; if (irowe>tbl%nrow) call abort()
+  icole=icol+ncol-1 ; if (icole>tbl%ncol) call abort()
+
+  tbl%data(irow:irowe,icol:icole)=data(1:nrow,1:ncol)
+
+end subroutine odb1_add_block
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_header(db1)
+
+  implicit none
+
+  type (todb1)     :: db1
+  
+  type (odb1_table), pointer :: tbl
+  integer                    :: i,j
+  character(len=128)         :: crange
+  character*8                :: cdum
+
+  write(*,'(3A,I4)')"ODB-1 odb1: ",trim(db1%dbname),", handle: ",db1%handle
+  do i=1,db1%ntable
+     tbl => db1%tbl(i)
+     do j=1,tbl%ncol
+        crange=""
+        if (trim(tbl%ctype(j))=='string') then
+           write(crange,'(", First Value=<",A,">")')transfer(tbl%data(1,j),cdum)
+!          write(crange,'(", First Value=<",A,">")')"Under construction"
+        else
+           write(crange,'(", Range=<",E15.8,",",E15.8,">")')&
+           minval(tbl%data(:,j)),maxval(tbl%data(:,j))
+        endif
+        write(*,'(2I4,I8,2X,A,":",2A)') &
+        & i,j,tbl%nrow,trim(tbl%ctype(j)),trim(tbl%cname(j)),trim(crange)
+     enddo
+  enddo
+
+end subroutine odb1_header
+
+!-----------------------------------------------------------------------------------------
+
+function odb1_find_index(db1,table)
+
+  implicit none
+
+  integer :: odb1_find_index
+  type (todb1)        :: db1
+  character(len=128) :: table
+
+  type (odb1_table), pointer :: tbl
+  integer                    :: i
+
+  do i=0,db1%ntable
+     tbl => db1%tbl(i)
+     if (trim(tbl%tname)==trim(table)) exit
+  enddo
+  odb1_find_index=i
+! In case not found return ntable+1
+
+
+end function odb1_find_index
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_reassign_seqno(db1,ioffset)
+! reassign seqno at hdr from ioffset+1 to ioffset+nrow
+
+  implicit none
+
+  type (todb1)       :: db1
+  integer            :: ioffset
+
+  type (odb1_table), pointer :: tbl
+  character(len=128)         :: column,table
+  integer                    :: i,j,k
+
+  column="seqno at hdr"
+  table="@hdr"
+
+  k=odb1_find_index(db1,table)
+  tbl => db1%tbl(k)
+  j=odb1_column_index(tbl,column)
+
+  do i=1,tbl%nrow
+     ioffset=ioffset+1
+     tbl%data(i,j)=ioffset
+  enddo
+write(*,*)"odb1_reassign_seqno: k,j,tbl%nrow: ", k,j,tbl%nrow
+
+end subroutine odb1_reassign_seqno
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_set_value(db1,cvalue)
+
+  implicit none
+
+  type (todb1)       :: db1
+  character(len=128) :: cvalue
+
+  type (odb1_table), pointer :: tbl
+  character(len=128)         :: column,table,child
+  real*8                     :: value
+  integer                    :: i,j,k
+  logical                    :: llink
+
+  i=index(cvalue,"=") ; if(i<=1)return
+    column="" ; write(column,'(A)')trim(cvalue(1:i-1))
+
+  j=index(column,"@") ; if(j==1)return
+    table=""  ; write(table,'(A)')trim(column(j:))
+  
+  llink=.false.
+  k=index(column,".")
+  if (k>0) then
+     llink=.true.
+     child="" ; write(child,'(A)')trim(column(1:k-1))
+     if (column(k:j-1)==".len") then
+       write(column,'(A,"(",A,")",A)')trim(clinklen),trim(child),trim(table)
+     else
+       write(*,*)"Only .len column can be set: ",trim(column)
+       return
+     endif
+  endif
+
+  k=odb1_find_index(db1,table)
+  if (k>db1%ntable) then
+     write(*,*)"table not found: ",trim(table)
+     return
+  endif
+
+  tbl => db1%tbl(k)
+  j=odb1_column_index(tbl,column)
+  if (j==0) then
+     write(*,*)"Column not found: ",trim(column)
+     return
+  endif
+  
+! Set value
+  read(cvalue(i+1:),*)value
+  write(*,*)'Set column: ',trim(column),value,tbl%nrow
+  tbl%data(1:tbl%nrow,j)=value
+
+! If required, update offset
+  if (.not.llink) return
+  j=j-1
+  tbl%data(1,j)=0
+  do k=2,tbl%nrow
+     tbl%data(k,j)=tbl%data(k-1,j)+value
+  enddo
+
+
+end subroutine odb1_set_value
+
+!-----------------------------------------------------------------------------------------
+
+function odb1_find_parent(db1,table,parent)
+
+  implicit none
+
+  integer            :: odb1_find_parent
+  type (todb1)        :: db1
+  character(len=128) :: table,parent
+  
+  type (odb1_table), pointer :: tbl
+  integer                    :: i,j,i1,i2
+
+  odb1_find_parent=0  ! no parent found, so far
+  parent          ="@"
+
+  do i=1,db1%ntable
+     tbl => db1%tbl(i)
+     do j=1,tbl%ncol
+        if (index(tbl%cname(j),trim(cloffset)) == 0) cycle
+        i1=index(tbl%cname(j),"(")+1
+        i2=index(tbl%cname(j),")")-1
+        if (trim(table) == "@"//tbl%cname(j)(i1:i2) ) then
+           write(parent,'(A)')trim(tbl%tname)
+           odb1_find_parent=i
+           exit
+        endif
+     enddo
+  enddo
+
+end function odb1_find_parent
+
+!-----------------------------------------------------------------------------------------
+
+function odb1_child_linkindex(tbl,child)
+
+  implicit none
+  integer odb1_child_linkindex
+  type (odb1_table)  :: tbl
+  character(len=128) :: child
+
+  character(len=128) :: cname
+
+  cname=""
+  write(cname,'(A,"(",A,")",A)')trim(cloffset),trim(child(2:)),trim(tbl%tname)
+  odb1_child_linkindex=odb1_column_index(tbl,cname)
+
+end function odb1_child_linkindex
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_find_type(db1,cname,ctype)
+! find ctype for cname as defined in odb1 file db1
+
+  implicit none
+  type (todb1)      :: db1
+  character(len=128) :: cname,ctype
+
+  type (odb1_table), pointer :: tbl
+  integer                    :: i,j
+
+  ctype=""
+  do i=1,db1%ntable
+     tbl => db1%tbl(i)
+     do j=1,tbl%ncol
+        if ( trim(cname) == trim(tbl%cname(j)) ) then
+          write(ctype,'(A)')trim(tbl%ctype(j))
+          return
+        endif
+     enddo
+  enddo
+
+end subroutine odb1_find_type
+
+!-----------------------------------------------------------------------------------------
+
+function odb1_column_index(tbl,cname)
+
+  implicit none
+  integer            :: odb1_column_index
+  type (odb1_table)  :: tbl
+  character(len=128) :: cname
+
+  integer :: i
+
+  odb1_column_index=0
+  do i=1,tbl%ncol
+     if ( trim(cname) == trim(tbl%cname(i)) ) then
+          odb1_column_index=i 
+          return
+     endif
+  enddo
+
+end function odb1_column_index
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_open(db1,dbname,npools,myproc,nproc)
+
+  use odb_module
+  implicit none
+
+  type (todb1)      :: db1
+  character(len=64) :: dbname
+  integer           :: npools,myproc,nproc
+
+  type (odb1_table), pointer :: tbl
+
+  integer :: iret, ihandle, io_method, ntables, ncols,nrows,i 
+  character(len=20)  :: copen
+  character(len=64)  :: chviewname
+  real*8,allocatable :: data(:,:)
+
+  character(len=128), allocatable :: ctable(:)
+
+! Clean odb1 file
+  call odb1_delete_odb1(db1)
+  
+! Open ODB-1
+  write(*,*) '            '
+  write(*,*) 'SET-UP ODB-1'
+  iret  = ODB_init(myproc=myproc,nproc=nproc)
+  write(*,'(A,2I6)')'odb1_open, myproc,nproc:',myproc,nproc
+
+  copen = 'NEW' ; chviewname='*'
+  ihandle = ODB_open   (dbname, copen, npools=npools)
+
+  io_method = ODB_io_method(ihandle)
+
+  db1%iundef=abs(odb_getval(ihandle,'$mdi'))
+  db1%rundef=-db1%iundef
+
+  ntables = ODB_getnames(ihandle,chviewname,'table'); allocate(ctable(ntables))
+     db1%handle=ihandle
+     db1%npools=npools
+     db1%dbname=trim(dbname)
+     db1%ntable=ntables
+     allocate(db1%tbl(0:ntables))
+
+  ntables = ODB_getnames(ihandle,chviewname,'table', ctable)
+  do i=0, ntables
+    tbl => db1%tbl(i)
+    tbl%tname="@" ; tbl%ncol=0 ; tbl%nrow=0
+    if(i==0) cycle
+    tbl%tname=trim(ctable(i))
+    ncols = ODB_getnames(ihandle,ctable(i),'name')
+      tbl%ncol=ncols
+      allocate(tbl%cname(ncols),tbl%ctype(ncols)) 
+    ncols=ODB_getnames(ihandle,ctable(i),'name', tbl%cname)
+    ncols=ODB_getnames(ihandle,ctable(i),'type', tbl%ctype)
+  enddo
+
+end subroutine odb1_open
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_merge(db1,db2)
+! Merge contents of db1 into db2
+
+  implicit none
+
+  type (todb1)     :: db1,db2
+
+  type (odb1_table), pointer :: tbl1,tbl2
+
+  integer                    :: i1,i2,j1,j2,nrow,ncol,iret
+
+! Link tables
+
+! Loop over tables
+  do i1=1,db1%ntable
+     tbl1 => db1%tbl(i1)
+
+   ! Match tables
+     do i2=1,db2%ntable
+        tbl2 => db2%tbl(i2)
+        if(trim(tbl1%tname)==trim(tbl2%tname))exit
+     enddo
+     if(i2>db2%ntable) call abort()
+
+   ! write(*,*)i1,i2,trim(tbl1%tname)
+
+   ! Allocate data section for tbl2
+     nrow=tbl1%nrow
+     ncol=tbl1%ncol
+     call odb1_alloc_data(tbl2,nrow)
+
+   ! Match columns
+     do j1=1,tbl1%ncol
+        do j2=1,tbl2%ncol
+           if( trim(tbl1%cname(j1)) == trim(tbl2%cname(j2)) ) exit
+        enddo
+        if(j1>ncol) call abort()
+      ! write(*,*)j1,j2,trim(tbl1%cname(j1))
+
+      ! Copy data contents
+        call odb1_add_block(tbl2,tbl1%data(1:nrow,j1:j1),1,j2,nrow,1)
+     enddo
+
+  enddo
+
+end subroutine odb1_merge
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_write(db1,ipool)
+
+  use odb_module
+  implicit none
+
+  type (todb1)     :: db1
+  integer          :: ipool
+
+  type (odb1_table), pointer :: tbl
+  integer                    :: i,j,i1,i2,iret,ihandle,nrows,ncols,ntable,ichild,ioffset
+  character(len=128)         :: child
+
+  integer,allocatable        :: noffset(:)
+
+  ntable =db1%ntable
+  ihandle=db1%handle
+
+  ! Figure out how much has already been stored. Is needed to calculate offset for links
+  allocate(noffset(ntable))
+  do i=1,ntable
+     tbl => db1%tbl(i)
+     iret = ODB_getsize(ihandle, tbl%tname, nrows, ncols,poolno=ipool)
+     noffset(i)=nrows
+  enddo
+
+  write(*,'(2A,I6)')"Write ODB-1 Database/poolno: ",trim(db1%dbname),ipool
+  do i=1,ntable
+     tbl => db1%tbl(i)
+   ! Only do non-empty ones 
+     if(tbl%nrow==0) cycle
+
+     ! Find any links to children, and add offset
+     do j=1,tbl%ncol
+        if (index(tbl%cname(j),trim(cloffset)) == 0) cycle
+        i1=index(tbl%cname(j),"(")+1
+        i2=index(tbl%cname(j),")")-1
+        child=""; write(child,'(2A)')"@",trim(tbl%cname(j)(i1:i2))
+        ioffset=odb1_find_index(db1,child)
+        tbl%data(:,j)=tbl%data(:,j)+noffset(ioffset)
+     enddo
+
+     iret = ODB_put    (ihandle, tbl%tname, tbl%data, tbl%nrow, poolno=ipool)
+     iret = ODB_getsize(ihandle, tbl%tname, nrows, ncols,poolno=ipool)        ! diagnostics
+     write(*,'(A,I6,A,I6,I4,2A)')'Pool: ',ipool,' PUT: ',nrows, ncols,' Rows/Cols in: ',trim(tbl%tname)
+  enddo
+
+  iret = ODB_release(db1%handle,poolno=ipool)
+
+  deallocate(noffset)
+
+end subroutine odb1_write
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb1_close(db1)
+  use odb_module
+  implicit none
+
+  type (todb1)     :: db1
+  integer          :: iret, ihandle
+
+  ihandle=db1%handle
+  iret = ODB_close(ihandle, save=.TRUE.)
+  iret = ODB_end()
+end subroutine odb1_close
+
+!-----------------------------------------------------------------------------------------
+
+end module odb1
diff --git a/odb_api/src/odb_api/migrator/odb2.f90 b/odb_api/src/odb_api/migrator/odb2.f90
new file mode 100644
index 0000000..c6886af
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/odb2.f90
@@ -0,0 +1,506 @@
+!-----------------------------------------------------------------------------------------
+! Hans Hersbach ECMWF, November 2011
+! 38r1 flag definitions
+
+module odb2
+!! Contains information in one odb-2 database
+  use, intrinsic :: iso_c_binding
+  use odb_c_binding
+  use odb2_flag_definitions
+  implicit none
+
+  integer, parameter            :: mlen    = 1024
+  real(kind=C_DOUBLE),parameter :: iundef  = 2147483647.0_C_DOUBLE
+  real(kind=C_DOUBLE),parameter :: rundef  = -iundef
+  real(kind=C_DOUBLE),parameter :: cundef  = transfer("????????",rundef)
+
+  type todb2
+     type(C_PTR)                     :: h,it
+     character(kind=C_CHAR,len=mlen) :: file
+     integer                         :: ncol,nrow
+     character                       :: status              ! 'r' or 'w'
+     character(len=mlen), pointer    :: cname(:) => NULL()  ! column name at table
+     integer            , pointer    :: itype(:) => NULL()  ! column type
+     real*8             , pointer    :: data(:,:)=> NULL()  ! odb2 contents
+  end type todb2
+
+  type tdef
+     integer                      :: ndef=0
+     character(len=1000),pointer  :: bdef(:) => NULL()
+     character(len=1000),pointer  :: bits(:) => NULL()
+  end type tdef
+contains
+
+!-----------------------------------------------------------------------------------------
+
+function cstr(fstring)
+! Convert Fortran string to C string by appending the "\0" character
+  implicit none
+  character(kind=C_CHAR, len=mlen) :: cstr
+  character(len=*)                 :: fstring
+
+  cstr=trim(fstring)//achar(0)
+end function cstr
+
+!-----------------------------------------------------------------------------------------
+
+function find_column(o2,cname)
+! Find matching column at table index"
+  implicit none
+  integer          :: find_column
+  type (todb2)     :: o2
+  character(len=*) :: cname
+
+  integer :: i,j,k,l
+
+  j=index(cname,":") +1
+  k=len_trim(cname)
+
+  do i=1,o2%ncol
+     l=len_trim(o2%cname(i))
+     if (cname(j:k)==o2%cname(i)(:l)) exit
+  enddo
+  if(i>o2%ncol) then
+     i=0
+     write(*,*)"Warning in find_column: cannot find column: ",trim(cname)
+  endif
+  find_column=i
+
+end function find_column
+
+!-----------------------------------------------------------------------------------------
+
+function xval(x8,xundef)
+  implicit none
+  real   :: xval,xundef
+  real*8 :: x8
+
+  xval=x8
+  if (x8==rundef) xval=xundef
+end function xval
+
+!-----------------------------------------------------------------------------------------
+
+function s2i(c)
+! Place 4-character string into integer*4
+  implicit none
+  integer*4   :: s2i
+  character*4 :: c
+  s2i=transfer(c,s2i)
+end function s2i
+
+!-----------------------------------------------------------------------------------------
+function s2x(c)
+! Place 8-character string into real*8
+  implicit none
+  real*8      :: s2x
+  character*8 :: c
+  s2x=transfer(c,s2x)
+end function s2x
+
+!-----------------------------------------------------------------------------------------
+
+function x2s(x)
+! Place real*8 into 8-character string
+  implicit none
+  character*8 :: x2s
+  real*8      :: x
+  x2s=transfer(x,x2s)
+end function x2s
+
+!-----------------------------------------------------------------------------------------
+
+function s2a(s)
+! Place 4-character string into concatenated ascii
+! Right-adjust it first, so " AB " => "  AB"
+! use 0 for blank ' ', rather than its ascii code 32
+! all numbers are negative
+
+! The fortran transfer function would also work, but
+! would give less intuitive results
+
+  implicit none
+  integer*4   :: s2a
+  character*4 :: s
+  character   :: c
+
+  integer :: i,ib
+
+  s2a=0 ; ib=1
+  do i=len_trim(s),1,-1
+      c=s(i:i)
+      if(c/=' ')s2a=s2a-ib*iachar(c)
+     ib=ib*256
+  enddo
+end function s2a
+
+function a2s(a)
+! Place concatenated ascii back into string*4
+  implicit none
+  integer     :: a
+  character*4 :: a2s
+
+  integer*4 :: i,ib,ic,ia
+
+  a2s="    " ; ib=256**3
+  if (a>0) then
+     write(*,*)"Warning a2s: unsuitable input ",a
+     return
+  endif
+
+  ia=-a
+  do i=1,4
+     ic=ia/ib
+     ia=ia-ic*ib
+     if (ic<0 .or. ic>=256) write(*,*)"Warning a2s: unsuitable input ",a
+     if (ic/=0) a2s(i:i)= achar(mod(ic,256))
+     ib=ib/256
+  enddo
+end function a2s
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_delete(o2)
+  implicit none
+  type (todb2)   :: o2
+
+  o2%ncol=0
+  if(associated(o2%cname))deallocate(o2%cname)
+  if(associated(o2%itype))deallocate(o2%itype)
+  if(associated(o2%data ))deallocate(o2%data)
+
+end subroutine odb2_delete
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_open(o2,odb2file,status)
+
+  implicit none
+  character(len=*)               :: odb2file
+  type (todb2), intent(inout)    :: o2
+  character, intent(in),optional :: status
+
+  type(C_PTR)                     :: odb_it,odb_handle
+  integer(kind=C_INT)             :: itype,cerr
+  character(kind=C_CHAR,len=mlen) :: config=C_NULL_CHAR
+  character                       :: stat='r'
+
+! Start from fresh odb
+  call odb2_delete(o2)
+
+! Read, write, old?
+  if (present(status)) stat=status
+  if (stat=='R'      ) stat='r'
+  if (stat=='W'      ) stat='w'
+  o2%status=stat
+
+! Open file and set ODB-2 pointers
+
+! write(*,'(4A)')'Open ODB-2 file (',stat,'): ',trim(odb2file)
+  o2%file=cstr(odb2file)
+
+  if (o2%status=='w') then
+     odb_handle = odb_write_new(config, cerr)
+     odb_it     = odb_write_iterator_new(odb_handle, o2%file, cerr)
+  endif
+
+  if (o2%status=='r') then
+     odb_handle = odb_read_new (config, cerr)
+     odb_it     = odb_read_iterator_new (odb_handle, o2%file, cerr)
+  endif
+
+  if (o2%status=='a') then
+     odb_handle = odb_write_new(config, cerr)
+     odb_it     = odb_append_iterator_new(odb_handle, o2%file, cerr)
+  endif
+
+  o2%h = odb_handle
+  o2%it= odb_it
+
+end subroutine odb2_open
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_rewind(o2)
+
+  implicit none
+  type (todb2)                   :: o2
+
+  integer(kind=C_INT)            :: cerr
+
+  if(o2%status/='r') call abort()
+  cerr  = odb_read_iterator_delete(o2%it)
+  o2%it = odb_read_iterator_new(o2%h, o2%file, cerr)
+
+end subroutine odb2_rewind
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_get(o2,ncol,cname,status)
+
+  implicit none
+  type (todb2)                    :: o2
+  integer,         intent(inout)          :: ncol
+  character(len=*),intent(  out),optional :: cname(ncol)
+  character,       intent(  out),optional :: status
+
+  integer(kind=C_INT)                                 :: cerr,c_ncolumns,c_nchar
+  type(C_PTR)                                         :: ptr_colname
+  character(kind=C_CHAR,len=1), dimension(:), pointer :: f_ptr_colname
+  character(len=mlen)                                 :: ctype
+  integer(kind=4)                                     :: i,j
+
+
+  if (o2%ncol==0) then
+     cerr = odb_read_get_no_of_columns(o2%it, c_ncolumns)
+     o2%ncol=c_ncolumns
+     if (.not.associated(o2%cname)) allocate(o2%cname(o2%ncol))
+     if (.not.associated(o2%itype)) allocate(o2%itype(o2%ncol))
+  endif
+
+  ! read column names into o2 structure
+  do j=1,o2%ncol
+     cerr = odb_read_get_column_type(o2%it, j-1, o2%itype(j))
+     cerr = odb_read_get_column_name(o2%it, j-1, ptr_colname,c_nchar)
+     call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/c_nchar/));
+     o2%cname(j)=""
+     do i=1, c_nchar
+        o2%cname(j)(i:i)  = f_ptr_colname(i)
+     end do
+  enddo
+
+  if (present(cname)) then
+     do j=1,min(o2%ncol,ncol)
+        select case (o2%itype(j))
+          case (ODB_INTEGER ) ; ctype="pk1int"
+          case (ODB_REAL    ) ; ctype="pk9real"
+          case (ODB_DOUBLE  ) ; ctype="pk9real"
+          case (ODB_STRING  ) ; ctype="string"
+          case (ODB_BITFIELD) ; call odb_find_ctype(o2%cname(j),ctype)
+        end select
+        cname(j)=""
+        write(cname(j),'(3A)')trim(ctype),":",trim(o2%cname(j))
+     enddo
+  endif
+
+  ncol=o2%ncol
+  if (present(status))status=o2%status
+
+end subroutine odb2_get
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_set_columns(o2, ncol, cname, bitfield_definitions)
+  implicit none
+
+  type (todb2),     intent(inout) :: o2
+  integer,          intent(inout) :: ncol
+  character(len=*), intent(inout) :: cname(ncol)
+  character(len=*),allocatable    :: bitfield_definitions(:)
+
+  !type(C_PTR)                     :: odb_it
+  integer(kind=C_INT)             :: itype, cerr, c_ncol
+  character(kind=C_CHAR,len=mlen) :: c_cname, cbdef, cbits
+  character(len=mlen)             :: ctype, bdef, bits
+  integer                         :: i, ic
+
+  c_ncol=ncol
+  !odb_it=o2%it
+  cerr = odb_write_set_no_of_columns(o2%it, c_ncol)
+
+  do ic=1,ncol
+     i=index(cname(ic),":")
+     c_cname=cstr(cname(ic)(i+1:))
+     ctype  =     cname(ic)(1:i-1)
+
+     if (trim(ctype)=="pk1int" ) then
+        itype=ODB_INTEGER
+        cerr=odb_write_set_column(o2%it,ic-1,itype,c_cname)
+        cerr=odb_write_set_missing_value(o2%it,ic-1,iundef)
+        cycle
+     endif
+
+     if (trim(ctype)=="pk9real" ) then
+        itype=ODB_REAL
+        cerr=odb_write_set_column(o2%it, ic-1, itype, c_cname)
+        cerr=odb_write_set_missing_value(o2%it, ic-1, rundef)
+        cycle
+     endif
+
+     if (trim(ctype)=="string" ) then
+        itype=ODB_STRING
+        cerr=odb_write_set_column(o2%it, ic-1, itype, c_cname)
+        cerr=odb_write_set_missing_value(o2%it, ic-1, cundef)
+        cycle
+     endif
+ 
+     if (trim(ctype)=="datum_event2_t" .or. trim(ctype)=="report_event2_t" ) then
+        itype=ODB_INTEGER
+        cerr=odb_write_set_column(o2%it, ic-1, itype, c_cname)
+        cerr=odb_write_set_missing_value(o2%it, ic-1, iundef)
+        cycle
+     endif
+
+   !- Else assume bit field
+     itype=ODB_BITFIELD
+     call get_odb_flags(ctype,bdef,bits)
+        if(trim(bdef)=="") call abort()
+        cbdef=cstr(trim(bdef))
+        cbits=cstr(trim(bits))
+        cerr=odb_write_set_bitfield(o2%it, ic-1, itype, c_cname, cbdef, cbits)
+        cerr=odb_write_set_missing_value(o2%it, ic-1, iundef)
+        cycle
+
+  enddo
+  cerr = odb_write_header(o2%it)
+
+! Let's find out what we really defined
+  !call odb2_get(o2,ncol,cname=cname)
+
+end subroutine odb2_set_columns
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_read(o2,xrow,ncol,nrow)
+
+  implicit none
+  type (todb2)           :: o2
+  integer, intent(in   ) :: ncol
+  integer, intent(inout) :: nrow
+  real*8                 :: xrow(ncol,nrow)
+
+  integer(kind=C_INT) :: c_ncol,cerr
+  integer(kind=C_INT) :: c_ncolumns,idum
+  character(len=8)    :: tmp_str,tmp_str2
+  real*8              :: tmp_dbl
+  integer             :: i,j,k
+  real(kind=C_DOUBLE), dimension(:), allocatable:: one_row
+
+  c_ncolumns=o2%ncol
+  allocate(one_row(c_ncolumns))
+  do i=1,nrow
+      cerr = odb_read_get_next_row(o2%it, c_ncolumns, one_row, idum)
+      if (cerr /= 0) exit
+      xrow(1:ncol,i)=one_row(1:ncol)
+  enddo
+  deallocate(one_row)
+  nrow=i-1
+
+! Remove '\0' characters for strings
+  do j=1,ncol
+     if(o2%itype(j)/=ODB_STRING)cycle
+     do i=1,nrow
+        tmp_str=transfer(xrow(j,i),tmp_str)
+        tmp_str2=""
+        do k=1,len(tmp_str) ! not pretty, but doesn't seem slow
+          if (tmp_str(k:k) .eq. char(0)) exit
+          tmp_str2(k:k)=tmp_str(k:k)
+        enddo
+        xrow(j,i)=transfer(tmp_str2,tmp_dbl)
+     enddo
+  enddo
+
+end subroutine odb2_read
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_read_all(o2,odbfile)
+! place the entire odb2 data contents in o2%data: to be phased out
+  implicit none
+  type (todb2)     :: o2
+  character(len=*) :: odbfile
+
+  integer                         :: ncol,nread
+  character(len=mlen),allocatable :: cname(:)
+
+  call odb2_open(o2,odbfile,status='r')
+  call odb2_get(o2,ncol)
+  allocate(cname(ncol))
+  call odb2_get(o2,ncol,cname=cname)
+
+  o2%nrow=odb_count(o2%file)
+  allocate(o2%data(ncol,o2%nrow))
+  nread=o2%nrow
+
+  call odb2_read(o2,o2%data,ncol,nread)
+  if (nread/=o2%nrow) then
+     write(*,*)"odb2_read_all: Problem with reading odb file: ",trim(odbfile),nread,o2%nrow
+     call abort()
+  endif
+
+  deallocate(cname)
+
+end subroutine odb2_read_all
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_write(o2,xrow,ncol,nrow)
+
+  implicit none
+  type (todb2) :: o2
+  integer      :: ncol,nrow
+  real*8       :: xrow(ncol,nrow)
+
+  integer(kind=C_INT) :: c_ncol,cerr
+  real(kind=C_DOUBLE) :: c_xrow(ncol)
+  integer             :: i
+
+  c_ncol=ncol
+  do i=1,nrow
+     c_xrow=xrow(:,i)
+     cerr = odb_write_set_next_row(o2%it, c_xrow, c_ncol)
+  enddo
+
+end subroutine odb2_write
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_set_undefined(o2,xrow,ncol,nrow)
+
+  implicit none
+  type (todb2) :: o2
+  integer      :: ncol,nrow
+  real*8       :: xrow(ncol,nrow)
+  integer :: icol
+
+  do icol=1,min(ncol,o2%ncol)
+     select case (o2%itype(icol))
+        case (ODB_INTEGER ) ; xrow(icol,1:nrow)=iundef
+        case (ODB_REAL    ) ; xrow(icol,1:nrow)=rundef
+        case (ODB_STRING  ) ; xrow(icol,1:nrow)=cundef
+        case (ODB_BITFIELD) ; xrow(icol,1:nrow)=iundef
+        case default        ; call abort()
+     end select
+  enddo
+end subroutine odb2_set_undefined
+
+!-----------------------------------------------------------------------------------------
+
+subroutine odb2_close(o2)
+
+  implicit none
+  type (todb2)        :: o2
+
+  integer(kind=C_INT) :: cerr
+
+  if (o2%status == 'w') then
+     cerr = odb_write_iterator_delete(o2%it)
+     cerr = odb_write_delete(o2%h)
+  endif
+
+  if (o2%status == 'r') then
+     cerr = odb_read_iterator_delete(o2%it)
+     cerr = odb_read_delete(o2%h)
+  endif
+
+  if (o2%status == 'a') then
+     cerr = odb_write_iterator_delete(o2%it)
+     cerr = odb_write_delete(o2%h)
+  endif
+
+end subroutine odb2_close
+
+!-----------------------------------------------------------------------------------------
+
+end module odb2
+
diff --git a/odb_api/src/odb_api/migrator/odb2_flag_definitions.f90 b/odb_api/src/odb_api/migrator/odb2_flag_definitions.f90
new file mode 100644
index 0000000..ccf54d9
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/odb2_flag_definitions.f90
@@ -0,0 +1,264 @@
+module odb2_flag_definitions
+! Module used in module odb2.F90
+! 25-09-2012 Hans Hersbach ECMWF
+
+  implicit none
+
+contains  ! naming and shaming of odb_api bitfield definitions and usage
+
+subroutine odb_find_ctype(cname,ctype)
+  character(len=*), intent( in) :: cname
+  character(len=*), intent(out) :: ctype
+
+  integer :: k
+
+  k=index(cname,"@") -1 ; if (k==-1) k=len_trim(cname)
+
+!-General rule
+  ctype=cname(:k)//"_t"
+
+!-Exceptions
+  if(cname(:k)== "datum_status") ctype="status_t"
+  if(cname(:k)== "datum_anflag") ctype="datum_flag_t"
+  if(cname(:k)== "report_status") ctype="status_t"
+
+end subroutine odb_find_ctype
+
+subroutine get_odb_flags(ctype,bdef,bits)
+! This should be a straight translation from type_definitions.h
+! Ensure that everything is LOWERCASE
+
+  character(len=*), intent( in) :: ctype
+  character(len=*), intent(out) :: bdef,bits
+
+  character(len=1024) :: cbits
+
+  cbits=""
+  if (trim(ctype)=="report_rdbflag_t") then
+     cbits=""                &
+     &// "lat_humon     :1;" &
+     &// "lat_qcsub     :1;" &
+     &// "lat_override  :1;" &
+     &// "lat_flag      :2;" &
+     &// "lat_hqc_flag  :1;" &
+     &// "lon_humon     :1;" &
+     &// "lon_qcsub     :1;" &
+     &// "lon_override  :1;" &
+     &// "lon_flag      :2;" &
+     &// "lon_hqc_flag  :1;" &
+     &// "date_humon    :1;" &
+     &// "date_qcsub    :1;" &
+     &// "date_override :1;" &
+     &// "date_flag     :2;" &
+     &// "date_hqc_flag :1;" &
+     &// "time_humon    :1;" &
+     &// "time_qcsub    :1;" &
+     &// "time_override :1;" &
+     &// "time_flag     :2;" &
+     &// "time_hqc_flag :1;" &
+     &// "stalt_humon   :1;" &
+     &// "stalt_qcsub   :1;" &
+     &// "stalt_override:1;" &
+     &// "stalt_flag    :2;" &
+     &// "stalt_hqc_flag:1;" &
+     &// ""
+  endif
+
+  if (trim(ctype)=="status_t") then
+     cbits=""                 &
+     &// "active         :1;" &        !// ACTIVE FLAG
+     &// "passive        :1;" &        !// PASSIVE FLAG
+     &// "rejected       :1;" &        !// REJECTED FLAG
+     &// "blacklisted    :1;" &        !// BLACKLISTED
+     &// "use_emiskf_only:1;" &        !// to be used for emiskf only
+     &// ""
+  endif
+
+  if (trim(ctype)=="datum_rdbflag_t") then
+     cbits=""                      &
+     &// "press_humon         :1;" &
+     &// "press_qcsub         :1;" &
+     &// "press_override      :1;" &
+     &// "press_flag          :2;" &
+     &// "press_hqc_flag      :1;" &
+     &// "press_judged_prev_an:2;" &
+     &// "press_used_prev_an  :1;" &
+     &// "_press_unused_6     :6;" &
+     &// "varno_humon         :1;" &
+     &// "varno_qcsub         :1;" &
+     &// "varno_override      :1;" &
+     &// "varno_flag          :2;" &
+     &// "varno_hqc_flag      :1;" &
+     &// "varno_judged_prev_an:2;" &
+     &// "varno_used_prev_an  :1;" &
+!    &// "_varno_unused_6     :6;" &
+     &// ""
+  endif
+
+  if (trim(ctype)=="datum_flag_t") then
+     cbits=""           &
+     &// "final    :4;" &                    !  // FINAL FLAG
+     &// "fg       :4;" &                    !  // FIRST GUESS FLAG
+     &// "depar    :4;" &                    !  // DEPARTURE FLAG
+     &// "varqc    :4;" &                    !  // VARIATIONAL QUALITY FLAG
+     &// "blacklist:4;" &                    !  // BLACKLIST FLAG
+     &// "ups      :1;" &                    !  // d'utilisation par analyse de pression de surface
+     &// "uvt      :1;" &                    !  // d'utilisation par analyse de vent et temperature
+     &// "uhu      :1;" &                    !  // d'utilisation par analyse d'humidite
+     &// "ut2      :1;" &                    !  // d'utilisation par analyse de temperat ure a 2m
+     &// "uh2      :1;" &                    !  // d'utilisation par analyse d'humidite a 2m
+     &// "uv1      :1;" &                    !  // d'utilisation par analyse de vent a 10m
+     &// "urr      :1;" &                    !  // d'utilisation par analyse de precipitations
+     &// "usn      :1;" &                    !  // d'utilisation par analyse de neige
+     &// "usst     :1;" &                    !  // d'utilisation par analyse de temperature de surface de la mer
+     &// ""
+  endif
+
+  if (trim(ctype)=="level_t") then
+     cbits=""             &
+!    &// "id         :9;" &                  !  // PILOT LEV. ID.
+     &// "maxwind    :1;" &                  !  // MAX WIND LEVEL
+     &// "tropopause :1;" &                  !  // TROPOPAUSE
+     &// "d_part     :1;" &                  !  // D PART
+     &// "c_part     :1;" &                  !  // C PART
+     &// "b_part     :1;" &                  !  // B PART
+     &// "a_part     :1;" &                  !  // A PART
+     &// "surface    :1;" &                  !  // SURFACE LEVEL
+     &// "signwind   :1;" &                  !  // SIGNIFICANT WIND LEVEL
+     &// "signtemp   :1;" &                  !  // SIGNIFICANT TEMPR. LEVEL
+     &// ""
+  endif
+
+  if (trim(ctype)=="report_event1_t") then
+     cbits=""               &
+     &// "no_data      :1;" &                !  // no data in the report
+     &// "all_rejected :1;" &                !  // all data rejected
+     &// "bad_practice :1;" &                !  // bad reporting practice
+     &// "rdb_rejected :1;" &                !  // rejected due to RDB flag
+     &// "redundant    :1;" &                !  // redundant report
+     &// "stalt_missing:1;" &                !  // missing station altitude
+     &// "qc_failed    :1;" &                !  // failed quality control
+     &// "overcast_ir  :1;" &                !  // report overcast IR
+     &// ""
+  endif
+
+  if (trim(ctype)=="report_blacklist_t") then
+     cbits=""                    &
+     &// "obstype           :1;" &
+     &// "statid            :1;" &
+     &// "codetype          :1;" &
+     &// "instype           :1;" &
+     &// "date              :1;" &
+     &// "time              :1;" &
+     &// "lat               :1;" &
+     &// "lon               :1;" &
+     &// "stalt             :1;" &
+     &// "scanpos           :1;" &
+     &// "retrtype          :1;" &
+     &// "qi_fc             :1;" &
+     &// "rff               :1;" &
+     &// "qi_nofc           :1;" &
+     &// "modoro            :1;" &
+     &// "lsmask            :1;" &
+     &// "rlsmask           :1;" &
+     &// "modps             :1;" &
+     &// "modts             :1;" &
+     &// "modt2m            :1;" &
+     &// "modtop            :1;" &
+     &// "sensor            :1;" &
+     &// "fov               :1;" &
+     &// "satza             :1;" &
+     &// "andate            :1;" &
+     &// "antime            :1;" &
+     &// "solar_elevation   :1;" &
+     &// "quality_retrieval :1;" &
+     &// "cloud_cover       :1;" &
+     &// "cloud_top_pressure:1;" &
+     &// "product_type      :1;" &
+     &// "sonde_type        :1;" &
+     &// ""
+  endif
+
+  if (trim(ctype)=="datum_event1_t")then
+     cbits=""                     &
+     &// "vertco_missing     :1;" &       ! // missing vertical coordinate
+     &// "obsvalue_missing   :1;" &       ! // missing observed value
+     &// "fg_missing         :1;" &       ! // missing first guess value
+     &// "rdb_rejected       :1;" &       ! // rejected due ti RDB flag
+     &// "assim_cld_flag     :1;" &       ! // assim of cloud-affected radiance
+     &// "bad_practice       :1;" &       ! // bad reporting practice
+     &// "vertpos_outrange   :1;" &       ! // vertical position out of range
+     &// "fg2big             :1;" &       ! // too big first guess departure
+     &// "depar2big          :1;" &       ! // too big departure in assimilation
+     &// "obs_error2big      :1;" &       ! // too big observation error
+     &// "datum_redundant    :1;" &       ! // dedundant datum
+     &// "level_redundant    :1;" &       ! // redundant level
+     &// "not_analysis_varno :1;" &       ! // not an analysis variable
+     &// "duplicate          :1;" &       ! // duplicated datum/level
+     &// "levels2many        :1;" &       ! // too many surface data/levels
+     &// "level_selection    :1;" &       ! // level selection
+     &// "vertco_consistency :1;" &       ! // vertical consistency check
+     &// "vertco_type_changed:1;" &       ! // vertical coordinate changed from Z to P
+     &// "combined_flagging  :1;" &       ! // combined flagging
+     &// "report_rejected    :1;" &       ! // datum rejected due to rejected report
+     &// "varqc_performed    :1;" &       ! // variational QC performed
+     &// "obserror_increased :1;" &       ! // obs error increased
+     &// "contam_cld_flag    :1;" &       ! // cloud contamination
+     &// "contam_rain_flag   :1;" &       ! // rain contamination
+     &// "contam_aerosol_flag:1;" &       ! // aerosol contamination
+     &// ""
+  endif
+
+
+  if (trim(ctype)=="datum_blacklist_t")then
+     cbits=""              &
+     &// "varno       :1;" &
+     &// "vertco_type :1;" &
+     &// "press       :1;" &
+     &// "press_rl    :1;" &
+     &// "ppcode      :1;" &
+     &// "obsvalue    :1;" &
+     &// "fg_depar    :1;" &
+     &// "obs_error   :1;" &
+     &// "fg_error    :1;" &
+     &// "winchan_dep :1;" &
+     &// "obs_t       :1;" &
+     &// "elevation   :1;" &
+     &// "winchan_dep2:1;" &
+     &// "tausfc      :1;" &
+     &// "csr_pclear  :1;" &
+     &// ""
+  endif
+
+  if (trim(ctype)=="aeolus_hdrflag_t")then
+     cbits=""                 &
+     &// "nadir_location :1;" &  ! //  bit#0 = 1 for location at nadir (calibration mode)
+     &// "orbit_predicted:1;" &  ! //      1         location from orbit predictor
+     &// "omit_from_ee   :1;" &  ! //      2         predicted location to omit from EE product
+     &// ""
+  endif
+
+  if(cbits=="") write(*,'(2a)')"Unknown bitfield: ",trim(ctype)
+  call split_odb_flag(cbits,bdef,bits)
+
+end subroutine get_odb_flags
+
+subroutine split_odb_flag(cbits,bdef,bits)
+  character(len=*), intent( in) :: cbits
+  character(len=*), intent(out) :: bdef,bits
+
+  integer :: i,k,l
+  bdef=""; bits=""
+
+  i=0
+  do
+    k=index(cbits(i+1:),":") ; if (k==0) exit
+    l=index(cbits(i+1:),";")
+    bdef=trim(bdef)//trim(adjustl(cbits(i  +1:i+k-1)))//":"
+    bits=trim(bits)//trim(adjustl(cbits(i+k+1:i+l-1)))//":"
+    i=i+l
+  enddo
+
+end subroutine split_odb_flag
+
+end module odb2_flag_definitions
diff --git a/odb_api/src/odb_api/migrator/odb2oda.cc b/odb_api/src/odb_api/migrator/odb2oda.cc
new file mode 100755
index 0000000..d457f27
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/odb2oda.cc
@@ -0,0 +1,66 @@
+/*
+ * © Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/FileHandle.h"
+#include "odb_api/migrator/FakeODBIterator.h"
+#include "odb_api/migrator/MigratorTool.h"
+#include "odb_api/migrator/ODB2ODATool.h"
+#include "odb_api/migrator/ODBIterator.h"
+#include "odb_api/migrator/ReptypeGenIterator.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/tools/ToolRunnerApplication.h"
+
+
+using namespace std;
+using namespace odb::tool;
+
+// Cannot use just string for str because of a clash with a typedef in ODB header...
+typedef std::string str;
+
+int gdb(int argc, char *argv[]);
+int valgrind(int argc, char *argv[]);
+
+//void test_schemaFile();
+
+int main(int argc, char *argv[])
+{
+    CommandLineParser clp(argc, argv);
+	clp.registerOptionWithArgument("-genreptype");
+    clp.registerOptionWithArgument("-reptypecfg");
+    clp.registerOptionWithArgument("-addcolumns");
+    clp.registerOptionWithArgument("-mdi");
+    clp.parameters();
+
+    cout << clp << std::endl;
+
+	ToolRunnerApplication runner(argc, argv, false, false);
+	//ToolRunnerApplication runner(clp, false, false);
+	MigratorTool migrator(clp);
+	runner.tool(&migrator);
+	return runner.start();
+}
+
+#if 0
+#include <assert.h>
+
+void test_schemaFile() {
+	assert(ODBIterator::schemaFile("/asdfasd/sdfas/ECMA.tmi") == "/asdfasd/sdfas/ECMA.tmi/ECMA.sch");
+	assert(ODBIterator::schemaFile("/asdfasd/sdfas/ODA.tmi") == "/asdfasd/sdfas/ODA.tmi/ODA.sch");
+	assert(ODBIterator::schemaFile("/asdfasd/sdfas/ODA.tmi/") == "/asdfasd/sdfas/ODA.tmi/ODA.sch");
+	assert(ODBIterator::schemaFile("ODA.tmi/") == "ODA.tmi/ODA.sch");
+	assert(ODBIterator::schemaFile("ODA.tmi") == "ODA.tmi/ODA.sch");
+	assert(ODBIterator::schemaFile("dupa/ODA.tmi") == "dupa/ODA.tmi/ODA.sch");
+}
+#endif 
+
+
diff --git a/odb_api/src/odb_api/migrator/odbdump.h b/odb_api/src/odb_api/migrator/odbdump.h
new file mode 100644
index 0000000..c3d8caa
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/odbdump.h
@@ -0,0 +1,45 @@
+#ifndef _ODBDUMP_H_
+#define _ODBDUMP_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* odbdump.h */
+
+#include "result.h"
+
+/* from aux/odbdump.c */
+
+extern void
+odbdump_reset_stderr(FILE *fp, const char *filename, const char *mode);
+
+extern void *odbdump_open(const char *database,
+			  const char *sql_query, /* Precedence on this over the queryfile, if both present */
+			  const char *queryfile,
+			  const char *poolmask,
+			  const char *varvalue,
+			  int *ncols);
+
+extern int odbdump_nextrow(void *Handle, 
+			   void *v, /* at least nd elements of sizeof(double) */
+			   int nd,
+			   int *new_dataset);
+
+extern int odbdump_nextrow_packed(void *Handle,
+				  void *v, /* at least npk elements sizeof(char) */
+				  int npk,
+				  int *new_dataset);
+
+extern int odbdump_close(void *Handle);
+
+extern colinfo_t *odbdump_create_colinfo(void *Handle, int *ncols);
+
+extern colinfo_t *odbdump_destroy_colinfo(colinfo_t *ci, int ncols);
+
+#ifdef __cplusplus
+} // closing brace for extern "C"
+#endif
+
+#endif /* _ODBDUMP_H_ */
+
diff --git a/odb_api/src/odb_api/migrator/pyodbdump.i b/odb_api/src/odb_api/migrator/pyodbdump.i
new file mode 100644
index 0000000..a211924
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/pyodbdump.i
@@ -0,0 +1,92 @@
+%module pyodbdump
+#include <cstdef>
+%{
+
+#define SWIG_FILE_WITH_INIT
+
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <math.h>
+#include <values.h>
+#include <stdint.h>
+#include <vector>
+
+using namespace std;
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Header.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/Column.h"
+
+#include "odb_api/migrator/migrator_api.h"
+
+#include "odb_api/migrator/OldODBReader.h"
+#include "odb_api/migrator/TSQLReader.h"
+#include "odb_api/migrator/ODBIterator.h"
+
+using namespace odb;
+using namespace odb::tool;
+
+%}
+
+%include "typemaps.i"
+%include "std_string.i"
+%include "exception.i"
+%include "std_except.i"
+%include "std_vector.i"
+
+
+%exception {
+	using namespace ::eckit;
+	using namespace ::odb;
+	using namespace ::odb::sql;
+    try {
+        $action
+    } catch (const ODBStopIteration& e) {
+		PyErr_SetString(PyExc_StopIteration, "no more data");
+		return NULL;
+    } catch (const ODBIndexError& e) {
+		PyErr_SetString(PyExc_IndexError, "column index out of range");
+		return NULL;
+	} catch (const FileError& e) {
+		PyErr_SetString(PyExc_IOError, e.what());
+		return NULL;
+	} catch (const ::odb::sql::SyntaxError& e) {
+		PyErr_SetString(PyExc_SyntaxError, e.what());
+		return NULL;
+	}  catch (const Exception& e) {
+		PyErr_SetString(PyExc_RuntimeError, e.what());
+		return NULL;
+	}
+	
+}
+
+%include "odb_api/ODBAPIVersion.h"
+%include "odb_api/ODBAPISettings.h"
+%include "odb_api/Column.h"
+%include "odb_api/IteratorProxy.h"
+%include "TSQLReader.h"
+%template(MetaDataBase) std::vector<odb::Column*>;
+%template(OldODBReader) odb::tool::TSQLReader<odb::tool::ODBIterator>;
+%template(ODBReader_iterator) odb::IteratorProxy<odb::tool::ODBIterator,odb::tool::TSQLReader<odb::tool::ODBIterator>, const double>;
+%include "odb_api/MetaData.h"
+#include "odb_api/Header.h"
+
+%include "migrator_api.h"
+%include "ODBIterator.h"
+
+%template(ODBReader) odb::tool::TSQLReader<ODBIterator>;
+
+#include "odbcapi.h"
+
+
+%init %{
+	void python_api_start();
+	python_api_start();
+%}
diff --git a/odb_api/src/odb_api/migrator/pyodbdump_example.py b/odb_api/src/odb_api/migrator/pyodbdump_example.py
new file mode 100644
index 0000000..c218261
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/pyodbdump_example.py
@@ -0,0 +1,12 @@
+import pyodbdump
+db = '/tmp/new_migrator/ECMA.conv'
+sql = open('/tmp/new_migrator/ECMA.conv/bigger_query.sql').read()
+
+#rows = [r [:] for r in pyodbdump.ODBReader(db, sql)]
+
+def columns(db, sql):
+    for r in pyodbdump.ODBReader(db, sql):
+        print dir(r)
+        return r
+
+print columns(db, sql)
diff --git a/odb_api/src/odb_api/migrator/solar_elevation.f90 b/odb_api/src/odb_api/migrator/solar_elevation.f90
new file mode 100644
index 0000000..8f07a16
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/solar_elevation.f90
@@ -0,0 +1,84 @@
+program solar_elevation
+! Insert and calculate solar_elevation at conv from date, time, lat, lon
+! Hans Hersbach, ECMWF  May 2013
+
+  use odb2
+  implicit none
+
+  integer                        :: ioptval,getopt
+  character*120                  :: carg
+  character                      :: options*4,copt
+  character(len=128)             :: ifile,ofile
+  type (todb2)                   :: odbi,odbo
+  character(len=128),allocatable :: cname(:)
+  real*8,allocatable             :: xrow(:)
+  integer                        :: ncol,nout,iread,ic
+  integer                        :: idat,itim,ilat,ilon,isol
+  integer                        :: yyyymmdd,hhmmss,mm,dd,hh
+  real                           :: xlat,xlon,xsol
+
+! 1. Crack options
+! ----------------
+  data options/'o:i:'/
+
+! Set default values
+  ifile="" ; ofile=""
+  do
+    ioptval=getopt(options,carg)
+    carg=trim(carg)
+    copt=char(ioptval)
+
+    if (ioptval <=0) exit
+    if (copt == 'o') ofile = trim(carg)
+    if (copt == 'i') ifile = trim(carg)
+  enddo
+
+!-Open input file
+  call odb_start()
+  call odb2_open(odbi,ifile,status='r')
+  call odb2_get(odbi,ncol=ncol)
+     allocate(cname(ncol+1))
+     allocate(xrow(ncol+1))
+     call odb2_get(odbi,ncol=ncol,cname=cname)
+
+  idat=find_column(odbi,            "date at hdr" )
+  itim=find_column(odbi,            "time at hdr" )
+  ilat=find_column(odbi,             "lat at hdr" )
+  ilon=find_column(odbi,             "lon at hdr" )
+
+!-Add my solar elevation
+  ic=ncol
+  ic=ic+1 ; cname(ic)="pk9real:solar_elevation at peter"; isol=ic
+  nout=ic
+
+!-Open output and create columns
+  call odb2_open(odbo,ofile,status='w')
+  call odb2_set_columns(odbo,nout,cname)
+
+!-Loop through odb file
+  iread=1
+  do
+    call odb2_read (odbi,xrow,ncol,iread) ; if(iread==0) exit
+
+    xlat=xrow(ilat)
+    xlon=xrow(ilon)
+
+    yyyymmdd=int(xrow(idat))
+      hhmmss=int(xrow(itim)) ; if(mod(hhmmss,100)==60) hhmmss=100*(hhmmss/100)+59
+
+    mm=mod(yyyymmdd,10000)/100
+    dd=mod(yyyymmdd,100)
+    hh=hhmmss/10000
+
+    call diurnal(mm,dd,hh,xlat,xlon,xsol)
+    xrow(isol)=xsol
+
+    call odb2_write(odbo,xrow,nout,1)
+  enddo
+  call odb2_close(odbi)
+  call odb2_close(odbo)
+
+!-Clean up
+  deallocate(cname,xrow)
+
+end program solar_elevation
diff --git a/odb_api/src/odb_api/migrator/solar_elevation.sc b/odb_api/src/odb_api/migrator/solar_elevation.sc
new file mode 100755
index 0000000..cf048ee
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/solar_elevation.sc
@@ -0,0 +1,26 @@
+# ---------------------------------------------------------------------------------------
+
+  flist="odb2_flag_definitions.f90 odb2.f90 diurnal.F90 solar_elevation.f90"
+  WDIR=$SCRATCH/solar_elevation ; [[ ! -d $WDIR ]] && mkdir -p $WDIR
+  SDIR=$PWD
+
+  cd $SDIR ; cp $flist $WDIR
+
+  prog=solar_elevation.x
+
+  cd $WDIR
+  [[ -f $prog ]] && rm -f $prog
+
+
+  unset ODB_API_VERSION ;  export ODB_VERSION=CY38R2.001
+  use odb
+  use pgi-10.8
+  F90="pgf90 -fPIC -r8 -O3"
+
+  LINKS=" $ECLIB $ODB_FORTRAN_INCLUDE $ODB_FORTRAN_LIB $GRIB_API_INCLUDE $GRIB_API_LIB"
+  $F90 $flist -o $prog $LINKS -rpath=$ODB_API_DIR/$ODB_API_VERSION/lib
+
+
+  odbi=/scratch/er/er9/radiosondes.odb
+  odbo=$WDIR/radiosondes_myse.odb
+  time ./$prog -i $odbi -o $odbo
diff --git a/odb_api/src/odb_api/migrator/test_migrator.ecml b/odb_api/src/odb_api/migrator/test_migrator.ecml
new file mode 100644
index 0000000..ebaa86e
--- /dev/null
+++ b/odb_api/src/odb_api/migrator/test_migrator.ecml
@@ -0,0 +1,27 @@
+println, values = "ODB_API_TEST_DATA_PATH=" / (getenv, values = ODB_API_TEST_DATA_PATH)
+
+function, of = label / do, shell = (
+    test, label = (label), do = (
+        println, values = "##### shell: executing '" / (label) / "'"
+        system, values = (do)
+    ), 
+    expect = 0
+)
+
+function, unpack_data = (
+    shell, label = "unpack test input data: 2000010106.old.ECMA.tar.gz", 
+    do = "tar zxf 2000010106.old.ECMA.tar.gz"
+
+    shell, label = "recreate indices with dcagen", 
+    do = "cd 2000010106/ECMA && dcagen -N 1 -z -F -n -q "
+)
+
+test, label = Check migrate verb works,
+do = (
+    unpack_data
+
+    let, output_files = (migrate, source = "2000010106/ECMA",
+                                  target = "{varno}/varno={varno}.odb",
+                                  filter = "select varno,obsvalue from hdr,body;")
+    output_files
+), expect = "{varno}/varno={varno}.odb" # TODO: this should be a list of files produced
diff --git a/odb_api/src/odb_api/odb2_to_odb1/CMakeLists.txt b/odb_api/src/odb_api/odb2_to_odb1/CMakeLists.txt
new file mode 100644
index 0000000..4bbacc7
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/CMakeLists.txt
@@ -0,0 +1,19 @@
+ecbuild_add_executable(TARGET odb2_to_odb1.x
+    INCLUDES ${ODB_INCLUDE_DIRS} ${ODB_INCLUDE_DIRS}/../module 
+    SOURCES
+              Odb2Odb1.cc
+              Odb2Odb1.h
+              Odb2Odb1Main.cc
+              mpi_wrapper.F90
+              mpif.h
+              odb_wrapper.F90
+              odbi.F90
+
+    LIBS      Odb_fortran Odb eckit ${ODB_LIBRARIES}
+
+    CONDITION HAVE_FORTRAN AND ODB_FOUND)
+
+if(HAVE_FORTRAN AND ODB_FOUND)
+    include(odb_link_schemas)
+    odb_link_schemas(odb2_to_odb1.x ${ODB_SCHEMAS})
+endif()
diff --git a/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.cc b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.cc
new file mode 100644
index 0000000..dd90fcf
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.cc
@@ -0,0 +1,269 @@
+/// @file   Odb2Odb1.cc
+/// @author Anne Fouilloux
+
+#include "Odb2Odb1.h"
+
+#include <iostream>
+#include <sstream>
+
+#include "odb_api/odbcapi.h"
+#include "odb_api/Select.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/MetaDataReader.h"
+
+
+using namespace std;
+using namespace odb;
+using namespace eckit;
+
+extern "C" {
+//=================================
+int typeoftable(char *c_linkname, map<string,int> flaglist) // return ODB_HDR_ALIGNED or ODB_BODY_ALIGNED
+//=================================
+{
+  int type = -1;
+  map < string, int> ::iterator it;
+  string linkname = c_linkname;
+  int pl= linkname.find("(");
+  int pr= linkname.find(")");
+  int pa= linkname.find("@");
+  string table;
+  if (pl > 0 && pr > 0) {
+    table = linkname.substr(pl+1, pr-pl-1);;
+  } else if (pa!=string::npos) {
+    table = linkname.substr(pa+1);;
+  } else {
+    table = linkname;
+  }
+
+
+  size_t startpos = table.find_first_not_of(" \t");
+  size_t endpos = table.find_last_not_of(" \t");
+  if((string::npos == startpos) || (string::npos == endpos))
+      table="";
+  else
+      table=table.substr(startpos, endpos - startpos + 1);
+
+/*
+   for(it = flaglist.begin(); it != flaglist.end(); ++it)
+     {
+  Log::info() << linkname << " typeoftable table " << table << "|" << it->first << "|" << endl;
+   }
+*/
+
+  it=flaglist.find(table);
+  if (it != flaglist.end()) {
+    type = it->second;
+//     Log::info() << linkname << " typeoftable table " << table << "|" << it->first << "|" << endl;
+  }
+
+  return type;
+
+}
+
+//=================================
+int typeoftable_(char *c_linkname, map<string,int> flaglist) // return ODB_HDR_ALIGNED or ODB_BODY_ALIGNED
+//=================================
+{
+  return typeoftable(c_linkname, flaglist);
+}
+
+}
+
+namespace odb {
+namespace tool {
+
+//=================================
+void Odb2Odb1::tableListInit()
+//=================================
+{
+ Log::info() << "Odb2Odb1::tableListInit " << tableListFile_ << endl;
+
+ vector<string> tablelist = StringTool::readLines(tableListFile_);
+
+ int type = ODB_NMDI;
+ for (size_t i = 0; i < tablelist.size(); ++i)
+  {
+    int idx_hdr=tablelist[i].find("# HDR_ALIGNED TABLES");
+    if (idx_hdr!=string::npos) {
+      type = ODB_HDR_ALIGNED;
+    }
+    int idx_body=tablelist[i].find("# BODY_ALIGNED TABLES");
+    if (idx_body!=string::npos) {
+      type = ODB_BODY_ALIGNED;
+    }
+    if (idx_hdr == string::npos && idx_body == string::npos) {
+      tableList_[tablelist[i]] = type;
+    }
+  }
+}
+
+
+//=================================
+Odb2Odb1::Odb2Odb1(int argc, char ** argv)
+//=================================
+: tableList_(), CommandLineParser(argc, argv)
+{
+   registerOptionWithArgument("-i");
+   registerOptionWithArgument("-o");
+   registerOptionWithArgument("-npools");
+   registerOptionWithArgument("-t");
+   registerOptionWithArgument("-masterKey");
+
+   mpi_setup_f90();
+   set_err_trap_f90();
+   if (! optionIsSet("-i") || ! optionIsSet("-t") ) {
+     Log::error() << "Usage: ";
+     usage(parameters(0), Log::error());
+     Log::error() << endl;
+   } else {
+
+     prefixInputfile_ = optionArgument("-i", std::string(""));
+     masterKey_ = optionArgument("-masterKey", std::string("seqno"));
+
+     if (optionIsSet("-o")) {
+       outputfile_ = optionArgument("-o", std::string(""));;
+     } else {
+       outputfile_ = "ECMA";
+     }
+     npools_ = optionArgument("-npools",1);
+
+
+     tableListFile_ = optionArgument("-t", std::string(""));
+
+     mpi_print_info_f90();
+     string open_mode="NEW";
+     Log::info() << "File to open " << outputfile_.c_str() << " " << outputfile_.size() << endl;
+     handle_ = odb_open_f90(const_cast<char *>(outputfile_.c_str()),outputfile_.size(),
+                            const_cast<char *>(open_mode.c_str()), open_mode.length(),&npools_);
+     tableListInit();
+   }
+}
+
+//=================================
+Odb2Odb1::~Odb2Odb1()
+//=================================
+{
+  //Log::info() <<  "destructor Odb2Odb1 handle_ = " << handle_ << endl;
+  if (handle_>0)
+    odb_close_f90(handle_,true);
+
+  mpi_finalize_f90();
+}
+
+
+//=================================
+void Odb2Odb1::computeHdrCount(const PathName &db)
+//=================================
+{
+    string sql = "select distinct " + masterKey_ + " from \"" + db + "\";";
+    odb::Select sodb(sql);
+
+    odb::Select::iterator it = sodb.begin();
+    odb::Select::iterator end = sodb.end();
+    hdrlen_.clear();
+    for( ; it != end; ++it) {
+      size_t seqno_index = it->columns().columnIndex(masterKey_);
+      hdrlen_.push_back((*it)[seqno_index]);
+    }
+}
+
+//=================================
+void Odb2Odb1::computeBodyCount(const PathName &db)
+//=================================
+{
+    unsigned long long n = 0;
+
+    if ( db != "") {
+    typedef MetaDataReader<MetaDataReaderIterator> MDR;
+    MDR mdReader(db);
+    MDR::iterator it = mdReader.begin();
+    MDR::iterator end = mdReader.end();
+    for (; it != end; ++it)
+    {
+        const MetaData &md = it->columns();
+        n += md.rowsNumber();
+    }
+    bodylen_.resize(n,0);
+    }
+}
+
+//=================================
+void Odb2Odb1::updateLinks(string table, const int &np)
+//=================================
+{
+  PathName inputfile;
+  std::stringstream out;
+  out << np;
+  vector<string> sqlBuf;
+  PathName sqlFile;
+  StringTool::trimInPlace(table);
+  int err;
+  string sql = table + "_update_links";
+  c_int_array c_hdrlen;
+  c_hdrlen.len=hdrlen_.size();
+  c_hdrlen.array = &hdrlen_[0];
+  c_int_array c_bodylen;
+  c_bodylen.len=bodylen_.size();
+  c_bodylen.array = &bodylen_[0];
+  Log::info() << " SQL SELECT: " << sql << " hdrlen_size = " << hdrlen_.size() << " bodylen_size = " << bodylen_.size() << endl;
+  err = odb_update_links_f90(handle_, sql.c_str(), sql.size(), np, &c_hdrlen, &c_bodylen, tableList_);
+}
+
+
+
+//=================================
+void Odb2Odb1::create()
+//=================================
+{
+ int nmypools = odb_no_of_local_pools_f90(handle_);
+ vector<int> cMypools(nmypools);
+ c_int_array arrays;
+ arrays.len = cMypools.size();
+ arrays.array = &cMypools[0];
+ nmypools = odb_get_local_pools_f90(handle_, &arrays);
+
+ cout << " I have " << nmypools << " pools on my processor" << endl;
+/*
+  for (size_t i = 0; i < tableList_.size(); ++i)
+   {
+     Log::info() << "Table " << tableList_[i] << endl;
+   }
+*/
+
+ int np;
+ for (int jp = 0 ; jp < nmypools ; jp++)
+  {
+   np = cMypools[jp];
+   PathName inputfile;
+   std::stringstream out;
+   out << np;
+   inputfile = prefixInputfile_  + "." + out.str() + ".odb";
+   if (inputfile.exists()) {
+     Log::info() << " Processing pools : " << np << " with file " << inputfile << endl;
+     computeHdrCount(inputfile);
+     computeBodyCount(inputfile);
+     c_int_array c_hdrlen;
+     c_hdrlen.len=hdrlen_.size();
+     c_hdrlen.array = &hdrlen_[0];
+     c_int_array c_bodylen;
+     c_bodylen.len=bodylen_.size();
+     c_bodylen.array = &bodylen_[0];
+
+     odb_fill_f90(handle_, string(inputfile).c_str(), &c_hdrlen,&c_bodylen, tableList_, masterKey_.c_str(), masterKey_.size(), np);
+     // update links for this pool
+     map<string, int>::const_iterator it;
+     for(it = tableList_.begin(); it != tableList_.end(); ++it)
+       {
+       updateLinks((*it).first, np);
+       }
+      string dtname="*";
+      int err = odb_swapout_f90(handle_, dtname.c_str(), dtname.size(), np, true, true);
+
+    }
+  }
+}
+
+//=================================
+}
+}
diff --git a/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.h b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.h
new file mode 100644
index 0000000..a62d3c6
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1.h
@@ -0,0 +1,111 @@
+/// @file   Odb2Odb1.h
+/// @author Anne Fouilloux
+
+#ifndef ODB2_TO_ODB1_H
+#define ODB2_TO_ODB1_H
+
+#include <vector>
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/CommandLineParser.h"
+#include "odb_api/StringTool.h"
+
+#define ODB_tag_delim ";"
+#define ODB_NMDI 2147483647
+
+#define ODB_HDR_ALIGNED 1
+#define ODB_BODY_ALIGNED 2
+
+using namespace std;
+
+struct c_int_array {
+  int len;
+  int *array;
+};
+
+extern "C" {
+  typedef void* fortran_ptr;
+  void set_err_trap_f90();
+  void mpi_setup_f90();
+  void mpi_finalize_f90();
+  void mpi_print_info_f90();
+  int odb_open_f90(char *, int,  char *,int, int *);
+  int odb_close_f90(int, bool);
+  int odb_get_no_of_columns_f90(int,const char *, int);
+  int odb_create_index_f90(int, const char *, int, void *, int*, int);
+  int odb_put_one_row_f90(int, const char *, int, double *, int*, int, int);
+  int odb_no_of_local_pools_f90(int);
+  int odb_get_local_pools_f90(int, struct c_int_array *);
+  int odb_swapout_f90(int, const char *, int, int, bool, bool);
+  int ODB_maxcols();
+  int typeoftable(char *, map<string,int>); // return ODB_HDR_ALIGNED or ODB_BODY_ALIGNED
+  int typeoftable_(char *, map<string,int>); // return ODB_HDR_ALIGNED or ODB_BODY_ALIGNED
+  void odb_fill_f90(int, const char *, struct c_int_array *, struct c_int_array *, map<string,int>, const char *, int, int);
+  int odb_update_links_f90(int, const char *, int, int, struct c_int_array *, struct c_int_array *, map<string,int>);
+}
+
+namespace odb{
+namespace tool {
+
+class Odb2Odb1 : public StringTool, public CommandLineParser {
+ public:
+
+    static void help(ostream & o)
+    {
+      o << "Shuffle an odb file into npools outputfiles";
+    }
+
+   static void usage(const string & name, ostream & o)
+    {
+      o << name << " -i <prefix_inputfile> -t <table_list> [-o <dbname>] [-npools <npools>]";
+    }
+
+   ~Odb2Odb1();
+
+   Odb2Odb1(int argc, char ** argv);
+
+   string name() { return name_; }
+   void name(string s) { name_ = s; }
+
+   string outputfile() { return outputfile_; }
+   string prefixInputfile() { return prefixInputfile_; }
+
+   const vector<string> tableList();
+   std::string tableList(size_t i) { return tableList()[i]; }
+
+   int npools() { return npools_; }
+
+   void create();
+   void fillOneTable(string , const int &);
+
+private:
+    string name_;
+    string prefixInputfile_;
+    string outputfile_;
+    eckit::PathName tableListFile_;
+    vector<int> hdrlen_;
+    vector<int> bodylen_;
+    map<string,int> tableList_;
+    string masterKey_;
+
+    int npools_;
+    int handle_;
+
+    vector< vector<double> > data_;
+
+    // No copy allowed
+
+    Odb2Odb1(const Odb2Odb1&);
+    Odb2Odb1& operator=(const Odb2Odb1&);
+
+    void tableListInit();
+    void updateLinks(string , const int &);
+    void computeHdrCount(const eckit::PathName &db);
+    void computeBodyCount(const eckit::PathName &db);
+
+};
+
+}
+}
+
+#endif // ODB2_TO_ODB1_H
diff --git a/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1Main.cc b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1Main.cc
new file mode 100644
index 0000000..c0b6d2b
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/Odb2Odb1Main.cc
@@ -0,0 +1,24 @@
+/// @file   Odb2Odb1Main.cc
+/// @author Anne Fouilloux
+
+#include <iostream>
+#include <string>
+#include <unistd.h>
+#include "odb_api/odbcapi.h"
+
+#include "Odb2Odb1.h"
+
+using namespace std;
+
+int
+main(int argc, char *argv[])
+{
+ odb_start_with_args(argc, argv);
+
+ odb::tool::Odb2Odb1 sodb(argc, argv);
+
+ if (sodb.prefixInputfile() != "") {
+   cout << " number of pools " << sodb.npools() << endl;;
+   sodb.create();
+ }
+}
diff --git a/odb_api/src/odb_api/odb2_to_odb1/mpi_wrapper.F90 b/odb_api/src/odb_api/odb2_to_odb1/mpi_wrapper.F90
new file mode 100644
index 0000000..454c2aa
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/mpi_wrapper.F90
@@ -0,0 +1,70 @@
+!
+! Copyright 2011 ECMWF
+!
+! This software was developed at ECMWF for evaluation
+! and may be used for academic and research purposes only.
+! The software is provided as is without any warranty.
+!
+! This software can be used, copied and modified but not
+! redistributed or sold. This notice must be reproduced
+! on each copy made.
+!
+
+!> Interfaces to be called from C++ for MPI initialize/finalize
+!> @author Anne Fouilloux
+
+! ------------------------------------------------------------------------------
+
+subroutine mpi_setup_c() bind(c,name='mpi_setup_f90')
+use iso_c_binding
+USE MPL_MODULE, ONLY  : MPLUSERCOMM, LMPLUSERCOMM
+implicit none
+include "mpif.h"
+
+integer :: IER
+
+CALL MPI_INIT(IER)
+
+LMPLUSERCOMM = .TRUE.
+MPLUSERCOMM = MPI_COMM_WORLD
+
+end subroutine mpi_setup_c
+
+! ------------------------------------------------------------------------------
+
+subroutine mpi_finalize_c() bind(c,name='mpi_finalize_f90')
+use iso_c_binding
+implicit none
+
+integer :: IER
+logical :: LL
+
+CALL MPI_FINALIZED(LL,IER)
+IF (.not.LL) THEN
+  CALL MPI_FINALIZE(IER)
+ENDIF
+
+end subroutine mpi_finalize_c
+
+! ------------------------------------------------------------------------------
+
+subroutine mpi_print_info_c() bind(c,name='mpi_print_info_f90')
+use iso_c_binding
+USE MPL_MODULE, ONLY  : MPLUSERCOMM, LMPLUSERCOMM
+implicit none
+include "mpif.h"
+
+integer :: NP, ME, IER
+CALL MPI_COMM_SIZE(MPI_COMM_WORLD,NP,IER)
+CALL MPI_COMM_RANk(MPI_COMM_WORLD,ME,IER)
+write(0,*) "MPI_SETUP_F90: rank,nprocs=",me,np
+write(0,*) "MPI_SETUP_F90: MPLUSERCOMM=",MPLUSERCOMM
+
+end subroutine mpi_print_info_c
+
+! ------------------------------------------------------------------------------
+
+subroutine set_err_trap_c() bind(c,name='set_err_trap_f90')
+    use yomerrtrap
+    call set_err_trap()
+end subroutine
diff --git a/odb_api/src/odb_api/odb2_to_odb1/mpif.h b/odb_api/src/odb_api/odb2_to_odb1/mpif.h
new file mode 100644
index 0000000..3c3ca13
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/mpif.h
@@ -0,0 +1,221 @@
+!
+!
+!  (C) 1993 by Argonne National Laboratory and Mississipi State University.
+!      All rights reserved.  See COPYRIGHT in top-level directory.
+!
+!
+! user include file for MPI programs, with no dependencies
+!
+! It really is not possible to make a perfect include file that can
+! be used by both F77 and F90 compilers, but this is close.  We have removed
+! continuation lines (allows free form input in F90); systems whose
+! Fortran compilers support ! instead of just C or * for comments can
+! globally replace a C in the first column with !; the resulting file
+! should work for both Fortran 77 and Fortran 90.
+!
+! If your Fortran compiler supports ! for comments, you can run this
+! through sed with
+!     sed -e 's/^C/\!/g'
+!
+! We have also removed the use of contractions (involving the single quote)
+! character because some users use .F instead of .f files (to invoke the
+! cpp preprocessor) and further, their preprocessor is determined to find
+! matching single quote pairs (and probably double quotes; given the
+! different rules in C and Fortran, this sounds like a disaster).  Rather than
+! take the position that the poor users should get a better system, we
+! have removed the text that caused problems.  Of course, the users SHOULD
+! get a better system...
+!
+! return codes
+      INTEGER MPI_SUCCESS,MPI_ERR_BUFFER,MPI_ERR_COUNT,MPI_ERR_TYPE
+      INTEGER MPI_ERR_TAG,MPI_ERR_COMM,MPI_ERR_RANK,MPI_ERR_ROOT
+      INTEGER MPI_ERR_GROUP
+      INTEGER MPI_ERR_OP,MPI_ERR_TOPOLOGY,MPI_ERR_DIMS,MPI_ERR_ARG
+      INTEGER MPI_ERR_UNKNOWN,MPI_ERR_TRUNCATE,MPI_ERR_OTHER
+      INTEGER MPI_ERR_INTERN,MPI_ERR_IN_STATUS,MPI_ERR_PENDING
+      INTEGER MPI_ERR_REQUEST, MPI_ERR_LASTCODE
+      PARAMETER (MPI_SUCCESS=0,MPI_ERR_BUFFER=1,MPI_ERR_COUNT=2)
+      PARAMETER (MPI_ERR_TYPE=3,MPI_ERR_TAG=4,MPI_ERR_COMM=5)
+      PARAMETER (MPI_ERR_RANK=6,MPI_ERR_ROOT=7,MPI_ERR_GROUP=8)
+      PARAMETER (MPI_ERR_OP=9,MPI_ERR_TOPOLOGY=10,MPI_ERR_DIMS=11)
+      PARAMETER (MPI_ERR_ARG=12,MPI_ERR_UNKNOWN=13)
+      PARAMETER (MPI_ERR_TRUNCATE=14,MPI_ERR_OTHER=15)
+      PARAMETER (MPI_ERR_INTERN=16,MPI_ERR_IN_STATUS=17)
+      PARAMETER (MPI_ERR_PENDING=18,MPI_ERR_REQUEST=19)
+      PARAMETER (MPI_ERR_LASTCODE=1073741823)
+!
+      INTEGER MPI_UNDEFINED
+      parameter (MPI_UNDEFINED = (-32766))
+!
+      INTEGER MPI_GRAPH, MPI_CART
+      PARAMETER (MPI_GRAPH = 1, MPI_CART = 2)
+      INTEGER  MPI_PROC_NULL
+      PARAMETER ( MPI_PROC_NULL = (-1) )
+!
+      INTEGER MPI_BSEND_OVERHEAD
+      PARAMETER ( MPI_BSEND_OVERHEAD = 512 )
+
+      INTEGER MPI_SOURCE, MPI_TAG, MPI_ERROR
+      PARAMETER(MPI_SOURCE=2, MPI_TAG=3, MPI_ERROR=4)
+      INTEGER MPI_STATUS_SIZE
+      PARAMETER (MPI_STATUS_SIZE=4)
+      INTEGER MPI_MAX_PROCESSOR_NAME, MPI_MAX_ERROR_STRING
+      PARAMETER (MPI_MAX_PROCESSOR_NAME=256)
+      PARAMETER (MPI_MAX_ERROR_STRING=512)
+      INTEGER MPI_MAX_NAME_STRING
+      PARAMETER (MPI_MAX_NAME_STRING=63)
+!
+      INTEGER MPI_COMM_NULL
+      PARAMETER (MPI_COMM_NULL=0)
+!
+      INTEGER MPI_DATATYPE_NULL
+      PARAMETER (MPI_DATATYPE_NULL = 0)
+
+      INTEGER MPI_ERRHANDLER_NULL
+      PARAMETER (MPI_ERRHANDLER_NULL = 0)
+
+      INTEGER MPI_GROUP_NULL
+      PARAMETER (MPI_GROUP_NULL = 0)
+
+      INTEGER MPI_KEYVAL_INVALID
+      PARAMETER (MPI_KEYVAL_INVALID = 0)
+
+      INTEGER MPI_REQUEST_NULL
+      PARAMETER (MPI_REQUEST_NULL = 0)
+!
+      INTEGER MPI_IDENT, MPI_CONGRUENT, MPI_SIMILAR, MPI_UNEQUAL
+      PARAMETER (MPI_IDENT=0, MPI_CONGRUENT=1, MPI_SIMILAR=2)
+      PARAMETER (MPI_UNEQUAL=3)
+!
+!     MPI_BOTTOM needs to be a known address; here we put it at the
+!     beginning of the common block.  The point-to-point and collective
+!     routines know about MPI_BOTTOM, but MPI_TYPE_STRUCT as yet does not.
+!
+!     MPI_STATUS_IGNORE and MPI_STATUSES_IGNORE are similar objects
+!     Until the underlying MPI library implements the C version of these
+!     (a null pointer), these are declared as arrays of MPI_STATUS_SIZE
+!
+!     The types MPI_INTEGER1,2,4 and MPI_REAL4,8 are OPTIONAL.
+!     Their values are zero if they are not available.  Note that
+!     using these reduces the portability of code (though may enhance
+!     portability between Crays and other systems)
+!
+      INTEGER MPI_TAG_UB, MPI_HOST, MPI_IO
+      INTEGER MPI_BOTTOM
+      INTEGER MPI_STATUS_IGNORE(MPI_STATUS_SIZE)
+      INTEGER MPI_STATUSES_IGNORE(MPI_STATUS_SIZE)
+      INTEGER MPI_INTEGER, MPI_REAL, MPI_DOUBLE_PRECISION
+      INTEGER MPI_COMPLEX, MPI_DOUBLE_COMPLEX,MPI_LOGICAL
+      INTEGER MPI_CHARACTER, MPI_BYTE, MPI_2INTEGER, MPI_2REAL
+      INTEGER MPI_2DOUBLE_PRECISION, MPI_2COMPLEX, MPI_2DOUBLE_COMPLEX
+      INTEGER MPI_UB, MPI_LB
+      INTEGER MPI_PACKED, MPI_WTIME_IS_GLOBAL
+      INTEGER MPI_COMM_WORLD, MPI_COMM_SELF, MPI_GROUP_EMPTY
+      INTEGER MPI_SUM, MPI_MAX, MPI_MIN, MPI_PROD, MPI_LAND, MPI_BAND
+      INTEGER MPI_LOR, MPI_BOR, MPI_LXOR, MPI_BXOR, MPI_MINLOC
+      INTEGER MPI_MAXLOC
+      INTEGER MPI_OP_NULL
+      INTEGER MPI_ERRORS_ARE_FATAL, MPI_ERRORS_RETURN
+      INTEGER MPI_THREAD_SINGLE, MPI_THREAD_FUNNELED, MPI_THREAD_SERIALIZED
+      INTEGER MPI_THREAD_MULTIPLE
+      INTEGER MPI_MODE_RDONLY, MPI_MODE_WRONLY, MPI_MODE_CREATE
+
+!
+      PARAMETER (MPI_ERRORS_ARE_FATAL=119)
+      PARAMETER (MPI_ERRORS_RETURN=120)
+!
+      PARAMETER (MPI_COMPLEX=23,MPI_DOUBLE_COMPLEX=24,MPI_LOGICAL=25)
+      PARAMETER (MPI_REAL=26,MPI_DOUBLE_PRECISION=27,MPI_INTEGER=28)
+      PARAMETER (MPI_2INTEGER=29,MPI_2COMPLEX=30,MPI_2DOUBLE_COMPLEX=31)
+      PARAMETER (MPI_2REAL=32,MPI_2DOUBLE_PRECISION=33,MPI_CHARACTER=1)
+      PARAMETER (MPI_BYTE=3,MPI_UB=16,MPI_LB=15,MPI_PACKED=14)
+
+      INTEGER MPI_ORDER_C, MPI_ORDER_FORTRAN
+      PARAMETER (MPI_ORDER_C=56, MPI_ORDER_FORTRAN=57)
+      INTEGER MPI_DISTRIBUTE_BLOCK, MPI_DISTRIBUTE_CYCLIC
+      INTEGER MPI_DISTRIBUTE_NONE, MPI_DISTRIBUTE_DFLT_DARG
+      PARAMETER (MPI_DISTRIBUTE_BLOCK=121, MPI_DISTRIBUTE_CYCLIC=122)
+      PARAMETER (MPI_DISTRIBUTE_NONE=123)
+      PARAMETER (MPI_DISTRIBUTE_DFLT_DARG=-49767)
+      INTEGER MPI_MAX_INFO_KEY, MPI_MAX_INFO_VAL
+      PARAMETER (MPI_MAX_INFO_KEY=255, MPI_MAX_INFO_VAL=1024)
+      INTEGER MPI_INFO_NULL
+      PARAMETER (MPI_INFO_NULL=0)
+
+!
+! Optional Fortran Types.  Configure attempts to determine these.
+!
+      INTEGER MPI_INTEGER1, MPI_INTEGER2, MPI_INTEGER4, MPI_INTEGER8
+      INTEGER MPI_INTEGER16
+      INTEGER MPI_REAL4, MPI_REAL8, MPI_REAL16
+      INTEGER MPI_COMPLEX8, MPI_COMPLEX16, MPI_COMPLEX32
+      PARAMETER (MPI_INTEGER1=1,MPI_INTEGER2=4)
+      PARAMETER (MPI_INTEGER4=6)
+      PARAMETER (MPI_INTEGER8=13)
+      PARAMETER (MPI_INTEGER16=0)
+      PARAMETER (MPI_REAL4=10)
+      PARAMETER (MPI_REAL8=11)
+      PARAMETER (MPI_REAL16=0)
+      PARAMETER (MPI_COMPLEX8=23)
+      PARAMETER (MPI_COMPLEX16=24)
+      PARAMETER (MPI_COMPLEX32=0)
+!
+!    This is now handled with either the "pointer" extension or this same
+!    code, appended at the end.
+!      COMMON /MPIPRIV/ MPI_BOTTOM,MPI_STATUS_IGNORE,MPI_STATUSES_IGNORE
+!C
+!C     Without this save, some Fortran implementations may make the common
+!C     dynamic!
+!C
+!C     For a Fortran90 module, we might replace /MPIPRIV/ with a simple
+!C     SAVE MPI_BOTTOM
+!C
+!      SAVE /MPIPRIV/
+!
+      PARAMETER (MPI_MAX=100,MPI_MIN=101,MPI_SUM=102,MPI_PROD=103)
+      PARAMETER (MPI_LAND=104,MPI_BAND=105,MPI_LOR=106,MPI_BOR=107)
+      PARAMETER (MPI_LXOR=108,MPI_BXOR=109,MPI_MINLOC=110)
+      PARAMETER (MPI_MAXLOC=111, MPI_OP_NULL=0)
+!
+      PARAMETER (MPI_GROUP_EMPTY=90,MPI_COMM_WORLD=91,MPI_COMM_SELF=92)
+      PARAMETER (MPI_TAG_UB=80,MPI_HOST=82,MPI_IO=84)
+      PARAMETER (MPI_WTIME_IS_GLOBAL=86)
+!
+      INTEGER MPI_ANY_SOURCE
+      PARAMETER (MPI_ANY_SOURCE = (-2))
+      INTEGER MPI_ANY_TAG
+      PARAMETER (MPI_ANY_TAG = (-1))
+!
+      INTEGER MPI_VERSION, MPI_SUBVERSION
+      PARAMETER (MPI_VERSION    = 1, MPI_SUBVERSION = 2)
+!
+!     There are additional MPI-2 constants
+      INTEGER MPI_ADDRESS_KIND, MPI_OFFSET_KIND
+      PARAMETER (MPI_ADDRESS_KIND=4)
+      PARAMETER (MPI_OFFSET_KIND=8)
+!
+!     All other MPI routines are subroutines
+!     This may cause some Fortran compilers to complain about defined and
+!     not used.  Such compilers should be improved.
+!
+!     Some Fortran compilers will not link programs that contain
+!     external statements to routines that are not provided, even if
+!     the routine is never called.  Remove PMPI_WTIME and PMPI_WTICK
+!     if you have trouble with them.
+!
+      DOUBLE PRECISION MPI_WTIME, MPI_WTICK,PMPI_WTIME,PMPI_WTICK
+      EXTERNAL MPI_WTIME, MPI_WTICK,PMPI_WTIME,PMPI_WTICK
+!
+!     The attribute copy/delete subroutines are symbols that can be passed
+!     to MPI routines
+!
+      EXTERNAL MPI_NULL_COPY_FN, MPI_NULL_DELETE_FN, MPI_DUP_FN
+      COMMON /MPIPRIV/ MPI_BOTTOM,MPI_STATUS_IGNORE,MPI_STATUSES_IGNORE
+!
+!     Without this save, some Fortran implementations may make the common
+!     dynamic!
+!
+!     For a Fortran90 module, we might replace /MPIPRIV/ with a simple
+!     SAVE MPI_BOTTOM
+!
+      SAVE /MPIPRIV/
diff --git a/odb_api/src/odb_api/odb2_to_odb1/odb_wrapper.F90 b/odb_api/src/odb_api/odb2_to_odb1/odb_wrapper.F90
new file mode 100644
index 0000000..746f95f
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/odb_wrapper.F90
@@ -0,0 +1,753 @@
+!> @file   odb_wrapper.F90
+!> @author Anne Fouilloux
+
+!----------------------------------------------------------------------
+function odb_no_of_local_pools_c(handle) bind(C, name="odb_no_of_local_pools_f90")
+  use, intrinsic :: iso_c_binding
+  use            :: odb_module
+
+  implicit none
+
+  integer(C_INT),VALUE                :: handle
+  integer(C_INT)                      :: odb_no_of_local_pools_c
+
+  integer(kind=JPIM)                  :: f90_handle
+  integer(kind=JPIM)                  :: f90_nmypools
+
+  f90_handle = handle
+
+  f90_nmypools = ODB_poolinfo(f90_handle)
+
+  odb_no_of_local_pools_c = f90_nmypools
+
+end function odb_no_of_local_pools_c
+!----------------------------------------------------------------------
+function odb_get_local_pools_c(handle, local_pools) bind(C, name="odb_get_local_pools_f90")
+  use, intrinsic :: iso_c_binding
+  use            :: odb_module
+
+  implicit none
+
+  type, bind(c) :: c_int_array
+    integer(C_INT)                     :: len
+    type(C_PTR)                        :: array
+  end type c_int_array
+
+  integer(C_INT),VALUE                :: handle
+  integer(C_INT)                      :: odb_get_local_pools_c
+  type(c_int_array), intent(out)      :: local_pools
+  integer(C_INT), pointer             :: c_local_pools(:)
+
+  integer(kind=JPIM)                  :: f90_handle
+  integer(kind=JPIM)                  :: f90_nmypools
+
+  f90_handle = handle
+
+  call C_F_POINTER(local_pools%array, c_local_pools, (/local_pools%len/))
+
+  f90_nmypools = ODB_poolinfo(f90_handle, c_local_pools)
+
+  odb_get_local_pools_c = f90_nmypools
+
+end function odb_get_local_pools_c
+!----------------------------------------------------------------------
+function odb_open_c(dbname, dbname_length, mode, mode_length, npools) bind(C, name="odb_open_f90")
+  use, intrinsic :: iso_c_binding
+  use            :: odb_module
+  use mpl_module
+
+  implicit none
+
+  character(kind=C_CHAR), dimension(*) :: dbname
+  character(kind=C_CHAR), dimension(*) :: mode
+  integer(C_INT)                       :: npools
+  integer(C_INT),VALUE                 :: dbname_length
+  integer(C_INT),VALUE                 :: mode_length
+  integer(C_INT)                       :: odb_open_c
+
+  character(len=64)                    :: f90_dbname
+  character(len=64)                    :: f90_mode
+  integer(kind=JPIM)                   :: f90_npools
+  integer(kind=JPIM)                   :: f90_handle
+  integer(kind=JPIM)                   :: f90_rc
+  integer(kind=JPIM)                   :: i
+
+
+  f90_npools = npools
+  f90_dbname=""
+  do i=1, dbname_length
+    if (dbname(i) .eq. C_NULL_CHAR) exit
+    f90_dbname(i:i)  = dbname(i)
+  end do
+  f90_mode=""
+  do i=1, mode_length
+    if (mode(i) .eq. C_NULL_CHAR) exit
+    f90_mode(i:i)  = mode(i)
+  end do
+
+  f90_handle = ODB_open(f90_dbname, f90_mode, f90_npools)
+  odb_open_c = f90_handle
+! write flag files
+  call cODB_print_flags_file(f90_dbname, MPL_MYRANK(), f90_rc)
+end function odb_open_c
+!----------------------------------------------------------------------
+function odb_close_c(handle, save) bind(C, name="odb_close_f90")
+  use, intrinsic                      :: iso_c_binding
+  use odb_module
+
+  implicit none
+
+  integer(C_INT),VALUE                :: handle
+  logical(C_BOOL), VALUE              :: save
+  integer(C_INT)                      :: odb_close_c
+
+  logical                             :: f90_save
+  integer(kind=JPIM)                  :: f90_handle
+  integer(kind=JPIM)                  :: f90_err
+
+  f90_save = save
+  f90_handle = handle
+  write(0,*) 'ODB_close f90_save = ', f90_save, ' f90_handle = ', f90_handle
+  f90_err = ODB_close(f90_handle, f90_save)
+  odb_close_c = f90_err
+end function odb_close_c
+!----------------------------------------------------------------------
+function odb_swapout_c(handle, dtname, dtname_length, poolno, save, repack) bind(C, name="odb_swapout_f90")
+  use, intrinsic                      :: iso_c_binding
+  use odb_module
+
+  implicit none
+
+  integer(C_INT),VALUE                 :: handle
+  character(kind=C_CHAR), dimension(*) :: dtname
+  integer(C_INT),VALUE                 :: dtname_length
+  integer(C_INT),VALUE                 :: poolno
+  logical(C_BOOL), VALUE               :: save
+  logical(C_BOOL), VALUE               :: repack
+  integer(C_INT)                       :: odb_swapout_c
+
+  integer(kind=JPIM)                   :: f90_poolno
+  integer(kind=JPIM)                   :: f90_handle
+  character(len=64)                    :: f90_dtname
+  logical                              :: f90_save, f90_repack
+  integer(kind=JPIM)                   :: i
+  integer(kind=JPIM)                   :: rc
+
+  f90_dtname=""
+  do i=1, dtname_length
+    if (dtname(i) .eq. C_NULL_CHAR) exit
+    f90_dtname(i:i)  = dtname(i)
+  end do
+
+  f90_handle = handle
+  f90_poolno = poolno
+  f90_save = save
+  f90_repack = repack
+
+  rc = ODB_swapout(f90_handle, f90_dtname, poolno=f90_poolno, save=f90_save, repack=f90_repack)
+
+  odb_swapout_c = rc
+end function odb_swapout_c
+!----------------------------------------------------------------------
+function odb_get_no_of_columns_c(handle, dtname, dtname_length) bind(C, name="odb_get_no_of_columns_f90")
+  use, intrinsic                      :: iso_c_binding
+  use odb_module
+
+  implicit none
+
+  integer(C_INT), VALUE                :: handle
+  character(kind=C_CHAR), dimension(*) :: dtname
+  integer(C_INT),VALUE                 :: dtname_length
+  integer(C_INT)                       :: odb_get_no_of_columns_c
+
+  integer(kind=JPIM)                   :: f90_ncols
+  integer(kind=JPIM)                   :: f90_handle
+  character(len=64)                    :: f90_dtname
+  integer(kind=JPIM)                   :: i, at_offset
+
+  if (dtname(1) /= '@') then
+    at_offset = 1
+    f90_dtname='@'
+  else
+    at_offset=0
+    f90_dtname=""
+  endif
+  do i=1, dtname_length
+    if (dtname(i) .eq. C_NULL_CHAR) exit
+    f90_dtname(i+at_offset:i+at_offset)  = dtname(i)
+  end do
+
+  f90_handle = handle
+  f90_ncols = ODB_getnames(f90_handle,f90_dtname,'name')
+  odb_get_no_of_columns_c = f90_ncols
+end function odb_get_no_of_columns_c
+!----------------------------------------------------------------------
+subroutine odb_api_get_colnames(odb_iterator, colnames, c_ncols, idx_seqno, masterKey)
+  use, intrinsic                      :: iso_c_binding
+  use odb_module
+  use odb_c_binding
+
+  implicit none
+
+  TYPE(C_PTR)                               :: odb_iterator
+  character(len=maxvarlen), dimension(*)    :: colnames
+  integer(kind=C_INT)                       :: c_ncols
+  integer(kind=JPIM), intent(out)           :: idx_seqno
+  character(len=maxvarlen)                  :: masterKey
+
+  integer(kind=JPIM)                   :: i, j, at_offset, at_idx
+
+  type(C_PTR)                                   :: ptr_colname
+  integer(kind=C_INT)                           :: cerr, size_name
+  character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+  character(len=256)                            :: colname
+
+    idx_seqno =-1
+
+    do i=1, c_ncols
+      ptr_colname = C_NULL_PTR
+      colname(:) = ""
+      cerr = odb_read_get_column_name(odb_iterator, i-1, ptr_colname, size_name)
+      call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+      do j=1, size_name
+       if (f_ptr_colname(j) .eq. C_NULL_CHAR) exit  ! should be C_CHAR_NULL
+       colname(j:j)  = f_ptr_colname(j)
+      end do
+      at_idx = index(colname, '@')
+      if (at_idx > 0) then
+        colnames(i) = colname(:at_idx-1)
+      else
+        colnames(i) = colname
+      endif
+      if (trim(colnames(i)) .eq. masterKey) then
+        idx_seqno = i
+      endif
+    enddo
+end subroutine odb_api_get_colnames
+!----------------------------------------------------------------------
+function odb_create_index_c(handle, dtname, dtname_length, odb_iterator, &
+                            idx_odb, idx_size ) bind(C, name="odb_create_index_f90")
+  use, intrinsic                      :: iso_c_binding
+  use odb_module
+  use odb_c_binding
+
+  implicit none
+
+  integer(C_INT), VALUE                :: handle
+  character(kind=C_CHAR), dimension(*) :: dtname
+  integer(C_INT),VALUE                 :: dtname_length
+  TYPE(C_PTR)                          :: odb_iterator
+  integer(kind=C_INT), dimension(*)    :: idx_odb
+  integer(C_INT),VALUE                 :: idx_size
+  integer(kind=C_INT)                  :: odb_create_index_c
+
+  integer(kind=JPIM)                   :: f90_ncols
+  integer(kind=JPIM)                   :: f90_handle
+  character(len=64)                    :: f90_dtname
+  integer(kind=JPIM)                   :: i, j, at_offset, at_idx
+
+  INTEGER(kind=C_INT)                  :: c_ncols
+
+  type(C_PTR)                                   :: ptr_colname
+  integer(kind=C_INT)                           :: size_name
+  character(kind=C_CHAR), dimension(:), pointer :: f_ptr_colname
+  character(len=256)                            :: coltable, colname, temp(1)
+  integer(kind=JPIM)                            :: f90_idx(1)
+  integer(kind=JPIM)                            :: rc
+
+
+
+  odb_create_index_c = 0
+  if (dtname(1) /= '@') then
+    at_offset = 1
+    f90_dtname='@'
+  else
+    at_offset=0
+    f90_dtname=""
+  endif
+  do i=1, dtname_length
+    if (dtname(i) .eq. C_NULL_CHAR) exit
+    f90_dtname(i+at_offset:i+at_offset)  = dtname(i)
+  end do
+
+  f90_handle = handle
+  f90_ncols = ODB_getnames(f90_handle,f90_dtname,'name')
+
+  odb_create_index_c = odb_read_get_no_of_columns(odb_iterator, c_ncols)
+
+  if (idx_size == c_ncols) then
+    do i=1, c_ncols
+      odb_create_index_c = odb_read_get_column_name(odb_iterator, i-1, ptr_colname, size_name)
+      call C_F_POINTER(CPTR=ptr_colname, FPTR=f_ptr_colname, shape=(/size_name/));
+      do j=1, size_name
+       if (f_ptr_colname(j) .eq. C_NULL_CHAR) exit  ! should be C_CHAR_NULL
+       colname(j:j)  = f_ptr_colname(j)
+      end do
+      at_idx = index(colname, '@')
+      if (at_idx > 0) then
+        temp(1) = colname(:at_idx-1)
+        coltable = colname(at_idx:)
+
+      else
+        temp(1) = colname
+        coltable=""
+      endif
+      f90_idx=-1
+      if (trim(coltable) == trim(f90_dtname)) then
+
+      rc = ODB_varindex(f90_handle, f90_dtname, temp, f90_idx)
+      if (f90_idx(1) > 0) then
+        idx_odb(i)  = f90_idx(1)
+      endif
+      endif
+    enddo
+  endif
+
+end function odb_create_index_c
+!----------------------------------------------------------------------
+function ODB_whatis(colname, flaglist)
+  use, intrinsic                       :: iso_c_binding
+  use odb_module
+
+  implicit none
+  character(len=*), intent(in)    :: colname
+  type(C_PTR),        intent(in)  :: flaglist
+  integer(kind=JPIM)              :: ODB_whatis
+  integer(kind=JPIM)              :: idx_l, idx_r, i
+  integer(kind=C_INT)             :: c_whatis, typeOfTable
+  character(kind=C_CHAR),dimension(257)  :: c_colname
+
+  ODB_whatis = ODB_NMDI
+
+  c_colname(:)=' '
+  do i=1,len(trim(colname))
+    c_colname(i) = colname(i:i)
+  enddo
+  c_colname(i+1) = C_NULL_CHAR
+  c_whatis = typeOfTable(c_colname, flaglist)
+  ODB_whatis = c_whatis
+  !write(0,*) 'ODB_whatis = ', trim(colname), ' ' ,  ODB_whatis
+end function ODB_whatis
+!----------------------------------------------------------------------
+subroutine odb_fill_c(handle, filename, odb_hdr_array, odb_body_array, flaglist, c_masterKey, c_len, np) bind(C, name="odb_fill_f90")
+  use, intrinsic                       :: iso_c_binding
+  use odb_module
+  use odb_c_binding
+  use odbi
+
+  implicit none
+
+  type, bind(c) :: c_int_array
+    integer(C_INT)                     :: len
+    type(C_PTR)                        :: array
+  end type c_int_array
+
+  interface
+     function ODB_whatis(colname, flaglist)
+       use, intrinsic :: iso_c_binding
+       use odb_module
+
+       implicit none
+       character(len=*), intent(in)    :: colname
+       type(C_PTR), intent(in)         :: flaglist
+       integer(kind=JPIM)              :: ODB_whatis
+     end function ODB_whatis
+  end interface
+
+
+  integer(C_INT), VALUE                  :: handle
+  character(kind=C_CHAR), dimension(*)   :: filename
+  character(kind=C_CHAR), dimension(*)   :: c_masterKey
+  type(C_PTR), intent(in)                :: flaglist
+  integer(C_INT),VALUE                   :: c_len, np
+
+  type(c_int_array), intent(in)          :: odb_hdr_array
+  type(c_int_array), intent(inout)       :: odb_body_array
+  integer(kind=JPIM)                     :: f90_handle
+  integer(kind=JPIM)                     :: f90_np
+
+  character(len=256), allocatable        :: tablenames(:)
+  type(t_odbtable), allocatable          :: odbtables(:)
+  integer(kind=JPIM)                     :: ntables
+  integer(kind=JPIM)                     :: rc, i, j, f90_nrows, f90_ncols
+  integer(kind=JPIM)                     :: idx_seqno, ihdr, ibody
+  character(len=maxvarlen)               :: masterKey, cltable
+  integer(C_INT), pointer                :: c_odb_hdr_array(:)
+  integer(C_INT), pointer                :: c_odb_body_array(:)
+
+  type(C_PTR)                                            :: odb_handler, odb_it
+  character(kind=C_CHAR, len=64)                         :: config = C_NULL_CHAR
+  integer(kind=C_INT)                                    :: cerr, new_dataset, ncols
+  logical                                                :: LLfirstdataset, LLindexNotdone
+  real(kind=C_DOUBLE), dimension(:), allocatable         :: one_row
+  character(len=maxvarlen), allocatable                  :: colnames_in(:)
+  character(len=64)                                      :: f90_filename
+  integer(kind=JPIM)                                     :: previous_seqno
+
+  f90_filename=""
+  do i=1, maxvarlen
+    if (filename(i) .eq. C_NULL_CHAR) exit
+    f90_filename(i:i)  = filename(i)
+  end do
+
+
+  masterKey=""
+  do i=1, c_len
+    if (c_masterKey(i) .eq. C_NULL_CHAR) exit
+    masterKey(i:i)  = c_masterKey(i)
+  end do
+
+! associate c_odb_hdr_array with an array allocated and filled in C++
+  call C_F_POINTER( odb_hdr_array%array, c_odb_hdr_array, (/odb_hdr_array%len/))
+  call C_F_POINTER( odb_body_array%array, c_odb_body_array, (/odb_body_array%len/))
+
+  write(0,*) 'masterKey= ', trim(masterKey)
+  write(0,*) 'number of BODY entries = ', odb_body_array%len
+  write(0,*) 'number of HDR entries = ', odb_hdr_array%len
+  f90_handle = handle
+  f90_np = np
+
+  ntables = ODB_getnames(f90_handle, '*', 'table')
+  allocate(tablenames(ntables))
+  ntables = ODB_getnames(f90_handle, '*', 'table', tablenames)
+  allocate(odbtables(ntables))
+  odbtables(:)%table_kind = -1
+  do j=1,ntables
+    NULLIFY(odbtables(j)%data)
+    NULLIFY(odbtables(j)%index)
+    NULLIFY(odbtables(j)%colnames)
+    odbtables(j)%table_kind = ODB_whatis(trim(tablenames(j)), flaglist)
+    if (odbtables(j)%table_kind == ODB_HDR_ALIGNED .or. odbtables(j)%table_kind  == ODB_BODY_ALIGNED) then
+      odbtables(j)%tablename = trim(tablenames(j))
+      cltable = odbtables(j)%tablename
+      f90_ncols = ODB_getnames(f90_handle,cltable,'name')
+      allocate(odbtables(j)%colnames(f90_ncols))
+      rc = ODB_getnames(f90_handle,cltable,'name', outnames=odbtables(j)%colnames)
+!*AF      write(0,*) trim(cltable), 'f90_ncols = ', f90_ncols
+      if (odbtables(j)%table_kind  == ODB_HDR_ALIGNED) then
+        allocate(odbtables(j)%data(int(odb_hdr_array%len),0:f90_ncols))
+      else
+        allocate(odbtables(j)%data(int(odb_body_array%len),0:f90_ncols))
+      endif
+      odbtables(j)%data(:,:) = 0.0
+    endif
+  enddo
+
+! start to read the input file
+  LLfirstdataset=.true.
+  LLindexNotdone=.true.
+  ihdr=1
+  ibody=1
+!*AF  write(0,*) 'LLfirstdataset=.true. ', LLfirstdataset, ' LLindexNotdone= ', LLindexNotdone
+  odb_handler = odb_read_new(config, cerr)
+  odb_it = odb_read_iterator_new(odb_handler, filename, cerr);
+  cerr = odb_read_get_no_of_columns(odb_it, ncols)
+  if (allocated(one_row)) deallocate(one_row)
+  allocate(one_row(ncols))
+
+  previous_seqno = -1
+  do while (odb_read_get_next_row(odb_it, ncols, one_row, new_dataset) == 0)
+    LLindexNotdone = (new_dataset > 0) .or. LLfirstdataset
+    if (LLindexNotDone) then
+      LLfirstdataset = .false.
+! we must compute the list of index in ODB-1 valid in the current ODB-2 one_row
+      allocate(colnames_in(ncols))
+      colnames_in(:) = ""
+      call odb_api_get_colnames(odb_it, colnames_in, ncols, idx_seqno, masterKey)
+!*AF      do j=1, ncols
+!*AF        write(0,*) 'colnames_in (odb-api) (', j,')= ', trim(colnames_in(j))
+!*AF      enddo
+      do j=1, ntables
+        if (odbtables(j)%table_kind > 0) then
+          allocate(odbtables(j)%index(ncols))
+          rc = ODB_varindex(f90_handle,odbtables(j)%tablename , colnames_in, odbtables(j)%index)
+          !write(0,*) 'table = ', trim(odbtables(j)%tablename), ' ', odbtables(j)%index
+        endif
+      enddo
+
+      deallocate(colnames_in)
+
+!*AF      write(0,*) 'new_dataset'
+    endif
+    do j=1, ntables
+      if (odbtables(j)%table_kind > 0) then
+       do i=1, ncols
+         if (odbtables(j)%table_kind == ODB_HDR_ALIGNED .and. &
+             one_row(idx_seqno) /= previous_seqno &
+             .and. odbtables(j)%index(i) > 0) then
+           !write(0,*) one_row(idx_seqno), c_odb_hdr_array(ihdr), ihdr, 'size odbtables ', j, trim(odbtables(j)%tablename), ' ', odbtables(j)%index(i)
+           odbtables(j)%data(ihdr, odbtables(j)%index(i)) = one_row(i)
+         else if (odbtables(j)%table_kind == ODB_BODY_ALIGNED .and. &
+             odbtables(j)%index(i) > 0) then
+           !write(0,*) ibody, 'size odbtables ', j, trim(odbtables(j)%tablename), ' ', odbtables(j)%index(i)
+           odbtables(j)%data(ibody, odbtables(j)%index(i)) = one_row(i)
+         endif
+       enddo
+      endif
+    enddo
+    c_odb_body_array(ibody) = one_row(idx_seqno)
+    ibody = ibody + 1
+    if (one_row(idx_seqno) /= previous_seqno) then
+      previous_seqno = c_odb_hdr_array(ihdr)
+      ihdr = ihdr + 1
+    endif
+  enddo
+  if (allocated(one_row)) deallocate(one_row)
+  cerr = odb_read_iterator_delete(odb_it)
+  cerr = odb_read_delete(odb_handler)
+! deallocation
+ do j=1,ntables
+    if (odbtables(j)%table_kind > 0) then
+     f90_nrows = size(odbtables(j)%data,1)
+     f90_ncols = size(odbtables(j)%data,2)
+!*AF     write(0,*) 'ODB_put ', trim(odbtables(j)%tablename), f90_nrows, f90_ncols, f90_np
+     !do i=1, f90_nrows
+     !  write(*,*) i, 'row ', odbtables(j)%data(i,:)
+     !enddo
+     rc = ODB_put(f90_handle, odbtables(j)%tablename, odbtables(j)%data, &
+        f90_nrows, f90_ncols, poolno = f90_np)
+    endif
+    if (associated(odbtables(j)%data)) deallocate(odbtables(j)%data)
+    if (associated(odbtables(j)%index)) deallocate(odbtables(j)%index)
+    if (associated(odbtables(j)%colnames)) deallocate(odbtables(j)%colnames)
+ enddo
+ deallocate(odbtables)
+ deallocate(tablenames)
+end subroutine odb_fill_c
+!----------------------------------------------------------------------
+function odb_put_one_row_c(handle, dtname, dtname_length, odb_row, idx_odb, ncols, np) bind(C, name="odb_put_one_row_f90")
+  use, intrinsic                       :: iso_c_binding
+  use odb_module
+  use odb_c_binding
+
+  implicit none
+
+  integer(C_INT), VALUE                :: handle
+  character(kind=C_CHAR), dimension(*) :: dtname
+  integer(C_INT),VALUE                 :: dtname_length
+  real(kind=C_DOUBLE), dimension(*)    :: odb_row
+  integer(kind=C_INT), dimension(*)    :: idx_odb
+  integer(C_INT),VALUE                 :: ncols
+  integer(C_INT),VALUE                 :: np
+  integer(kind=C_INT)                  :: odb_put_one_row_c
+
+  integer(kind=JPIM)                   :: f90_ncols
+  integer(kind=JPIM)                   :: f90_handle
+  integer(kind=JPIM)                   :: f90_np
+  character(len=64)                    :: f90_dtname
+  integer(kind=JPIM)                   :: i, at_offset
+  integer(kind=JPIM)                   :: nrows
+  real(kind=JPRB), allocatable         :: f90_one_row(:,:)
+
+  odb_put_one_row_c = 0
+  nrows = 1
+
+  if (dtname(1) /= '@') then
+    at_offset = 1
+    f90_dtname='@'
+  else
+    at_offset=0
+    f90_dtname=""
+  endif
+  do i=1, dtname_length
+    if (dtname(i) .eq. C_NULL_CHAR) exit
+    f90_dtname(i+at_offset:i+at_offset)  = dtname(i)
+  end do
+
+  f90_handle = handle
+  f90_ncols = ODB_getnames(f90_handle,f90_dtname,'name')
+
+  f90_np = np
+  allocate(f90_one_row(nrows,0:f90_ncols))
+
+  f90_one_row=0
+  do i=1, ncols
+    f90_one_row(1,idx_odb(i)) = odb_row(i)
+  enddo
+
+  odb_put_one_row_c = ODB_put(f90_handle, f90_dtname, f90_one_row, nrows, f90_ncols, poolno = f90_np)
+  deallocate(f90_one_row)
+
+end function odb_put_one_row_c
+!----------------------------------------------------------------------
+subroutine compute_links(ilink, c_odb_len_array)
+  use, intrinsic :: iso_c_binding
+  use            :: odb_module
+
+  implicit none
+
+  integer(C_INT), intent(in)         :: c_odb_len_array(:)
+  integer(KIND=JPIM), intent(inout)    :: ilink(:)
+
+  integer(kind=JPIM)          :: i, j
+  integer(kind=JPIM)          :: previous_seqno, bodylen
+
+  !write(0,*) 'Size of ilink = ', size(ilink, dim=1), size(c_odb_len_array, dim=1)
+
+  if (size(ilink, dim=1) == size(c_odb_len_array, dim=1)) then
+    ilink=1
+  else
+    ilink=-1
+    previous_seqno = c_odb_len_array(1)
+    bodylen=0
+    j=1
+    do i=1, size(c_odb_len_array, dim=1)
+!*AF      write(0,*) 'previous_seqno = ', previous_seqno, ' seqno = ', c_odb_len_array(i)
+      if (previous_seqno /= c_odb_len_array(i)) then
+! we have a different seqno so we can set the len of the previous body part
+        ilink(j) = bodylen
+!*AF        write(0,*) 'ilink(', j, ')=', ilink(j)
+        bodylen=0
+        j = j+1
+        previous_seqno = c_odb_len_array(i)
+      endif
+      bodylen = bodylen + 1
+    enddo
+    if (previous_seqno == c_odb_len_array(size(c_odb_len_array, dim=1))) ilink(j) = bodylen
+  endif
+end subroutine compute_links
+!----------------------------------------------------------------------
+function odb_update_links_c(handle, dtname, dtname_length, poolno, odb_hdr_array, odb_body_array, flaglist) bind(C, name="odb_update_links_f90")
+  use, intrinsic :: iso_c_binding
+  use            :: odb_module
+
+  implicit none
+
+  interface
+    subroutine compute_links(ilink, c_odb_len_array)
+      use, intrinsic :: iso_c_binding
+      use            :: odb_module
+      integer(C_INT), intent(in)         :: c_odb_len_array(:)
+      integer(KIND=JPIM), intent(inout)  :: ilink(:)
+     end subroutine compute_links
+
+     function ODB_whatis(colname, flaglist)
+       use, intrinsic :: iso_c_binding
+       use odb_module
+
+       implicit none
+       character(len=*), intent(in)    :: colname
+       type(C_PTR), intent(in)  :: flaglist
+       integer(kind=JPIM)              :: ODB_whatis
+     end function ODB_whatis
+  end interface
+
+  type, bind(c) :: c_int_array
+    integer(C_INT)                     :: len
+    type(C_PTR)                        :: array
+  end type c_int_array
+
+  type(C_PTR), intent(in)              :: flaglist
+  integer(C_INT),VALUE                 :: handle
+  character(kind=C_CHAR), dimension(*) :: dtname
+  integer(C_INT),VALUE                 :: dtname_length
+  integer(C_INT),VALUE                 :: poolno
+  type(c_int_array), intent(in)        :: odb_hdr_array
+  type(c_int_array), intent(in)        :: odb_body_array
+  integer(C_INT)                       :: odb_update_links_c
+
+  integer(C_INT), pointer              :: c_odb_hdr_array(:)
+  integer(C_INT), pointer              :: c_odb_body_array(:)
+
+  character(len=64)                    :: f90_dtname
+  integer(kind=JPIM)                   :: f90_poolno
+  integer(kind=JPIM)                   :: f90_handle
+  integer(kind=JPIM)                   :: i, j
+  integer(kind=JPIM)                   :: rc
+  integer(kind=JPIM)                   :: nrows, ncols, nra
+  character(len=256), allocatable      :: colnames(:)
+  integer(kind=JPIM), allocatable      :: col_aligned(:)
+  character(len=256)                   :: colname
+  real(KIND=JPRB), allocatable         :: x(:,:)
+  integer(KIND=JPIM), allocatable      :: ilink_hdr(:)
+  integer(KIND=JPIM), allocatable      :: ilink_body(:)
+  integer(kind=JPIM)                   :: ioffset_hdr
+  integer(kind=JPIM)                   :: ioffset_body
+
+  odb_update_links_c = 0
+
+! associate c_odb_hdr_array with an array allocated and filled in C++
+  call C_F_POINTER( odb_hdr_array%array, c_odb_hdr_array, (/odb_hdr_array%len/))
+
+! associate c_odb_body_array with an array allocated and filled in C++
+  call C_F_POINTER( odb_body_array%array, c_odb_body_array, (/odb_body_array%len/))
+
+  f90_handle = handle
+  f90_poolno = poolno
+  f90_dtname=""
+  do i=1, dtname_length
+    if (dtname(i) .eq. C_NULL_CHAR) exit
+    f90_dtname(i:i)  = dtname(i)
+  end do
+
+
+  rc = ODB_addview(f90_handle, f90_dtname, abort = .false.)
+  write(0,*) 'rc = ', rc, 'odb_update_links_c f90_handle = ', f90_handle, ' f90_poolno = ', f90_poolno, &
+             ' f90_dtname = ', trim(f90_dtname), ' odb_hdr_array.len = ',size(c_odb_hdr_array), &
+             ' odb_body_array.len = ',size(c_odb_body_array)
+
+  if (rc > 0) then
+! this SQL is registered and we can fecth data from ODB
+    rc = ODB_select(f90_handle,f90_dtname,nrows,ncols,nra=nra,poolno=f90_poolno)
+!*AF    write(0,*) 'odb_update_links_c ', trim(f90_dtname), ' nrows = ', nrows, ' ncols = ', ncols
+    if (nrows > 0) then
+      ALLOCATE(x(nra,0:ncols))
+      ALLOCATE(ilink_hdr(nrows))
+      ALLOCATE(ilink_body(nrows))
+      ALLOCATE(colnames(ncols))
+      ALLOCATE(col_aligned(ncols))
+      rc = ODB_get(f90_handle,f90_dtname,x,nrows,ncols,poolno=f90_poolno)
+      call compute_links(ilink_hdr, c_odb_hdr_array)
+      call compute_links(ilink_body, c_odb_body_array)
+      rc = ODB_getnames(f90_handle, f90_dtname, 'name', colnames)
+      col_aligned(:)=ODB_NMDI
+      do j=1, ncols
+        colname = trim(colnames(j))
+        if (len(colname) > 10 .and. colname(1:10) == 'LINKOFFSET') then
+           col_aligned(j) = ODB_whatis(colname, flaglist) ! offset
+           col_aligned(j+1) = col_aligned(j)              ! len
+        endif
+      enddo
+
+      ioffset_hdr = 0
+      ioffset_body = 0
+       ! write(0,*) 'nrows = ', nrows, ' ncols = ', ncols
+      do i=1, nrows
+        do j=1, ncols
+          colname = trim(colnames(j))
+          if (len(colname) > 7 .and. colname(1:7) == 'LINKLEN') then
+             if (col_aligned(j) == ODB_HDR_ALIGNED) then
+               x(i,j) = ilink_hdr(i)
+             else if (col_aligned(j)  == ODB_BODY_ALIGNED) then
+               x(i,j) = ilink_body(i)
+             else
+               x(i,j) = 0
+             endif
+          else if (len(colname) > 10 .and. colname(1:10) == 'LINKOFFSET') then
+             if (col_aligned(j)  == ODB_HDR_ALIGNED) then
+               x(i,j) = ioffset_hdr
+             else if (col_aligned(j) == ODB_BODY_ALIGNED) then
+               x(i,j) = ioffset_body
+             else
+               x(i,j) = 0
+             endif
+          endif
+        enddo
+        ioffset_hdr = ioffset_hdr + ilink_hdr(i)
+        ioffset_body = ioffset_body + ilink_body(i)
+      enddo
+      rc = ODB_put(f90_handle,f90_dtname,x, nrows,ncols,poolno=f90_poolno)
+      DEALLOCATE(x)
+      DEALLOCATE(ilink_hdr)
+      DEALLOCATE(ilink_body)
+      DEALLOCATE(colnames)
+      DEALLOCATE(col_aligned)
+    endif
+    rc = ODB_cancel(f90_handle,f90_dtname,poolno=f90_poolno)
+  else
+   write(0,*) '******Warning: retrieval ', trim(f90_dtname), ' is not registered'
+  endif
+
+end function odb_update_links_c
+
+!----------------------------------------------------------------------
diff --git a/odb_api/src/odb_api/odb2_to_odb1/odbi.F90 b/odb_api/src/odb_api/odb2_to_odb1/odbi.F90
new file mode 100644
index 0000000..39cc31b
--- /dev/null
+++ b/odb_api/src/odb_api/odb2_to_odb1/odbi.F90
@@ -0,0 +1,286 @@
+!> @file   odbi.F90
+!> @author Anne Fouilloux
+
+module odbi
+
+  use iso_c_binding
+  use odb_c_binding
+  use odb_module
+  USE YOMHOOK   ,ONLY : LHOOK,   DR_HOOK
+
+  implicit none
+  integer, parameter :: MAX_STRING=1024
+  integer, parameter :: ODB_MAX_ENTRYNO = 1000
+  integer, parameter :: ODB_MAX_ROWS = 100000
+
+  type t_odbtable
+    character(len=maxvarlen)                         :: tablename
+    integer(kind=JPIM)                               :: table_kind
+    real(kind=JPRB), pointer                         :: data(:,:)
+    character(len=MAX_STRING), dimension(:), pointer :: colnames
+    integer(kind=JPIM), dimension(:), pointer        :: index
+  end type t_odbtable
+  type t_bufr2odb
+    logical                                          :: LLcreate_odb_header
+    type(C_PTR)                                      :: c_handle
+    type(C_PTR)                                      :: c_odb_it
+    integer(kind=JPIM)                               :: handle
+    integer(kind=C_INT)                              :: c_ncolumns
+    character(len=MAX_STRING), dimension(:), pointer :: ODBcolname
+    integer(kind=JPIM), dimension(:), pointer        :: ODBcoltype
+    integer(kind=JPIM)                               :: nrows
+  end type t_bufr2odb
+
+  type t_odb2odb1
+    character(len=MAX_STRING)              :: tablename
+    logical                                :: aligned_header
+    logical                                :: aligned_body
+    integer(kind=JPIM), pointer            :: ODB1index(:)
+    character(len=MAX_STRING), pointer     :: colnames(:)
+    logical, pointer                       :: col_is_len(:)
+    logical, pointer                       :: child_is_body(:)
+    type(t_odb2odb1), pointer              :: next
+  end type t_odb2odb1
+
+
+  type relative
+     integer(kind=JPIM)                                   :: idx     ! index dans dbinfo%tables
+     type(relative), pointer                              :: next
+  end type relative
+
+  type tableinfo
+     character(len=MAX_STRING)                            :: ODBtablename  ! ODB table name @table i.e. contains "@"
+     integer(JPIM)                                        :: ODBncols      ! number of ODB columns
+     character(len=MAX_STRING), dimension(:), allocatable :: ODBcolname
+     character(len=MAX_STRING), dimension(:), allocatable :: ODBtypename
+     integer(kind=JPIM), dimension(:), allocatable        :: ODB2ODAindex
+     integer(kind=JPIM)                                   :: ODAncols  ! if 0 it means this table is not present in ODA
+     real(kind=JPRB), dimension(:,:), allocatable         :: ODBrow
+     real(kind=JPRB), dimension(:), allocatable           :: ODBonerow
+     integer(kind=JPIM)                                   :: ODBnrows
+     integer(kind=JPIM)                                   :: nchildren
+     logical                                              :: cut ! true if we need to cut
+     logical                                              :: done ! true if already proceeded
+     logical                                              :: ODBwrite ! true if we need to write in ODB
+     integer(kind=JPIM)                                   :: len_idx ! index in ODBcol of LEN
+     integer(kind=JPIM)                                   :: offset_idx ! index in ODBcol of OFFSET
+     type(relative), pointer                              :: child
+     type(relative), pointer                              :: aligned
+     type(relative), pointer                              :: onelooper
+     type(relative), pointer                              :: parent
+  end type tableinfo
+
+  type dbinfo
+     character(len=MAX_STRING)                  :: dbname
+     integer(kind = JPIM)                       :: handle
+     integer(kind = JPIM)                       :: ntables
+     integer(kind = JPIM)                       :: npools
+     integer(kind = JPIM)                       :: current_poolno
+     integer(kind = JPIM)                       :: current_seqno
+     integer(kind = JPIM)                       :: idx_seqno ! idx in ODA
+     integer(kind = JPIM)                       :: ODAncols ! total number of columns in ODA; can be less or greater than the number of column in ODB
+     character(len=MAX_STRING), dimension(:), allocatable :: ODAcolname
+     real(kind=JPRB)                            :: mdi
+     integer(kind=JPIM)                         :: master       ! root table (desc)
+     type(tableinfo), dimension(:), allocatable :: tables
+  end type dbinfo
+
+contains
+function get_coltype(khandle, colnames, types, ftntypes, one_colname, itype) RESULT(idx_col)
+  implicit none
+  INTEGER(KIND=JPIM), intent(in)         :: khandle
+  character(len=*),intent(in)           :: colnames(:)
+  character(len=maxvarlen), intent(in)  :: types(:), ftntypes(:)
+  character(len=*), intent(in)          :: one_colname
+  integer(kind=C_INT), intent(out)      :: itype
+
+  integer(kind=JPIM)                    :: idx_col, jc
+  real(kind=JPRB)                       :: mdi
+
+  REAL(KIND=JPRB)                       :: ZHOOK_HANDLE
+
+  IF (LHOOK) CALL DR_HOOK('GET_COLTYPE',0,ZHOOK_HANDLE)
+  mdi = abs(ODB_getval(khandle, '$mdi'))
+
+  idx_col = -1
+  do jc=1, size(colnames)
+    if (trim(colnames(jc)) == trim(one_colname)) then
+      idx_col = jc
+      exit
+    endif
+  end do
+
+  if ( ftntypes(idx_col)(1:7) == 'REAL(8)' .and.&
+  &       types(idx_col)(1:7) == 'string ' ) then
+      itype = ODB_STRING
+  else if ( ftntypes(idx_col)(1:7) == 'CHAR(8)') then ! a future extension
+      itype = ODB_STRING
+  else if (ftntypes(idx_col)(1:5) == 'REAL(') then
+      itype = ODB_REAL
+  else if (ftntypes(idx_col)(1:8) == 'INTEGER(' .and. &
+  &           types(idx_col)(1:8) == 'Bitfield') then
+      itype = ODB_BITFIELD
+  else if (ftntypes(idx_col)(1:8) == 'INTEGER(') then
+      itype = ODB_INTEGER
+  else
+      itype = ODB_INTEGER
+  endif
+  IF (LHOOK) CALL DR_HOOK('GET_COLTYPE',1,ZHOOK_HANDLE)
+end function get_coltype
+SUBROUTINE odb_bitfield_definition(khandle, coln, bnames, bsizes)
+implicit none
+INTEGER(KIND=JPIM), intent(in)        :: khandle
+character(len=*), intent(in)          :: coln
+character(len=*), intent(out)         :: bnames, bsizes
+character(len=maxvarlen)              :: tablename
+integer(kind=jpim)                    :: nextnames, rc, jj, jg
+character(len=maxvarlen), allocatable :: extnames(:), exttypes(:)
+
+integer(kind=jpim)                    :: idx_at, idx_at_ext
+REAL(KIND=JPRB)                       :: ZHOOK_HANDLE
+
+IF (LHOOK) CALL DR_HOOK('ODB_BITFIELD_DEFINITION',0,ZHOOK_HANDLE)
+
+idx_at = index(coln,'@')
+tablename= coln(idx_at:)
+nextnames = ODB_getnames(khandle, trim(tablename),'extname')
+allocate(extnames(nextnames))
+allocate(exttypes(nextnames))
+
+rc = ODB_getnames(khandle, trim(tablename),'extname', extnames(1:nextnames))
+rc = ODB_getnames(khandle, trim(tablename),'exttype', exttypes(1:nextnames))
+
+jj=1
+
+
+do while (extnames(jj)(1:idx_at) /= coln(1:idx_at-1)//'.')
+  jj=jj+1
+enddo
+bnames=""
+bsizes=""
+
+do jg=jj,nextnames
+  if  (extnames(jg)(1:idx_at-1) /= coln(1:idx_at-1)) exit
+
+  idx_at_ext = index(extnames(jg),'@')
+  bnames=trim(bnames)//extnames(jg)(idx_at+1:idx_at_ext-1)//':'
+  bsizes=trim(bsizes)//trim(exttypes(jg)(4:))//':'
+enddo
+
+deallocate(extnames)
+deallocate(exttypes)
+
+IF (LHOOK) CALL DR_HOOK('ODB_BITFIELD_DEFINITION',1,ZHOOK_HANDLE)
+
+END SUBROUTINE odb_bitfield_definition
+
+subroutine create_header_table(b2o_info, jc, tablename, list_cols)
+ use, intrinsic :: iso_c_binding
+ use odb_c_binding
+!*AF use varindex_module
+
+implicit none
+ type(t_bufr2odb), intent(inout)             :: b2o_info
+ integer(kind=JPIM), intent(inout)           :: jc
+ character(len=maxvarlen), intent(in)        :: tablename
+ character(len=64) , dimension(:),intent(in) :: list_cols
+
+ integer(kind=C_INT)                    :: cerr
+ integer(kind=JPIM)                     :: jj
+ integer(kind=JPIM)                     :: rc
+ integer(kind=JPIM)                     :: idx_col
+ integer(kind=JPIM)                     :: ncolumns
+ integer(kind=C_INT)                    :: itype
+ character(len=maxvarlen)               :: bitfield_names, bitfield_sizes
+ character(len=maxvarlen)               :: full_colname
+ character(len=maxvarlen), allocatable  :: types(:), ftntypes(:)
+ character(len=maxvarlen), allocatable  :: colnames(:)
+ REAL(KIND=JPRB)                        :: ZHOOK_HANDLE
+
+
+  IF (LHOOK) CALL DR_HOOK('CREATE_HEADER_TABLE',0,ZHOOK_HANDLE)
+  ncolumns = ODB_getnames(b2o_info%handle,tablename,'name')
+  allocate(colnames(ncolumns))
+  rc = ODB_getnames(b2o_info%handle,tablename,'name',colnames)
+  allocate(types(ncolumns))
+  allocate(ftntypes(ncolumns))
+  rc = ODB_getnames(b2o_info%handle, tablename,'type', types(1:ncolumns))
+  rc = ODB_getnames(b2o_info%handle, tablename,'ftntype', ftntypes(1:ncolumns))
+  do jj=1, size(list_cols)
+    full_colname = trim(list_cols(jj))//trim(tablename)
+    idx_col = get_coltype(b2o_info%handle, colnames, types, ftntypes, full_colname, itype)
+
+!*AF    write(0,*) 'column = ', trim(list_cols(jj)), itype
+    if (itype /= ODB_BITFIELD) then
+      if (len(list_cols(jj)) > 10 .and. list_cols(jj)(1:10) == 'linkoffset') then
+! change linkoffset by linken because linkoffset will be recomputed
+           full_colname(1:10) = '   linklen'
+      endif
+      full_colname = trim(adjustl(full_colname))
+      b2o_info%ODBcolname(jc+1) = full_colname
+      full_colname = trim(adjustl(full_colname))//C_NULL_CHAR
+      b2o_info%ODBcoltype(jc+1) = itype
+      cerr = odb_write_set_column(b2o_info%c_odb_it, jc, itype, full_colname)
+    else
+      full_colname = trim(adjustl(full_colname))
+      b2o_info%ODBcolname(jc+1) = full_colname
+      b2o_info%ODBcoltype(jc+1) = itype
+      full_colname = trim(adjustl(full_colname))//C_NULL_CHAR
+      call odb_bitfield_definition(b2o_info%handle,full_colname, bitfield_names, bitfield_sizes)
+      bitfield_names=trim(bitfield_names)//C_NULL_CHAR
+      bitfield_sizes=trim(bitfield_sizes)//C_NULL_CHAR
+      cerr = odb_write_set_bitfield(b2o_info%c_odb_it, jc, itype, full_colname, bitfield_names, bitfield_sizes)
+    endif
+    jc = jc + 1
+  enddo
+  deallocate(colnames)
+  deallocate(types)
+  deallocate(ftntypes)
+  IF (LHOOK) CALL DR_HOOK('CREATE_HEADER_TABLE',1,ZHOOK_HANDLE)
+end subroutine create_header_table
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+subroutine init_one_row(b2o_info, one_row)
+implicit none
+
+ type(t_bufr2odb), intent(inout)             :: b2o_info
+ real(kind=C_DOUBLE), intent(inout)          :: one_row(:)
+
+ integer(kind=JPIM)                          :: i
+ character(len=8)                            :: csgn=""
+ REAL(KIND=JPRB)                             :: str_real8
+ real(kind=JPRB)                             :: mdi
+
+
+ mdi = abs(ODB_getval(b2o_info%handle, '$mdi'))
+ do i=1, b2o_info%c_ncolumns
+   select case (b2o_info%ODBcoltype(i))
+      case(ODB_BITFIELD)
+        one_row(i) = 0
+      case(ODB_INTEGER)
+        if (b2o_info%ODBcolname(i)(1:4) == 'link' .or. &
+            b2o_info%ODBcolname(i)(1:4) == 'LINK') then
+
+            one_row(i) = 0
+        else
+          one_row(i) = mdi
+        endif
+      case(ODB_REAL)
+        one_row(i) = -mdi
+      case(ODB_STRING)
+        one_row(i) = transfer(csgn,str_real8)
+    end select
+ enddo
+
+end subroutine init_one_row
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+subroutine delete_header_table(b2o_info)
+implicit none
+
+ type(t_bufr2odb), intent(inout)             :: b2o_info
+
+ if (associated(b2o_info%ODBcolname)) deallocate(b2o_info%ODBcolname)
+ if (associated(b2o_info%ODBcoltype)) deallocate(b2o_info%ODBcoltype)
+end subroutine delete_header_table
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+end module odbi
diff --git a/odb_api/src/odb_api/odb2netcdf/CMakeLists.txt b/odb_api/src/odb_api/odb2netcdf/CMakeLists.txt
new file mode 100644
index 0000000..d1a522f
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/CMakeLists.txt
@@ -0,0 +1,35 @@
+list( APPEND odb2netcdf_src_files
+            Odb2NetCDF.cc 
+            Odb2NetCDF.h 
+            Odb2NetcdfModule.h
+            Odb2NetcdfModule.cc
+            ecml_verbs/Odb2NetcdfHandler.cc
+            ecml_verbs/Odb2NetcdfHandler.h
+)
+
+ecbuild_add_executable(TARGET odb2netcdf.x
+    SOURCES 
+            odb2netcdf_main.cc
+            ${odb2netcdf_src_files}
+
+    INCLUDES 
+            ${NETCDF_INCLUDE_DIRS} 
+            ${ODB_API_INCLUDE_DIRS}
+            ${ECKIT_INCLUDE_DIRS}
+
+    LIBS    
+            ${NETCDF_LIBRARIES} 
+            ${ODB_API_LIBRARIES}
+            ${ECKIT_LIBRARIES}
+
+    CONDITION HAVE_NETCDF)
+
+ecbuild_add_library( TARGET     Odb2Netcdf
+                     #INSTALL_HEADERS LISTED
+                     #HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api
+                     #COMPONENT  server
+                     SOURCES    ${odb2netcdf_src_files}
+                     PRIVATE_INCLUDES ${NETCDF_INCLUDE_DIRS}
+                     LIBS       Odb odbtools ${NETCDF_LIBRARIES}
+                     CONDITION HAVE_NETCDF)
+
diff --git a/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.cc b/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.cc
new file mode 100644
index 0000000..27ac187
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.cc
@@ -0,0 +1,318 @@
+#include "Odb2NetCDF.h"
+
+#include <iostream>
+#include <netcdfcpp.h>
+#include <algorithm>
+
+#include <odb_api/Reader.h>
+#include <odb_api/Select.h>
+
+#include "eckit/exception/Exceptions.h"
+
+/// @author Anne Fouilloux
+
+using namespace std;
+using namespace eckit;
+
+/// Replaces all occurences of '@' with '_'
+string patchName(const string& s)
+{
+    string r (s);
+    // ODB-334 
+    //replace(r.begin(), r.end(), '@', '_');
+    return r;
+}
+
+Odb2NetCDF::Odb2NetCDF(const string & inputfile, const string & outputfile)
+: inputfile_(inputfile), outputfile_(outputfile)
+{}
+
+Odb2NetCDF::~Odb2NetCDF() {}
+
+Odb2NetCDF_1D::Odb2NetCDF_1D(const string & inputfile, const string & outputfile)
+: Odb2NetCDF(inputfile,outputfile)
+{
+    Log::info() << "The following files will be opened and read: " << endl;
+    Log::info() << "ODB filename = " <<  inputfile << endl;
+    Log::info() << "Output NetCDF filename = " << outputfile << endl;
+}
+
+vector<NcVar*> Odb2NetCDF_1D::createVariables(NcFile& dataFile, const odb::MetaData& columns, NcDim* xDim)
+{
+    vector<NcVar*> vars;
+    for (size_t i(0); i < columns.size(); ++i)
+    {
+        const odb::Column& column (*(columns[i]));
+        NcVar* v;
+        switch (column.type())
+        {
+            case odb::INTEGER:
+            case odb::BITFIELD:
+                v = dataFile.add_var(patchName(column.name()).c_str(), ncInt, xDim);
+                v->add_att(_FillValue, int (column.missingValue()));
+                break;
+            case odb::REAL:
+                v = dataFile.add_var(patchName(column.name()).c_str(), ncFloat, xDim);
+                v->add_att(_FillValue, float (column.missingValue()));
+                break;
+            case odb::DOUBLE:
+                v = dataFile.add_var(patchName(column.name()).c_str(), ncDouble, xDim);
+                v->add_att(_FillValue, double (column.missingValue()));
+                break;
+            case odb::STRING:
+                v = dataFile.add_var(patchName(column.name()).c_str(), ncChar, xDim);
+                //v->add_att(_FillValue, ""); // TODO: missing value for strings ????
+                break;
+            case odb::IGNORE:
+            default:
+                Log::error() << "Unknown column type: name=" << column.name() << ", type=" << column.type() << endl;
+                ASSERT("Unknown type" && false);
+                break;
+        }
+        vars.push_back(v);
+    }
+    return vars;
+}
+
+void Odb2NetCDF_1D::convert() {
+    // Create the file. The Replace parameter tells netCDF to overwrite
+    // this file, if it already exists.
+    NcFile dataFile(outputfile().c_str(), NcFile::Replace);
+
+    // You should always check whether a netCDF file creation or open
+    // constructor succeeded.
+    if (! dataFile.is_valid())
+        throw UserError("Could not open file"); // TODO: find appropriate exception
+
+    Log::info() << "Conversion to NetCDF 1D" << endl;
+
+    //////////////////////////////           ODB from MARS             //////////////////////////////
+    NcDim* xDim (dataFile.add_dim("hdrlen"));
+    dataFile.add_att("Conventions", "CF-1.6"); 
+
+    odb::Reader odb(inputfile());
+    odb::Reader::iterator it (odb.begin());
+
+    vector<NcVar*> vars (createVariables(dataFile, it->columns(), xDim));
+
+    union { double n; char b[sizeof(double) + 1]; } buffer;
+    memset(&buffer, 0, sizeof(buffer));
+
+    size_t nrows (0);
+    for(; it != odb.end(); ++it, ++nrows)
+        for (int i(0); i < it->columns().size(); ++i)
+        {
+            buffer.n = ((*it)[i]);
+            switch (it->columns()[i]->type()) 
+            {
+                case odb::STRING:
+                    vars[i]->put_rec(buffer.b, nrows);
+                    break;
+                default:
+                    vars[i]->put_rec(&buffer.n, nrows);
+                    break;
+            }
+        }
+    Log::info() << "Converted " << nrows << " row(s)." << endl;
+}
+
+//----------------------------------------------------------------
+Odb2NetCDF_2D::Odb2NetCDF_2D(const string & inputfile, const string & outputfile)
+  : Odb2NetCDF(inputfile,outputfile) {
+
+  fileNameHdr_ = inputfile + "_hdr.odb";
+  fileNameBody_ = inputfile  + "_body.odb";
+
+  Log::info() << "The following files will be opened and read: " << endl;
+  Log::info() << "Header filename = " <<  fileNameHdr_ << endl;
+  Log::info() << "Body filename = " << fileNameBody_ << endl;
+  Log::info() << "Output filename = " << outputfile << endl;
+}
+
+//----------------------------------------------------------------
+void Odb2NetCDF_2D::convert() {
+  // Create the file. The Replace parameter tells netCDF to overwrite
+  // this file, if it already exists.
+  NcFile dataFile(outputfile().c_str(), NcFile::Replace);
+
+  // You should always check whether a netCDF file creation or open
+  // constructor succeeded.
+  if (! dataFile.is_valid())
+      throw UserError ("Couldn't open file!");
+
+    Log::info() << "Conversion to NetCDF 2D" << endl;
+    // Check how many channel/bodylen
+    string sql = "select distinct vertco_reference_1 from \"" + fileNameBody_ + "\" order by vertco_reference_1;";
+    odb::Select odbs(sql);
+    int nmaxchannel = 0;
+    for (odb::Select::iterator its = odbs.begin();  its != odbs.end(); ++its, ++nmaxchannel);
+
+    Log::info() << " There are  = " <<  nmaxchannel << " channels" << endl;
+
+    // When we create netCDF dimensions, we get back a pointer to an
+    // NcDim for each one.
+    NcDim* xDim = dataFile.add_dim("hdrlen");
+    NcDim* yDim = dataFile.add_dim("maxbodylen", nmaxchannel);
+
+    NcVar *colChannel;
+
+    int * channel = new int [nmaxchannel];
+    int i=0;
+
+    odb::Select odbs2(sql);
+    for (odb::Select::iterator its = odbs2.begin();  its != odbs2.end(); ++its, ++i)
+    {
+        if (i==0)
+            colChannel = dataFile.add_var(its->columns()[0]->name().c_str(), ncInt, yDim);
+        channel[i] = ((*its)[0]);
+    }
+
+    colChannel->put(channel, nmaxchannel);
+
+    //////////////////////////////           HDR             //////////////////////////////
+
+    odb::Reader odb_hdr(fileNameHdr_);
+    odb::Reader::iterator it_hdr = odb_hdr.begin();
+    NcVar **colHdr;
+    colHdr = new NcVar * [it_hdr->columns().size()];
+
+    for (int i=0;i<it_hdr->columns().size();++i) {
+      switch(it_hdr->columns()[i]->type())
+	{
+	case odb::INTEGER:
+	case odb::BITFIELD:
+	  colHdr[i] = dataFile.add_var(it_hdr->columns()[i]->name().c_str(), ncInt, xDim);
+	  colHdr[i]->add_att(_FillValue,(int)it_hdr->columns()[i]->missingValue());
+	  break;
+	case odb::REAL:
+	  colHdr[i] = dataFile.add_var(it_hdr->columns()[i]->name().c_str(), ncFloat, xDim);
+	  colHdr[i]->add_att(_FillValue, (float)it_hdr->columns()[i]->missingValue());
+	  break;
+	case odb::STRING:
+	  //        colHdr[i] = dataFile.add_var(it_hdr->columns()[i]->name().c_str(), ncInt, xDim);
+	  //        colHdr[i]->add_att();
+	  break;
+	case odb::IGNORE:
+	default:
+	  ASSERT("Unknown type" && false);
+	  break;
+	}
+    }
+
+    int nrows=0;
+    double nr;
+    for(; it_hdr != odb_hdr.end(); ++it_hdr)
+      {
+	++nrows;
+	for (int i=0;i<it_hdr->columns().size();++i) {
+	  nr = ((*it_hdr)[i]);
+	  colHdr[i]->put(&nr, 1);
+	  colHdr[i]->set_cur(nrows);
+	}
+      }
+
+    //////////////////////////////           BODY             //////////////////////////////
+
+
+    odb::Reader odb_body(fileNameBody_);
+    odb::Reader::iterator it_body = odb_body.begin();
+    NcVar **colBody;
+    colBody = new NcVar * [it_body->columns().size()];
+
+    int index_channel=-1;
+    int index_seqno=-1;
+    for (int i=0;i<it_body->columns().size();++i) {
+      if (it_body->columns()[i]->name() == "vertco_reference_1" || it_body->columns()[i]->name() == "vertco_reference_1 at body")
+	    index_channel = i;
+      if (it_body->columns()[i]->name() == "seqno" || it_body->columns()[i]->name() == "seqno at hdr")
+	    index_seqno = i;
+    }
+
+    Log::info() << " index_vertco_reference_1 = " << index_channel << endl;
+    Log::info() << " index_seqno = " << index_seqno << endl;
+
+    if (index_channel != -1 && index_seqno != -1) {
+      for (int i=0;i<it_body->columns().size();++i) {
+	if ((it_body->columns()[i]->name() != "seqno" && it_body->columns()[i]->name() != "vertco_reference_1") &&
+        (it_body->columns()[i]->name() != "seqno at hdr" && it_body->columns()[i]->name() != "vertco_reference_1 at body")) {
+	  switch(it_body->columns()[i]->type())
+	    {
+	    case odb::INTEGER:
+	    case odb::BITFIELD:
+	      colBody[i] = dataFile.add_var(it_body->columns()[i]->name().c_str(), ncInt, xDim, yDim);
+	      colBody[i]->add_att(_FillValue,(int)it_body->columns()[i]->missingValue());
+	      break;
+	    case odb::REAL:
+	      colBody[i] = dataFile.add_var(it_body->columns()[i]->name().c_str(), ncFloat, xDim, yDim);
+	      colBody[i]->add_att(_FillValue,(float) it_body->columns()[i]->missingValue());
+	      break;
+	    case odb::STRING:
+	      //            colBody[i] = dataFile.add_var(it_body->columns()[i]->name().c_str(), ncInt, xDim, yDim);
+	      break;
+	    case odb::IGNORE:
+	    default:
+	      ASSERT("Unknown type" && false);
+	      break;
+	    }
+	}
+      }
+
+      int nrows = 0;
+      int nchannel = 0;
+      double nd[it_body->columns().size()][nmaxchannel];
+      double lnd[nmaxchannel];
+      long icurrent_seqno=-1;
+      int icurrent_channel=-1;
+      for(; it_body != odb_body.end(); ++it_body)
+	{
+	  if (((*it_body)[index_seqno]) != icurrent_seqno) {
+	    if (nrows > 0) {  // not the first time
+	      for (int i=0;i<it_body->columns().size();++i) {
+	if ((it_body->columns()[i]->name() != "seqno" && it_body->columns()[i]->name() != "vertco_reference_1") &&
+        (it_body->columns()[i]->name() != "seqno at hdr" && it_body->columns()[i]->name() != "vertco_reference_1 at body")) {
+		  // copy to a local array (this specific ODB column) to write in netCDF
+		  for (int j=0; j<nmaxchannel; ++j)
+		    lnd[j] = nd[i][j];
+		  colBody[i]->put_rec(&lnd[0], nrows-1);  // nrows -1 because we start to write at line 0 and not 1
+		}
+	      }
+	    }
+	    ++nrows;
+	    icurrent_seqno = ((*it_body)[index_seqno]);
+	    icurrent_channel =  0;
+	    nchannel=0;
+	    // initialize to missing value nd for all columns and all channel
+	    for (int i=0;i<it_body->columns().size();++i) {
+	      for (int j=0; j< nmaxchannel; ++j) {
+		nd[i][j] = it_body->columns()[i]->missingValue();
+	      }
+	    }
+	  }
+	  while (icurrent_channel < nmaxchannel && channel[icurrent_channel] < ((*it_body)[index_channel])) {
+	    ++icurrent_channel;
+	    ++nchannel;
+	  }
+
+	  for (int i=0;i<it_body->columns().size();++i) {
+	if ((it_body->columns()[i]->name() != "seqno" && it_body->columns()[i]->name() != "vertco_reference_1") &&
+        (it_body->columns()[i]->name() != "seqno at hdr" && it_body->columns()[i]->name() != "vertco_reference_1 at body")) {
+	      nd[i][icurrent_channel] = ((*it_body)[i]);
+	    }
+	  }
+	  ++nchannel;
+	  ++icurrent_channel;
+	}
+      for (int i=0;i<it_body->columns().size();++i) {
+	if ((it_body->columns()[i]->name() != "seqno" && it_body->columns()[i]->name() != "vertco_reference_1") &&
+        (it_body->columns()[i]->name() != "seqno at hdr" && it_body->columns()[i]->name() != "vertco_reference_1 at body")) {
+	  for (int j=0; j<nmaxchannel; ++j)
+	    lnd[j] = nd[i][j];
+	  for (int j=0; j<nmaxchannel; ++j)
+	    Log::info() << nrows << " lnd[" << j << "]=" << lnd[j] << " " ;
+	  Log::info() << endl;
+	  colBody[i]->put_rec(&lnd[0], nrows-1);
+	}
+      }
+
+    }
+}
diff --git a/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.h b/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.h
new file mode 100644
index 0000000..ddafbd5
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/Odb2NetCDF.h
@@ -0,0 +1,43 @@
+#ifndef ODB2NETCDF_H
+#define ODB2NETCDF_H
+
+#include <string>
+#include <vector>
+#include <netcdfcpp.h>
+
+/// @author Anne Fouilloux
+
+namespace odb { class MetaData; }
+
+class Odb2NetCDF {
+public:
+    Odb2NetCDF(const std::string & inputfile, const std::string & outputfile);
+    virtual ~Odb2NetCDF();
+    virtual void convert()=0;
+    std::string & inputfile() { return inputfile_;};
+    std::string & outputfile() { return outputfile_;};
+private:
+    std::string inputfile_;
+    std::string outputfile_;
+};
+
+class Odb2NetCDF_1D : public Odb2NetCDF {
+public:
+    Odb2NetCDF_1D(const std::string & inputfile, const std::string & outputfile);
+    ~Odb2NetCDF_1D() {};
+    virtual void convert();
+private:
+    std::vector<NcVar*> createVariables(NcFile& dataFile, const odb::MetaData& columns, NcDim*);
+};
+
+class Odb2NetCDF_2D : public Odb2NetCDF {
+public:
+    Odb2NetCDF_2D(const std::string & inputfile, const std::string & outputfile);
+    ~Odb2NetCDF_2D() {};
+    virtual void convert();
+private:
+    std::string fileNameHdr_;
+    std::string fileNameBody_;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.cc b/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.cc
new file mode 100644
index 0000000..f5ab474
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.cc
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @file Odb2NetcdfModule.cc
+/// @author Piotr Kuchta - (c) ECMWF September 2015
+
+#include <string>
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h"
+
+#include "Odb2NetcdfModule.h"
+
+namespace odb {
+
+using namespace std;
+using namespace eckit;
+
+Odb2NetcdfModule::Odb2NetcdfModule() {}
+Odb2NetcdfModule::~Odb2NetcdfModule() {}
+
+void Odb2NetcdfModule::importInto(ecml::ExecutionContext& context)
+{
+    static Odb2NetcdfHandler odb2netcdf("odb.odb2netcdf");
+    context.registerHandler("odb2netcdf", odb2netcdf);
+}
+
+} // namespace odb 
diff --git a/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.h b/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.h
new file mode 100644
index 0000000..de26214
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/Odb2NetcdfModule.h
@@ -0,0 +1,31 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+// File Odb2NetcdfModule.h
+// Piotr Kuchta - (c) ECMWF May 2015
+
+#ifndef Odb2NetcdfModule_H
+#define Odb2NetcdfModule_H
+
+#include "ecml/core/Module.h"
+#include "ecml/core/ExecutionContext.h"
+
+namespace odb {
+
+class Odb2NetcdfModule : public ecml::Module {
+public:
+    Odb2NetcdfModule();
+    ~Odb2NetcdfModule();
+    void importInto(ecml::ExecutionContext&);
+};
+
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.cc b/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.cc
new file mode 100644
index 0000000..08bd58c
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <sstream>
+
+#include "odb_api/Comparator.h"
+
+#include "eckit/types/Types.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/data/DataHandleFactory.h"
+#include "odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h"
+#include "odb_api/odb2netcdf/Odb2NetCDF.h"
+
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+Odb2NetcdfHandler::Odb2NetcdfHandler(const string& name) : ecml::RequestHandler(name) {}
+
+ecml::Values Odb2NetcdfHandler::handle(ecml::ExecutionContext& context)
+{
+    vector<string> source (context.environment().lookupList("source", context));
+    vector<string> target (context.environment().lookupList("target", context));
+    string format (context.environment().lookup("format", "1d", context));
+
+    if (! (format == "1d" || format == "2d"))
+        throw UserError ("odb2netcdf: FORMAT must be one of 1d or 2d");
+
+    if (source.size() != target.size())
+        throw UserError ("odb2netcdf: number of elements of SOURCE and TARGET must be the same");
+
+    ecml::List l;
+	for(size_t i(0); i < source.size(); ++i)
+	{
+        const string& input (source[i]), output (target[i]);
+
+        if (format == "2d")
+        {
+            Odb2NetCDF_2D converter (input, output);
+            converter.convert();
+        }
+        else
+        {
+            Odb2NetCDF_1D converter (input, output);
+            converter.convert();
+        }
+        l.append(output);
+	}
+    return l;
+}
diff --git a/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h b/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h
new file mode 100644
index 0000000..7523514
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/ecml_verbs/Odb2NetcdfHandler.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, September 2015
+
+#ifndef odb_api_Odb2NetcdfHandler_h
+#define odb_api_Odb2NetcdfHandler_h
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/core/RequestHandler.h"
+
+class Odb2NetcdfHandler : public ecml::RequestHandler {
+public:
+    Odb2NetcdfHandler(const std::string&);
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/odb2netcdf/odb2netcdf_main.cc b/odb_api/src/odb_api/odb2netcdf/odb2netcdf_main.cc
new file mode 100644
index 0000000..f7d7c33
--- /dev/null
+++ b/odb_api/src/odb_api/odb2netcdf/odb2netcdf_main.cc
@@ -0,0 +1,80 @@
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+
+#include <netcdfcpp.h>
+
+#include "odb_api/odbcapi.h"
+#include "odb_api/CommandLineParser.h"
+#include "odb_api/ODBModule.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "Odb2NetCDF.h"
+#include "Odb2NetcdfModule.h"
+
+/// @author Anne Fouilloux
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+using namespace odb::tool;
+
+static const string usage = "Usage: odb2netcdf -i [odb_filename|odb_filename_prefix] [-2d] -o netcdf_filename";
+
+int runScripts(const vector<string>& params)
+{
+    ecml::ExecutionContext context;
+    ODBModule odbModule;
+    Odb2NetcdfModule odb2NetcdfModule;
+    context.import(odbModule);
+    context.import(odb2NetcdfModule);
+
+    for (size_t i(0); i < params.size(); ++i)
+    {
+        context.executeScriptFile(params[i]);
+    }
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    odb_start_with_args(argc, argv);
+
+    CommandLineParser args(argc, argv);
+    args.registerOptionWithArgument("-i");
+    args.registerOptionWithArgument("-o");
+
+    bool twoD (args.optionIsSet("-2d"));
+    bool ecml (args.optionIsSet("-ecml"));
+    string input (args.optionArgument<string>("-i", "")),
+           output (args.optionArgument<string>("-o", ""));
+
+    if (input.size() && output.size())
+    {
+        if (twoD)
+        {
+            Odb2NetCDF_2D converter (input, output);
+            converter.convert();
+        }
+        else
+        {
+            Odb2NetCDF_1D converter (input, output);
+            converter.convert();
+        }
+        return 0;
+    }
+
+    if (ecml)
+    {
+        std::vector<std::string> params(args.parameters());
+        params.erase(params.begin());
+        return runScripts(params);
+    }
+
+    cerr << usage << endl;
+
+    return 1;
+}
+
diff --git a/odb_api/src/odb_api/odb_api.h b/odb_api/src/odb_api/odb_api.h
new file mode 100644
index 0000000..1d1a72e
--- /dev/null
+++ b/odb_api/src/odb_api/odb_api.h
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file odb_api.h
+///
+/// @author Piotr Kuchta, Feb 2009
+
+#ifndef PUBLIC_ODB_API_H
+#define PUBLIC_ODB_API_H
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Column.h"
+#include "odb_api/Select.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/Reader.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/Writer.h"
+
+// For importing data from text - used by examples and tests
+#include "odb_api/tools/ImportTool.h"
+
+// For importing data from text - used by examples and tests
+#include "odb_api/tools/ImportTool.h"
+
+// For ECML support
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/core/Environment.h"
+#include "ecml/core/RequestHandler.h"
+#include "ecml/parser/RequestParser.h"
+#include "ecml/parser/Cell.h"
+#include "odb_api/ODBModule.h"
+#include "odb_api/ecml_data/ResultSet.h"
+#include "odb_api/ecml_data/ResultSetStore.h"
+
+// Exceptions.
+#include "eckit/exception/Exceptions.h"
+
+#endif
diff --git a/odb_api/src/odb_api/odbcapi.cc b/odb_api/src/odb_api/odbcapi.cc
new file mode 100644
index 0000000..df6eb77
--- /dev/null
+++ b/odb_api/src/odb_api/odbcapi.cc
@@ -0,0 +1,481 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file odbcapi.cc
+///
+/// @author Piotr Kuchta, March 2009
+
+#include "odb_api/odb_api.h"
+
+#include "eckit/runtime/Main.h"
+
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/ODAHandle.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/odbcapi.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+#include "odb_api/ecml_data/LocalHandleFactory.h"
+
+using namespace eckit;
+using namespace odb;
+
+char *dummyCommandLineArgs[] = { const_cast<char*>("odbcapi"), 0 };
+
+//#include "odbcapi.h"
+
+template <typename T, typename I> 
+int get_bitfield(T it,
+	int index,
+	char** bitfield_names,
+	char** bitfield_sizes,
+	int* nSize,
+	int* sSize)
+{
+	I* iter = reinterpret_cast<I*>(it);
+	const BitfieldDef& bitfieldDef(iter->columns()[index]->bitfieldDef());
+	FieldNames fieldNames(bitfieldDef.first);
+	Sizes sizes(bitfieldDef.second);
+
+    std::stringstream ns, ss;
+	for (size_t i = 0; i < fieldNames.size(); ++i)
+	{
+		ns << fieldNames[i] << ":";
+		ss << sizes[i] << ":";
+	}
+
+	std::string names(ns.str());
+	std::string ssizes(ss.str());
+
+	//FIXME: the memory allocated by strdup should be freed on Fortran side
+	*bitfield_names = strdup(names.c_str());
+	*bitfield_sizes = strdup(ssizes.c_str());
+
+	*nSize = names.size();
+	*sSize = ssizes.size();
+	return 0;
+}
+
+extern "C" {
+
+void register_extra_data_handle_factories()
+{
+    static LocalHandleFactory localHandleFactory;
+}
+
+void odb_start()
+{
+    static const char *argv[2] = {"odb_start", 0};
+
+    odb_start_with_args(1, const_cast<char **>(argv));
+}
+
+void odb_start_with_args(int argc, char* argv[])
+{
+    register_extra_data_handle_factories();
+    eckit::Main::initialise(argc, argv);
+}
+
+double odb_count(const char * filename)
+{
+	double n = 0;
+
+	PathName path = filename;
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+	MDR mdReader(path);
+	for (MDR::iterator it(mdReader.begin()), end(mdReader.end()); it != end; ++it)
+		n += it->columns().rowsNumber();
+	return n;
+}
+
+int get_blocks_offsets(const char* fileName, size_t* numberOfBlocks,  off_t** offsets, size_t** sizes)
+{
+	FastODA2Request<ODA2RequestClientTraits> o;
+	o.mergeSimilarBlocks(false);
+
+	OffsetList offs;
+	LengthList lengths;
+	std::vector<ODAHandle*> handles;
+
+	o.scanFile(fileName, offs, lengths, handles);
+
+	ASSERT(offs.size() == lengths.size());
+	ASSERT(offs.size() == handles.size());
+
+	size_t n = offs.size();
+
+	*numberOfBlocks = n;
+	*offsets = new off_t[n];
+	*sizes = new size_t[n];
+	
+	for (size_t i = 0; i < n; ++i)
+	{
+		(*offsets)[i] = offs[i];
+		(*sizes)[i] = lengths[i];
+		delete handles[i];
+	}
+
+	return 0;
+}
+
+int release_blocks_offsets(off_t** offsets) { delete [] *offsets; *offsets = 0; return 0; }
+int release_blocks_sizes(size_t** sizes) { delete [] *sizes; *sizes = 0; return 0; }
+
+unsigned int odb_get_headerBufferSize() { return ODBAPISettings::instance().headerBufferSize(); } 
+void odb_set_headerBufferSize(unsigned int n) { ODBAPISettings::instance().headerBufferSize(n); }
+
+unsigned int odb_get_setvbufferSize() { return ODBAPISettings::instance().setvbufferSize(); } 
+void odb_set_setvbufferSize(unsigned int n) { ODBAPISettings::instance().setvbufferSize(n); }
+
+const char* odb_api_version() { return odb::ODBAPIVersion::version(); }
+const char* odb_api_git_sha1() { return odb::ODBAPIVersion::gitsha1(); }
+
+unsigned int odb_api_format_version_major() { return odb::ODBAPIVersion::formatVersionMajor(); }
+unsigned int odb_api_format_version_minor() { return odb::ODBAPIVersion::formatVersionMinor(); }
+
+/// @param config  ignored for now.
+oda_ptr odb_read_create(const char *config, int *err)
+{
+	Reader* o = new Reader;
+	*err = !o;
+	return oda_ptr(o);
+}
+
+oda_ptr odb_create(const char *config, int *err)
+{
+    return odb_read_create(config, err);
+}
+
+/// @param config  ignored for now.
+oda_ptr odb_select_create(const char *config, int *err)
+{
+	Select* o = new Select;
+	*err = !o;
+	return oda_ptr(o);
+}
+
+/// @param config  ignored for now.
+oda_writer_ptr odb_writer_create(const char *config, int *err)
+{
+	//PathName path = filename;
+	Writer<>* o = new Writer<>; //(path);
+	*err = !o;
+	return oda_writer_ptr(o);
+}
+
+int odb_read_destroy(oda_ptr o)
+{
+	delete reinterpret_cast<Reader *>(o);
+	return 0;
+}
+
+int odb_destroy(oda_ptr o)
+{
+    return odb_read_destroy(o);
+}
+
+int odb_select_destroy(oda_ptr o)
+{
+	delete reinterpret_cast<Select *>(o);
+	return 0;
+}
+
+int odb_writer_destroy(oda_writer_ptr o)
+{
+	delete reinterpret_cast<Writer<> *>(o);
+	return 0;
+}
+
+oda_read_iterator_ptr odb_create_read_iterator(oda_ptr co, const char *filename, int *err)
+{
+	Reader *o (reinterpret_cast<Reader*>(co));
+	std::string fileName (filename);
+	PathName fn (fileName);
+	if (! fn.exists())
+	{
+		*err = 2; //TODO: define error codes
+		return 0;
+	}
+	
+	ReaderIterator* iter (o->createReadIterator(fn));
+	*err = !iter;
+	return oda_read_iterator_ptr(iter);
+	
+}
+
+oda_select_iterator_ptr odb_create_select_iterator(oda_ptr co, const char *sql, int *err)
+{
+    Select *o (reinterpret_cast<Select*>(co));
+    try { 
+        SelectIterator* iter (o->createSelectIterator(sql, /*ExecutionContext*/ 0));
+        *err = !iter;
+        return oda_select_iterator_ptr(iter);
+    }
+    catch (eckit::CantOpenFile e)
+    {
+		*err = 1;
+		return 0;
+    }
+    catch (eckit::ReadError e)
+    {
+		*err = 2; 
+		return 0;
+    }
+}
+
+oda_select_iterator_ptr odb_create_select_iterator_from_file(oda_ptr co, const char *sql, const char *filename, int *err)
+{
+    Select *o (reinterpret_cast<Select*>(co));
+
+    std::string full_sql (std::string(sql) + " from \"" + std::string(filename) + "\"");
+
+    SelectIterator* iter (o->createSelectIterator(full_sql, /*ExecutionContext*/ 0));
+    *err = !iter;
+    return oda_select_iterator_ptr(iter);
+}
+
+
+int odb_read_iterator_destroy(oda_read_iterator_ptr it)
+{
+	delete reinterpret_cast<ReaderIterator*>(it);
+	return 0;
+}
+
+int odb_select_iterator_destroy(oda_select_iterator_ptr it)
+{
+	delete reinterpret_cast<SelectIterator*>(it);
+	return 0;
+}
+
+int odb_read_iterator_get_no_of_columns(oda_read_iterator_ptr it, int *numberOfColumns)
+{
+	ReaderIterator* iter (reinterpret_cast<ReaderIterator*>(it));
+	*numberOfColumns = iter->columns().size();
+	return 0;
+}
+
+int odb_select_iterator_get_no_of_columns(oda_select_iterator_ptr it, int *numberOfColumns)
+{
+	SelectIterator* iter (reinterpret_cast<SelectIterator*>(it));
+	*numberOfColumns = iter->columns().size();
+	return 0;
+}
+
+int odb_read_iterator_get_column_type(oda_read_iterator_ptr it, int n, int *type)
+{
+	ReaderIterator* iter (reinterpret_cast<ReaderIterator*>(it));
+	*type = iter->columns()[n]->type();
+	return 0;
+}
+
+int odb_select_iterator_get_column_type(oda_select_iterator_ptr it, int n, int *type)
+{
+	SelectIterator* iter (reinterpret_cast<SelectIterator*>(it));
+	*type = iter->columns()[n]->type();
+	return 0;
+}
+
+int odb_read_iterator_get_column_name(oda_read_iterator_ptr it, int n, char **name, int *size_name)
+{
+	ReaderIterator* iter (reinterpret_cast<ReaderIterator*>(it));
+	*name = const_cast<char*>(iter->columns()[n]->name().c_str());
+	*size_name = iter->columns()[n]->name().length();
+    return 0;
+}
+
+int odb_select_iterator_get_column_name(oda_select_iterator_ptr it, int n, char **name, int *size_name)
+{
+	SelectIterator* iter (reinterpret_cast<SelectIterator*>(it));
+	*name = const_cast<char*>(iter->columns()[n]->name().c_str());
+	*size_name = iter->columns()[n]->name().length();
+     return 0;
+}
+
+int odb_read_iterator_get_next_row(oda_read_iterator_ptr it, int count, double* data, int *new_dataset)
+{
+	ReaderIterator* iter (reinterpret_cast<ReaderIterator*>(it));
+	if (! iter->next(/*ExecutionContext*/ 0))
+		return 1;
+
+	if (iter->isNewDataset())
+		*new_dataset = 1;
+	else
+		*new_dataset = 0;
+
+	if (count != static_cast<int>(iter->columns().size()))
+		return 2; // TDOO: define error codes
+
+	for (int i (0); i < count; ++i)
+		data[i] = iter->data()[i];
+
+	return 0;
+}
+
+int odb_select_iterator_get_next_row(oda_select_iterator_ptr it, int count, double* data, int *new_dataset)
+{
+	SelectIterator* iter (reinterpret_cast<SelectIterator*>(it));
+	if (! iter->next(/*ExecutionContext*/ 0))
+		return 1;
+
+	if (iter->isNewDataset())
+		*new_dataset = 1;
+	else
+		*new_dataset = 0;
+
+	if (count != static_cast<int>(iter->columns().size()))
+		return 2; // TDOO: define error codes
+
+	for (int i (0); i < count; ++i)
+		data[i] = iter->data()[i];
+
+	return 0;
+}
+
+oda_write_iterator_ptr odb_create_append_iterator(oda_ptr co, const char *filename, int *err)
+{
+	Writer<> *o (reinterpret_cast<Writer<> *>(co));
+    eckit::Length estimatedLength(0);
+	DataHandle *fh = ODBAPISettings::instance().appendToFile(PathName(std::string(filename)), estimatedLength, true);
+
+	// TODO: make sure there's no leaks (FileHandle)
+	Writer<>::iterator_class* w (new Writer<>::iterator_class(*o, fh, false));
+	*err = !w;
+	return oda_write_iterator_ptr(w);
+}
+
+oda_write_iterator_ptr odb_create_write_iterator(oda_ptr co, const char *filename, int *err)
+{
+	Writer<> *o (reinterpret_cast<Writer<> *>(co));
+    eckit::Length estimatedLength(0);
+	DataHandle *fh = ODBAPISettings::instance().writeToFile(PathName(std::string(filename)), estimatedLength, true);
+
+	// TODO: make sure there's no leaks (FileHandle)
+	Writer<>::iterator_class* w (new Writer<>::iterator_class(*o, fh, true));
+	*err = !w;
+	return oda_write_iterator_ptr(w);
+}
+
+int odb_write_iterator_destroy(oda_write_iterator_ptr wi)
+{
+	delete reinterpret_cast<Writer<>::iterator_class *>(wi);
+	return 0;
+}
+
+int odb_write_iterator_set_no_of_columns(oda_write_iterator_ptr wi, int n)
+{
+	Writer<>::iterator_class *w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+	w->setNumberOfColumns(n);
+	return 0;
+}
+
+int odb_write_iterator_set_column(oda_write_iterator_ptr wi, int index, int type, const char *name)
+{
+	Writer<>::iterator_class * w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+	return w->setColumn(index, std::string(name), ColumnType(type));
+}
+
+int odb_write_iterator_set_bitfield(oda_write_iterator_ptr wi, int index, int type, const char *name, const char* bitfieldNames, const char *bitfieldSizes)
+{
+	std::string bnames (bitfieldNames);
+    std::string bsizes (bitfieldSizes);
+    odb::FieldNames    (bitfield_names);
+    odb::Sizes         (bitfield_sizes);
+ 
+//	std::cout << " columnName = " << name << " " << bnames << " " << bsizes << std::endl;
+	size_t iprev (0);
+	for (size_t i (0); i < bnames.size(); i++)
+    {
+		if (bnames[i] == ':')
+        {
+			std::string name (bnames.substr(iprev,i-iprev));
+			iprev = i+1;    
+			bitfield_names.push_back(name);
+		}
+	}
+
+    iprev = 0;
+	for (size_t i (0); i < bsizes.size(); i++)
+    {
+		if (bsizes[i] == ':')
+        {
+			std::string name (bsizes.substr(iprev, i-iprev));
+			size_t size (atof(name.c_str())); // bit[0-9]+
+			iprev = i+1;    
+			bitfield_sizes.push_back(size);
+		}
+	}
+
+    odb::BitfieldDef bitfieldType(make_pair(bitfield_names, bitfield_sizes));
+
+	Writer<>::iterator_class * w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+	std::string columnName(name);
+	
+	int rc (w->setBitfieldColumn(index, columnName, ColumnType(type), bitfieldType));
+	return rc;
+}
+
+int odb_read_iterator_get_missing_value(oda_read_iterator_ptr ri, int index, double* value)
+{
+    ReaderIterator* r (reinterpret_cast<ReaderIterator*>(ri));
+    if (index < 0 && r->columns().size() < index) 
+    {
+        stringstream ss;
+        ss << "odb_read_iterator_get_missing_value: index " << index 
+            << " out of range, should be between 0 and " << r->columns().size();
+        throw UserError(ss.str());
+    }
+    *value = r->columns()[index]->missingValue();
+    return 0;
+}
+
+int odb_write_iterator_set_missing_value(oda_write_iterator_ptr wi, int index, double value)
+{
+	Writer<>::iterator_class * w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+	w->missingValue(index, value);
+	return 0;
+}
+
+int odb_write_iterator_write_header(oda_write_iterator_ptr wi)
+{
+	Writer<>::iterator_class * w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+	w->writeHeader();
+	return 0;
+}
+
+int odb_write_iterator_set_next_row(oda_write_iterator_ptr wi, double *data, int count)
+{ 
+	Writer<>::iterator_class * w (reinterpret_cast<Writer<>::iterator_class *>(wi));
+    return w->writeRow(data, count);
+}
+
+int odb_read_iterator_get_bitfield(oda_read_iterator_ptr it,
+	int index,
+	char** bitfield_names,
+	char** bitfield_sizes,
+	int* nSize,
+	int* sSize)
+{ return get_bitfield<oda_read_iterator_ptr,ReaderIterator>(it, index, bitfield_names, bitfield_sizes, nSize, sSize); }
+
+int odb_select_iterator_get_bitfield(oda_select_iterator_ptr it,
+	int index,
+	char** bitfield_names,
+	char** bitfield_sizes,
+	int* nSize,
+	int* sSize)
+{ return get_bitfield<oda_select_iterator_ptr,SelectIterator>(it, index, bitfield_names, bitfield_sizes, nSize, sSize); }
+
+} // extern "C" 
+
diff --git a/odb_api/src/odb_api/odbcapi.h b/odb_api/src/odb_api/odbcapi.h
new file mode 100644
index 0000000..6d55934
--- /dev/null
+++ b/odb_api/src/odb_api/odbcapi.h
@@ -0,0 +1,111 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODB_C_API_H
+
+
+/**
+ * \file odbcapi.h
+ *
+ * @author Piotr Kuchta, March 2009
+ *
+ */
+
+#include <stddef.h>
+#include <sys/types.h>
+// For off_t on Cray:
+#include <unistd.h>
+
+#if defined(__cplusplus) || defined(c_plusplus) || defined(SWIGPYTHON)
+extern "C" {
+#endif
+
+typedef void* oda_ptr;
+typedef void* oda_read_iterator_ptr;
+typedef void* oda_select_iterator_ptr;
+
+typedef void* oda_writer_ptr;
+typedef void* oda_write_iterator_ptr;
+
+//typedef void oda;
+typedef void oda_read_iterator;
+typedef void oda_select_iterator;
+
+typedef void oda_writer;
+typedef void oda_write_iterator;
+
+
+void odb_start();
+void odb_start_with_args(int argc, char* argv[]);
+void register_extra_data_handle_factories();
+
+unsigned int odb_get_headerBufferSize();
+void odb_set_headerBufferSize(unsigned int );
+
+unsigned int odb_get_setvbufferSize();
+void odb_set_setvbufferSize(unsigned int);
+
+const char* odb_api_version();
+const char* odb_api_git_sha1();
+unsigned int odb_api_format_version_major();
+unsigned int odb_api_format_version_minor();
+
+oda_ptr odb_read_create(const char *, int *);
+oda_ptr odb_create(const char *, int *); // Compatibility
+int odb_read_destroy(oda_ptr);
+int odb_destroy(oda_ptr); // Compatibility
+oda_read_iterator_ptr odb_create_read_iterator(oda_ptr, const char *, int *);
+int odb_read_iterator_destroy(oda_read_iterator_ptr);
+int odb_read_iterator_get_no_of_columns(oda_read_iterator_ptr, int*);
+int odb_read_iterator_get_column_type(oda_read_iterator_ptr, int, int*);
+int odb_read_iterator_get_column_name(oda_read_iterator_ptr, int, char**, int*);
+int odb_read_iterator_get_bitfield(oda_read_iterator_ptr, int, char**, char**, int*, int*);
+int odb_read_iterator_get_next_row(oda_read_iterator_ptr, int, double*, int*);
+int odb_read_iterator_get_missing_value(oda_read_iterator_ptr, int, double*);
+
+oda_ptr odb_select_create(const char *, int *);
+int odb_select_destroy(oda_ptr);
+oda_select_iterator_ptr odb_create_select_iterator(oda_ptr, const char *, int *);
+oda_select_iterator_ptr odb_create_select_iterator_from_file(oda_ptr, const char *, const char *, int *);
+int odb_select_iterator_destroy(oda_select_iterator_ptr);
+int odb_select_iterator_get_no_of_columns(oda_select_iterator_ptr, int*);
+int odb_select_iterator_get_column_type(oda_select_iterator_ptr, int, int *);
+int odb_select_iterator_get_column_name(oda_select_iterator_ptr, int, char **, int*);
+int odb_select_iterator_get_bitfield(oda_select_iterator_ptr, int, char**, char**, int*, int*);
+int odb_select_iterator_get_next_row(oda_select_iterator_ptr, int, double*, int*);
+
+oda_writer_ptr odb_writer_create(const char *, int *);
+int odb_writer_destroy(oda_writer_ptr);
+int odb_write_iterator_destroy(oda_write_iterator_ptr);
+
+oda_write_iterator_ptr odb_create_write_iterator(oda_writer_ptr, const char* , int *);
+oda_write_iterator_ptr odb_create_append_iterator(oda_writer_ptr, const char* , int *);
+int odb_write_iterator_set_no_of_columns(oda_write_iterator_ptr, int);
+
+int odb_write_iterator_set_column(oda_write_iterator_ptr, int, int, const char *);
+int odb_write_iterator_set_bitfield(oda_write_iterator_ptr, int, int, const char *, const char*, const char *);
+
+int odb_write_iterator_set_missing_value(oda_write_iterator_ptr, int, double);
+
+int odb_write_iterator_write_header(oda_write_iterator_ptr);
+int odb_write_iterator_set_next_row(oda_write_iterator_ptr, double *, int);
+
+// FIXME: This needs to be changed: return error code like all the rest of the functions
+double odb_count(const char *);
+
+int get_blocks_offsets(const char* fileName, size_t* numberOfBlocks, off_t** offsets, size_t** sizes);
+int release_blocks_offsets(off_t**);
+int release_blocks_sizes(size_t**);
+
+#if defined(__cplusplus) || defined(c_plusplus) || defined(SWIGPYTHON)
+}
+#endif
+#endif
+
diff --git a/odb_api/src/odb_api/odblib_lex.h b/odb_api/src/odb_api/odblib_lex.h
new file mode 100644
index 0000000..290b5f2
--- /dev/null
+++ b/odb_api/src/odb_api/odblib_lex.h
@@ -0,0 +1,39 @@
+#ifndef YYSTYPE_DEFINITION
+#define YYSTYPE_DEFINITION
+
+using namespace SQLYacc;
+
+typedef SQLExpression* SQLExpressionPtr; // For casts.
+
+struct YYSTYPE {
+    SQLExpression         *exp;
+    Table                 table;
+    double                num;
+    std::string           val;
+    std::vector<std::string>         list;
+    Expressions            explist;
+    std::pair<SQLExpression*,bool> orderexp;
+    std::pair<Expressions,std::vector<bool> > orderlist;
+    std::vector<Table>     tablist;
+    ColumnDefs             coldefs;
+    ColumnDef              coldef;
+    ConstraintDefs         condefs;
+    ConstraintDef          condef;
+    Range                  r;
+    bool                   bol;
+    SelectAST              select_statement;
+    InsertAST              insert_statement;
+    EmbeddedAST            embedded_statement;
+    std::pair<ColumnDefs, ConstraintDefs> tablemd;             
+};
+#endif
+
+#ifdef YY_DECL
+#undef YY_DECL
+#endif
+#define YY_DECL int odblib_lex (YYSTYPE *odblib_lval_param, odblib_scan_t odblib_scanner, odb::sql::SQLSession*)
+
+#define YYSTYPE_IS_DECLARED 1
+
+YY_DECL;
+
diff --git a/odb_api/src/odb_api/odbql.cc b/odb_api/src/odb_api/odbql.cc
new file mode 100644
index 0000000..2e6644f
--- /dev/null
+++ b/odb_api/src/odb_api/odbql.cc
@@ -0,0 +1,757 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file odbql.cc
+///
+/// @author Piotr Kuchta, July 2016
+
+#include "odb_api/odb_api.h"
+
+#include "eckit/io/MultiHandle.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/ODAHandle.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/odbcapi.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/InMemoryDataHandle.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLNonInteractiveSession.h"
+#include "odb_api/SQLIteratorSession.h"
+#include "odb_api/SQLOutputConfig.h"
+#include "odb_api/SQLDatabase.h"
+#include "odb_api/SQLEmbedded.h"
+#include "ecml/data/DataHandleFactory.h"
+#include "odb_api/ODBModule.h"
+
+#include "odbql.h"
+
+using namespace eckit;
+using namespace odb;
+
+/////////////////////////////////////////////////////////////
+// #     #                            #    ######    ###   
+// ##    #  ######  #    #           # #   #     #    #    
+// # #   #  #       #    #          #   #  #     #    #    
+// #  #  #  #####   #    #         #     # ######     #    
+// #   # #  #       # ## #         ####### #          #    
+// #    ##  #       ##  ##         #     # #          #    
+// #     #  ######  #    #         #     # #         ###
+
+
+typedef int error_code_t;
+typedef odbql * p_odbql;
+
+class DataBaseImpl {
+public:
+    DataBaseImpl(const char* filename)
+    : session_(odb::sql::SQLOutputConfig::defaultConfig(), ","),
+      filename_(filename),
+      input_( (filename && strlen(filename))
+             ? static_cast<DataHandle*>(ecml::DataHandleFactory::openForRead(filename))
+             : static_cast<DataHandle*>(new odb::InMemoryDataHandle() ))
+    {
+        if (filename && strlen(filename))
+            eckit::Log::info() << "Open file '" << filename << "'" << std::endl;
+    }
+
+    ~DataBaseImpl() 
+    {
+        input_->close();
+        delete input_;
+    }
+
+    eckit::DataHandle* input() { return input_; }
+    const char * errmsg() { return errmsg_.c_str(); }
+    void errmsg(const std::string& s) { errmsg_ = s; }
+    error_code_t error_code(error_code_t e) { return error_code_ = e; }
+    error_code_t error_code() { return error_code_; }
+    odb::sql::SQLNonInteractiveSession& session() { return session_; }
+
+private:
+    odb::sql::SQLNonInteractiveSession session_;
+    const std::string filename_;
+    eckit::DataHandle* input_;
+    std::string errmsg_;
+    error_code_t error_code_;
+
+    friend std::ostream& operator<<(std::ostream& s, const DataBaseImpl& p)
+    {
+        s << "[database@" << &p << ", session: " << p.session_ << "]" << std::endl;
+        return s;
+    }
+};
+
+DataBaseImpl& database (odbql* db) { return reinterpret_cast<DataBaseImpl&>(*db); }
+
+class StatementImpl {
+public:
+    StatementImpl(DataBaseImpl& db) : db_(db) {}
+
+    virtual ~StatementImpl() {}
+
+    virtual int step() = 0;
+    virtual const unsigned char *column_text(int iCol) = 0;
+    virtual const char *column_name(int iCol) = 0;
+    virtual int column_count() = 0;
+    virtual int column_type(int iCol) = 0;
+    virtual error_code_t bind_double(int iCol, double v) = 0;
+    virtual error_code_t bind_int(int iCol, int) = 0;
+    virtual error_code_t bind_text(int iCol, const char*, int) = 0;
+
+    // NULL handling functions:
+    virtual error_code_t bind_null(int iCol) = 0;
+    virtual odbql_value* column_value(int iCol) = 0; 
+
+    DataBaseImpl& database() { return db_; }
+
+private:
+    DataBaseImpl& db_;
+};
+
+class SelectImpl : public StatementImpl {
+public:
+    SelectImpl(DataBaseImpl& db, const char* sql)
+    : StatementImpl(db),
+      sql_(sql),
+      stmt_(sql, db.session()),
+      //stmt_(/*db.filename() + ";\n" +*/ sql),
+      it_(stmt_.begin()),
+      end_(stmt_.end()),
+      firstStep(true)
+    {} 
+    ~SelectImpl() {}
+
+    int step();
+    const unsigned char *column_text(int iCol);
+    const char *column_name(int iCol);
+    int column_count();
+    int column_type(int iCol);
+    error_code_t bind_double(int iCol, double v);
+    error_code_t bind_int(int iCol, int);
+    error_code_t bind_text(int iCol, const char*, int);
+    error_code_t bind_null(int iCol);
+    virtual odbql_value* column_value(int iCol); 
+ 
+private:
+    bool firstStep;
+    const std::string sql_;
+    odb::Select stmt_;
+    odb::Select::iterator it_;
+    odb::Select::iterator end_;
+
+    std::vector<std::string> stringCache_;
+    std::vector<std::string> columnNameCache_;
+};
+
+class SelectAllImpl : public StatementImpl {
+public:
+    SelectAllImpl(DataBaseImpl& db, const std::vector<std::string>& descriptors)
+    : StatementImpl(db),
+      dh_(openForRead(descriptors)),
+      stmt_(dh_),
+      it_(stmt_.end()),
+      end_(stmt_.end()),
+      firstStep(true)
+    {} 
+
+    ~SelectAllImpl() {}
+
+    static std::vector<eckit::DataHandle*> openForRead(const std::vector<std::string> descriptors)
+    {
+        std::vector<eckit::DataHandle*> r;
+        for (size_t i(0); i < descriptors.size(); ++i)
+            r.push_back( ecml::DataHandleFactory::openForRead(descriptors[i]));
+        return r;
+    }
+
+    int step();
+    const unsigned char *column_text(int iCol);
+    const char *column_name(int iCol);
+    int column_count();
+    int column_type(int iCol);
+    error_code_t bind_double(int iCol, double v);
+    error_code_t bind_int(int iCol, int);
+    error_code_t bind_text(int iCol, const char*, int);
+    error_code_t bind_null(int iCol);
+    virtual odbql_value* column_value(int iCol); 
+ 
+private:
+    bool firstStep;
+    eckit::MultiHandle dh_;
+    odb::Reader stmt_;
+    odb::Reader::iterator it_;
+    odb::Reader::iterator end_;
+
+    std::vector<std::string> stringCache_;
+    std::vector<std::string> columnNameCache_;
+
+    odb::MetaData currentMetaData_;
+};
+
+StatementImpl& statement (odbql_stmt* stmt) { return reinterpret_cast<StatementImpl&>(*stmt); }
+
+class InsertImpl : public StatementImpl {
+public:
+    InsertImpl(DataBaseImpl&, const odb::MetaData& metaData, const std::string& into);
+    ~InsertImpl();
+
+    int step();
+    const unsigned char *column_text(int iCol) { NOTIMP; }
+    const char *column_name(int iCol) { NOTIMP; }
+    int column_count(); 
+    int column_type(int iCol) { NOTIMP; }
+    error_code_t bind_double(int iCol, double v);
+    error_code_t bind_int(int iCol, int);
+    error_code_t bind_text(int iCol, const char*, int);
+    error_code_t bind_null(int iCol);
+    virtual odbql_value* column_value(int iCol);
+
+private:
+    FileHandle* fileHandle_;
+    odb::Writer<> writer_;
+    odb::Writer<>::iterator it_;
+};
+
+// INSERT implementation
+
+InsertImpl::InsertImpl(DataBaseImpl& db, const odb::MetaData& metaData, const std::string& into)
+: StatementImpl(db),
+  fileHandle_(new FileHandle(into)),
+  writer_(fileHandle_, true, true),
+  it_(writer_.begin())
+{
+    it_->columns(metaData);
+    it_->writeHeader();
+}
+
+InsertImpl::~InsertImpl()
+{
+    it_->close();
+    fileHandle_->close();
+}
+
+error_code_t InsertImpl::bind_double(int iCol, double v) 
+{ 
+    (*it_)[iCol] = v;
+    return ODBQL_OK;
+}
+
+error_code_t InsertImpl::bind_int(int iCol, int v) 
+{ 
+    (*it_)[iCol] = v;
+    return ODBQL_OK;
+}
+
+error_code_t InsertImpl::bind_text(int iCol, const char* s, int n)
+{ 
+    if (n > sizeof(double))
+    {
+        // this is the current limitation...
+        // TODO: set error messqge
+        return ODBQL_ERROR;
+    }
+
+    char v[sizeof(double) + 1];
+
+    memset(v, ' ', sizeof(double));
+    v[sizeof(double)] = 0;
+
+    for (size_t j(0); j < n; ++j)
+        v[sizeof(double) - n + j] = s[j];
+
+    (*it_)[iCol] = *reinterpret_cast<double*>(&v);
+    return ODBQL_OK;
+}
+
+error_code_t InsertImpl::bind_null(int iCol)
+{
+    (*it_)[iCol] = it_->columns()[iCol]->missingValue();
+    return ODBQL_OK;
+}
+
+odbql_value* InsertImpl::column_value(int iCol)
+{
+    if ((*it_)[iCol] == it_->columns()[iCol]->missingValue())
+        return 0;
+
+    typedef odbql_value* odbql_value_p;
+    return odbql_value_p(&(*it_)[iCol]);
+}
+
+int InsertImpl::step()
+{
+    ++it_;
+    return ODBQL_ROW;
+}
+
+int InsertImpl::column_count() { return it_->columns().size(); }
+
+// SELECT implementation
+
+int SelectImpl::step()
+{
+    if (firstStep)
+    {
+        firstStep = false;
+        return it_ != end_ ? ODBQL_ROW : ODBQL_DONE;
+    }
+
+    if (! (it_ != end_)) 
+        return ODBQL_DONE;
+    else
+    {
+        ++it_;
+        return it_ != end_ ? ODBQL_ROW : ODBQL_DONE;
+    }
+
+}
+
+int SelectImpl::column_count() { return it_->columns().size(); }
+
+const unsigned char *SelectImpl::column_text(int iCol)
+{
+    if (iCol + 1 > stringCache_.size())
+        stringCache_.resize(iCol + 1);
+
+    if (it_->columns()[iCol]->type() == odb::STRING)
+        stringCache_[iCol] = it_->string(iCol);
+    else 
+    {
+        stringstream ss;
+        ss << it_->data(iCol);
+        stringCache_[iCol] = ss.str();
+    }
+
+    typedef const unsigned char * cucp_t;
+    return cucp_t(stringCache_[iCol].c_str());
+}
+
+const char *SelectImpl::column_name(int iCol)
+{
+    if (iCol + 1 > columnNameCache_.size())
+        columnNameCache_.resize(iCol + 1);
+
+    columnNameCache_[iCol] = it_->columns()[iCol]->name();
+    return columnNameCache_[iCol].c_str();
+}
+
+int SelectImpl::column_type(int iCol)
+{
+//  ODBQL_INTEGER, ODBQL_FLOAT, ODBQL_TEXT, ODBQL_BLOB, or ODBQL_NULL.
+    switch (it_->columns()[iCol]->type())
+    {
+        case STRING:   return ODBQL_TEXT;
+        case INTEGER:  return ODBQL_INTEGER;
+        case BITFIELD: return ODBQL_BITFIELD;
+        case REAL:     return ODBQL_FLOAT;
+        case DOUBLE:   return ODBQL_FLOAT;
+        default:
+            return ODBQL_NULL; // TODO?
+    }
+}
+
+error_code_t SelectImpl::bind_double(int iCol, double v)
+{
+    NOTIMP;
+}
+
+error_code_t SelectImpl::bind_int(int iCol, int v)
+{
+    NOTIMP;
+}
+
+error_code_t SelectImpl::bind_text(int iCol, const char* s, int n)
+{
+    NOTIMP;
+}
+
+error_code_t SelectImpl::bind_null(int iCol)
+{
+    NOTIMP;
+}
+
+odbql_value* SelectImpl::column_value(int iCol)
+{
+    if ((*it_)[iCol] == it_->columns()[iCol]->missingValue())
+        return 0;
+
+    typedef odbql_value* odbql_value_p;
+    return odbql_value_p(&(*it_)[iCol]);
+}
+
+// 'SELECT ALL *' implementation
+
+int SelectAllImpl::step()
+{
+    if (firstStep)
+    {
+        if (! (it_ != end_))
+        {
+            dh_.openForRead();
+            if (dh_.estimate() == eckit::Length(0))
+                return ODBQL_DONE;
+            it_ = stmt_.begin();
+            currentMetaData_ = it_->columns();
+        }
+        firstStep = false;
+        return it_ != end_ ? ODBQL_ROW : ODBQL_DONE;
+    }
+
+    if (! (it_ != end_)) return ODBQL_DONE;
+    else
+    {
+        ++it_;
+
+        if (it_->isNewDataset() && currentMetaData_ != it_->columns())
+        {
+            currentMetaData_ = it_->columns();
+            return ODBQL_METADATA_CHANGED;
+        }
+
+        return it_ != end_ ? ODBQL_ROW : ODBQL_DONE;
+    }
+
+}
+
+int SelectAllImpl::column_count() 
+{ 
+    if (! (it_ != end_))
+    {
+        dh_.openForRead();
+        if (dh_.estimate() == eckit::Length(0))
+            return 0;
+        it_ = stmt_.begin();
+        currentMetaData_ = it_->columns();
+    }
+    return it_->columns().size(); 
+}
+
+const unsigned char *SelectAllImpl::column_text(int iCol)
+{
+    if (iCol + 1 > stringCache_.size())
+        stringCache_.resize(iCol + 1);
+
+    if (it_->columns()[iCol]->type() == odb::STRING)
+        stringCache_[iCol] = it_->string(iCol);
+    else 
+    {
+        stringstream ss;
+        ss << it_->data(iCol);
+        stringCache_[iCol] = ss.str();
+    }
+
+    typedef const unsigned char * cucp_t;
+    return cucp_t(stringCache_[iCol].c_str());
+}
+
+const char *SelectAllImpl::column_name(int iCol)
+{
+    if (iCol + 1 > columnNameCache_.size())
+        columnNameCache_.resize(iCol + 1);
+
+    columnNameCache_[iCol] = it_->columns()[iCol]->name();
+    return columnNameCache_[iCol].c_str();
+}
+
+int SelectAllImpl::column_type(int iCol)
+{
+//  ODBQL_INTEGER, ODBQL_FLOAT, ODBQL_TEXT, ODBQL_BLOB, or ODBQL_NULL.
+    switch (it_->columns()[iCol]->type())
+    {
+        case STRING:   return ODBQL_TEXT;
+        case INTEGER:  return ODBQL_INTEGER;
+        case BITFIELD: return ODBQL_INTEGER; // TODO?
+        case REAL:     return ODBQL_FLOAT;
+        case DOUBLE:   return ODBQL_FLOAT;
+        default:
+            return ODBQL_NULL; // TODO?
+    }
+}
+
+error_code_t SelectAllImpl::bind_double(int iCol, double v)
+{
+    NOTIMP;
+}
+
+error_code_t SelectAllImpl::bind_int(int iCol, int v)
+{
+    NOTIMP;
+}
+
+error_code_t SelectAllImpl::bind_text(int iCol, const char* s, int n)
+{
+    NOTIMP;
+}
+
+error_code_t SelectAllImpl::bind_null(int iCol)
+{
+    NOTIMP;
+}
+
+odbql_value* SelectAllImpl::column_value(int iCol)
+{
+    if ((*it_)[iCol] == it_->columns()[iCol]->missingValue())
+        return 0;
+
+    typedef odbql_value* odbql_value_p;
+    return odbql_value_p(&(*it_)[iCol]);
+}
+
+
+#define TRY_WITH_DB(d) do { DataBaseImpl *p (d); try { 
+ 
+#define CATCH_ALL  \
+    } \
+    catch(const odb::sql::SyntaxError &e)  { if (p) p->errmsg("syntax error"); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const eckit::CantOpenFile &e) { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const eckit::ShortFile &e)    { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const eckit::ReadError &e)    { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const eckit::UserError &e)    { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const eckit::Exception &e)    { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(const std::exception &e)      { if (p) p->errmsg(e.what()); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    catch(...) { if (p) p->errmsg("unknown error"); return p ? p->error_code(ODBQL_ERROR) : ODBQL_ERROR; } \
+    } while(false); 
+
+
+extern "C" {
+//ODBQL_API const char *ODBQL_STDCALL odbql_errmsg(odbql*);
+const char * odbql_errmsg(odbql* db)
+{
+    return database(db).errmsg();
+}
+
+//ODBQL_API const char *ODBQL_STDCALL odbql_libversion(void);
+const char * odbql_libversion(void)
+{
+    return odb::ODBAPIVersion::version();
+}
+
+//ODBQL_API int ODBQL_STDCALL odbql_open(
+//  const char *filename,   /* Database filename (UTF-8) */
+//  odbql **ppDb          /* OUT: SQLite db handle */
+//);
+
+error_code_t odbql_open(const char *filename, odbql **ppDb) 
+{
+    static char* argv[] = { const_cast<char *>("odbql"), 0 };
+    odb_start_with_args(1, argv);
+
+    TRY_WITH_DB (0)
+
+    (*ppDb) = p_odbql( p = new  DataBaseImpl(filename) );
+    return ODBQL_OK;
+
+    CATCH_ALL
+}
+
+//ODBQL_API int ODBQL_STDCALL odbql_close(odbql*);
+error_code_t odbql_close(odbql* db)
+{
+    delete &database(db);
+    return ODBQL_OK;
+}
+
+//ODBQL_API int ODBQL_STDCALL odbql_prepare_v2(
+//  odbql *db,            /* Database handle */
+//  const char *zSql,       /* SQL statement, UTF-8 encoded */
+//  int nByte,              /* Maximum length of zSql in bytes. */
+//  odbql_stmt **ppStmt,  /* OUT: Statement handle */
+//  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
+//);
+
+error_code_t odbql_prepare_v2(odbql *db, const char *zSql, int nByte, odbql_stmt **ppStmt, const char **pzTail)
+{
+    TRY_WITH_DB(&database(db))
+
+    //eckit::Log::info() << "Prepare statement '" << zSql << "' db = " << database(db) << std::endl;
+
+    odb::sql::SQLParser parser;
+    odb::sql::SQLNonInteractiveSession& session (database(db).session());
+    odb::sql::SQLOutputConfig config (session.selectFactory().config());
+    parser.parseString(session, zSql, database(db).input(), config, false);
+    odb::sql::SQLStatement* statement (session.statement());
+
+    typedef odbql_stmt* stmt_ptr_t; 
+
+    if (dynamic_cast<odb::sql::SQLInsert*>(statement))
+    {
+        odb::sql::SQLInsert& sqlInsert (dynamic_cast<odb::sql::SQLInsert&>(*statement));
+        const odb::sql::Table& table (sqlInsert.table());
+        const odb::sql::TableDef& tableDef (session.currentDatabase().schemaAnalyzer().findTable(table.name));
+
+        const MetaData md (odb::sql::SQLSelectFactory::toODAColumns(session, tableDef));
+        const std::string& location (tableDef.location());
+
+        *ppStmt = stmt_ptr_t (new InsertImpl(database(db), md, location));
+    }
+    else if (dynamic_cast<odb::sql::SQLSelect*>(statement))
+    {
+        odb::sql::SQLSelect* select (dynamic_cast<odb::sql::SQLSelect*>(statement));
+
+        if (select->all())
+        {
+            const std::vector<odb::sql::SQLTable*>& tables (select->tables());
+            if (tables.size() != 1)
+                throw UserError("Only one table required in FROM clause of 'SELECT ALL *'");
+            if (select->where())
+                throw UserError("'SELECT ALL *' cannot have WHERE clause yet");
+            const odb::sql::SQLTable& from (*tables[0]);
+            std::vector<std::string> paths;
+            paths.push_back (from.path());
+            *ppStmt = stmt_ptr_t (new SelectAllImpl(database(db), paths));
+        }
+        else
+        {
+            // Assume this is a regular SELECT for now 
+            *ppStmt = stmt_ptr_t (new SelectImpl(database(db), zSql));
+        }
+    }
+    else if (dynamic_cast<odb::sql::SQLEmbedded*>(statement))
+    {
+        std::string code ( dynamic_cast<odb::sql::SQLEmbedded&>(*statement).code());
+
+        ecml::ExecutionContext context;
+        odb::ODBModule odbModule;
+        context.import(odbModule);
+        ecml::Values values (context.execute(code));
+        std::vector<std::string> descriptors;
+        for (ecml::Request e (values); e; e = e->rest())
+            if (e->value())
+                descriptors.push_back(e->value()->text());
+
+        *ppStmt = stmt_ptr_t (new SelectAllImpl(database(db), descriptors));
+    }
+
+    return ODBQL_OK;
+
+    CATCH_ALL
+}
+
+//ODBQL_API int ODBQL_STDCALL odbql_step(odbql_stmt*)
+error_code_t odbql_step(odbql_stmt* stmt)
+{
+    TRY_WITH_DB(&statement(stmt).database())
+
+    if (! stmt) 
+        return ODBQL_ERROR;
+
+    return statement(stmt).step();
+
+    CATCH_ALL
+}
+
+// The last argument of odbql_bind_blob and similar: https://www.sqlite.org/c3ref/c_static.html
+//error_code_t odbql_bind_blob(odbql_stmt*, int, const void*, int n, void(*)(void*));
+//error_code_t odbql_bind_blob64(odbql_stmt*, int, const void*, odbql_uint64, void(*)(void*));
+error_code_t odbql_bind_double(odbql_stmt* stmt, int iCol, double v)
+{
+    TRY_WITH_DB(&statement(stmt).database())
+    return statement(stmt).bind_double(iCol, v);
+    CATCH_ALL
+}
+
+error_code_t odbql_bind_int(odbql_stmt* stmt, int iCol, int v)
+{
+    TRY_WITH_DB(&statement(stmt).database())
+    return statement(stmt).bind_int(iCol, v);
+    CATCH_ALL
+}
+
+//int odbql_bind_int64(odbql_stmt*, int, odbql_int64);
+error_code_t odbql_bind_null(odbql_stmt* stmt, int iCol)
+{
+    TRY_WITH_DB(&statement(stmt).database())
+    return statement(stmt).bind_null(iCol);
+    CATCH_ALL
+}
+
+error_code_t odbql_bind_text(odbql_stmt* stmt, int iCol, const char* s, int n, void(*d)(void*))
+{
+    TRY_WITH_DB(&statement(stmt).database())
+    return statement(stmt).bind_text(iCol, s, n);
+    CATCH_ALL
+}
+
+//error_code_t odbql_bind_text16(odbql_stmt*, int, const void*, int, void(*)(void*));
+//error_code_t odbql_bind_text64(odbql_stmt*, int, const char*, odbql_uint64, void(*)(void*), unsigned char encoding);
+//error_code_t odbql_bind_value(odbql_stmt*, int, const odbql_value*);
+//error_code_t odbql_bind_zeroblob(odbql_stmt*, int, int n);
+//error_code_t odbql_bind_zeroblob64(odbql_stmt*, int, odbql_uint64);
+
+
+//ODBQL_API const unsigned char *ODBQL_STDCALL odbql_column_text(odbql_stmt*, int iCol);
+const unsigned char *odbql_column_text(odbql_stmt* stmt, int iCol)
+{
+    return statement(stmt).column_text(iCol);
+}
+
+//ODBQL_API int ODBQL_STDCALL odbql_finalize(odbql_stmt *pStmt);
+error_code_t odbql_finalize(odbql_stmt *stmt)
+{
+    if (stmt == 0)
+        return ODBQL_OK;
+  
+    TRY_WITH_DB(&statement(stmt).database())
+    delete &statement(stmt);
+    return ODBQL_OK;
+    CATCH_ALL
+}
+
+// https://www.sqlite.org/c3ref/column_name.html
+const char *odbql_column_name(odbql_stmt* stmt, int iCol)
+{
+    return statement(stmt).column_name(iCol);
+}
+
+//https://www.sqlite.org/c3ref/column_blob.html
+int odbql_column_type(odbql_stmt* stmt, int iCol)
+{
+    return statement(stmt).column_type(iCol);
+}
+
+/// Use odbql_column_value to check if value of the current row of column iCol is NULL.
+/// @returns  0 is the value is NULL
+///           any other value means the value is not NULL
+odbql_value *odbql_column_value(odbql_stmt* stmt, int iCol)
+{
+    return reinterpret_cast<odbql_value*>( statement(stmt).column_value(iCol) );
+}
+
+// https://www.sqlite.org/c3ref/column_count.html
+int odbql_column_count(odbql_stmt *stmt)
+{
+    return stmt ? statement(stmt).column_count() : 0;
+}
+
+double odbql_value_double(odbql_value* vp)
+{
+    typedef double* double_p; 
+    return *double_p(vp);
+}
+
+int odbql_value_int(odbql_value* vp)
+{
+    typedef double* double_p; 
+    return *double_p(vp);
+}
+
+} // extern "C" 
+
diff --git a/odb_api/src/odb_api/odbql.h b/odb_api/src/odb_api/odbql.h
new file mode 100644
index 0000000..15d5311
--- /dev/null
+++ b/odb_api/src/odb_api/odbql.h
@@ -0,0 +1,10193 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This header file defines the interface that the SQLite library
+** presents to client programs.  If a C-function, structure, datatype,
+** or constant definition does not appear in this file, then it is
+** not a published API of SQLite, is subject to change without
+** notice, and should not be referenced by programs that use SQLite.
+**
+** Some of the definitions that are in this file are marked as
+** "experimental".  Experimental interfaces are normally new
+** features recently added to SQLite.  We do not anticipate changes
+** to experimental interfaces but reserve the right to make minor changes
+** if experience from use "in the wild" suggest such changes are prudent.
+**
+** The official C-language API documentation for SQLite is derived
+** from comments in this file.  This file is the authoritative source
+** on how SQLite interfaces are supposed to operate.
+**
+** The name of this file under configuration management is "sqlite.h.in".
+** The makefile makes some minor changes to this file (such as inserting
+** the version number) and changes its name to "odbql.h" as
+** part of the build process.
+*/
+#ifndef _ODBQL_H_
+#define _ODBQL_H_
+#include <stdarg.h>     /* Needed for the definition of va_list */
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** Provide the ability to override linkage features of the interface.
+*/
+#ifndef ODBQL_EXTERN
+# define ODBQL_EXTERN extern
+#endif
+#ifndef ODBQL_API
+# define ODBQL_API
+#endif
+#ifndef ODBQL_CDECL
+# define ODBQL_CDECL
+#endif
+#ifndef ODBQL_STDCALL
+# define ODBQL_STDCALL
+#endif
+
+/*
+** These no-op macros are used in front of interfaces to mark those
+** interfaces as either deprecated or experimental.  New applications
+** should not use deprecated interfaces - they are supported for backwards
+** compatibility only.  Application writers should be aware that
+** experimental interfaces are subject to change in point releases.
+**
+** These macros used to resolve to various kinds of compiler magic that
+** would generate warning messages when they were used.  But that
+** compiler magic ended up generating such a flurry of bug reports
+** that we have taken it all out and gone back to using simple
+** noop macros.
+*/
+#define ODBQL_DEPRECATED
+#define ODBQL_EXPERIMENTAL
+
+/*
+** Ensure these symbols were not defined by some previous header file.
+*/
+#ifdef ODBQL_VERSION
+# undef ODBQL_VERSION
+#endif
+#ifdef ODBQL_VERSION_NUMBER
+# undef ODBQL_VERSION_NUMBER
+#endif
+
+/*
+** CAPI3REF: Compile-Time Library Version Numbers
+**
+** ^(The [ODBQL_VERSION] C preprocessor macro in the odbql.h header
+** evaluates to a string literal that is the SQLite version in the
+** format "X.Y.Z" where X is the major version number (always 3 for
+** SQLite3) and Y is the minor version number and Z is the release number.)^
+** ^(The [ODBQL_VERSION_NUMBER] C preprocessor macro resolves to an integer
+** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same
+** numbers used in [ODBQL_VERSION].)^
+** The ODBQL_VERSION_NUMBER for any given release of SQLite will also
+** be larger than the release from which it is derived.  Either Y will
+** be held constant and Z will be incremented or else Y will be incremented
+** and Z will be reset to zero.
+**
+** Since version 3.6.18, SQLite source code has been stored in the
+** <a href="http://www.fossil-scm.org/">Fossil configuration management
+** system</a>.  ^The ODBQL_SOURCE_ID macro evaluates to
+** a string which identifies a particular check-in of SQLite
+** within its configuration management system.  ^The ODBQL_SOURCE_ID
+** string contains the date and time of the check-in (UTC) and an SHA1
+** hash of the entire source tree.
+**
+** See also: [odbql_libversion()],
+** [odbql_libversion_number()], [odbql_sourceid()],
+** [sqlite_version()] and [sqlite_source_id()].
+*/
+#define ODBQL_VERSION        "3.13.0"
+#define ODBQL_VERSION_NUMBER 3013000
+#define ODBQL_SOURCE_ID      "2016-05-18 10:57:30 fc49f556e48970561d7ab6a2f24fdd7d9eb81ff2"
+
+/*
+** CAPI3REF: Run-Time Library Version Numbers
+** KEYWORDS: odbql_version, odbql_sourceid
+**
+** These interfaces provide the same information as the [ODBQL_VERSION],
+** [ODBQL_VERSION_NUMBER], and [ODBQL_SOURCE_ID] C preprocessor macros
+** but are associated with the library instead of the header file.  ^(Cautious
+** programmers might include assert() statements in their application to
+** verify that values returned by these interfaces match the macros in
+** the header, and thus ensure that the application is
+** compiled with matching library and header files.
+**
+** <blockquote><pre>
+** assert( odbql_libversion_number()==ODBQL_VERSION_NUMBER );
+** assert( strcmp(odbql_sourceid(),ODBQL_SOURCE_ID)==0 );
+** assert( strcmp(odbql_libversion(),ODBQL_VERSION)==0 );
+** </pre></blockquote>)^
+**
+** ^The odbql_version[] string constant contains the text of [ODBQL_VERSION]
+** macro.  ^The odbql_libversion() function returns a pointer to the
+** to the odbql_version[] string constant.  The odbql_libversion()
+** function is provided for use in DLLs since DLL users usually do not have
+** direct access to string constants within the DLL.  ^The
+** odbql_libversion_number() function returns an integer equal to
+** [ODBQL_VERSION_NUMBER].  ^The odbql_sourceid() function returns 
+** a pointer to a string constant whose value is the same as the 
+** [ODBQL_SOURCE_ID] C preprocessor macro.
+**
+** See also: [sqlite_version()] and [sqlite_source_id()].
+*/
+ODBQL_API ODBQL_EXTERN const char odbql_version[];
+ODBQL_API const char *ODBQL_STDCALL odbql_libversion(void);
+ODBQL_API const char *ODBQL_STDCALL odbql_sourceid(void);
+ODBQL_API int ODBQL_STDCALL odbql_libversion_number(void);
+
+/*
+** CAPI3REF: Run-Time Library Compilation Options Diagnostics
+**
+** ^The odbql_compileoption_used() function returns 0 or 1 
+** indicating whether the specified option was defined at 
+** compile time.  ^The ODBQL_ prefix may be omitted from the 
+** option name passed to odbql_compileoption_used().  
+**
+** ^The odbql_compileoption_get() function allows iterating
+** over the list of options that were defined at compile time by
+** returning the N-th compile time option string.  ^If N is out of range,
+** odbql_compileoption_get() returns a NULL pointer.  ^The ODBQL_ 
+** prefix is omitted from any strings returned by 
+** odbql_compileoption_get().
+**
+** ^Support for the diagnostic functions odbql_compileoption_used()
+** and odbql_compileoption_get() may be omitted by specifying the 
+** [ODBQL_OMIT_COMPILEOPTION_DIAGS] option at compile time.
+**
+** See also: SQL functions [sqlite_compileoption_used()] and
+** [sqlite_compileoption_get()] and the [compile_options pragma].
+*/
+#ifndef ODBQL_OMIT_COMPILEOPTION_DIAGS
+ODBQL_API int ODBQL_STDCALL odbql_compileoption_used(const char *zOptName);
+ODBQL_API const char *ODBQL_STDCALL odbql_compileoption_get(int N);
+#endif
+
+/*
+** CAPI3REF: Test To See If The Library Is Threadsafe
+**
+** ^The odbql_threadsafe() function returns zero if and only if
+** SQLite was compiled with mutexing code omitted due to the
+** [ODBQL_THREADSAFE] compile-time option being set to 0.
+**
+** SQLite can be compiled with or without mutexes.  When
+** the [ODBQL_THREADSAFE] C preprocessor macro is 1 or 2, mutexes
+** are enabled and SQLite is threadsafe.  When the
+** [ODBQL_THREADSAFE] macro is 0, 
+** the mutexes are omitted.  Without the mutexes, it is not safe
+** to use SQLite concurrently from more than one thread.
+**
+** Enabling mutexes incurs a measurable performance penalty.
+** So if speed is of utmost importance, it makes sense to disable
+** the mutexes.  But for maximum safety, mutexes should be enabled.
+** ^The default behavior is for mutexes to be enabled.
+**
+** This interface can be used by an application to make sure that the
+** version of SQLite that it is linking against was compiled with
+** the desired setting of the [ODBQL_THREADSAFE] macro.
+**
+** This interface only reports on the compile-time mutex setting
+** of the [ODBQL_THREADSAFE] flag.  If SQLite is compiled with
+** ODBQL_THREADSAFE=1 or =2 then mutexes are enabled by default but
+** can be fully or partially disabled using a call to [odbql_config()]
+** with the verbs [ODBQL_CONFIG_SINGLETHREAD], [ODBQL_CONFIG_MULTITHREAD],
+** or [ODBQL_CONFIG_SERIALIZED].  ^(The return value of the
+** odbql_threadsafe() function shows only the compile-time setting of
+** thread safety, not any run-time changes to that setting made by
+** odbql_config(). In other words, the return value from odbql_threadsafe()
+** is unchanged by calls to odbql_config().)^
+**
+** See the [threading mode] documentation for additional information.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_threadsafe(void);
+
+/*
+** CAPI3REF: Database Connection Handle
+** KEYWORDS: {database connection} {database connections}
+**
+** Each open SQLite database is represented by a pointer to an instance of
+** the opaque structure named "odbql".  It is useful to think of an odbql
+** pointer as an object.  The [odbql_open()], [odbql_open16()], and
+** [odbql_open_v2()] interfaces are its constructors, and [odbql_close()]
+** and [odbql_close_v2()] are its destructors.  There are many other
+** interfaces (such as
+** [odbql_prepare_v2()], [odbql_create_function()], and
+** [odbql_busy_timeout()] to name but three) that are methods on an
+** odbql object.
+*/
+typedef struct odbql odbql;
+
+/*
+** CAPI3REF: 64-Bit Integer Types
+** KEYWORDS: sqlite_int64 sqlite_uint64
+**
+** Because there is no cross-platform way to specify 64-bit integer types
+** SQLite includes typedefs for 64-bit signed and unsigned integers.
+**
+** The odbql_int64 and odbql_uint64 are the preferred type definitions.
+** The sqlite_int64 and sqlite_uint64 types are supported for backwards
+** compatibility only.
+**
+** ^The odbql_int64 and sqlite_int64 types can store integer values
+** between -9223372036854775808 and +9223372036854775807 inclusive.  ^The
+** odbql_uint64 and sqlite_uint64 types can store integer values 
+** between 0 and +18446744073709551615 inclusive.
+*/
+#ifdef ODBQL_INT64_TYPE
+  typedef ODBQL_INT64_TYPE sqlite_int64;
+  typedef unsigned ODBQL_INT64_TYPE sqlite_uint64;
+#elif defined(_MSC_VER) || defined(__BORLANDC__)
+  typedef __int64 sqlite_int64;
+  typedef unsigned __int64 sqlite_uint64;
+#else
+  typedef long long int sqlite_int64;
+  typedef unsigned long long int sqlite_uint64;
+#endif
+typedef sqlite_int64 odbql_int64;
+typedef sqlite_uint64 odbql_uint64;
+
+/*
+** If compiling for a processor that lacks floating point support,
+** substitute integer for floating-point.
+*/
+#ifdef ODBQL_OMIT_FLOATING_POINT
+# define double odbql_int64
+#endif
+
+/*
+** CAPI3REF: Closing A Database Connection
+** DESTRUCTOR: odbql
+**
+** ^The odbql_close() and odbql_close_v2() routines are destructors
+** for the [odbql] object.
+** ^Calls to odbql_close() and odbql_close_v2() return [ODBQL_OK] if
+** the [odbql] object is successfully destroyed and all associated
+** resources are deallocated.
+**
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished odbql_backup objects then odbql_close()
+** will leave the database connection open and return [ODBQL_BUSY].
+** ^If odbql_close_v2() is called with unfinalized prepared statements
+** and/or unfinished odbql_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last odbql_backup is
+** finished.  The odbql_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [odbql_finalize | finalize] all [prepared statements],
+** [odbql_blob_close | close] all [BLOB handles], and 
+** [odbql_backup_finish | finish] all [odbql_backup] objects associated
+** with the [odbql] object prior to attempting to close the object.  ^If
+** odbql_close_v2() is called on a [database connection] that still has
+** outstanding [prepared statements], [BLOB handles], and/or
+** [odbql_backup] objects then it returns [ODBQL_OK] and the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [odbql_backup] objects are also destroyed.
+**
+** ^If an [odbql] object is destroyed while a transaction is open,
+** the transaction is automatically rolled back.
+**
+** The C parameter to [odbql_close(C)] and [odbql_close_v2(C)]
+** must be either a NULL
+** pointer or an [odbql] object pointer obtained
+** from [odbql_open()], [odbql_open16()], or
+** [odbql_open_v2()], and not previously closed.
+** ^Calling odbql_close() or odbql_close_v2() with a NULL pointer
+** argument is a harmless no-op.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_close(odbql*);
+ODBQL_API int ODBQL_STDCALL odbql_close_v2(odbql*);
+
+/*
+** The type for a callback function.
+** This is legacy and deprecated.  It is included for historical
+** compatibility and is not documented.
+*/
+typedef int (*odbql_callback)(void*,int,char**, char**);
+
+/*
+** CAPI3REF: One-Step Query Execution Interface
+** METHOD: odbql
+**
+** The odbql_exec() interface is a convenience wrapper around
+** [odbql_prepare_v2()], [odbql_step()], and [odbql_finalize()],
+** that allows an application to run multiple statements of SQL
+** without having to use a lot of C code. 
+**
+** ^The odbql_exec() interface runs zero or more UTF-8 encoded,
+** semicolon-separate SQL statements passed into its 2nd argument,
+** in the context of the [database connection] passed in as its 1st
+** argument.  ^If the callback function of the 3rd argument to
+** odbql_exec() is not NULL, then it is invoked for each result row
+** coming out of the evaluated SQL statements.  ^The 4th argument to
+** odbql_exec() is relayed through to the 1st argument of each
+** callback invocation.  ^If the callback pointer to odbql_exec()
+** is NULL, then no callback is ever invoked and result rows are
+** ignored.
+**
+** ^If an error occurs while evaluating the SQL statements passed into
+** odbql_exec(), then execution of the current statement stops and
+** subsequent statements are skipped.  ^If the 5th parameter to odbql_exec()
+** is not NULL then any error message is written into memory obtained
+** from [odbql_malloc()] and passed back through the 5th parameter.
+** To avoid memory leaks, the application should invoke [odbql_free()]
+** on error message strings returned through the 5th parameter of
+** odbql_exec() after the error message string is no longer needed.
+** ^If the 5th parameter to odbql_exec() is not NULL and no errors
+** occur, then odbql_exec() sets the pointer in its 5th parameter to
+** NULL before returning.
+**
+** ^If an odbql_exec() callback returns non-zero, the odbql_exec()
+** routine returns ODBQL_ABORT without invoking the callback again and
+** without running any subsequent SQL statements.
+**
+** ^The 2nd argument to the odbql_exec() callback function is the
+** number of columns in the result.  ^The 3rd argument to the odbql_exec()
+** callback is an array of pointers to strings obtained as if from
+** [odbql_column_text()], one for each column.  ^If an element of a
+** result row is NULL then the corresponding string pointer for the
+** odbql_exec() callback is a NULL pointer.  ^The 4th argument to the
+** odbql_exec() callback is an array of pointers to strings where each
+** entry represents the name of corresponding result column as obtained
+** from [odbql_column_name()].
+**
+** ^If the 2nd parameter to odbql_exec() is a NULL pointer, a pointer
+** to an empty string, or a pointer that contains only whitespace and/or 
+** SQL comments, then no SQL statements are evaluated and the database
+** is not changed.
+**
+** Restrictions:
+**
+** <ul>
+** <li> The application must ensure that the 1st parameter to odbql_exec()
+**      is a valid and open [database connection].
+** <li> The application must not close the [database connection] specified by
+**      the 1st parameter to odbql_exec() while odbql_exec() is running.
+** <li> The application must not modify the SQL statement text passed into
+**      the 2nd parameter of odbql_exec() while odbql_exec() is running.
+** </ul>
+*/
+ODBQL_API int ODBQL_STDCALL odbql_exec(
+  odbql*,                                  /* An open database */
+  const char *sql,                           /* SQL to be evaluated */
+  int (*callback)(void*,int,char**,char**),  /* Callback function */
+  void *,                                    /* 1st argument to callback */
+  char **errmsg                              /* Error msg written here */
+);
+
+/*
+** CAPI3REF: Result Codes
+** KEYWORDS: {result code definitions}
+**
+** Many SQLite functions return an integer result code from the set shown
+** here in order to indicate success or failure.
+**
+** New error codes may be added in future versions of SQLite.
+**
+** See also: [extended result code definitions]
+*/
+#define ODBQL_OK           0   /* Successful result */
+/* beginning-of-error-codes */
+#define ODBQL_ERROR        1   /* SQL error or missing database */
+#define ODBQL_INTERNAL     2   /* Internal logic error in SQLite */
+#define ODBQL_PERM         3   /* Access permission denied */
+#define ODBQL_ABORT        4   /* Callback routine requested an abort */
+#define ODBQL_BUSY         5   /* The database file is locked */
+#define ODBQL_LOCKED       6   /* A table in the database is locked */
+#define ODBQL_NOMEM        7   /* A malloc() failed */
+#define ODBQL_READONLY     8   /* Attempt to write a readonly database */
+#define ODBQL_INTERRUPT    9   /* Operation terminated by odbql_interrupt()*/
+#define ODBQL_IOERR       10   /* Some kind of disk I/O error occurred */
+#define ODBQL_CORRUPT     11   /* The database disk image is malformed */
+#define ODBQL_NOTFOUND    12   /* Unknown opcode in odbql_file_control() */
+#define ODBQL_FULL        13   /* Insertion failed because database is full */
+#define ODBQL_CANTOPEN    14   /* Unable to open the database file */
+#define ODBQL_PROTOCOL    15   /* Database lock protocol error */
+#define ODBQL_EMPTY       16   /* Database is empty */
+#define ODBQL_SCHEMA      17   /* The database schema changed */
+#define ODBQL_TOOBIG      18   /* String or BLOB exceeds size limit */
+#define ODBQL_CONSTRAINT  19   /* Abort due to constraint violation */
+#define ODBQL_MISMATCH    20   /* Data type mismatch */
+#define ODBQL_MISUSE      21   /* Library used incorrectly */
+#define ODBQL_NOLFS       22   /* Uses OS features not supported on host */
+#define ODBQL_AUTH        23   /* Authorization denied */
+#define ODBQL_FORMAT      24   /* Auxiliary database format error */
+#define ODBQL_RANGE       25   /* 2nd parameter to odbql_bind out of range */
+#define ODBQL_NOTADB      26   /* File opened that is not a database file */
+#define ODBQL_NOTICE      27   /* Notifications from odbql_log() */
+#define ODBQL_WARNING     28   /* Warnings from odbql_log() */
+#define ODBQL_ROW         100  /* odbql_step() has another row ready */
+#define ODBQL_DONE        101  /* odbql_step() has finished executing */
+///////// ODB API specific:
+#define ODBQL_METADATA_CHANGED        102  /* odbql_step(): metadata has changed */
+/* end-of-error-codes */
+
+/*
+** CAPI3REF: Extended Result Codes
+** KEYWORDS: {extended result code definitions}
+**
+** In its default configuration, SQLite API routines return one of 30 integer
+** [result codes].  However, experience has shown that many of
+** these result codes are too coarse-grained.  They do not provide as
+** much information about problems as programmers might like.  In an effort to
+** address this, newer versions of SQLite (version 3.3.8 and later) include
+** support for additional result codes that provide more detailed information
+** about errors. These [extended result codes] are enabled or disabled
+** on a per database connection basis using the
+** [odbql_extended_result_codes()] API.  Or, the extended code for
+** the most recent error can be obtained using
+** [odbql_extended_errcode()].
+*/
+#define ODBQL_IOERR_READ              (ODBQL_IOERR | (1<<8))
+#define ODBQL_IOERR_SHORT_READ        (ODBQL_IOERR | (2<<8))
+#define ODBQL_IOERR_WRITE             (ODBQL_IOERR | (3<<8))
+#define ODBQL_IOERR_FSYNC             (ODBQL_IOERR | (4<<8))
+#define ODBQL_IOERR_DIR_FSYNC         (ODBQL_IOERR | (5<<8))
+#define ODBQL_IOERR_TRUNCATE          (ODBQL_IOERR | (6<<8))
+#define ODBQL_IOERR_FSTAT             (ODBQL_IOERR | (7<<8))
+#define ODBQL_IOERR_UNLOCK            (ODBQL_IOERR | (8<<8))
+#define ODBQL_IOERR_RDLOCK            (ODBQL_IOERR | (9<<8))
+#define ODBQL_IOERR_DELETE            (ODBQL_IOERR | (10<<8))
+#define ODBQL_IOERR_BLOCKED           (ODBQL_IOERR | (11<<8))
+#define ODBQL_IOERR_NOMEM             (ODBQL_IOERR | (12<<8))
+#define ODBQL_IOERR_ACCESS            (ODBQL_IOERR | (13<<8))
+#define ODBQL_IOERR_CHECKRESERVEDLOCK (ODBQL_IOERR | (14<<8))
+#define ODBQL_IOERR_LOCK              (ODBQL_IOERR | (15<<8))
+#define ODBQL_IOERR_CLOSE             (ODBQL_IOERR | (16<<8))
+#define ODBQL_IOERR_DIR_CLOSE         (ODBQL_IOERR | (17<<8))
+#define ODBQL_IOERR_SHMOPEN           (ODBQL_IOERR | (18<<8))
+#define ODBQL_IOERR_SHMSIZE           (ODBQL_IOERR | (19<<8))
+#define ODBQL_IOERR_SHMLOCK           (ODBQL_IOERR | (20<<8))
+#define ODBQL_IOERR_SHMMAP            (ODBQL_IOERR | (21<<8))
+#define ODBQL_IOERR_SEEK              (ODBQL_IOERR | (22<<8))
+#define ODBQL_IOERR_DELETE_NOENT      (ODBQL_IOERR | (23<<8))
+#define ODBQL_IOERR_MMAP              (ODBQL_IOERR | (24<<8))
+#define ODBQL_IOERR_GETTEMPPATH       (ODBQL_IOERR | (25<<8))
+#define ODBQL_IOERR_CONVPATH          (ODBQL_IOERR | (26<<8))
+#define ODBQL_IOERR_VNODE             (ODBQL_IOERR | (27<<8))
+#define ODBQL_IOERR_AUTH              (ODBQL_IOERR | (28<<8))
+#define ODBQL_LOCKED_SHAREDCACHE      (ODBQL_LOCKED |  (1<<8))
+#define ODBQL_BUSY_RECOVERY           (ODBQL_BUSY   |  (1<<8))
+#define ODBQL_BUSY_SNAPSHOT           (ODBQL_BUSY   |  (2<<8))
+#define ODBQL_CANTOPEN_NOTEMPDIR      (ODBQL_CANTOPEN | (1<<8))
+#define ODBQL_CANTOPEN_ISDIR          (ODBQL_CANTOPEN | (2<<8))
+#define ODBQL_CANTOPEN_FULLPATH       (ODBQL_CANTOPEN | (3<<8))
+#define ODBQL_CANTOPEN_CONVPATH       (ODBQL_CANTOPEN | (4<<8))
+#define ODBQL_CORRUPT_VTAB            (ODBQL_CORRUPT | (1<<8))
+#define ODBQL_READONLY_RECOVERY       (ODBQL_READONLY | (1<<8))
+#define ODBQL_READONLY_CANTLOCK       (ODBQL_READONLY | (2<<8))
+#define ODBQL_READONLY_ROLLBACK       (ODBQL_READONLY | (3<<8))
+#define ODBQL_READONLY_DBMOVED        (ODBQL_READONLY | (4<<8))
+#define ODBQL_ABORT_ROLLBACK          (ODBQL_ABORT | (2<<8))
+#define ODBQL_CONSTRAINT_CHECK        (ODBQL_CONSTRAINT | (1<<8))
+#define ODBQL_CONSTRAINT_COMMITHOOK   (ODBQL_CONSTRAINT | (2<<8))
+#define ODBQL_CONSTRAINT_FOREIGNKEY   (ODBQL_CONSTRAINT | (3<<8))
+#define ODBQL_CONSTRAINT_FUNCTION     (ODBQL_CONSTRAINT | (4<<8))
+#define ODBQL_CONSTRAINT_NOTNULL      (ODBQL_CONSTRAINT | (5<<8))
+#define ODBQL_CONSTRAINT_PRIMARYKEY   (ODBQL_CONSTRAINT | (6<<8))
+#define ODBQL_CONSTRAINT_TRIGGER      (ODBQL_CONSTRAINT | (7<<8))
+#define ODBQL_CONSTRAINT_UNIQUE       (ODBQL_CONSTRAINT | (8<<8))
+#define ODBQL_CONSTRAINT_VTAB         (ODBQL_CONSTRAINT | (9<<8))
+#define ODBQL_CONSTRAINT_ROWID        (ODBQL_CONSTRAINT |(10<<8))
+#define ODBQL_NOTICE_RECOVER_WAL      (ODBQL_NOTICE | (1<<8))
+#define ODBQL_NOTICE_RECOVER_ROLLBACK (ODBQL_NOTICE | (2<<8))
+#define ODBQL_WARNING_AUTOINDEX       (ODBQL_WARNING | (1<<8))
+#define ODBQL_AUTH_USER               (ODBQL_AUTH | (1<<8))
+
+/*
+** CAPI3REF: Flags For File Open Operations
+**
+** These bit values are intended for use in the
+** 3rd parameter to the [odbql_open_v2()] interface and
+** in the 4th parameter to the [odbql_vfs.xOpen] method.
+*/
+#define ODBQL_OPEN_READONLY         0x00000001  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_READWRITE        0x00000002  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_CREATE           0x00000004  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_DELETEONCLOSE    0x00000008  /* VFS only */
+#define ODBQL_OPEN_EXCLUSIVE        0x00000010  /* VFS only */
+#define ODBQL_OPEN_AUTOPROXY        0x00000020  /* VFS only */
+#define ODBQL_OPEN_URI              0x00000040  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_MEMORY           0x00000080  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_MAIN_DB          0x00000100  /* VFS only */
+#define ODBQL_OPEN_TEMP_DB          0x00000200  /* VFS only */
+#define ODBQL_OPEN_TRANSIENT_DB     0x00000400  /* VFS only */
+#define ODBQL_OPEN_MAIN_JOURNAL     0x00000800  /* VFS only */
+#define ODBQL_OPEN_TEMP_JOURNAL     0x00001000  /* VFS only */
+#define ODBQL_OPEN_SUBJOURNAL       0x00002000  /* VFS only */
+#define ODBQL_OPEN_MASTER_JOURNAL   0x00004000  /* VFS only */
+#define ODBQL_OPEN_NOMUTEX          0x00008000  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_FULLMUTEX        0x00010000  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_SHAREDCACHE      0x00020000  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_PRIVATECACHE     0x00040000  /* Ok for odbql_open_v2() */
+#define ODBQL_OPEN_WAL              0x00080000  /* VFS only */
+
+/* Reserved:                         0x00F00000 */
+
+/*
+** CAPI3REF: Device Characteristics
+**
+** The xDeviceCharacteristics method of the [odbql_io_methods]
+** object returns an integer which is a vector of these
+** bit values expressing I/O characteristics of the mass storage
+** device that holds the file that the [odbql_io_methods]
+** refers to.
+**
+** The ODBQL_IOCAP_ATOMIC property means that all writes of
+** any size are atomic.  The ODBQL_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic.  The ODBQL_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around.  The ODBQL_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().  The ODBQL_IOCAP_POWERSAFE_OVERWRITE property means that
+** after reboot following a crash or power loss, the only bytes in a
+** file that were written at the application level might have changed
+** and that adjacent bytes, even bytes within the same sector are
+** guaranteed to be unchanged.  The ODBQL_IOCAP_UNDELETABLE_WHEN_OPEN
+** flag indicate that a file cannot be deleted when open.  The
+** ODBQL_IOCAP_IMMUTABLE flag indicates that the file is on
+** read-only media and cannot be changed even by processes with
+** elevated privileges.
+*/
+#define ODBQL_IOCAP_ATOMIC                 0x00000001
+#define ODBQL_IOCAP_ATOMIC512              0x00000002
+#define ODBQL_IOCAP_ATOMIC1K               0x00000004
+#define ODBQL_IOCAP_ATOMIC2K               0x00000008
+#define ODBQL_IOCAP_ATOMIC4K               0x00000010
+#define ODBQL_IOCAP_ATOMIC8K               0x00000020
+#define ODBQL_IOCAP_ATOMIC16K              0x00000040
+#define ODBQL_IOCAP_ATOMIC32K              0x00000080
+#define ODBQL_IOCAP_ATOMIC64K              0x00000100
+#define ODBQL_IOCAP_SAFE_APPEND            0x00000200
+#define ODBQL_IOCAP_SEQUENTIAL             0x00000400
+#define ODBQL_IOCAP_UNDELETABLE_WHEN_OPEN  0x00000800
+#define ODBQL_IOCAP_POWERSAFE_OVERWRITE    0x00001000
+#define ODBQL_IOCAP_IMMUTABLE              0x00002000
+
+/*
+** CAPI3REF: File Locking Levels
+**
+** SQLite uses one of these integer values as the second
+** argument to calls it makes to the xLock() and xUnlock() methods
+** of an [odbql_io_methods] object.
+*/
+#define ODBQL_LOCK_NONE          0
+#define ODBQL_LOCK_SHARED        1
+#define ODBQL_LOCK_RESERVED      2
+#define ODBQL_LOCK_PENDING       3
+#define ODBQL_LOCK_EXCLUSIVE     4
+
+/*
+** CAPI3REF: Synchronization Type Flags
+**
+** When SQLite invokes the xSync() method of an
+** [odbql_io_methods] object it uses a combination of
+** these integer values as the second argument.
+**
+** When the ODBQL_SYNC_DATAONLY flag is used, it means that the
+** sync operation only needs to flush data to mass storage.  Inode
+** information need not be flushed. If the lower four bits of the flag
+** equal ODBQL_SYNC_NORMAL, that means to use normal fsync() semantics.
+** If the lower four bits equal ODBQL_SYNC_FULL, that means
+** to use Mac OS X style fullsync instead of fsync().
+**
+** Do not confuse the ODBQL_SYNC_NORMAL and ODBQL_SYNC_FULL flags
+** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL
+** settings.  The [synchronous pragma] determines when calls to the
+** xSync VFS method occur and applies uniformly across all platforms.
+** The ODBQL_SYNC_NORMAL and ODBQL_SYNC_FULL flags determine how
+** energetic or rigorous or forceful the sync operations are and
+** only make a difference on Mac OSX for the default SQLite code.
+** (Third-party VFS implementations might also make the distinction
+** between ODBQL_SYNC_NORMAL and ODBQL_SYNC_FULL, but among the
+** operating systems natively supported by SQLite, only Mac OSX
+** cares about the difference.)
+*/
+#define ODBQL_SYNC_NORMAL        0x00002
+#define ODBQL_SYNC_FULL          0x00003
+#define ODBQL_SYNC_DATAONLY      0x00010
+
+/*
+** CAPI3REF: OS Interface Open File Handle
+**
+** An [odbql_file] object represents an open file in the 
+** [odbql_vfs | OS interface layer].  Individual OS interface
+** implementations will
+** want to subclass this object by appending additional fields
+** for their own use.  The pMethods entry is a pointer to an
+** [odbql_io_methods] object that defines methods for performing
+** I/O operations on the open file.
+*/
+typedef struct odbql_file odbql_file;
+struct odbql_file {
+  const struct odbql_io_methods *pMethods;  /* Methods for an open file */
+};
+
+/*
+** CAPI3REF: OS Interface File Virtual Methods Object
+**
+** Every file opened by the [odbql_vfs.xOpen] method populates an
+** [odbql_file] object (or, more commonly, a subclass of the
+** [odbql_file] object) with a pointer to an instance of this object.
+** This object defines the methods used to perform various operations
+** against the open file represented by the [odbql_file] object.
+**
+** If the [odbql_vfs.xOpen] method sets the odbql_file.pMethods element 
+** to a non-NULL pointer, then the odbql_io_methods.xClose method
+** may be invoked even if the [odbql_vfs.xOpen] reported that it failed.  The
+** only way to prevent a call to xClose following a failed [odbql_vfs.xOpen]
+** is for the [odbql_vfs.xOpen] to set the odbql_file.pMethods element
+** to NULL.
+**
+** The flags argument to xSync may be one of [ODBQL_SYNC_NORMAL] or
+** [ODBQL_SYNC_FULL].  The first choice is the normal fsync().
+** The second choice is a Mac OS X style fullsync.  The [ODBQL_SYNC_DATAONLY]
+** flag may be ORed in to indicate that only the data of the file
+** and not its inode needs to be synced.
+**
+** The integer values to xLock() and xUnlock() are one of
+** <ul>
+** <li> [ODBQL_LOCK_NONE],
+** <li> [ODBQL_LOCK_SHARED],
+** <li> [ODBQL_LOCK_RESERVED],
+** <li> [ODBQL_LOCK_PENDING], or
+** <li> [ODBQL_LOCK_EXCLUSIVE].
+** </ul>
+** xLock() increases the lock. xUnlock() decreases the lock.
+** The xCheckReservedLock() method checks whether any database connection,
+** either in this process or in some other process, is holding a RESERVED,
+** PENDING, or EXCLUSIVE lock on the file.  It returns true
+** if such a lock exists and false otherwise.
+**
+** The xFileControl() method is a generic interface that allows custom
+** VFS implementations to directly control an open file using the
+** [odbql_file_control()] interface.  The second "op" argument is an
+** integer opcode.  The third argument is a generic pointer intended to
+** point to a structure that may contain arguments or space in which to
+** write return values.  Potential uses for xFileControl() might be
+** functions to enable blocking locks with timeouts, to change the
+** locking strategy (for example to use dot-file locks), to inquire
+** about the status of a lock, or to break stale locks.  The SQLite
+** core reserves all opcodes less than 100 for its own use.
+** A [file control opcodes | list of opcodes] less than 100 is available.
+** Applications that define a custom xFileControl method should use opcodes
+** greater than 100 to avoid conflicts.  VFS implementations should
+** return [ODBQL_NOTFOUND] for file control opcodes that they do not
+** recognize.
+**
+** The xSectorSize() method returns the sector size of the
+** device that underlies the file.  The sector size is the
+** minimum write that can be performed without disturbing
+** other bytes in the file.  The xDeviceCharacteristics()
+** method returns a bit vector describing behaviors of the
+** underlying device:
+**
+** <ul>
+** <li> [ODBQL_IOCAP_ATOMIC]
+** <li> [ODBQL_IOCAP_ATOMIC512]
+** <li> [ODBQL_IOCAP_ATOMIC1K]
+** <li> [ODBQL_IOCAP_ATOMIC2K]
+** <li> [ODBQL_IOCAP_ATOMIC4K]
+** <li> [ODBQL_IOCAP_ATOMIC8K]
+** <li> [ODBQL_IOCAP_ATOMIC16K]
+** <li> [ODBQL_IOCAP_ATOMIC32K]
+** <li> [ODBQL_IOCAP_ATOMIC64K]
+** <li> [ODBQL_IOCAP_SAFE_APPEND]
+** <li> [ODBQL_IOCAP_SEQUENTIAL]
+** </ul>
+**
+** The ODBQL_IOCAP_ATOMIC property means that all writes of
+** any size are atomic.  The ODBQL_IOCAP_ATOMICnnn values
+** mean that writes of blocks that are nnn bytes in size and
+** are aligned to an address which is an integer multiple of
+** nnn are atomic.  The ODBQL_IOCAP_SAFE_APPEND value means
+** that when data is appended to a file, the data is appended
+** first then the size of the file is extended, never the other
+** way around.  The ODBQL_IOCAP_SEQUENTIAL property means that
+** information is written to disk in the same order as calls
+** to xWrite().
+**
+** If xRead() returns ODBQL_IOERR_SHORT_READ it must also fill
+** in the unread portions of the buffer with zeros.  A VFS that
+** fails to zero-fill short reads might seem to work.  However,
+** failure to zero-fill short reads will eventually lead to
+** database corruption.
+*/
+typedef struct odbql_io_methods odbql_io_methods;
+struct odbql_io_methods {
+  int iVersion;
+  int (*xClose)(odbql_file*);
+  int (*xRead)(odbql_file*, void*, int iAmt, odbql_int64 iOfst);
+  int (*xWrite)(odbql_file*, const void*, int iAmt, odbql_int64 iOfst);
+  int (*xTruncate)(odbql_file*, odbql_int64 size);
+  int (*xSync)(odbql_file*, int flags);
+  int (*xFileSize)(odbql_file*, odbql_int64 *pSize);
+  int (*xLock)(odbql_file*, int);
+  int (*xUnlock)(odbql_file*, int);
+  int (*xCheckReservedLock)(odbql_file*, int *pResOut);
+  int (*xFileControl)(odbql_file*, int op, void *pArg);
+  int (*xSectorSize)(odbql_file*);
+  int (*xDeviceCharacteristics)(odbql_file*);
+  /* Methods above are valid for version 1 */
+  int (*xShmMap)(odbql_file*, int iPg, int pgsz, int, void volatile**);
+  int (*xShmLock)(odbql_file*, int offset, int n, int flags);
+  void (*xShmBarrier)(odbql_file*);
+  int (*xShmUnmap)(odbql_file*, int deleteFlag);
+  /* Methods above are valid for version 2 */
+  int (*xFetch)(odbql_file*, odbql_int64 iOfst, int iAmt, void **pp);
+  int (*xUnfetch)(odbql_file*, odbql_int64 iOfst, void *p);
+  /* Methods above are valid for version 3 */
+  /* Additional methods may be added in future releases */
+};
+
+/*
+** CAPI3REF: Standard File Control Opcodes
+** KEYWORDS: {file control opcodes} {file control opcode}
+**
+** These integer constants are opcodes for the xFileControl method
+** of the [odbql_io_methods] object and for the [odbql_file_control()]
+** interface.
+**
+** <ul>
+** <li>[[ODBQL_FCNTL_LOCKSTATE]]
+** The [ODBQL_FCNTL_LOCKSTATE] opcode is used for debugging.  This
+** opcode causes the xFileControl method to write the current state of
+** the lock (one of [ODBQL_LOCK_NONE], [ODBQL_LOCK_SHARED],
+** [ODBQL_LOCK_RESERVED], [ODBQL_LOCK_PENDING], or [ODBQL_LOCK_EXCLUSIVE])
+** into an integer that the pArg argument points to. This capability
+** is used during testing and is only available when the ODBQL_TEST
+** compile-time option is used.
+**
+** <li>[[ODBQL_FCNTL_SIZE_HINT]]
+** The [ODBQL_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
+** layer a hint of how large the database file will grow to be during the
+** current transaction.  This hint is not guaranteed to be accurate but it
+** is often close.  The underlying VFS might choose to preallocate database
+** file space based on this hint in order to help writes to the database
+** file run faster.
+**
+** <li>[[ODBQL_FCNTL_CHUNK_SIZE]]
+** The [ODBQL_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
+** extends and truncates the database file in chunks of a size specified
+** by the user. The fourth argument to [odbql_file_control()] should 
+** point to an integer (type int) containing the new chunk-size to use
+** for the nominated database. Allocating database file space in large
+** chunks (say 1MB at a time), may reduce file-system fragmentation and
+** improve performance on some systems.
+**
+** <li>[[ODBQL_FCNTL_FILE_POINTER]]
+** The [ODBQL_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
+** to the [odbql_file] object associated with a particular database
+** connection.  See also [ODBQL_FCNTL_JOURNAL_POINTER].
+**
+** <li>[[ODBQL_FCNTL_JOURNAL_POINTER]]
+** The [ODBQL_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
+** to the [odbql_file] object associated with the journal file (either
+** the [rollback journal] or the [write-ahead log]) for a particular database
+** connection.  See also [ODBQL_FCNTL_FILE_POINTER].
+**
+** <li>[[ODBQL_FCNTL_SYNC_OMITTED]]
+** No longer in use.
+**
+** <li>[[ODBQL_FCNTL_SYNC]]
+** The [ODBQL_FCNTL_SYNC] opcode is generated internally by SQLite and
+** sent to the VFS immediately before the xSync method is invoked on a
+** database file descriptor. Or, if the xSync method is not invoked 
+** because the user has configured SQLite with 
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place 
+** of the xSync method. In most cases, the pointer argument passed with
+** this file-control is NULL. However, if the database file is being synced
+** as part of a multi-database commit, the argument points to a nul-terminated
+** string containing the transactions master-journal file name. VFSes that 
+** do not need this signal should silently ignore this opcode. Applications 
+** should not call [odbql_file_control()] with this opcode as doing so may 
+** disrupt the operation of the specialized VFSes that do require it.  
+**
+** <li>[[ODBQL_FCNTL_COMMIT_PHASETWO]]
+** The [ODBQL_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
+** and sent to the VFS after a transaction has been committed immediately
+** but before the database is unlocked. VFSes that do not need this signal
+** should silently ignore this opcode. Applications should not call
+** [odbql_file_control()] with this opcode as doing so may disrupt the 
+** operation of the specialized VFSes that do require it.  
+**
+** <li>[[ODBQL_FCNTL_WIN32_AV_RETRY]]
+** ^The [ODBQL_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
+** retry counts and intervals for certain disk I/O operations for the
+** windows [VFS] in order to provide robustness in the presence of
+** anti-virus programs.  By default, the windows VFS will retry file read,
+** file write, and file delete operations up to 10 times, with a delay
+** of 25 milliseconds before the first retry and with the delay increasing
+** by an additional 25 milliseconds with each subsequent retry.  This
+** opcode allows these two values (10 retries and 25 milliseconds of delay)
+** to be adjusted.  The values are changed for all database connections
+** within the same process.  The argument is a pointer to an array of two
+** integers where the first integer i the new retry count and the second
+** integer is the delay.  If either integer is negative, then the setting
+** is not changed but instead the prior value of that setting is written
+** into the array entry, allowing the current retry settings to be
+** interrogated.  The zDbName parameter is ignored.
+**
+** <li>[[ODBQL_FCNTL_PERSIST_WAL]]
+** ^The [ODBQL_FCNTL_PERSIST_WAL] opcode is used to set or query the
+** persistent [WAL | Write Ahead Log] setting.  By default, the auxiliary
+** write ahead log and shared memory files used for transaction control
+** are automatically deleted when the latest connection to the database
+** closes.  Setting persistent WAL mode causes those files to persist after
+** close.  Persisting the files is useful when other processes that do not
+** have write permission on the directory containing the database file want
+** to read the database file, as the WAL and shared memory files must exist
+** in order for the database to be readable.  The fourth parameter to
+** [odbql_file_control()] for this opcode should be a pointer to an integer.
+** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
+** WAL mode.  If the integer is -1, then it is overwritten with the current
+** WAL persistence setting.
+**
+** <li>[[ODBQL_FCNTL_POWERSAFE_OVERWRITE]]
+** ^The [ODBQL_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
+** persistent "powersafe-overwrite" or "PSOW" setting.  The PSOW setting
+** determines the [ODBQL_IOCAP_POWERSAFE_OVERWRITE] bit of the
+** xDeviceCharacteristics methods. The fourth parameter to
+** [odbql_file_control()] for this opcode should be a pointer to an integer.
+** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
+** mode.  If the integer is -1, then it is overwritten with the current
+** zero-damage mode setting.
+**
+** <li>[[ODBQL_FCNTL_OVERWRITE]]
+** ^The [ODBQL_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
+** a write transaction to indicate that, unless it is rolled back for some
+** reason, the entire database file will be overwritten by the current 
+** transaction. This is used by VACUUM operations.
+**
+** <li>[[ODBQL_FCNTL_VFSNAME]]
+** ^The [ODBQL_FCNTL_VFSNAME] opcode can be used to obtain the names of
+** all [VFSes] in the VFS stack.  The names are of all VFS shims and the
+** final bottom-level VFS are written into memory obtained from 
+** [odbql_malloc()] and the result is stored in the char* variable
+** that the fourth parameter of [odbql_file_control()] points to.
+** The caller is responsible for freeing the memory when done.  As with
+** all file-control actions, there is no guarantee that this will actually
+** do anything.  Callers should initialize the char* variable to a NULL
+** pointer in case this file-control is not implemented.  This file-control
+** is intended for diagnostic use only.
+**
+** <li>[[ODBQL_FCNTL_VFS_POINTER]]
+** ^The [ODBQL_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
+** [VFSes] currently in use.  ^(The argument X in
+** odbql_file_control(db,ODBQL_FCNTL_VFS_POINTER,X) must be
+** of type "[odbql_vfs] **".  This opcodes will set *X
+** to a pointer to the top-level VFS.)^
+** ^When there are multiple VFS shims in the stack, this opcode finds the
+** upper-most shim only.
+**
+** <li>[[ODBQL_FCNTL_PRAGMA]]
+** ^Whenever a [PRAGMA] statement is parsed, an [ODBQL_FCNTL_PRAGMA] 
+** file control is sent to the open [odbql_file] object corresponding
+** to the database file to which the pragma statement refers. ^The argument
+** to the [ODBQL_FCNTL_PRAGMA] file control is an array of
+** pointers to strings (char**) in which the second element of the array
+** is the name of the pragma and the third element is the argument to the
+** pragma or NULL if the pragma has no argument.  ^The handler for an
+** [ODBQL_FCNTL_PRAGMA] file control can optionally make the first element
+** of the char** argument point to a string obtained from [odbql_mprintf()]
+** or the equivalent and that string will become the result of the pragma or
+** the error message if the pragma fails. ^If the
+** [ODBQL_FCNTL_PRAGMA] file control returns [ODBQL_NOTFOUND], then normal 
+** [PRAGMA] processing continues.  ^If the [ODBQL_FCNTL_PRAGMA]
+** file control returns [ODBQL_OK], then the parser assumes that the
+** VFS has handled the PRAGMA itself and the parser generates a no-op
+** prepared statement if result string is NULL, or that returns a copy
+** of the result string if the string is non-NULL.
+** ^If the [ODBQL_FCNTL_PRAGMA] file control returns
+** any result code other than [ODBQL_OK] or [ODBQL_NOTFOUND], that means
+** that the VFS encountered an error while handling the [PRAGMA] and the
+** compilation of the PRAGMA fails with an error.  ^The [ODBQL_FCNTL_PRAGMA]
+** file control occurs at the beginning of pragma statement analysis and so
+** it is able to override built-in [PRAGMA] statements.
+**
+** <li>[[ODBQL_FCNTL_BUSYHANDLER]]
+** ^The [ODBQL_FCNTL_BUSYHANDLER]
+** file-control may be invoked by SQLite on the database file handle
+** shortly after it is opened in order to provide a custom VFS with access
+** to the connections busy-handler callback. The argument is of type (void **)
+** - an array of two (void *) values. The first (void *) actually points
+** to a function of type (int (*)(void *)). In order to invoke the connections
+** busy-handler, this function should be invoked with the second (void *) in
+** the array as the only argument. If it returns non-zero, then the operation
+** should be retried. If it returns zero, the custom VFS should abandon the
+** current operation.
+**
+** <li>[[ODBQL_FCNTL_TEMPFILENAME]]
+** ^Application can invoke the [ODBQL_FCNTL_TEMPFILENAME] file-control
+** to have SQLite generate a
+** temporary filename using the same algorithm that is followed to generate
+** temporary filenames for TEMP tables and other internal uses.  The
+** argument should be a char** which will be filled with the filename
+** written into memory obtained from [odbql_malloc()].  The caller should
+** invoke [odbql_free()] on the result to avoid a memory leak.
+**
+** <li>[[ODBQL_FCNTL_MMAP_SIZE]]
+** The [ODBQL_FCNTL_MMAP_SIZE] file control is used to query or set the
+** maximum number of bytes that will be used for memory-mapped I/O.
+** The argument is a pointer to a value of type odbql_int64 that
+** is an advisory maximum number of bytes in the file to memory map.  The
+** pointer is overwritten with the old value.  The limit is not changed if
+** the value originally pointed to is negative, and so the current limit 
+** can be queried by passing in a pointer to a negative number.  This
+** file-control is used internally to implement [PRAGMA mmap_size].
+**
+** <li>[[ODBQL_FCNTL_TRACE]]
+** The [ODBQL_FCNTL_TRACE] file control provides advisory information
+** to the VFS about what the higher layers of the SQLite stack are doing.
+** This file control is used by some VFS activity tracing [shims].
+** The argument is a zero-terminated string.  Higher layers in the
+** SQLite stack may generate instances of this file control if
+** the [ODBQL_USE_FCNTL_TRACE] compile-time option is enabled.
+**
+** <li>[[ODBQL_FCNTL_HAS_MOVED]]
+** The [ODBQL_FCNTL_HAS_MOVED] file control interprets its argument as a
+** pointer to an integer and it writes a boolean into that integer depending
+** on whether or not the file has been renamed, moved, or deleted since it
+** was first opened.
+**
+** <li>[[ODBQL_FCNTL_WIN32_SET_HANDLE]]
+** The [ODBQL_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging.  This
+** opcode causes the xFileControl method to swap the file handle with the one
+** pointed to by the pArg argument.  This capability is used during testing
+** and only needs to be supported when ODBQL_TEST is defined.
+**
+** <li>[[ODBQL_FCNTL_WAL_BLOCK]]
+** The [ODBQL_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
+** be advantageous to block on the next WAL lock if the lock is not immediately
+** available.  The WAL subsystem issues this signal during rare
+** circumstances in order to fix a problem with priority inversion.
+** Applications should <em>not</em> use this file-control.
+**
+** <li>[[ODBQL_FCNTL_ZIPVFS]]
+** The [ODBQL_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
+** VFS should return ODBQL_NOTFOUND for this opcode.
+**
+** <li>[[ODBQL_FCNTL_RBU]]
+** The [ODBQL_FCNTL_RBU] opcode is implemented by the special VFS used by
+** the RBU extension only.  All other VFS should return ODBQL_NOTFOUND for
+** this opcode.  
+** </ul>
+*/
+#define ODBQL_FCNTL_LOCKSTATE               1
+#define ODBQL_FCNTL_GET_LOCKPROXYFILE       2
+#define ODBQL_FCNTL_SET_LOCKPROXYFILE       3
+#define ODBQL_FCNTL_LAST_ERRNO              4
+#define ODBQL_FCNTL_SIZE_HINT               5
+#define ODBQL_FCNTL_CHUNK_SIZE              6
+#define ODBQL_FCNTL_FILE_POINTER            7
+#define ODBQL_FCNTL_SYNC_OMITTED            8
+#define ODBQL_FCNTL_WIN32_AV_RETRY          9
+#define ODBQL_FCNTL_PERSIST_WAL            10
+#define ODBQL_FCNTL_OVERWRITE              11
+#define ODBQL_FCNTL_VFSNAME                12
+#define ODBQL_FCNTL_POWERSAFE_OVERWRITE    13
+#define ODBQL_FCNTL_PRAGMA                 14
+#define ODBQL_FCNTL_BUSYHANDLER            15
+#define ODBQL_FCNTL_TEMPFILENAME           16
+#define ODBQL_FCNTL_MMAP_SIZE              18
+#define ODBQL_FCNTL_TRACE                  19
+#define ODBQL_FCNTL_HAS_MOVED              20
+#define ODBQL_FCNTL_SYNC                   21
+#define ODBQL_FCNTL_COMMIT_PHASETWO        22
+#define ODBQL_FCNTL_WIN32_SET_HANDLE       23
+#define ODBQL_FCNTL_WAL_BLOCK              24
+#define ODBQL_FCNTL_ZIPVFS                 25
+#define ODBQL_FCNTL_RBU                    26
+#define ODBQL_FCNTL_VFS_POINTER            27
+#define ODBQL_FCNTL_JOURNAL_POINTER        28
+
+/* deprecated names */
+#define ODBQL_GET_LOCKPROXYFILE      ODBQL_FCNTL_GET_LOCKPROXYFILE
+#define ODBQL_SET_LOCKPROXYFILE      ODBQL_FCNTL_SET_LOCKPROXYFILE
+#define ODBQL_LAST_ERRNO             ODBQL_FCNTL_LAST_ERRNO
+
+
+/*
+** CAPI3REF: Mutex Handle
+**
+** The mutex module within SQLite defines [odbql_mutex] to be an
+** abstract type for a mutex object.  The SQLite core never looks
+** at the internal representation of an [odbql_mutex].  It only
+** deals with pointers to the [odbql_mutex] object.
+**
+** Mutexes are created using [odbql_mutex_alloc()].
+*/
+typedef struct odbql_mutex odbql_mutex;
+
+/*
+** CAPI3REF: OS Interface Object
+**
+** An instance of the odbql_vfs object defines the interface between
+** the SQLite core and the underlying operating system.  The "vfs"
+** in the name of the object stands for "virtual file system".  See
+** the [VFS | VFS documentation] for further information.
+**
+** The value of the iVersion field is initially 1 but may be larger in
+** future versions of SQLite.  Additional fields may be appended to this
+** object when the iVersion value is increased.  Note that the structure
+** of the odbql_vfs object changes in the transaction between
+** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not
+** modified.
+**
+** The szOsFile field is the size of the subclassed [odbql_file]
+** structure used by this VFS.  mxPathname is the maximum length of
+** a pathname in this VFS.
+**
+** Registered odbql_vfs objects are kept on a linked list formed by
+** the pNext pointer.  The [odbql_vfs_register()]
+** and [odbql_vfs_unregister()] interfaces manage this list
+** in a thread-safe way.  The [odbql_vfs_find()] interface
+** searches the list.  Neither the application code nor the VFS
+** implementation should use the pNext pointer.
+**
+** The pNext field is the only field in the odbql_vfs
+** structure that SQLite will ever modify.  SQLite will only access
+** or modify this field while holding a particular static mutex.
+** The application should never modify anything within the odbql_vfs
+** object once the object has been registered.
+**
+** The zName field holds the name of the VFS module.  The name must
+** be unique across all VFS modules.
+**
+** [[odbql_vfs.xOpen]]
+** ^SQLite guarantees that the zFilename parameter to xOpen
+** is either a NULL pointer or string obtained
+** from xFullPathname() with an optional suffix added.
+** ^If a suffix is added to the zFilename parameter, it will
+** consist of a single "-" character followed by no more than
+** 11 alphanumeric and/or "-" characters.
+** ^SQLite further guarantees that
+** the string will be valid and unchanged until xClose() is
+** called. Because of the previous sentence,
+** the [odbql_file] can safely store a pointer to the
+** filename if it needs to remember the filename for some reason.
+** If the zFilename parameter to xOpen is a NULL pointer then xOpen
+** must invent its own temporary name for the file.  ^Whenever the 
+** xFilename parameter is NULL it will also be the case that the
+** flags parameter will include [ODBQL_OPEN_DELETEONCLOSE].
+**
+** The flags argument to xOpen() includes all bits set in
+** the flags argument to [odbql_open_v2()].  Or if [odbql_open()]
+** or [odbql_open16()] is used, then flags includes at least
+** [ODBQL_OPEN_READWRITE] | [ODBQL_OPEN_CREATE]. 
+** If xOpen() opens a file read-only then it sets *pOutFlags to
+** include [ODBQL_OPEN_READONLY].  Other bits in *pOutFlags may be set.
+**
+** ^(SQLite will also add one of the following flags to the xOpen()
+** call, depending on the object being opened:
+**
+** <ul>
+** <li>  [ODBQL_OPEN_MAIN_DB]
+** <li>  [ODBQL_OPEN_MAIN_JOURNAL]
+** <li>  [ODBQL_OPEN_TEMP_DB]
+** <li>  [ODBQL_OPEN_TEMP_JOURNAL]
+** <li>  [ODBQL_OPEN_TRANSIENT_DB]
+** <li>  [ODBQL_OPEN_SUBJOURNAL]
+** <li>  [ODBQL_OPEN_MASTER_JOURNAL]
+** <li>  [ODBQL_OPEN_WAL]
+** </ul>)^
+**
+** The file I/O implementation can use the object type flags to
+** change the way it deals with files.  For example, an application
+** that does not care about crash recovery or rollback might make
+** the open of a journal file a no-op.  Writes to this journal would
+** also be no-ops, and any attempt to read the journal would return
+** ODBQL_IOERR.  Or the implementation might recognize that a database
+** file will be doing page-aligned sector reads and writes in a random
+** order and set up its I/O subsystem accordingly.
+**
+** SQLite might also add one of the following flags to the xOpen method:
+**
+** <ul>
+** <li> [ODBQL_OPEN_DELETEONCLOSE]
+** <li> [ODBQL_OPEN_EXCLUSIVE]
+** </ul>
+**
+** The [ODBQL_OPEN_DELETEONCLOSE] flag means the file should be
+** deleted when it is closed.  ^The [ODBQL_OPEN_DELETEONCLOSE]
+** will be set for TEMP databases and their journals, transient
+** databases, and subjournals.
+**
+** ^The [ODBQL_OPEN_EXCLUSIVE] flag is always used in conjunction
+** with the [ODBQL_OPEN_CREATE] flag, which are both directly
+** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
+** API.  The ODBQL_OPEN_EXCLUSIVE flag, when paired with the 
+** ODBQL_OPEN_CREATE, is used to indicate that file should always
+** be created, and that it is an error if it already exists.
+** It is <i>not</i> used to indicate the file should be opened 
+** for exclusive access.
+**
+** ^At least szOsFile bytes of memory are allocated by SQLite
+** to hold the  [odbql_file] structure passed as the third
+** argument to xOpen.  The xOpen method does not have to
+** allocate the structure; it should just fill it in.  Note that
+** the xOpen method must set the odbql_file.pMethods to either
+** a valid [odbql_io_methods] object or to NULL.  xOpen must do
+** this even if the open fails.  SQLite expects that the odbql_file.pMethods
+** element will be valid after xOpen returns regardless of the success
+** or failure of the xOpen call.
+**
+** [[odbql_vfs.xAccess]]
+** ^The flags argument to xAccess() may be [ODBQL_ACCESS_EXISTS]
+** to test for the existence of a file, or [ODBQL_ACCESS_READWRITE] to
+** test whether a file is readable and writable, or [ODBQL_ACCESS_READ]
+** to test whether a file is at least readable.   The file can be a
+** directory.
+**
+** ^SQLite will always allocate at least mxPathname+1 bytes for the
+** output buffer xFullPathname.  The exact size of the output buffer
+** is also passed as a parameter to both  methods. If the output buffer
+** is not large enough, [ODBQL_CANTOPEN] should be returned. Since this is
+** handled as a fatal error by SQLite, vfs implementations should endeavor
+** to prevent this by setting mxPathname to a sufficiently large value.
+**
+** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64()
+** interfaces are not strictly a part of the filesystem, but they are
+** included in the VFS structure for completeness.
+** The xRandomness() function attempts to return nBytes bytes
+** of good-quality randomness into zOut.  The return value is
+** the actual number of bytes of randomness obtained.
+** The xSleep() method causes the calling thread to sleep for at
+** least the number of microseconds given.  ^The xCurrentTime()
+** method returns a Julian Day Number for the current date and time as
+** a floating point value.
+** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
+** Day Number multiplied by 86400000 (the number of milliseconds in 
+** a 24-hour day).  
+** ^SQLite will use the xCurrentTimeInt64() method to get the current
+** date and time if that method is available (if iVersion is 2 or 
+** greater and the function pointer is not NULL) and will fall back
+** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
+**
+** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
+** are not used by the SQLite core.  These optional interfaces are provided
+** by some VFSes to facilitate testing of the VFS code. By overriding 
+** system calls with functions under its control, a test program can
+** simulate faults and error conditions that would otherwise be difficult
+** or impossible to induce.  The set of system calls that can be overridden
+** varies from one VFS to another, and from one version of the same VFS to the
+** next.  Applications that use these interfaces must be prepared for any
+** or all of these interfaces to be NULL or for their behavior to change
+** from one release to the next.  Applications must not attempt to access
+** any of these methods if the iVersion of the VFS is less than 3.
+*/
+typedef struct odbql_vfs odbql_vfs;
+typedef void (*odbql_syscall_ptr)(void);
+struct odbql_vfs {
+  int iVersion;            /* Structure version number (currently 3) */
+  int szOsFile;            /* Size of subclassed odbql_file */
+  int mxPathname;          /* Maximum file pathname length */
+  odbql_vfs *pNext;      /* Next registered VFS */
+  const char *zName;       /* Name of this virtual file system */
+  void *pAppData;          /* Pointer to application-specific data */
+  int (*xOpen)(odbql_vfs*, const char *zName, odbql_file*,
+               int flags, int *pOutFlags);
+  int (*xDelete)(odbql_vfs*, const char *zName, int syncDir);
+  int (*xAccess)(odbql_vfs*, const char *zName, int flags, int *pResOut);
+  int (*xFullPathname)(odbql_vfs*, const char *zName, int nOut, char *zOut);
+  void *(*xDlOpen)(odbql_vfs*, const char *zFilename);
+  void (*xDlError)(odbql_vfs*, int nByte, char *zErrMsg);
+  void (*(*xDlSym)(odbql_vfs*,void*, const char *zSymbol))(void);
+  void (*xDlClose)(odbql_vfs*, void*);
+  int (*xRandomness)(odbql_vfs*, int nByte, char *zOut);
+  int (*xSleep)(odbql_vfs*, int microseconds);
+  int (*xCurrentTime)(odbql_vfs*, double*);
+  int (*xGetLastError)(odbql_vfs*, int, char *);
+  /*
+  ** The methods above are in version 1 of the sqlite_vfs object
+  ** definition.  Those that follow are added in version 2 or later
+  */
+  int (*xCurrentTimeInt64)(odbql_vfs*, odbql_int64*);
+  /*
+  ** The methods above are in versions 1 and 2 of the sqlite_vfs object.
+  ** Those below are for version 3 and greater.
+  */
+  int (*xSetSystemCall)(odbql_vfs*, const char *zName, odbql_syscall_ptr);
+  odbql_syscall_ptr (*xGetSystemCall)(odbql_vfs*, const char *zName);
+  const char *(*xNextSystemCall)(odbql_vfs*, const char *zName);
+  /*
+  ** The methods above are in versions 1 through 3 of the sqlite_vfs object.
+  ** New fields may be appended in future versions.  The iVersion
+  ** value will increment whenever this happens. 
+  */
+};
+
+/*
+** CAPI3REF: Flags for the xAccess VFS method
+**
+** These integer constants can be used as the third parameter to
+** the xAccess method of an [odbql_vfs] object.  They determine
+** what kind of permissions the xAccess method is looking for.
+** With ODBQL_ACCESS_EXISTS, the xAccess method
+** simply checks whether the file exists.
+** With ODBQL_ACCESS_READWRITE, the xAccess method
+** checks whether the named directory is both readable and writable
+** (in other words, if files can be added, removed, and renamed within
+** the directory).
+** The ODBQL_ACCESS_READWRITE constant is currently used only by the
+** [temp_store_directory pragma], though this could change in a future
+** release of SQLite.
+** With ODBQL_ACCESS_READ, the xAccess method
+** checks whether the file is readable.  The ODBQL_ACCESS_READ constant is
+** currently unused, though it might be used in a future release of
+** SQLite.
+*/
+#define ODBQL_ACCESS_EXISTS    0
+#define ODBQL_ACCESS_READWRITE 1   /* Used by PRAGMA temp_store_directory */
+#define ODBQL_ACCESS_READ      2   /* Unused */
+
+/*
+** CAPI3REF: Flags for the xShmLock VFS method
+**
+** These integer constants define the various locking operations
+** allowed by the xShmLock method of [odbql_io_methods].  The
+** following are the only legal combinations of flags to the
+** xShmLock method:
+**
+** <ul>
+** <li>  ODBQL_SHM_LOCK | ODBQL_SHM_SHARED
+** <li>  ODBQL_SHM_LOCK | ODBQL_SHM_EXCLUSIVE
+** <li>  ODBQL_SHM_UNLOCK | ODBQL_SHM_SHARED
+** <li>  ODBQL_SHM_UNLOCK | ODBQL_SHM_EXCLUSIVE
+** </ul>
+**
+** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
+** was given on the corresponding lock.  
+**
+** The xShmLock method can transition between unlocked and SHARED or
+** between unlocked and EXCLUSIVE.  It cannot transition between SHARED
+** and EXCLUSIVE.
+*/
+#define ODBQL_SHM_UNLOCK       1
+#define ODBQL_SHM_LOCK         2
+#define ODBQL_SHM_SHARED       4
+#define ODBQL_SHM_EXCLUSIVE    8
+
+/*
+** CAPI3REF: Maximum xShmLock index
+**
+** The xShmLock method on [odbql_io_methods] may use values
+** between 0 and this upper bound as its "offset" argument.
+** The SQLite core will never attempt to acquire or release a
+** lock outside of this range
+*/
+#define ODBQL_SHM_NLOCK        8
+
+
+/*
+** CAPI3REF: Initialize The SQLite Library
+**
+** ^The odbql_initialize() routine initializes the
+** SQLite library.  ^The odbql_shutdown() routine
+** deallocates any resources that were allocated by odbql_initialize().
+** These routines are designed to aid in process initialization and
+** shutdown on embedded systems.  Workstation applications using
+** SQLite normally do not need to invoke either of these routines.
+**
+** A call to odbql_initialize() is an "effective" call if it is
+** the first time odbql_initialize() is invoked during the lifetime of
+** the process, or if it is the first time odbql_initialize() is invoked
+** following a call to odbql_shutdown().  ^(Only an effective call
+** of odbql_initialize() does any initialization.  All other calls
+** are harmless no-ops.)^
+**
+** A call to odbql_shutdown() is an "effective" call if it is the first
+** call to odbql_shutdown() since the last odbql_initialize().  ^(Only
+** an effective call to odbql_shutdown() does any deinitialization.
+** All other valid calls to odbql_shutdown() are harmless no-ops.)^
+**
+** The odbql_initialize() interface is threadsafe, but odbql_shutdown()
+** is not.  The odbql_shutdown() interface must only be called from a
+** single thread.  All open [database connections] must be closed and all
+** other SQLite resources must be deallocated prior to invoking
+** odbql_shutdown().
+**
+** Among other things, ^odbql_initialize() will invoke
+** odbql_os_init().  Similarly, ^odbql_shutdown()
+** will invoke odbql_os_end().
+**
+** ^The odbql_initialize() routine returns [ODBQL_OK] on success.
+** ^If for some reason, odbql_initialize() is unable to initialize
+** the library (perhaps it is unable to allocate a needed resource such
+** as a mutex) it returns an [error code] other than [ODBQL_OK].
+**
+** ^The odbql_initialize() routine is called internally by many other
+** SQLite interfaces so that an application usually does not need to
+** invoke odbql_initialize() directly.  For example, [odbql_open()]
+** calls odbql_initialize() so the SQLite library will be automatically
+** initialized when [odbql_open()] is called if it has not be initialized
+** already.  ^However, if SQLite is compiled with the [ODBQL_OMIT_AUTOINIT]
+** compile-time option, then the automatic calls to odbql_initialize()
+** are omitted and the application must call odbql_initialize() directly
+** prior to using any other SQLite interface.  For maximum portability,
+** it is recommended that applications always invoke odbql_initialize()
+** directly prior to using any other SQLite interface.  Future releases
+** of SQLite may require this.  In other words, the behavior exhibited
+** when SQLite is compiled with [ODBQL_OMIT_AUTOINIT] might become the
+** default behavior in some future release of SQLite.
+**
+** The odbql_os_init() routine does operating-system specific
+** initialization of the SQLite library.  The odbql_os_end()
+** routine undoes the effect of odbql_os_init().  Typical tasks
+** performed by these routines include allocation or deallocation
+** of static resources, initialization of global variables,
+** setting up a default [odbql_vfs] module, or setting up
+** a default configuration using [odbql_config()].
+**
+** The application should never invoke either odbql_os_init()
+** or odbql_os_end() directly.  The application should only invoke
+** odbql_initialize() and odbql_shutdown().  The odbql_os_init()
+** interface is called automatically by odbql_initialize() and
+** odbql_os_end() is called by odbql_shutdown().  Appropriate
+** implementations for odbql_os_init() and odbql_os_end()
+** are built into SQLite when it is compiled for Unix, Windows, or OS/2.
+** When [custom builds | built for other platforms]
+** (using the [ODBQL_OS_OTHER=1] compile-time
+** option) the application must supply a suitable implementation for
+** odbql_os_init() and odbql_os_end().  An application-supplied
+** implementation of odbql_os_init() or odbql_os_end()
+** must return [ODBQL_OK] on success and some other [error code] upon
+** failure.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_initialize(void);
+ODBQL_API int ODBQL_STDCALL odbql_shutdown(void);
+ODBQL_API int ODBQL_STDCALL odbql_os_init(void);
+ODBQL_API int ODBQL_STDCALL odbql_os_end(void);
+
+/*
+** CAPI3REF: Configuring The SQLite Library
+**
+** The odbql_config() interface is used to make global configuration
+** changes to SQLite in order to tune SQLite to the specific needs of
+** the application.  The default configuration is recommended for most
+** applications and so this routine is usually not necessary.  It is
+** provided to support rare applications with unusual needs.
+**
+** <b>The odbql_config() interface is not threadsafe. The application
+** must ensure that no other SQLite interfaces are invoked by other
+** threads while odbql_config() is running.</b>
+**
+** The odbql_config() interface
+** may only be invoked prior to library initialization using
+** [odbql_initialize()] or after shutdown by [odbql_shutdown()].
+** ^If odbql_config() is called after [odbql_initialize()] and before
+** [odbql_shutdown()] then it will return ODBQL_MISUSE.
+** Note, however, that ^odbql_config() can be called as part of the
+** implementation of an application-defined [odbql_os_init()].
+**
+** The first argument to odbql_config() is an integer
+** [configuration option] that determines
+** what property of SQLite is to be configured.  Subsequent arguments
+** vary depending on the [configuration option]
+** in the first argument.
+**
+** ^When a configuration option is set, odbql_config() returns [ODBQL_OK].
+** ^If the option is unknown or SQLite is unable to set the option
+** then this routine returns a non-zero [error code].
+*/
+ODBQL_API int ODBQL_CDECL odbql_config(int, ...);
+
+/*
+** CAPI3REF: Configure database connections
+** METHOD: odbql
+**
+** The odbql_db_config() interface is used to make configuration
+** changes to a [database connection].  The interface is similar to
+** [odbql_config()] except that the changes apply to a single
+** [database connection] (specified in the first argument).
+**
+** The second argument to odbql_db_config(D,V,...)  is the
+** [ODBQL_DBCONFIG_LOOKASIDE | configuration verb] - an integer code 
+** that indicates what aspect of the [database connection] is being configured.
+** Subsequent arguments vary depending on the configuration verb.
+**
+** ^Calls to odbql_db_config() return ODBQL_OK if and only if
+** the call is considered successful.
+*/
+ODBQL_API int ODBQL_CDECL odbql_db_config(odbql*, int op, ...);
+
+/*
+** CAPI3REF: Memory Allocation Routines
+**
+** An instance of this object defines the interface between SQLite
+** and low-level memory allocation routines.
+**
+** This object is used in only one place in the SQLite interface.
+** A pointer to an instance of this object is the argument to
+** [odbql_config()] when the configuration option is
+** [ODBQL_CONFIG_MALLOC] or [ODBQL_CONFIG_GETMALLOC].  
+** By creating an instance of this object
+** and passing it to [odbql_config]([ODBQL_CONFIG_MALLOC])
+** during configuration, an application can specify an alternative
+** memory allocation subsystem for SQLite to use for all of its
+** dynamic memory needs.
+**
+** Note that SQLite comes with several [built-in memory allocators]
+** that are perfectly adequate for the overwhelming majority of applications
+** and that this object is only useful to a tiny minority of applications
+** with specialized memory allocation requirements.  This object is
+** also used during testing of SQLite in order to specify an alternative
+** memory allocator that simulates memory out-of-memory conditions in
+** order to verify that SQLite recovers gracefully from such
+** conditions.
+**
+** The xMalloc, xRealloc, and xFree methods must work like the
+** malloc(), realloc() and free() functions from the standard C library.
+** ^SQLite guarantees that the second argument to
+** xRealloc is always a value returned by a prior call to xRoundup.
+**
+** xSize should return the allocated size of a memory allocation
+** previously obtained from xMalloc or xRealloc.  The allocated size
+** is always at least as big as the requested size but may be larger.
+**
+** The xRoundup method returns what would be the allocated size of
+** a memory allocation given a particular requested size.  Most memory
+** allocators round up memory allocations at least to the next multiple
+** of 8.  Some allocators round up to a larger multiple or to a power of 2.
+** Every memory allocation request coming in through [odbql_malloc()]
+** or [odbql_realloc()] first calls xRoundup.  If xRoundup returns 0, 
+** that causes the corresponding memory allocation to fail.
+**
+** The xInit method initializes the memory allocator.  For example,
+** it might allocate any require mutexes or initialize internal data
+** structures.  The xShutdown method is invoked (indirectly) by
+** [odbql_shutdown()] and should deallocate any resources acquired
+** by xInit.  The pAppData pointer is used as the only parameter to
+** xInit and xShutdown.
+**
+** SQLite holds the [ODBQL_MUTEX_STATIC_MASTER] mutex when it invokes
+** the xInit method, so the xInit method need not be threadsafe.  The
+** xShutdown method is only called from [odbql_shutdown()] so it does
+** not need to be threadsafe either.  For all other methods, SQLite
+** holds the [ODBQL_MUTEX_STATIC_MEM] mutex as long as the
+** [ODBQL_CONFIG_MEMSTATUS] configuration option is turned on (which
+** it is by default) and so the methods are automatically serialized.
+** However, if [ODBQL_CONFIG_MEMSTATUS] is disabled, then the other
+** methods must be threadsafe or else make their own arrangements for
+** serialization.
+**
+** SQLite will never invoke xInit() more than once without an intervening
+** call to xShutdown().
+*/
+typedef struct odbql_mem_methods odbql_mem_methods;
+struct odbql_mem_methods {
+  void *(*xMalloc)(int);         /* Memory allocation function */
+  void (*xFree)(void*);          /* Free a prior allocation */
+  void *(*xRealloc)(void*,int);  /* Resize an allocation */
+  int (*xSize)(void*);           /* Return the size of an allocation */
+  int (*xRoundup)(int);          /* Round up request size to allocation size */
+  int (*xInit)(void*);           /* Initialize the memory allocator */
+  void (*xShutdown)(void*);      /* Deinitialize the memory allocator */
+  void *pAppData;                /* Argument to xInit() and xShutdown() */
+};
+
+/*
+** CAPI3REF: Configuration Options
+** KEYWORDS: {configuration option}
+**
+** These constants are the available integer configuration options that
+** can be passed as the first argument to the [odbql_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued.  Applications
+** should check the return code from [odbql_config()] to make sure that
+** the call worked.  The [odbql_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** [[ODBQL_CONFIG_SINGLETHREAD]] <dt>ODBQL_CONFIG_SINGLETHREAD</dt>
+** <dd>There are no arguments to this option.  ^This option sets the
+** [threading mode] to Single-thread.  In other words, it disables
+** all mutexing and puts SQLite into a mode where it can only be used
+** by a single thread.   ^If SQLite is compiled with
+** the [ODBQL_THREADSAFE | ODBQL_THREADSAFE=0] compile-time option then
+** it is not possible to change the [threading mode] from its default
+** value of Single-thread and so [odbql_config()] will return 
+** [ODBQL_ERROR] if called with the ODBQL_CONFIG_SINGLETHREAD
+** configuration option.</dd>
+**
+** [[ODBQL_CONFIG_MULTITHREAD]] <dt>ODBQL_CONFIG_MULTITHREAD</dt>
+** <dd>There are no arguments to this option.  ^This option sets the
+** [threading mode] to Multi-thread.  In other words, it disables
+** mutexing on [database connection] and [prepared statement] objects.
+** The application is responsible for serializing access to
+** [database connections] and [prepared statements].  But other mutexes
+** are enabled so that SQLite will be safe to use in a multi-threaded
+** environment as long as no two threads attempt to use the same
+** [database connection] at the same time.  ^If SQLite is compiled with
+** the [ODBQL_THREADSAFE | ODBQL_THREADSAFE=0] compile-time option then
+** it is not possible to set the Multi-thread [threading mode] and
+** [odbql_config()] will return [ODBQL_ERROR] if called with the
+** ODBQL_CONFIG_MULTITHREAD configuration option.</dd>
+**
+** [[ODBQL_CONFIG_SERIALIZED]] <dt>ODBQL_CONFIG_SERIALIZED</dt>
+** <dd>There are no arguments to this option.  ^This option sets the
+** [threading mode] to Serialized. In other words, this option enables
+** all mutexes including the recursive
+** mutexes on [database connection] and [prepared statement] objects.
+** In this mode (which is the default when SQLite is compiled with
+** [ODBQL_THREADSAFE=1]) the SQLite library will itself serialize access
+** to [database connections] and [prepared statements] so that the
+** application is free to use the same [database connection] or the
+** same [prepared statement] in different threads at the same time.
+** ^If SQLite is compiled with
+** the [ODBQL_THREADSAFE | ODBQL_THREADSAFE=0] compile-time option then
+** it is not possible to set the Serialized [threading mode] and
+** [odbql_config()] will return [ODBQL_ERROR] if called with the
+** ODBQL_CONFIG_SERIALIZED configuration option.</dd>
+**
+** [[ODBQL_CONFIG_MALLOC]] <dt>ODBQL_CONFIG_MALLOC</dt>
+** <dd> ^(The ODBQL_CONFIG_MALLOC option takes a single argument which is 
+** a pointer to an instance of the [odbql_mem_methods] structure.
+** The argument specifies
+** alternative low-level memory allocation routines to be used in place of
+** the memory allocation routines built into SQLite.)^ ^SQLite makes
+** its own private copy of the content of the [odbql_mem_methods] structure
+** before the [odbql_config()] call returns.</dd>
+**
+** [[ODBQL_CONFIG_GETMALLOC]] <dt>ODBQL_CONFIG_GETMALLOC</dt>
+** <dd> ^(The ODBQL_CONFIG_GETMALLOC option takes a single argument which
+** is a pointer to an instance of the [odbql_mem_methods] structure.
+** The [odbql_mem_methods]
+** structure is filled with the currently defined memory allocation routines.)^
+** This option can be used to overload the default memory allocation
+** routines with a wrapper that simulations memory allocation failure or
+** tracks memory usage, for example. </dd>
+**
+** [[ODBQL_CONFIG_MEMSTATUS]] <dt>ODBQL_CONFIG_MEMSTATUS</dt>
+** <dd> ^The ODBQL_CONFIG_MEMSTATUS option takes single argument of type int,
+** interpreted as a boolean, which enables or disables the collection of
+** memory allocation statistics. ^(When memory allocation statistics are
+** disabled, the following SQLite interfaces become non-operational:
+**   <ul>
+**   <li> [odbql_memory_used()]
+**   <li> [odbql_memory_highwater()]
+**   <li> [odbql_soft_heap_limit64()]
+**   <li> [odbql_status64()]
+**   </ul>)^
+** ^Memory allocation statistics are enabled by default unless SQLite is
+** compiled with [ODBQL_DEFAULT_MEMSTATUS]=0 in which case memory
+** allocation statistics are disabled by default.
+** </dd>
+**
+** [[ODBQL_CONFIG_SCRATCH]] <dt>ODBQL_CONFIG_SCRATCH</dt>
+** <dd> ^The ODBQL_CONFIG_SCRATCH option specifies a static memory buffer
+** that SQLite can use for scratch memory.  ^(There are three arguments
+** to ODBQL_CONFIG_SCRATCH:  A pointer an 8-byte
+** aligned memory buffer from which the scratch allocations will be
+** drawn, the size of each scratch allocation (sz),
+** and the maximum number of scratch allocations (N).)^
+** The first argument must be a pointer to an 8-byte aligned buffer
+** of at least sz*N bytes of memory.
+** ^SQLite will not use more than one scratch buffers per thread.
+** ^SQLite will never request a scratch buffer that is more than 6
+** times the database page size.
+** ^If SQLite needs needs additional
+** scratch memory beyond what is provided by this configuration option, then 
+** [odbql_malloc()] will be used to obtain the memory needed.<p>
+** ^When the application provides any amount of scratch memory using
+** ODBQL_CONFIG_SCRATCH, SQLite avoids unnecessary large
+** [odbql_malloc|heap allocations].
+** This can help [Robson proof|prevent memory allocation failures] due to heap
+** fragmentation in low-memory embedded systems.
+** </dd>
+**
+** [[ODBQL_CONFIG_PAGECACHE]] <dt>ODBQL_CONFIG_PAGECACHE</dt>
+** <dd> ^The ODBQL_CONFIG_PAGECACHE option specifies a memory pool
+** that SQLite can use for the database page cache with the default page
+** cache implementation.  
+** This configuration option is a no-op if an application-define page
+** cache implementation is loaded using the [ODBQL_CONFIG_PCACHE2].
+** ^There are three arguments to ODBQL_CONFIG_PAGECACHE: A pointer to
+** 8-byte aligned memory (pMem), the size of each page cache line (sz),
+** and the number of cache lines (N).
+** The sz argument should be the size of the largest database page
+** (a power of two between 512 and 65536) plus some extra bytes for each
+** page header.  ^The number of extra bytes needed by the page header
+** can be determined using [ODBQL_CONFIG_PCACHE_HDRSZ].
+** ^It is harmless, apart from the wasted memory,
+** for the sz parameter to be larger than necessary.  The pMem
+** argument must be either a NULL pointer or a pointer to an 8-byte
+** aligned block of memory of at least sz*N bytes, otherwise
+** subsequent behavior is undefined.
+** ^When pMem is not NULL, SQLite will strive to use the memory provided
+** to satisfy page cache needs, falling back to [odbql_malloc()] if
+** a page cache line is larger than sz bytes or if all of the pMem buffer
+** is exhausted.
+** ^If pMem is NULL and N is non-zero, then each database connection
+** does an initial bulk allocation for page cache memory
+** from [odbql_malloc()] sufficient for N cache lines if N is positive or
+** of -1024*N bytes if N is negative, . ^If additional
+** page cache memory is needed beyond what is provided by the initial
+** allocation, then SQLite goes to [odbql_malloc()] separately for each
+** additional cache line. </dd>
+**
+** [[ODBQL_CONFIG_HEAP]] <dt>ODBQL_CONFIG_HEAP</dt>
+** <dd> ^The ODBQL_CONFIG_HEAP option specifies a static memory buffer 
+** that SQLite will use for all of its dynamic memory allocation needs
+** beyond those provided for by [ODBQL_CONFIG_SCRATCH] and
+** [ODBQL_CONFIG_PAGECACHE].
+** ^The ODBQL_CONFIG_HEAP option is only available if SQLite is compiled
+** with either [ODBQL_ENABLE_MEMSYS3] or [ODBQL_ENABLE_MEMSYS5] and returns
+** [ODBQL_ERROR] if invoked otherwise.
+** ^There are three arguments to ODBQL_CONFIG_HEAP:
+** An 8-byte aligned pointer to the memory,
+** the number of bytes in the memory buffer, and the minimum allocation size.
+** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts
+** to using its default memory allocator (the system malloc() implementation),
+** undoing any prior invocation of [ODBQL_CONFIG_MALLOC].  ^If the
+** memory pointer is not NULL then the alternative memory
+** allocator is engaged to handle all of SQLites memory allocation needs.
+** The first pointer (the memory pointer) must be aligned to an 8-byte
+** boundary or subsequent behavior of SQLite will be undefined.
+** The minimum allocation size is capped at 2**12. Reasonable values
+** for the minimum allocation size are 2**5 through 2**8.</dd>
+**
+** [[ODBQL_CONFIG_MUTEX]] <dt>ODBQL_CONFIG_MUTEX</dt>
+** <dd> ^(The ODBQL_CONFIG_MUTEX option takes a single argument which is a
+** pointer to an instance of the [odbql_mutex_methods] structure.
+** The argument specifies alternative low-level mutex routines to be used
+** in place the mutex routines built into SQLite.)^  ^SQLite makes a copy of
+** the content of the [odbql_mutex_methods] structure before the call to
+** [odbql_config()] returns. ^If SQLite is compiled with
+** the [ODBQL_THREADSAFE | ODBQL_THREADSAFE=0] compile-time option then
+** the entire mutexing subsystem is omitted from the build and hence calls to
+** [odbql_config()] with the ODBQL_CONFIG_MUTEX configuration option will
+** return [ODBQL_ERROR].</dd>
+**
+** [[ODBQL_CONFIG_GETMUTEX]] <dt>ODBQL_CONFIG_GETMUTEX</dt>
+** <dd> ^(The ODBQL_CONFIG_GETMUTEX option takes a single argument which
+** is a pointer to an instance of the [odbql_mutex_methods] structure.  The
+** [odbql_mutex_methods]
+** structure is filled with the currently defined mutex routines.)^
+** This option can be used to overload the default mutex allocation
+** routines with a wrapper used to track mutex usage for performance
+** profiling or testing, for example.   ^If SQLite is compiled with
+** the [ODBQL_THREADSAFE | ODBQL_THREADSAFE=0] compile-time option then
+** the entire mutexing subsystem is omitted from the build and hence calls to
+** [odbql_config()] with the ODBQL_CONFIG_GETMUTEX configuration option will
+** return [ODBQL_ERROR].</dd>
+**
+** [[ODBQL_CONFIG_LOOKASIDE]] <dt>ODBQL_CONFIG_LOOKASIDE</dt>
+** <dd> ^(The ODBQL_CONFIG_LOOKASIDE option takes two arguments that determine
+** the default size of lookaside memory on each [database connection].
+** The first argument is the
+** size of each lookaside buffer slot and the second is the number of
+** slots allocated to each database connection.)^  ^(ODBQL_CONFIG_LOOKASIDE
+** sets the <i>default</i> lookaside size. The [ODBQL_DBCONFIG_LOOKASIDE]
+** option to [odbql_db_config()] can be used to change the lookaside
+** configuration on individual connections.)^ </dd>
+**
+** [[ODBQL_CONFIG_PCACHE2]] <dt>ODBQL_CONFIG_PCACHE2</dt>
+** <dd> ^(The ODBQL_CONFIG_PCACHE2 option takes a single argument which is 
+** a pointer to an [odbql_pcache_methods2] object.  This object specifies
+** the interface to a custom page cache implementation.)^
+** ^SQLite makes a copy of the [odbql_pcache_methods2] object.</dd>
+**
+** [[ODBQL_CONFIG_GETPCACHE2]] <dt>ODBQL_CONFIG_GETPCACHE2</dt>
+** <dd> ^(The ODBQL_CONFIG_GETPCACHE2 option takes a single argument which
+** is a pointer to an [odbql_pcache_methods2] object.  SQLite copies of
+** the current page cache implementation into that object.)^ </dd>
+**
+** [[ODBQL_CONFIG_LOG]] <dt>ODBQL_CONFIG_LOG</dt>
+** <dd> The ODBQL_CONFIG_LOG option is used to configure the SQLite
+** global [error log].
+** (^The ODBQL_CONFIG_LOG option takes two arguments: a pointer to a
+** function with a call signature of void(*)(void*,int,const char*), 
+** and a pointer to void. ^If the function pointer is not NULL, it is
+** invoked by [odbql_log()] to process each logging event.  ^If the
+** function pointer is NULL, the [odbql_log()] interface becomes a no-op.
+** ^The void pointer that is the second argument to ODBQL_CONFIG_LOG is
+** passed through as the first parameter to the application-defined logger
+** function whenever that function is invoked.  ^The second parameter to
+** the logger function is a copy of the first parameter to the corresponding
+** [odbql_log()] call and is intended to be a [result code] or an
+** [extended result code].  ^The third parameter passed to the logger is
+** log message after formatting via [odbql_snprintf()].
+** The SQLite logging interface is not reentrant; the logger function
+** supplied by the application must not invoke any SQLite interface.
+** In a multi-threaded application, the application-defined logger
+** function must be threadsafe. </dd>
+**
+** [[ODBQL_CONFIG_URI]] <dt>ODBQL_CONFIG_URI
+** <dd>^(The ODBQL_CONFIG_URI option takes a single argument of type int.
+** If non-zero, then URI handling is globally enabled. If the parameter is zero,
+** then URI handling is globally disabled.)^ ^If URI handling is globally
+** enabled, all filenames passed to [odbql_open()], [odbql_open_v2()],
+** [odbql_open16()] or
+** specified as part of [ATTACH] commands are interpreted as URIs, regardless
+** of whether or not the [ODBQL_OPEN_URI] flag is set when the database
+** connection is opened. ^If it is globally disabled, filenames are
+** only interpreted as URIs if the ODBQL_OPEN_URI flag is set when the
+** database connection is opened. ^(By default, URI handling is globally
+** disabled. The default value may be changed by compiling with the
+** [ODBQL_USE_URI] symbol defined.)^
+**
+** [[ODBQL_CONFIG_COVERING_INDEX_SCAN]] <dt>ODBQL_CONFIG_COVERING_INDEX_SCAN
+** <dd>^The ODBQL_CONFIG_COVERING_INDEX_SCAN option takes a single integer
+** argument which is interpreted as a boolean in order to enable or disable
+** the use of covering indices for full table scans in the query optimizer.
+** ^The default setting is determined
+** by the [ODBQL_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
+** if that compile-time option is omitted.
+** The ability to disable the use of covering indices for full table scans
+** is because some incorrectly coded legacy applications might malfunction
+** when the optimization is enabled.  Providing the ability to
+** disable the optimization allows the older, buggy application code to work
+** without change even with newer versions of SQLite.
+**
+** [[ODBQL_CONFIG_PCACHE]] [[ODBQL_CONFIG_GETPCACHE]]
+** <dt>ODBQL_CONFIG_PCACHE and ODBQL_CONFIG_GETPCACHE
+** <dd> These options are obsolete and should not be used by new code.
+** They are retained for backwards compatibility but are now no-ops.
+** </dd>
+**
+** [[ODBQL_CONFIG_SQLLOG]]
+** <dt>ODBQL_CONFIG_SQLLOG
+** <dd>This option is only available if sqlite is compiled with the
+** [ODBQL_ENABLE_SQLLOG] pre-processor macro defined. The first argument should
+** be a pointer to a function of type void(*)(void*,odbql*,const char*, int).
+** The second should be of type (void*). The callback is invoked by the library
+** in three separate circumstances, identified by the value passed as the
+** fourth parameter. If the fourth parameter is 0, then the database connection
+** passed as the second argument has just been opened. The third argument
+** points to a buffer containing the name of the main database file. If the
+** fourth parameter is 1, then the SQL statement that the third parameter
+** points to has just been executed. Or, if the fourth parameter is 2, then
+** the connection being passed as the second parameter is being closed. The
+** third parameter is passed NULL In this case.  An example of using this
+** configuration option can be seen in the "test_sqllog.c" source file in
+** the canonical SQLite source tree.</dd>
+**
+** [[ODBQL_CONFIG_MMAP_SIZE]]
+** <dt>ODBQL_CONFIG_MMAP_SIZE
+** <dd>^ODBQL_CONFIG_MMAP_SIZE takes two 64-bit integer (odbql_int64) values
+** that are the default mmap size limit (the default setting for
+** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
+** ^The default setting can be overridden by each database connection using
+** either the [PRAGMA mmap_size] command, or by using the
+** [ODBQL_FCNTL_MMAP_SIZE] file control.  ^(The maximum allowed mmap size
+** will be silently truncated if necessary so that it does not exceed the
+** compile-time maximum mmap size set by the
+** [ODBQL_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
+** changed to its compile-time default.
+**
+** [[ODBQL_CONFIG_WIN32_HEAPSIZE]]
+** <dt>ODBQL_CONFIG_WIN32_HEAPSIZE
+** <dd>^The ODBQL_CONFIG_WIN32_HEAPSIZE option is only available if SQLite is
+** compiled for Windows with the [ODBQL_WIN32_MALLOC] pre-processor macro
+** defined. ^ODBQL_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
+** that specifies the maximum size of the created heap.
+**
+** [[ODBQL_CONFIG_PCACHE_HDRSZ]]
+** <dt>ODBQL_CONFIG_PCACHE_HDRSZ
+** <dd>^The ODBQL_CONFIG_PCACHE_HDRSZ option takes a single parameter which
+** is a pointer to an integer and writes into that integer the number of extra
+** bytes per page required for each page in [ODBQL_CONFIG_PAGECACHE].
+** The amount of extra space required can change depending on the compiler,
+** target platform, and SQLite version.
+**
+** [[ODBQL_CONFIG_PMASZ]]
+** <dt>ODBQL_CONFIG_PMASZ
+** <dd>^The ODBQL_CONFIG_PMASZ option takes a single parameter which
+** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded
+** sorter to that integer.  The default minimum PMA Size is set by the
+** [ODBQL_SORTER_PMASZ] compile-time option.  New threads are launched
+** to help with sort operations when multithreaded sorting
+** is enabled (using the [PRAGMA threads] command) and the amount of content
+** to be sorted exceeds the page size times the minimum of the
+** [PRAGMA cache_size] setting and this value.
+**
+** [[ODBQL_CONFIG_STMTJRNL_SPILL]]
+** <dt>ODBQL_CONFIG_STMTJRNL_SPILL
+** <dd>^The ODBQL_CONFIG_STMTJRNL_SPILL option takes a single parameter which
+** becomes the [statement journal] spill-to-disk threshold.  
+** [Statement journals] are held in memory until their size (in bytes)
+** exceeds this threshold, at which point they are written to disk.
+** Or if the threshold is -1, statement journals are always held
+** exclusively in memory.
+** Since many statement journals never become large, setting the spill
+** threshold to a value such as 64KiB can greatly reduce the amount of
+** I/O required to support statement rollback.
+** The default value for this setting is controlled by the
+** [ODBQL_STMTJRNL_SPILL] compile-time option.
+** </dl>
+*/
+#define ODBQL_CONFIG_SINGLETHREAD  1  /* nil */
+#define ODBQL_CONFIG_MULTITHREAD   2  /* nil */
+#define ODBQL_CONFIG_SERIALIZED    3  /* nil */
+#define ODBQL_CONFIG_MALLOC        4  /* odbql_mem_methods* */
+#define ODBQL_CONFIG_GETMALLOC     5  /* odbql_mem_methods* */
+#define ODBQL_CONFIG_SCRATCH       6  /* void*, int sz, int N */
+#define ODBQL_CONFIG_PAGECACHE     7  /* void*, int sz, int N */
+#define ODBQL_CONFIG_HEAP          8  /* void*, int nByte, int min */
+#define ODBQL_CONFIG_MEMSTATUS     9  /* boolean */
+#define ODBQL_CONFIG_MUTEX        10  /* odbql_mutex_methods* */
+#define ODBQL_CONFIG_GETMUTEX     11  /* odbql_mutex_methods* */
+/* previously ODBQL_CONFIG_CHUNKALLOC 12 which is now unused. */ 
+#define ODBQL_CONFIG_LOOKASIDE    13  /* int int */
+#define ODBQL_CONFIG_PCACHE       14  /* no-op */
+#define ODBQL_CONFIG_GETPCACHE    15  /* no-op */
+#define ODBQL_CONFIG_LOG          16  /* xFunc, void* */
+#define ODBQL_CONFIG_URI          17  /* int */
+#define ODBQL_CONFIG_PCACHE2      18  /* odbql_pcache_methods2* */
+#define ODBQL_CONFIG_GETPCACHE2   19  /* odbql_pcache_methods2* */
+#define ODBQL_CONFIG_COVERING_INDEX_SCAN 20  /* int */
+#define ODBQL_CONFIG_SQLLOG       21  /* xSqllog, void* */
+#define ODBQL_CONFIG_MMAP_SIZE    22  /* odbql_int64, odbql_int64 */
+#define ODBQL_CONFIG_WIN32_HEAPSIZE      23  /* int nByte */
+#define ODBQL_CONFIG_PCACHE_HDRSZ        24  /* int *psz */
+#define ODBQL_CONFIG_PMASZ               25  /* unsigned int szPma */
+#define ODBQL_CONFIG_STMTJRNL_SPILL      26  /* int nByte */
+
+/*
+** CAPI3REF: Database Connection Configuration Options
+**
+** These constants are the available integer configuration options that
+** can be passed as the second argument to the [odbql_db_config()] interface.
+**
+** New configuration options may be added in future releases of SQLite.
+** Existing configuration options might be discontinued.  Applications
+** should check the return code from [odbql_db_config()] to make sure that
+** the call worked.  ^The [odbql_db_config()] interface will return a
+** non-zero [error code] if a discontinued or unsupported configuration option
+** is invoked.
+**
+** <dl>
+** <dt>ODBQL_DBCONFIG_LOOKASIDE</dt>
+** <dd> ^This option takes three additional arguments that determine the 
+** [lookaside memory allocator] configuration for the [database connection].
+** ^The first argument (the third parameter to [odbql_db_config()] is a
+** pointer to a memory buffer to use for lookaside memory.
+** ^The first argument after the ODBQL_DBCONFIG_LOOKASIDE verb
+** may be NULL in which case SQLite will allocate the
+** lookaside buffer itself using [odbql_malloc()]. ^The second argument is the
+** size of each lookaside buffer slot.  ^The third argument is the number of
+** slots.  The size of the buffer in the first argument must be greater than
+** or equal to the product of the second and third arguments.  The buffer
+** must be aligned to an 8-byte boundary.  ^If the second argument to
+** ODBQL_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
+** rounded down to the next smaller multiple of 8.  ^(The lookaside memory
+** configuration for a database connection can only be changed when that
+** connection is not currently using lookaside memory, or in other words
+** when the "current value" returned by
+** [odbql_db_status](D,[ODBQL_CONFIG_LOOKASIDE],...) is zero.
+** Any attempt to change the lookaside memory configuration when lookaside
+** memory is in use leaves the configuration unchanged and returns 
+** [ODBQL_BUSY].)^</dd>
+**
+** <dt>ODBQL_DBCONFIG_ENABLE_FKEY</dt>
+** <dd> ^This option is used to enable or disable the enforcement of
+** [foreign key constraints].  There should be two additional arguments.
+** The first argument is an integer which is 0 to disable FK enforcement,
+** positive to enable FK enforcement or negative to leave FK enforcement
+** unchanged.  The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether FK enforcement is off or on
+** following this call.  The second parameter may be a NULL pointer, in
+** which case the FK enforcement setting is not reported back. </dd>
+**
+** <dt>ODBQL_DBCONFIG_ENABLE_TRIGGER</dt>
+** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable triggers,
+** positive to enable triggers or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether triggers are disabled or enabled
+** following this call.  The second parameter may be a NULL pointer, in
+** which case the trigger setting is not reported back. </dd>
+**
+** <dt>ODBQL_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
+** <dd> ^This option is used to enable or disable the two-argument
+** version of the [fts3_tokenizer()] function which is part of the
+** [FTS3] full-text search engine extension.
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable fts3_tokenizer() or
+** positive to enable fts3_tokenizer() or negative to leave the setting
+** unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
+** following this call.  The second parameter may be a NULL pointer, in
+** which case the new setting is not reported back. </dd>
+**
+** <dt>ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
+** <dd> ^This option is used to enable or disable the [odbql_load_extension()]
+** interface independently of the [load_extension()] SQL function.
+** The [odbql_enable_load_extension()] API enables or disables both the
+** C-API [odbql_load_extension()] and the SQL function [load_extension()].
+** There should be two additional arguments.
+** When the first argument to this interface is 1, then only the C-API is
+** enabled and the SQL function remains disabled.  If the first argment to
+** this interface is 0, then both the C-API and the SQL function are disabled.
+** If the first argument is -1, then no changes are made to state of either the
+** C-API or the SQL function.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether [odbql_load_extension()] interface
+** is disabled or enabled following this call.  The second parameter may
+** be a NULL pointer, in which case the new setting is not reported back.
+** </dd>
+**
+** </dl>
+*/
+#define ODBQL_DBCONFIG_LOOKASIDE             1001 /* void* int int */
+#define ODBQL_DBCONFIG_ENABLE_FKEY           1002 /* int int* */
+#define ODBQL_DBCONFIG_ENABLE_TRIGGER        1003 /* int int* */
+#define ODBQL_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
+#define ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
+
+
+/*
+** CAPI3REF: Enable Or Disable Extended Result Codes
+** METHOD: odbql
+**
+** ^The odbql_extended_result_codes() routine enables or disables the
+** [extended result codes] feature of SQLite. ^The extended result
+** codes are disabled by default for historical compatibility.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_extended_result_codes(odbql*, int onoff);
+
+/*
+** CAPI3REF: Last Insert Rowid
+** METHOD: odbql
+**
+** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
+** has a unique 64-bit signed
+** integer key called the [ROWID | "rowid"]. ^The rowid is always available
+** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
+** names are not also used by explicitly declared columns. ^If
+** the table has a column of type [INTEGER PRIMARY KEY] then that column
+** is another alias for the rowid.
+**
+** ^The odbql_last_insert_rowid(D) interface returns the [rowid] of the 
+** most recent successful [INSERT] into a rowid table or [virtual table]
+** on database connection D.
+** ^Inserts into [WITHOUT ROWID] tables are not recorded.
+** ^If no successful [INSERT]s into rowid tables
+** have ever occurred on the database connection D, 
+** then odbql_last_insert_rowid(D) returns zero.
+**
+** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
+** method, then this routine will return the [rowid] of the inserted
+** row as long as the trigger or virtual table method is running.
+** But once the trigger or virtual table method ends, the value returned 
+** by this routine reverts to what it was before the trigger or virtual
+** table method began.)^
+**
+** ^An [INSERT] that fails due to a constraint violation is not a
+** successful [INSERT] and does not change the value returned by this
+** routine.  ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK,
+** and INSERT OR ABORT make no changes to the return value of this
+** routine when their insertion fails.  ^(When INSERT OR REPLACE
+** encounters a constraint violation, it does not fail.  The
+** INSERT continues to completion after deleting rows that caused
+** the constraint problem so INSERT OR REPLACE will always change
+** the return value of this interface.)^
+**
+** ^For the purposes of this routine, an [INSERT] is considered to
+** be successful even if it is subsequently rolled back.
+**
+** This function is accessible to SQL statements via the
+** [last_insert_rowid() SQL function].
+**
+** If a separate thread performs a new [INSERT] on the same
+** database connection while the [odbql_last_insert_rowid()]
+** function is running and thus changes the last insert [rowid],
+** then the value returned by [odbql_last_insert_rowid()] is
+** unpredictable and might not equal either the old or the new
+** last insert [rowid].
+*/
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_last_insert_rowid(odbql*);
+
+/*
+** CAPI3REF: Count The Number Of Rows Modified
+** METHOD: odbql
+**
+** ^This function returns the number of rows modified, inserted or
+** deleted by the most recently completed INSERT, UPDATE or DELETE
+** statement on the database connection specified by the only parameter.
+** ^Executing any other type of SQL statement does not modify the value
+** returned by this function.
+**
+** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
+** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], 
+** [foreign key actions] or [REPLACE] constraint resolution are not counted.
+** 
+** Changes to a view that are intercepted by 
+** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value 
+** returned by odbql_changes() immediately after an INSERT, UPDATE or 
+** DELETE statement run on a view is always zero. Only changes made to real 
+** tables are counted.
+**
+** Things are more complicated if the odbql_changes() function is
+** executed while a trigger program is running. This may happen if the
+** program uses the [changes() SQL function], or if some other callback
+** function invokes odbql_changes() directly. Essentially:
+** 
+** <ul>
+**   <li> ^(Before entering a trigger program the value returned by
+**        odbql_changes() function is saved. After the trigger program 
+**        has finished, the original value is restored.)^
+** 
+**   <li> ^(Within a trigger program each INSERT, UPDATE and DELETE 
+**        statement sets the value returned by odbql_changes() 
+**        upon completion as normal. Of course, this value will not include 
+**        any changes performed by sub-triggers, as the odbql_changes() 
+**        value will be saved and restored after each sub-trigger has run.)^
+** </ul>
+** 
+** ^This means that if the changes() SQL function (or similar) is used
+** by the first INSERT, UPDATE or DELETE statement within a trigger, it 
+** returns the value as set when the calling statement began executing.
+** ^If it is used by the second or subsequent such statement within a trigger 
+** program, the value returned reflects the number of rows modified by the 
+** previous INSERT, UPDATE or DELETE statement within the same trigger.
+**
+** See also the [odbql_total_changes()] interface, the
+** [count_changes pragma], and the [changes() SQL function].
+**
+** If a separate thread makes changes on the same database connection
+** while [odbql_changes()] is running then the value returned
+** is unpredictable and not meaningful.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_changes(odbql*);
+
+/*
+** CAPI3REF: Total Number Of Rows Modified
+** METHOD: odbql
+**
+** ^This function returns the total number of rows inserted, modified or
+** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
+** since the database connection was opened, including those executed as
+** part of trigger programs. ^Executing any other type of SQL statement
+** does not affect the value returned by odbql_total_changes().
+** 
+** ^Changes made as part of [foreign key actions] are included in the
+** count, but those made as part of REPLACE constraint resolution are
+** not. ^Changes to a view that are intercepted by INSTEAD OF triggers 
+** are not counted.
+** 
+** See also the [odbql_changes()] interface, the
+** [count_changes pragma], and the [total_changes() SQL function].
+**
+** If a separate thread makes changes on the same database connection
+** while [odbql_total_changes()] is running then the value
+** returned is unpredictable and not meaningful.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_total_changes(odbql*);
+
+/*
+** CAPI3REF: Interrupt A Long-Running Query
+** METHOD: odbql
+**
+** ^This function causes any pending database operation to abort and
+** return at its earliest opportunity. This routine is typically
+** called in response to a user action such as pressing "Cancel"
+** or Ctrl-C where the user wants a long query operation to halt
+** immediately.
+**
+** ^It is safe to call this routine from a thread different from the
+** thread that is currently running the database operation.  But it
+** is not safe to call this routine with a [database connection] that
+** is closed or might close before odbql_interrupt() returns.
+**
+** ^If an SQL operation is very nearly finished at the time when
+** odbql_interrupt() is called, then it might not have an opportunity
+** to be interrupted and might continue to completion.
+**
+** ^An SQL operation that is interrupted will return [ODBQL_INTERRUPT].
+** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE
+** that is inside an explicit transaction, then the entire transaction
+** will be rolled back automatically.
+**
+** ^The odbql_interrupt(D) call is in effect until all currently running
+** SQL statements on [database connection] D complete.  ^Any new SQL statements
+** that are started after the odbql_interrupt() call and before the 
+** running statements reaches zero are interrupted as if they had been
+** running prior to the odbql_interrupt() call.  ^New SQL statements
+** that are started after the running statement count reaches zero are
+** not effected by the odbql_interrupt().
+** ^A call to odbql_interrupt(D) that occurs when there are no running
+** SQL statements is a no-op and has no effect on SQL statements
+** that are started after the odbql_interrupt() call returns.
+**
+** If the database connection closes while [odbql_interrupt()]
+** is running then bad things will likely happen.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_interrupt(odbql*);
+
+/*
+** CAPI3REF: Determine If An SQL Statement Is Complete
+**
+** These routines are useful during command-line input to determine if the
+** currently entered text seems to form a complete SQL statement or
+** if additional input is needed before sending the text into
+** SQLite for parsing.  ^These routines return 1 if the input string
+** appears to be a complete SQL statement.  ^A statement is judged to be
+** complete if it ends with a semicolon token and is not a prefix of a
+** well-formed CREATE TRIGGER statement.  ^Semicolons that are embedded within
+** string literals or quoted identifier names or comments are not
+** independent tokens (they are part of the token in which they are
+** embedded) and thus do not count as a statement terminator.  ^Whitespace
+** and comments that follow the final semicolon are ignored.
+**
+** ^These routines return 0 if the statement is incomplete.  ^If a
+** memory allocation fails, then ODBQL_NOMEM is returned.
+**
+** ^These routines do not parse the SQL statements thus
+** will not detect syntactically incorrect SQL.
+**
+** ^(If SQLite has not been initialized using [odbql_initialize()] prior 
+** to invoking odbql_complete16() then odbql_initialize() is invoked
+** automatically by odbql_complete16().  If that initialization fails,
+** then the return value from odbql_complete16() will be non-zero
+** regardless of whether or not the input SQL is complete.)^
+**
+** The input to [odbql_complete()] must be a zero-terminated
+** UTF-8 string.
+**
+** The input to [odbql_complete16()] must be a zero-terminated
+** UTF-16 string in native byte order.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_complete(const char *sql);
+ODBQL_API int ODBQL_STDCALL odbql_complete16(const void *sql);
+
+/*
+** CAPI3REF: Register A Callback To Handle ODBQL_BUSY Errors
+** KEYWORDS: {busy-handler callback} {busy handler}
+** METHOD: odbql
+**
+** ^The odbql_busy_handler(D,X,P) routine sets a callback function X
+** that might be invoked with argument P whenever
+** an attempt is made to access a database table associated with
+** [database connection] D when another thread
+** or process has the table locked.
+** The odbql_busy_handler() interface is used to implement
+** [odbql_busy_timeout()] and [PRAGMA busy_timeout].
+**
+** ^If the busy callback is NULL, then [ODBQL_BUSY]
+** is returned immediately upon encountering the lock.  ^If the busy callback
+** is not NULL, then the callback might be invoked with two arguments.
+**
+** ^The first argument to the busy handler is a copy of the void* pointer which
+** is the third argument to odbql_busy_handler().  ^The second argument to
+** the busy handler callback is the number of times that the busy handler has
+** been invoked previously for the same locking event.  ^If the
+** busy callback returns 0, then no additional attempts are made to
+** access the database and [ODBQL_BUSY] is returned
+** to the application.
+** ^If the callback returns non-zero, then another attempt
+** is made to access the database and the cycle repeats.
+**
+** The presence of a busy handler does not guarantee that it will be invoked
+** when there is lock contention. ^If SQLite determines that invoking the busy
+** handler could result in a deadlock, it will go ahead and return [ODBQL_BUSY]
+** to the application instead of invoking the 
+** busy handler.
+** Consider a scenario where one process is holding a read lock that
+** it is trying to promote to a reserved lock and
+** a second process is holding a reserved lock that it is trying
+** to promote to an exclusive lock.  The first process cannot proceed
+** because it is blocked by the second and the second process cannot
+** proceed because it is blocked by the first.  If both processes
+** invoke the busy handlers, neither will make any progress.  Therefore,
+** SQLite returns [ODBQL_BUSY] for the first process, hoping that this
+** will induce the first process to release its read lock and allow
+** the second process to proceed.
+**
+** ^The default busy callback is NULL.
+**
+** ^(There can only be a single busy handler defined for each
+** [database connection].  Setting a new busy handler clears any
+** previously set handler.)^  ^Note that calling [odbql_busy_timeout()]
+** or evaluating [PRAGMA busy_timeout=N] will change the
+** busy handler and thus clear any previously set busy handler.
+**
+** The busy callback should not take any actions which modify the
+** database connection that invoked the busy handler.  In other words,
+** the busy handler is not reentrant.  Any such actions
+** result in undefined behavior.
+** 
+** A busy handler must not close the database connection
+** or [prepared statement] that invoked the busy handler.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_busy_handler(odbql*, int(*)(void*,int), void*);
+
+/*
+** CAPI3REF: Set A Busy Timeout
+** METHOD: odbql
+**
+** ^This routine sets a [odbql_busy_handler | busy handler] that sleeps
+** for a specified amount of time when a table is locked.  ^The handler
+** will sleep multiple times until at least "ms" milliseconds of sleeping
+** have accumulated.  ^After at least "ms" milliseconds of sleeping,
+** the handler returns 0 which causes [odbql_step()] to return
+** [ODBQL_BUSY].
+**
+** ^Calling this routine with an argument less than or equal to zero
+** turns off all busy handlers.
+**
+** ^(There can only be a single busy handler for a particular
+** [database connection] at any given moment.  If another busy handler
+** was defined  (using [odbql_busy_handler()]) prior to calling
+** this routine, that other busy handler is cleared.)^
+**
+** See also:  [PRAGMA busy_timeout]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_busy_timeout(odbql*, int ms);
+
+/*
+** CAPI3REF: Convenience Routines For Running Queries
+** METHOD: odbql
+**
+** This is a legacy interface that is preserved for backwards compatibility.
+** Use of this interface is not recommended.
+**
+** Definition: A <b>result table</b> is memory data structure created by the
+** [odbql_get_table()] interface.  A result table records the
+** complete query results from one or more queries.
+**
+** The table conceptually has a number of rows and columns.  But
+** these numbers are not part of the result table itself.  These
+** numbers are obtained separately.  Let N be the number of rows
+** and M be the number of columns.
+**
+** A result table is an array of pointers to zero-terminated UTF-8 strings.
+** There are (N+1)*M elements in the array.  The first M pointers point
+** to zero-terminated strings that  contain the names of the columns.
+** The remaining entries all point to query results.  NULL values result
+** in NULL pointers.  All other values are in their UTF-8 zero-terminated
+** string representation as returned by [odbql_column_text()].
+**
+** A result table might consist of one or more memory allocations.
+** It is not safe to pass a result table directly to [odbql_free()].
+** A result table should be deallocated using [odbql_free_table()].
+**
+** ^(As an example of the result table format, suppose a query result
+** is as follows:
+**
+** <blockquote><pre>
+**        Name        | Age
+**        -----------------------
+**        Alice       | 43
+**        Bob         | 28
+**        Cindy       | 21
+** </pre></blockquote>
+**
+** There are two column (M==2) and three rows (N==3).  Thus the
+** result table has 8 entries.  Suppose the result table is stored
+** in an array names azResult.  Then azResult holds this content:
+**
+** <blockquote><pre>
+**        azResult[0] = "Name";
+**        azResult[1] = "Age";
+**        azResult[2] = "Alice";
+**        azResult[3] = "43";
+**        azResult[4] = "Bob";
+**        azResult[5] = "28";
+**        azResult[6] = "Cindy";
+**        azResult[7] = "21";
+** </pre></blockquote>)^
+**
+** ^The odbql_get_table() function evaluates one or more
+** semicolon-separated SQL statements in the zero-terminated UTF-8
+** string of its 2nd parameter and returns a result table to the
+** pointer given in its 3rd parameter.
+**
+** After the application has finished with the result from odbql_get_table(),
+** it must pass the result table pointer to odbql_free_table() in order to
+** release the memory that was malloced.  Because of the way the
+** [odbql_malloc()] happens within odbql_get_table(), the calling
+** function must not try to call [odbql_free()] directly.  Only
+** [odbql_free_table()] is able to release the memory properly and safely.
+**
+** The odbql_get_table() interface is implemented as a wrapper around
+** [odbql_exec()].  The odbql_get_table() routine does not have access
+** to any internal data structures of SQLite.  It uses only the public
+** interface defined here.  As a consequence, errors that occur in the
+** wrapper layer outside of the internal [odbql_exec()] call are not
+** reflected in subsequent calls to [odbql_errcode()] or
+** [odbql_errmsg()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_get_table(
+  odbql *db,          /* An open database */
+  const char *zSql,     /* SQL to be evaluated */
+  char ***pazResult,    /* Results of the query */
+  int *pnRow,           /* Number of result rows written here */
+  int *pnColumn,        /* Number of result columns written here */
+  char **pzErrmsg       /* Error msg written here */
+);
+ODBQL_API void ODBQL_STDCALL odbql_free_table(char **result);
+
+/*
+** CAPI3REF: Formatted String Printing Functions
+**
+** These routines are work-alikes of the "printf()" family of functions
+** from the standard C library.
+** These routines understand most of the common K&R formatting options,
+** plus some additional non-standard formats, detailed below.
+** Note that some of the more obscure formatting options from recent
+** C-library standards are omitted from this implementation.
+**
+** ^The odbql_mprintf() and odbql_vmprintf() routines write their
+** results into memory obtained from [odbql_malloc()].
+** The strings returned by these two routines should be
+** released by [odbql_free()].  ^Both routines return a
+** NULL pointer if [odbql_malloc()] is unable to allocate enough
+** memory to hold the resulting string.
+**
+** ^(The odbql_snprintf() routine is similar to "snprintf()" from
+** the standard C library.  The result is written into the
+** buffer supplied as the second parameter whose size is given by
+** the first parameter. Note that the order of the
+** first two parameters is reversed from snprintf().)^  This is an
+** historical accident that cannot be fixed without breaking
+** backwards compatibility.  ^(Note also that odbql_snprintf()
+** returns a pointer to its buffer instead of the number of
+** characters actually written into the buffer.)^  We admit that
+** the number of characters written would be a more useful return
+** value but we cannot change the implementation of odbql_snprintf()
+** now without breaking compatibility.
+**
+** ^As long as the buffer size is greater than zero, odbql_snprintf()
+** guarantees that the buffer is always zero-terminated.  ^The first
+** parameter "n" is the total size of the buffer, including space for
+** the zero terminator.  So the longest string that can be completely
+** written will be n-1 characters.
+**
+** ^The odbql_vsnprintf() routine is a varargs version of odbql_snprintf().
+**
+** These routines all implement some additional formatting
+** options that are useful for constructing SQL statements.
+** All of the usual printf() formatting options apply.  In addition, there
+** is are "%q", "%Q", "%w" and "%z" options.
+**
+** ^(The %q option works like %s in that it substitutes a nul-terminated
+** string from the argument list.  But %q also doubles every '\'' character.
+** %q is designed for use inside a string literal.)^  By doubling each '\''
+** character it escapes that character and allows it to be inserted into
+** the string.
+**
+** For example, assume the string variable zText contains text as follows:
+**
+** <blockquote><pre>
+**  char *zText = "It's a happy day!";
+** </pre></blockquote>
+**
+** One can use this text in an SQL statement as follows:
+**
+** <blockquote><pre>
+**  char *zSQL = odbql_mprintf("INSERT INTO table VALUES('%q')", zText);
+**  odbql_exec(db, zSQL, 0, 0, 0);
+**  odbql_free(zSQL);
+** </pre></blockquote>
+**
+** Because the %q format string is used, the '\'' character in zText
+** is escaped and the SQL generated is as follows:
+**
+** <blockquote><pre>
+**  INSERT INTO table1 VALUES('It''s a happy day!')
+** </pre></blockquote>
+**
+** This is correct.  Had we used %s instead of %q, the generated SQL
+** would have looked like this:
+**
+** <blockquote><pre>
+**  INSERT INTO table1 VALUES('It's a happy day!');
+** </pre></blockquote>
+**
+** This second example is an SQL syntax error.  As a general rule you should
+** always use %q instead of %s when inserting text into a string literal.
+**
+** ^(The %Q option works like %q except it also adds single quotes around
+** the outside of the total string.  Additionally, if the parameter in the
+** argument list is a NULL pointer, %Q substitutes the text "NULL" (without
+** single quotes).)^  So, for example, one could say:
+**
+** <blockquote><pre>
+**  char *zSQL = odbql_mprintf("INSERT INTO table VALUES(%Q)", zText);
+**  odbql_exec(db, zSQL, 0, 0, 0);
+**  odbql_free(zSQL);
+** </pre></blockquote>
+**
+** The code above will render a correct SQL statement in the zSQL
+** variable even if the zText variable is a NULL pointer.
+**
+** ^(The "%w" formatting option is like "%q" except that it expects to
+** be contained within double-quotes instead of single quotes, and it
+** escapes the double-quote character instead of the single-quote
+** character.)^  The "%w" formatting option is intended for safely inserting
+** table and column names into a constructed SQL statement.
+**
+** ^(The "%z" formatting option works like "%s" but with the
+** addition that after the string has been read and copied into
+** the result, [odbql_free()] is called on the input string.)^
+*/
+ODBQL_API char *ODBQL_CDECL odbql_mprintf(const char*,...);
+ODBQL_API char *ODBQL_STDCALL odbql_vmprintf(const char*, va_list);
+ODBQL_API char *ODBQL_CDECL odbql_snprintf(int,char*,const char*, ...);
+ODBQL_API char *ODBQL_STDCALL odbql_vsnprintf(int,char*,const char*, va_list);
+
+/*
+** CAPI3REF: Memory Allocation Subsystem
+**
+** The SQLite core uses these three routines for all of its own
+** internal memory allocation needs. "Core" in the previous sentence
+** does not include operating-system specific VFS implementation.  The
+** Windows VFS uses native malloc() and free() for some operations.
+**
+** ^The odbql_malloc() routine returns a pointer to a block
+** of memory at least N bytes in length, where N is the parameter.
+** ^If odbql_malloc() is unable to obtain sufficient free
+** memory, it returns a NULL pointer.  ^If the parameter N to
+** odbql_malloc() is zero or negative then odbql_malloc() returns
+** a NULL pointer.
+**
+** ^The odbql_malloc64(N) routine works just like
+** odbql_malloc(N) except that N is an unsigned 64-bit integer instead
+** of a signed 32-bit integer.
+**
+** ^Calling odbql_free() with a pointer previously returned
+** by odbql_malloc() or odbql_realloc() releases that memory so
+** that it might be reused.  ^The odbql_free() routine is
+** a no-op if is called with a NULL pointer.  Passing a NULL pointer
+** to odbql_free() is harmless.  After being freed, memory
+** should neither be read nor written.  Even reading previously freed
+** memory might result in a segmentation fault or other severe error.
+** Memory corruption, a segmentation fault, or other severe error
+** might result if odbql_free() is called with a non-NULL pointer that
+** was not obtained from odbql_malloc() or odbql_realloc().
+**
+** ^The odbql_realloc(X,N) interface attempts to resize a
+** prior memory allocation X to be at least N bytes.
+** ^If the X parameter to odbql_realloc(X,N)
+** is a NULL pointer then its behavior is identical to calling
+** odbql_malloc(N).
+** ^If the N parameter to odbql_realloc(X,N) is zero or
+** negative then the behavior is exactly the same as calling
+** odbql_free(X).
+** ^odbql_realloc(X,N) returns a pointer to a memory allocation
+** of at least N bytes in size or NULL if insufficient memory is available.
+** ^If M is the size of the prior allocation, then min(N,M) bytes
+** of the prior allocation are copied into the beginning of buffer returned
+** by odbql_realloc(X,N) and the prior allocation is freed.
+** ^If odbql_realloc(X,N) returns NULL and N is positive, then the
+** prior allocation is not freed.
+**
+** ^The odbql_realloc64(X,N) interfaces works the same as
+** odbql_realloc(X,N) except that N is a 64-bit unsigned integer instead
+** of a 32-bit signed integer.
+**
+** ^If X is a memory allocation previously obtained from odbql_malloc(),
+** odbql_malloc64(), odbql_realloc(), or odbql_realloc64(), then
+** odbql_msize(X) returns the size of that memory allocation in bytes.
+** ^The value returned by odbql_msize(X) might be larger than the number
+** of bytes requested when X was allocated.  ^If X is a NULL pointer then
+** odbql_msize(X) returns zero.  If X points to something that is not
+** the beginning of memory allocation, or if it points to a formerly
+** valid memory allocation that has now been freed, then the behavior
+** of odbql_msize(X) is undefined and possibly harmful.
+**
+** ^The memory returned by odbql_malloc(), odbql_realloc(),
+** odbql_malloc64(), and odbql_realloc64()
+** is always aligned to at least an 8 byte boundary, or to a
+** 4 byte boundary if the [ODBQL_4_BYTE_ALIGNED_MALLOC] compile-time
+** option is used.
+**
+** In SQLite version 3.5.0 and 3.5.1, it was possible to define
+** the ODBQL_OMIT_MEMORY_ALLOCATION which would cause the built-in
+** implementation of these routines to be omitted.  That capability
+** is no longer provided.  Only built-in memory allocators can be used.
+**
+** Prior to SQLite version 3.7.10, the Windows OS interface layer called
+** the system malloc() and free() directly when converting
+** filenames between the UTF-8 encoding used by SQLite
+** and whatever filename encoding is used by the particular Windows
+** installation.  Memory allocation errors were detected, but
+** they were reported back as [ODBQL_CANTOPEN] or
+** [ODBQL_IOERR] rather than [ODBQL_NOMEM].
+**
+** The pointer arguments to [odbql_free()] and [odbql_realloc()]
+** must be either NULL or else pointers obtained from a prior
+** invocation of [odbql_malloc()] or [odbql_realloc()] that have
+** not yet been released.
+**
+** The application must not read or write any part of
+** a block of memory after it has been released using
+** [odbql_free()] or [odbql_realloc()].
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_malloc(int);
+ODBQL_API void *ODBQL_STDCALL odbql_malloc64(odbql_uint64);
+ODBQL_API void *ODBQL_STDCALL odbql_realloc(void*, int);
+ODBQL_API void *ODBQL_STDCALL odbql_realloc64(void*, odbql_uint64);
+ODBQL_API void ODBQL_STDCALL odbql_free(void*);
+ODBQL_API odbql_uint64 ODBQL_STDCALL odbql_msize(void*);
+
+/*
+** CAPI3REF: Memory Allocator Statistics
+**
+** SQLite provides these two interfaces for reporting on the status
+** of the [odbql_malloc()], [odbql_free()], and [odbql_realloc()]
+** routines, which form the built-in memory allocation subsystem.
+**
+** ^The [odbql_memory_used()] routine returns the number of bytes
+** of memory currently outstanding (malloced but not freed).
+** ^The [odbql_memory_highwater()] routine returns the maximum
+** value of [odbql_memory_used()] since the high-water mark
+** was last reset.  ^The values returned by [odbql_memory_used()] and
+** [odbql_memory_highwater()] include any overhead
+** added by SQLite in its implementation of [odbql_malloc()],
+** but not overhead added by the any underlying system library
+** routines that [odbql_malloc()] may call.
+**
+** ^The memory high-water mark is reset to the current value of
+** [odbql_memory_used()] if and only if the parameter to
+** [odbql_memory_highwater()] is true.  ^The value returned
+** by [odbql_memory_highwater(1)] is the high-water mark
+** prior to the reset.
+*/
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_memory_used(void);
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_memory_highwater(int resetFlag);
+
+/*
+** CAPI3REF: Pseudo-Random Number Generator
+**
+** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
+** select random [ROWID | ROWIDs] when inserting new records into a table that
+** already uses the largest possible [ROWID].  The PRNG is also used for
+** the build-in random() and randomblob() SQL functions.  This interface allows
+** applications to access the same PRNG for other purposes.
+**
+** ^A call to this routine stores N bytes of randomness into buffer P.
+** ^The P parameter can be a NULL pointer.
+**
+** ^If this routine has not been previously called or if the previous
+** call had N less than one or a NULL pointer for P, then the PRNG is
+** seeded using randomness obtained from the xRandomness method of
+** the default [odbql_vfs] object.
+** ^If the previous call to this routine had an N of 1 or more and a
+** non-NULL P then the pseudo-randomness is generated
+** internally and without recourse to the [odbql_vfs] xRandomness
+** method.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_randomness(int N, void *P);
+
+/*
+** CAPI3REF: Compile-Time Authorization Callbacks
+** METHOD: odbql
+**
+** ^This routine registers an authorizer callback with a particular
+** [database connection], supplied in the first argument.
+** ^The authorizer callback is invoked as SQL statements are being compiled
+** by [odbql_prepare()] or its variants [odbql_prepare_v2()],
+** [odbql_prepare16()] and [odbql_prepare16_v2()].  ^At various
+** points during the compilation process, as logic is being created
+** to perform various actions, the authorizer callback is invoked to
+** see if those actions are allowed.  ^The authorizer callback should
+** return [ODBQL_OK] to allow the action, [ODBQL_IGNORE] to disallow the
+** specific action but allow the SQL statement to continue to be
+** compiled, or [ODBQL_DENY] to cause the entire SQL statement to be
+** rejected with an error.  ^If the authorizer callback returns
+** any value other than [ODBQL_IGNORE], [ODBQL_OK], or [ODBQL_DENY]
+** then the [odbql_prepare_v2()] or equivalent call that triggered
+** the authorizer will fail with an error message.
+**
+** When the callback returns [ODBQL_OK], that means the operation
+** requested is ok.  ^When the callback returns [ODBQL_DENY], the
+** [odbql_prepare_v2()] or equivalent call that triggered the
+** authorizer will fail with an error message explaining that
+** access is denied. 
+**
+** ^The first parameter to the authorizer callback is a copy of the third
+** parameter to the odbql_set_authorizer() interface. ^The second parameter
+** to the callback is an integer [ODBQL_COPY | action code] that specifies
+** the particular action to be authorized. ^The third through sixth parameters
+** to the callback are zero-terminated strings that contain additional
+** details about the action to be authorized.
+**
+** ^If the action code is [ODBQL_READ]
+** and the callback returns [ODBQL_IGNORE] then the
+** [prepared statement] statement is constructed to substitute
+** a NULL value in place of the table column that would have
+** been read if [ODBQL_OK] had been returned.  The [ODBQL_IGNORE]
+** return can be used to deny an untrusted user access to individual
+** columns of a table.
+** ^If the action code is [ODBQL_DELETE] and the callback returns
+** [ODBQL_IGNORE] then the [DELETE] operation proceeds but the
+** [truncate optimization] is disabled and all rows are deleted individually.
+**
+** An authorizer is used when [odbql_prepare | preparing]
+** SQL statements from an untrusted source, to ensure that the SQL statements
+** do not try to access data they are not allowed to see, or that they do not
+** try to execute malicious statements that damage the database.  For
+** example, an application may allow a user to enter arbitrary
+** SQL queries for evaluation by a database.  But the application does
+** not want the user to be able to make arbitrary changes to the
+** database.  An authorizer could then be put in place while the
+** user-entered SQL is being [odbql_prepare | prepared] that
+** disallows everything except [SELECT] statements.
+**
+** Applications that need to process SQL from untrusted sources
+** might also consider lowering resource limits using [odbql_limit()]
+** and limiting database size using the [max_page_count] [PRAGMA]
+** in addition to using an authorizer.
+**
+** ^(Only a single authorizer can be in place on a database connection
+** at a time.  Each call to odbql_set_authorizer overrides the
+** previous call.)^  ^Disable the authorizer by installing a NULL callback.
+** The authorizer is disabled by default.
+**
+** The authorizer callback must not do anything that will modify
+** the database connection that invoked the authorizer callback.
+** Note that [odbql_prepare_v2()] and [odbql_step()] both modify their
+** database connections for the meaning of "modify" in this paragraph.
+**
+** ^When [odbql_prepare_v2()] is used to prepare a statement, the
+** statement might be re-prepared during [odbql_step()] due to a 
+** schema change.  Hence, the application should ensure that the
+** correct authorizer callback remains in place during the [odbql_step()].
+**
+** ^Note that the authorizer callback is invoked only during
+** [odbql_prepare()] or its variants.  Authorization is not
+** performed during statement evaluation in [odbql_step()], unless
+** as stated in the previous paragraph, odbql_step() invokes
+** odbql_prepare_v2() to reprepare a statement after a schema change.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_set_authorizer(
+  odbql*,
+  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+  void *pUserData
+);
+
+/*
+** CAPI3REF: Authorizer Return Codes
+**
+** The [odbql_set_authorizer | authorizer callback function] must
+** return either [ODBQL_OK] or one of these two constants in order
+** to signal SQLite whether or not the action is permitted.  See the
+** [odbql_set_authorizer | authorizer documentation] for additional
+** information.
+**
+** Note that ODBQL_IGNORE is also used as a [conflict resolution mode]
+** returned from the [odbql_vtab_on_conflict()] interface.
+*/
+#define ODBQL_DENY   1   /* Abort the SQL statement with an error */
+#define ODBQL_IGNORE 2   /* Don't allow access, but don't generate an error */
+
+/*
+** CAPI3REF: Authorizer Action Codes
+**
+** The [odbql_set_authorizer()] interface registers a callback function
+** that is invoked to authorize certain SQL statement actions.  The
+** second parameter to the callback is an integer code that specifies
+** what action is being authorized.  These are the integer action codes that
+** the authorizer callback may be passed.
+**
+** These action code values signify what kind of operation is to be
+** authorized.  The 3rd and 4th parameters to the authorization
+** callback function will be parameters or NULL depending on which of these
+** codes is used as the second parameter.  ^(The 5th parameter to the
+** authorizer callback is the name of the database ("main", "temp",
+** etc.) if applicable.)^  ^The 6th parameter to the authorizer callback
+** is the name of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** top-level SQL code.
+*/
+/******************************************* 3rd ************ 4th ***********/
+#define ODBQL_CREATE_INDEX          1   /* Index Name      Table Name      */
+#define ODBQL_CREATE_TABLE          2   /* Table Name      NULL            */
+#define ODBQL_CREATE_TEMP_INDEX     3   /* Index Name      Table Name      */
+#define ODBQL_CREATE_TEMP_TABLE     4   /* Table Name      NULL            */
+#define ODBQL_CREATE_TEMP_TRIGGER   5   /* Trigger Name    Table Name      */
+#define ODBQL_CREATE_TEMP_VIEW      6   /* View Name       NULL            */
+#define ODBQL_CREATE_TRIGGER        7   /* Trigger Name    Table Name      */
+#define ODBQL_CREATE_VIEW           8   /* View Name       NULL            */
+#define ODBQL_DELETE                9   /* Table Name      NULL            */
+#define ODBQL_DROP_INDEX           10   /* Index Name      Table Name      */
+#define ODBQL_DROP_TABLE           11   /* Table Name      NULL            */
+#define ODBQL_DROP_TEMP_INDEX      12   /* Index Name      Table Name      */
+#define ODBQL_DROP_TEMP_TABLE      13   /* Table Name      NULL            */
+#define ODBQL_DROP_TEMP_TRIGGER    14   /* Trigger Name    Table Name      */
+#define ODBQL_DROP_TEMP_VIEW       15   /* View Name       NULL            */
+#define ODBQL_DROP_TRIGGER         16   /* Trigger Name    Table Name      */
+#define ODBQL_DROP_VIEW            17   /* View Name       NULL            */
+#define ODBQL_INSERT               18   /* Table Name      NULL            */
+#define ODBQL_PRAGMA               19   /* Pragma Name     1st arg or NULL */
+#define ODBQL_READ                 20   /* Table Name      Column Name     */
+#define ODBQL_SELECT               21   /* NULL            NULL            */
+#define ODBQL_TRANSACTION          22   /* Operation       NULL            */
+#define ODBQL_UPDATE               23   /* Table Name      Column Name     */
+#define ODBQL_ATTACH               24   /* Filename        NULL            */
+#define ODBQL_DETACH               25   /* Database Name   NULL            */
+#define ODBQL_ALTER_TABLE          26   /* Database Name   Table Name      */
+#define ODBQL_REINDEX              27   /* Index Name      NULL            */
+#define ODBQL_ANALYZE              28   /* Table Name      NULL            */
+#define ODBQL_CREATE_VTABLE        29   /* Table Name      Module Name     */
+#define ODBQL_DROP_VTABLE          30   /* Table Name      Module Name     */
+#define ODBQL_FUNCTION             31   /* NULL            Function Name   */
+#define ODBQL_SAVEPOINT            32   /* Operation       Savepoint Name  */
+#define ODBQL_COPY                  0   /* No longer used */
+#define ODBQL_RECURSIVE            33   /* NULL            NULL            */
+
+/*
+** CAPI3REF: Tracing And Profiling Functions
+** METHOD: odbql
+**
+** These routines register callback functions that can be used for
+** tracing and profiling the execution of SQL statements.
+**
+** ^The callback function registered by odbql_trace() is invoked at
+** various times when an SQL statement is being run by [odbql_step()].
+** ^The odbql_trace() callback is invoked with a UTF-8 rendering of the
+** SQL statement text as the statement first begins executing.
+** ^(Additional odbql_trace() callbacks might occur
+** as each triggered subprogram is entered.  The callbacks for triggers
+** contain a UTF-8 SQL comment that identifies the trigger.)^
+**
+** The [ODBQL_TRACE_SIZE_LIMIT] compile-time option can be used to limit
+** the length of [bound parameter] expansion in the output of odbql_trace().
+**
+** ^The callback function registered by odbql_profile() is invoked
+** as each SQL statement finishes.  ^The profile callback contains
+** the original statement text and an estimate of wall-clock time
+** of how long that statement took to run.  ^The profile callback
+** time is in units of nanoseconds, however the current implementation
+** is only capable of millisecond resolution so the six least significant
+** digits in the time are meaningless.  Future versions of SQLite
+** might provide greater resolution on the profiler callback.  The
+** odbql_profile() function is considered experimental and is
+** subject to change in future versions of SQLite.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_trace(odbql*, void(*xTrace)(void*,const char*), void*);
+ODBQL_API ODBQL_EXPERIMENTAL void *ODBQL_STDCALL odbql_profile(odbql*,
+   void(*xProfile)(void*,const char*,odbql_uint64), void*);
+
+/*
+** CAPI3REF: Query Progress Callbacks
+** METHOD: odbql
+**
+** ^The odbql_progress_handler(D,N,X,P) interface causes the callback
+** function X to be invoked periodically during long running calls to
+** [odbql_exec()], [odbql_step()] and [odbql_get_table()] for
+** database connection D.  An example use for this
+** interface is to keep a GUI updated during a large query.
+**
+** ^The parameter P is passed through as the only parameter to the 
+** callback function X.  ^The parameter N is the approximate number of 
+** [virtual machine instructions] that are evaluated between successive
+** invocations of the callback X.  ^If N is less than one then the progress
+** handler is disabled.
+**
+** ^Only a single progress handler may be defined at one time per
+** [database connection]; setting a new progress handler cancels the
+** old one.  ^Setting parameter X to NULL disables the progress handler.
+** ^The progress handler is also disabled by setting N to a value less
+** than 1.
+**
+** ^If the progress callback returns non-zero, the operation is
+** interrupted.  This feature can be used to implement a
+** "Cancel" button on a GUI progress dialog box.
+**
+** The progress handler callback must not do anything that will modify
+** the database connection that invoked the progress handler.
+** Note that [odbql_prepare_v2()] and [odbql_step()] both modify their
+** database connections for the meaning of "modify" in this paragraph.
+**
+*/
+ODBQL_API void ODBQL_STDCALL odbql_progress_handler(odbql*, int, int(*)(void*), void*);
+
+/*
+** CAPI3REF: Opening A New Database Connection
+** CONSTRUCTOR: odbql
+**
+** ^These routines open an SQLite database file as specified by the 
+** filename argument. ^The filename argument is interpreted as UTF-8 for
+** odbql_open() and odbql_open_v2() and as UTF-16 in the native byte
+** order for odbql_open16(). ^(A [database connection] handle is usually
+** returned in *ppDb, even if an error occurs.  The only exception is that
+** if SQLite is unable to allocate memory to hold the [odbql] object,
+** a NULL will be written into *ppDb instead of a pointer to the [odbql]
+** object.)^ ^(If the database is opened (and/or created) successfully, then
+** [ODBQL_OK] is returned.  Otherwise an [error code] is returned.)^ ^The
+** [odbql_errmsg()] or [odbql_errmsg16()] routines can be used to obtain
+** an English language description of the error following a failure of any
+** of the odbql_open() routines.
+**
+** ^The default encoding will be UTF-8 for databases created using
+** odbql_open() or odbql_open_v2().  ^The default encoding for databases
+** created using odbql_open16() will be UTF-16 in the native byte order.
+**
+** Whether or not an error occurs when it is opened, resources
+** associated with the [database connection] handle should be released by
+** passing it to [odbql_close()] when it is no longer required.
+**
+** The odbql_open_v2() interface works like odbql_open()
+** except that it accepts two additional parameters for additional control
+** over the new database connection.  ^(The flags parameter to
+** odbql_open_v2() can take one of
+** the following three values, optionally combined with the 
+** [ODBQL_OPEN_NOMUTEX], [ODBQL_OPEN_FULLMUTEX], [ODBQL_OPEN_SHAREDCACHE],
+** [ODBQL_OPEN_PRIVATECACHE], and/or [ODBQL_OPEN_URI] flags:)^
+**
+** <dl>
+** ^(<dt>[ODBQL_OPEN_READONLY]</dt>
+** <dd>The database is opened in read-only mode.  If the database does not
+** already exist, an error is returned.</dd>)^
+**
+** ^(<dt>[ODBQL_OPEN_READWRITE]</dt>
+** <dd>The database is opened for reading and writing if possible, or reading
+** only if the file is write protected by the operating system.  In either
+** case the database must already exist, otherwise an error is returned.</dd>)^
+**
+** ^(<dt>[ODBQL_OPEN_READWRITE] | [ODBQL_OPEN_CREATE]</dt>
+** <dd>The database is opened for reading and writing, and is created if
+** it does not already exist. This is the behavior that is always used for
+** odbql_open() and odbql_open16().</dd>)^
+** </dl>
+**
+** If the 3rd parameter to odbql_open_v2() is not one of the
+** combinations shown above optionally combined with other
+** [ODBQL_OPEN_READONLY | ODBQL_OPEN_* bits]
+** then the behavior is undefined.
+**
+** ^If the [ODBQL_OPEN_NOMUTEX] flag is set, then the database connection
+** opens in the multi-thread [threading mode] as long as the single-thread
+** mode has not been set at compile-time or start-time.  ^If the
+** [ODBQL_OPEN_FULLMUTEX] flag is set then the database connection opens
+** in the serialized [threading mode] unless single-thread was
+** previously selected at compile-time or start-time.
+** ^The [ODBQL_OPEN_SHAREDCACHE] flag causes the database connection to be
+** eligible to use [shared cache mode], regardless of whether or not shared
+** cache is enabled using [odbql_enable_shared_cache()].  ^The
+** [ODBQL_OPEN_PRIVATECACHE] flag causes the database connection to not
+** participate in [shared cache mode] even if it is enabled.
+**
+** ^The fourth parameter to odbql_open_v2() is the name of the
+** [odbql_vfs] object that defines the operating system interface that
+** the new database connection should use.  ^If the fourth parameter is
+** a NULL pointer then the default [odbql_vfs] object is used.
+**
+** ^If the filename is ":memory:", then a private, temporary in-memory database
+** is created for the connection.  ^This in-memory database will vanish when
+** the database connection is closed.  Future versions of SQLite might
+** make use of additional special filenames that begin with the ":" character.
+** It is recommended that when a database filename actually does begin with
+** a ":" character you should prefix the filename with a pathname such as
+** "./" to avoid ambiguity.
+**
+** ^If the filename is an empty string, then a private, temporary
+** on-disk database will be created.  ^This private database will be
+** automatically deleted as soon as the database connection is closed.
+**
+** [[URI filenames in odbql_open()]] <h3>URI Filenames</h3>
+**
+** ^If [URI filename] interpretation is enabled, and the filename argument
+** begins with "file:", then the filename is interpreted as a URI. ^URI
+** filename interpretation is enabled if the [ODBQL_OPEN_URI] flag is
+** set in the fourth argument to odbql_open_v2(), or if it has
+** been enabled globally using the [ODBQL_CONFIG_URI] option with the
+** [odbql_config()] method or by the [ODBQL_USE_URI] compile-time option.
+** As of SQLite version 3.7.7, URI filename interpretation is turned off
+** by default, but future releases of SQLite might enable URI filename
+** interpretation by default.  See "[URI filenames]" for additional
+** information.
+**
+** URI filenames are parsed according to RFC 3986. ^If the URI contains an
+** authority, then it must be either an empty string or the string 
+** "localhost". ^If the authority is not an empty string or "localhost", an 
+** error is returned to the caller. ^The fragment component of a URI, if 
+** present, is ignored.
+**
+** ^SQLite uses the path component of the URI as the name of the disk file
+** which contains the database. ^If the path begins with a '/' character, 
+** then it is interpreted as an absolute path. ^If the path does not begin 
+** with a '/' (meaning that the authority section is omitted from the URI)
+** then the path is interpreted as a relative path. 
+** ^(On windows, the first component of an absolute path 
+** is a drive specification (e.g. "C:").)^
+**
+** [[core URI query parameters]]
+** The query component of a URI may contain parameters that are interpreted
+** either by SQLite itself, or by a [VFS | custom VFS implementation].
+** SQLite and its built-in [VFSes] interpret the
+** following query parameters:
+**
+** <ul>
+**   <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
+**     a VFS object that provides the operating system interface that should
+**     be used to access the database file on disk. ^If this option is set to
+**     an empty string the default VFS object is used. ^Specifying an unknown
+**     VFS is an error. ^If odbql_open_v2() is used and the vfs option is
+**     present, then the VFS specified by the option takes precedence over
+**     the value passed as the fourth parameter to odbql_open_v2().
+**
+**   <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
+**     "rwc", or "memory". Attempting to set it to any other value is
+**     an error)^. 
+**     ^If "ro" is specified, then the database is opened for read-only 
+**     access, just as if the [ODBQL_OPEN_READONLY] flag had been set in the 
+**     third argument to odbql_open_v2(). ^If the mode option is set to 
+**     "rw", then the database is opened for read-write (but not create) 
+**     access, as if ODBQL_OPEN_READWRITE (but not ODBQL_OPEN_CREATE) had 
+**     been set. ^Value "rwc" is equivalent to setting both 
+**     ODBQL_OPEN_READWRITE and ODBQL_OPEN_CREATE.  ^If the mode option is
+**     set to "memory" then a pure [in-memory database] that never reads
+**     or writes from disk is used. ^It is an error to specify a value for
+**     the mode parameter that is less restrictive than that specified by
+**     the flags passed in the third parameter to odbql_open_v2().
+**
+**   <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
+**     "private". ^Setting it to "shared" is equivalent to setting the
+**     ODBQL_OPEN_SHAREDCACHE bit in the flags argument passed to
+**     odbql_open_v2(). ^Setting the cache parameter to "private" is 
+**     equivalent to setting the ODBQL_OPEN_PRIVATECACHE bit.
+**     ^If odbql_open_v2() is used and the "cache" parameter is present in
+**     a URI filename, its value overrides any behavior requested by setting
+**     ODBQL_OPEN_PRIVATECACHE or ODBQL_OPEN_SHAREDCACHE flag.
+**
+**  <li> <b>psow</b>: ^The psow parameter indicates whether or not the
+**     [powersafe overwrite] property does or does not apply to the
+**     storage media on which the database file resides.
+**
+**  <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter
+**     which if set disables file locking in rollback journal modes.  This
+**     is useful for accessing a database on a filesystem that does not
+**     support locking.  Caution:  Database corruption might result if two
+**     or more processes write to the same database and any one of those
+**     processes uses nolock=1.
+**
+**  <li> <b>immutable</b>: ^The immutable parameter is a boolean query
+**     parameter that indicates that the database file is stored on
+**     read-only media.  ^When immutable is set, SQLite assumes that the
+**     database file cannot be changed, even by a process with higher
+**     privilege, and so the database is opened read-only and all locking
+**     and change detection is disabled.  Caution: Setting the immutable
+**     property on a database file that does in fact change can result
+**     in incorrect query results and/or [ODBQL_CORRUPT] errors.
+**     See also: [ODBQL_IOCAP_IMMUTABLE].
+**       
+** </ul>
+**
+** ^Specifying an unknown parameter in the query component of a URI is not an
+** error.  Future versions of SQLite might understand additional query
+** parameters.  See "[query parameters with special meaning to SQLite]" for
+** additional information.
+**
+** [[URI filename examples]] <h3>URI filename examples</h3>
+**
+** <table border="1" align=center cellpadding=5>
+** <tr><th> URI filenames <th> Results
+** <tr><td> file:data.db <td> 
+**          Open the file "data.db" in the current directory.
+** <tr><td> file:/home/fred/data.db<br>
+**          file:///home/fred/data.db <br> 
+**          file://localhost/home/fred/data.db <br> <td> 
+**          Open the database file "/home/fred/data.db".
+** <tr><td> file://darkstar/home/fred/data.db <td> 
+**          An error. "darkstar" is not a recognized authority.
+** <tr><td style="white-space:nowrap"> 
+**          file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
+**     <td> Windows only: Open the file "data.db" on fred's desktop on drive
+**          C:. Note that the %20 escaping in this example is not strictly 
+**          necessary - space characters can be used literally
+**          in URI filenames.
+** <tr><td> file:data.db?mode=ro&cache=private <td> 
+**          Open file "data.db" in the current directory for read-only access.
+**          Regardless of whether or not shared-cache mode is enabled by
+**          default, use a private cache.
+** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
+**          Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
+**          that uses dot-files in place of posix advisory locking.
+** <tr><td> file:data.db?mode=readonly <td> 
+**          An error. "readonly" is not a valid option for the "mode" parameter.
+** </table>
+**
+** ^URI hexadecimal escape sequences (%HH) are supported within the path and
+** query components of a URI. A hexadecimal escape sequence consists of a
+** percent sign - "%" - followed by exactly two hexadecimal digits 
+** specifying an octet value. ^Before the path or query components of a
+** URI filename are interpreted, they are encoded using UTF-8 and all 
+** hexadecimal escape sequences replaced by a single byte containing the
+** corresponding octet. If this process generates an invalid UTF-8 encoding,
+** the results are undefined.
+**
+** <b>Note to Windows users:</b>  The encoding used for the filename argument
+** of odbql_open() and odbql_open_v2() must be UTF-8, not whatever
+** codepage is currently defined.  Filenames containing international
+** characters must be converted to UTF-8 prior to passing them into
+** odbql_open() or odbql_open_v2().
+**
+** <b>Note to Windows Runtime users:</b>  The temporary directory must be set
+** prior to calling odbql_open() or odbql_open_v2().  Otherwise, various
+** features that require the use of temporary files may fail.
+**
+** See also: [odbql_temp_directory]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_open(
+  const char *filename,   /* Database filename (UTF-8) */
+  odbql **ppDb          /* OUT: SQLite db handle */
+);
+ODBQL_API int ODBQL_STDCALL odbql_open16(
+  const void *filename,   /* Database filename (UTF-16) */
+  odbql **ppDb          /* OUT: SQLite db handle */
+);
+ODBQL_API int ODBQL_STDCALL odbql_open_v2(
+  const char *filename,   /* Database filename (UTF-8) */
+  odbql **ppDb,         /* OUT: SQLite db handle */
+  int flags,              /* Flags */
+  const char *zVfs        /* Name of VFS module to use */
+);
+
+/*
+** CAPI3REF: Obtain Values For URI Parameters
+**
+** These are utility routines, useful to VFS implementations, that check
+** to see if a database file was a URI that contained a specific query 
+** parameter, and if so obtains the value of that query parameter.
+**
+** If F is the database filename pointer passed into the xOpen() method of 
+** a VFS implementation when the flags parameter to xOpen() has one or 
+** more of the [ODBQL_OPEN_URI] or [ODBQL_OPEN_MAIN_DB] bits set and
+** P is the name of the query parameter, then
+** odbql_uri_parameter(F,P) returns the value of the P
+** parameter if it exists or a NULL pointer if P does not appear as a 
+** query parameter on F.  If P is a query parameter of F
+** has no explicit value, then odbql_uri_parameter(F,P) returns
+** a pointer to an empty string.
+**
+** The odbql_uri_boolean(F,P,B) routine assumes that P is a boolean
+** parameter and returns true (1) or false (0) according to the value
+** of P.  The odbql_uri_boolean(F,P,B) routine returns true (1) if the
+** value of query parameter P is one of "yes", "true", or "on" in any
+** case or if the value begins with a non-zero number.  The 
+** odbql_uri_boolean(F,P,B) routines returns false (0) if the value of
+** query parameter P is one of "no", "false", or "off" in any case or
+** if the value begins with a numeric zero.  If P is not a query
+** parameter on F or if the value of P is does not match any of the
+** above, then odbql_uri_boolean(F,P,B) returns (B!=0).
+**
+** The odbql_uri_int64(F,P,D) routine converts the value of P into a
+** 64-bit signed integer and returns that integer, or D if P does not
+** exist.  If the value of P is something other than an integer, then
+** zero is returned.
+** 
+** If F is a NULL pointer, then odbql_uri_parameter(F,P) returns NULL and
+** odbql_uri_boolean(F,P,B) returns B.  If F is not a NULL pointer and
+** is not a database file pathname pointer that SQLite passed into the xOpen
+** VFS method, then the behavior of this routine is undefined and probably
+** undesirable.
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_uri_parameter(const char *zFilename, const char *zParam);
+ODBQL_API int ODBQL_STDCALL odbql_uri_boolean(const char *zFile, const char *zParam, int bDefault);
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_uri_int64(const char*, const char*, odbql_int64);
+
+
+/*
+** CAPI3REF: Error Codes And Messages
+** METHOD: odbql
+**
+** ^If the most recent odbql_* API call associated with 
+** [database connection] D failed, then the odbql_errcode(D) interface
+** returns the numeric [result code] or [extended result code] for that
+** API call.
+** If the most recent API call was successful,
+** then the return value from odbql_errcode() is undefined.
+** ^The odbql_extended_errcode()
+** interface is the same except that it always returns the 
+** [extended result code] even when extended result codes are
+** disabled.
+**
+** ^The odbql_errmsg() and odbql_errmsg16() return English-language
+** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** ^(Memory to hold the error message string is managed internally.
+** The application does not need to worry about freeing the result.
+** However, the error string might be overwritten or deallocated by
+** subsequent calls to other SQLite interface functions.)^
+**
+** ^The odbql_errstr() interface returns the English-language text
+** that describes the [result code], as UTF-8.
+** ^(Memory to hold the error message string is managed internally
+** and must not be freed by the application)^.
+**
+** When the serialized [threading mode] is in use, it might be the
+** case that a second error occurs on a separate thread in between
+** the time of the first error and the call to these interfaces.
+** When that happens, the second error will be reported since these
+** interfaces always report the most recent result.  To avoid
+** this, each thread can obtain exclusive use of the [database connection] D
+** by invoking [odbql_mutex_enter]([odbql_db_mutex](D)) before beginning
+** to use D and invoking [odbql_mutex_leave]([odbql_db_mutex](D)) after
+** all calls to the interfaces listed here are completed.
+**
+** If an interface fails with ODBQL_MISUSE, that means the interface
+** was invoked incorrectly by the application.  In that case, the
+** error code and message may or may not be set.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_errcode(odbql *db);
+ODBQL_API int ODBQL_STDCALL odbql_extended_errcode(odbql *db);
+ODBQL_API const char *ODBQL_STDCALL odbql_errmsg(odbql*);
+ODBQL_API const void *ODBQL_STDCALL odbql_errmsg16(odbql*);
+ODBQL_API const char *ODBQL_STDCALL odbql_errstr(int);
+
+/*
+** CAPI3REF: Prepared Statement Object
+** KEYWORDS: {prepared statement} {prepared statements}
+**
+** An instance of this object represents a single SQL statement that
+** has been compiled into binary form and is ready to be evaluated.
+**
+** Think of each SQL statement as a separate computer program.  The
+** original SQL text is source code.  A prepared statement object 
+** is the compiled object code.  All SQL must be converted into a
+** prepared statement before it can be run.
+**
+** The life-cycle of a prepared statement object usually goes like this:
+**
+** <ol>
+** <li> Create the prepared statement object using [odbql_prepare_v2()].
+** <li> Bind values to [parameters] using the odbql_bind_*()
+**      interfaces.
+** <li> Run the SQL by calling [odbql_step()] one or more times.
+** <li> Reset the prepared statement using [odbql_reset()] then go back
+**      to step 2.  Do this zero or more times.
+** <li> Destroy the object using [odbql_finalize()].
+** </ol>
+*/
+typedef struct odbql_stmt odbql_stmt;
+
+/*
+** CAPI3REF: Run-time Limits
+** METHOD: odbql
+**
+** ^(This interface allows the size of various constructs to be limited
+** on a connection by connection basis.  The first parameter is the
+** [database connection] whose limit is to be set or queried.  The
+** second parameter is one of the [limit categories] that define a
+** class of constructs to be size limited.  The third parameter is the
+** new limit for that construct.)^
+**
+** ^If the new limit is a negative number, the limit is unchanged.
+** ^(For each limit category ODBQL_LIMIT_<i>NAME</i> there is a 
+** [limits | hard upper bound]
+** set at compile-time by a C preprocessor macro called
+** [limits | ODBQL_MAX_<i>NAME</i>].
+** (The "_LIMIT_" in the name is changed to "_MAX_".))^
+** ^Attempts to increase a limit above its hard upper bound are
+** silently truncated to the hard upper bound.
+**
+** ^Regardless of whether or not the limit was changed, the 
+** [odbql_limit()] interface returns the prior value of the limit.
+** ^Hence, to find the current value of a limit without changing it,
+** simply invoke this interface with the third parameter set to -1.
+**
+** Run-time limits are intended for use in applications that manage
+** both their own internal database and also databases that are controlled
+** by untrusted external sources.  An example application might be a
+** web browser that has its own databases for storing history and
+** separate databases controlled by JavaScript applications downloaded
+** off the Internet.  The internal databases can be given the
+** large, default limits.  Databases managed by external sources can
+** be given much smaller limits designed to prevent a denial of service
+** attack.  Developers might also want to use the [odbql_set_authorizer()]
+** interface to further control untrusted SQL.  The size of the database
+** created by an untrusted script can be contained using the
+** [max_page_count] [PRAGMA].
+**
+** New run-time limit categories may be added in future releases.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_limit(odbql*, int id, int newVal);
+
+/*
+** CAPI3REF: Run-Time Limit Categories
+** KEYWORDS: {limit category} {*limit categories}
+**
+** These constants define various performance limits
+** that can be lowered at run-time using [odbql_limit()].
+** The synopsis of the meanings of the various limits is shown below.
+** Additional information is available at [limits | Limits in SQLite].
+**
+** <dl>
+** [[ODBQL_LIMIT_LENGTH]] ^(<dt>ODBQL_LIMIT_LENGTH</dt>
+** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^
+**
+** [[ODBQL_LIMIT_SQL_LENGTH]] ^(<dt>ODBQL_LIMIT_SQL_LENGTH</dt>
+** <dd>The maximum length of an SQL statement, in bytes.</dd>)^
+**
+** [[ODBQL_LIMIT_COLUMN]] ^(<dt>ODBQL_LIMIT_COLUMN</dt>
+** <dd>The maximum number of columns in a table definition or in the
+** result set of a [SELECT] or the maximum number of columns in an index
+** or in an ORDER BY or GROUP BY clause.</dd>)^
+**
+** [[ODBQL_LIMIT_EXPR_DEPTH]] ^(<dt>ODBQL_LIMIT_EXPR_DEPTH</dt>
+** <dd>The maximum depth of the parse tree on any expression.</dd>)^
+**
+** [[ODBQL_LIMIT_COMPOUND_SELECT]] ^(<dt>ODBQL_LIMIT_COMPOUND_SELECT</dt>
+** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^
+**
+** [[ODBQL_LIMIT_VDBE_OP]] ^(<dt>ODBQL_LIMIT_VDBE_OP</dt>
+** <dd>The maximum number of instructions in a virtual machine program
+** used to implement an SQL statement.  This limit is not currently
+** enforced, though that might be added in some future release of
+** SQLite.</dd>)^
+**
+** [[ODBQL_LIMIT_FUNCTION_ARG]] ^(<dt>ODBQL_LIMIT_FUNCTION_ARG</dt>
+** <dd>The maximum number of arguments on a function.</dd>)^
+**
+** [[ODBQL_LIMIT_ATTACHED]] ^(<dt>ODBQL_LIMIT_ATTACHED</dt>
+** <dd>The maximum number of [ATTACH | attached databases].)^</dd>
+**
+** [[ODBQL_LIMIT_LIKE_PATTERN_LENGTH]]
+** ^(<dt>ODBQL_LIMIT_LIKE_PATTERN_LENGTH</dt>
+** <dd>The maximum length of the pattern argument to the [LIKE] or
+** [GLOB] operators.</dd>)^
+**
+** [[ODBQL_LIMIT_VARIABLE_NUMBER]]
+** ^(<dt>ODBQL_LIMIT_VARIABLE_NUMBER</dt>
+** <dd>The maximum index number of any [parameter] in an SQL statement.)^
+**
+** [[ODBQL_LIMIT_TRIGGER_DEPTH]] ^(<dt>ODBQL_LIMIT_TRIGGER_DEPTH</dt>
+** <dd>The maximum depth of recursion for triggers.</dd>)^
+**
+** [[ODBQL_LIMIT_WORKER_THREADS]] ^(<dt>ODBQL_LIMIT_WORKER_THREADS</dt>
+** <dd>The maximum number of auxiliary worker threads that a single
+** [prepared statement] may start.</dd>)^
+** </dl>
+*/
+#define ODBQL_LIMIT_LENGTH                    0
+#define ODBQL_LIMIT_SQL_LENGTH                1
+#define ODBQL_LIMIT_COLUMN                    2
+#define ODBQL_LIMIT_EXPR_DEPTH                3
+#define ODBQL_LIMIT_COMPOUND_SELECT           4
+#define ODBQL_LIMIT_VDBE_OP                   5
+#define ODBQL_LIMIT_FUNCTION_ARG              6
+#define ODBQL_LIMIT_ATTACHED                  7
+#define ODBQL_LIMIT_LIKE_PATTERN_LENGTH       8
+#define ODBQL_LIMIT_VARIABLE_NUMBER           9
+#define ODBQL_LIMIT_TRIGGER_DEPTH            10
+#define ODBQL_LIMIT_WORKER_THREADS           11
+
+/*
+** CAPI3REF: Compiling An SQL Statement
+** KEYWORDS: {SQL statement compiler}
+** METHOD: odbql
+** CONSTRUCTOR: odbql_stmt
+**
+** To execute an SQL query, it must first be compiled into a byte-code
+** program using one of these routines.
+**
+** The first argument, "db", is a [database connection] obtained from a
+** prior successful call to [odbql_open()], [odbql_open_v2()] or
+** [odbql_open16()].  The database connection must not have been closed.
+**
+** The second argument, "zSql", is the statement to be compiled, encoded
+** as either UTF-8 or UTF-16.  The odbql_prepare() and odbql_prepare_v2()
+** interfaces use UTF-8, and odbql_prepare16() and odbql_prepare16_v2()
+** use UTF-16.
+**
+** ^If the nByte argument is negative, then zSql is read up to the
+** first zero terminator. ^If nByte is positive, then it is the
+** number of bytes read from zSql.  ^If nByte is zero, then no prepared
+** statement is generated.
+** If the caller knows that the supplied string is nul-terminated, then
+** there is a small performance advantage to passing an nByte parameter that
+** is the number of bytes in the input string <i>including</i>
+** the nul-terminator.
+**
+** ^If pzTail is not NULL then *pzTail is made to point to the first byte
+** past the end of the first SQL statement in zSql.  These routines only
+** compile the first statement in zSql, so *pzTail is left pointing to
+** what remains uncompiled.
+**
+** ^*ppStmt is left pointing to a compiled [prepared statement] that can be
+** executed using [odbql_step()].  ^If there is an error, *ppStmt is set
+** to NULL.  ^If the input text contains no SQL (if the input is an empty
+** string or a comment) then *ppStmt is set to NULL.
+** The calling procedure is responsible for deleting the compiled
+** SQL statement using [odbql_finalize()] after it has finished with it.
+** ppStmt may not be NULL.
+**
+** ^On success, the odbql_prepare() family of routines return [ODBQL_OK];
+** otherwise an [error code] is returned.
+**
+** The odbql_prepare_v2() and odbql_prepare16_v2() interfaces are
+** recommended for all new programs. The two older interfaces are retained
+** for backwards compatibility, but their use is discouraged.
+** ^In the "v2" interfaces, the prepared statement
+** that is returned (the [odbql_stmt] object) contains a copy of the
+** original SQL text. This causes the [odbql_step()] interface to
+** behave differently in three ways:
+**
+** <ol>
+** <li>
+** ^If the database schema changes, instead of returning [ODBQL_SCHEMA] as it
+** always used to do, [odbql_step()] will automatically recompile the SQL
+** statement and try to run it again. As many as [ODBQL_MAX_SCHEMA_RETRY]
+** retries will occur before odbql_step() gives up and returns an error.
+** </li>
+**
+** <li>
+** ^When an error occurs, [odbql_step()] will return one of the detailed
+** [error codes] or [extended error codes].  ^The legacy behavior was that
+** [odbql_step()] would only return a generic [ODBQL_ERROR] result code
+** and the application would have to make a second call to [odbql_reset()]
+** in order to find the underlying cause of the problem. With the "v2" prepare
+** interfaces, the underlying reason for the error is returned immediately.
+** </li>
+**
+** <li>
+** ^If the specific value bound to [parameter | host parameter] in the 
+** WHERE clause might influence the choice of query plan for a statement,
+** then the statement will be automatically recompiled, as if there had been 
+** a schema change, on the first  [odbql_step()] call following any change
+** to the [odbql_bind_text | bindings] of that [parameter]. 
+** ^The specific value of WHERE-clause [parameter] might influence the 
+** choice of query plan if the parameter is the left-hand side of a [LIKE]
+** or [GLOB] operator or if the parameter is compared to an indexed column
+** and the [ODBQL_ENABLE_STAT3] compile-time option is enabled.
+** </li>
+** </ol>
+*/
+ODBQL_API int ODBQL_STDCALL odbql_prepare(
+  odbql *db,            /* Database handle */
+  const char *zSql,       /* SQL statement, UTF-8 encoded */
+  int nByte,              /* Maximum length of zSql in bytes. */
+  odbql_stmt **ppStmt,  /* OUT: Statement handle */
+  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+ODBQL_API int ODBQL_STDCALL odbql_prepare_v2(
+  odbql *db,            /* Database handle */
+  const char *zSql,       /* SQL statement, UTF-8 encoded */
+  int nByte,              /* Maximum length of zSql in bytes. */
+  odbql_stmt **ppStmt,  /* OUT: Statement handle */
+  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+ODBQL_API int ODBQL_STDCALL odbql_prepare16(
+  odbql *db,            /* Database handle */
+  const void *zSql,       /* SQL statement, UTF-16 encoded */
+  int nByte,              /* Maximum length of zSql in bytes. */
+  odbql_stmt **ppStmt,  /* OUT: Statement handle */
+  const void **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+ODBQL_API int ODBQL_STDCALL odbql_prepare16_v2(
+  odbql *db,            /* Database handle */
+  const void *zSql,       /* SQL statement, UTF-16 encoded */
+  int nByte,              /* Maximum length of zSql in bytes. */
+  odbql_stmt **ppStmt,  /* OUT: Statement handle */
+  const void **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+
+/*
+** CAPI3REF: Retrieving Statement SQL
+** METHOD: odbql_stmt
+**
+** ^This interface can be used to retrieve a saved copy of the original
+** SQL text used to create a [prepared statement] if that statement was
+** compiled using either [odbql_prepare_v2()] or [odbql_prepare16_v2()].
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_sql(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Determine If An SQL Statement Writes The Database
+** METHOD: odbql_stmt
+**
+** ^The odbql_stmt_readonly(X) interface returns true (non-zero) if
+** and only if the [prepared statement] X makes no direct changes to
+** the content of the database file.
+**
+** Note that [application-defined SQL functions] or
+** [virtual tables] might change the database indirectly as a side effect.  
+** ^(For example, if an application defines a function "eval()" that 
+** calls [odbql_exec()], then the following SQL statement would
+** change the database file through side-effects:
+**
+** <blockquote><pre>
+**    SELECT eval('DELETE FROM t1') FROM t2;
+** </pre></blockquote>
+**
+** But because the [SELECT] statement does not change the database file
+** directly, odbql_stmt_readonly() would still return true.)^
+**
+** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK],
+** [SAVEPOINT], and [RELEASE] cause odbql_stmt_readonly() to return true,
+** since the statements themselves do not actually modify the database but
+** rather they control the timing of when other statements modify the 
+** database.  ^The [ATTACH] and [DETACH] statements also cause
+** odbql_stmt_readonly() to return true since, while those statements
+** change the configuration of a database connection, they do not make 
+** changes to the content of the database files on disk.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_stmt_readonly(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Determine If A Prepared Statement Has Been Reset
+** METHOD: odbql_stmt
+**
+** ^The odbql_stmt_busy(S) interface returns true (non-zero) if the
+** [prepared statement] S has been stepped at least once using 
+** [odbql_step(S)] but has neither run to completion (returned
+** [ODBQL_DONE] from [odbql_step(S)]) nor
+** been reset using [odbql_reset(S)].  ^The odbql_stmt_busy(S)
+** interface returns false if S is a NULL pointer.  If S is not a 
+** NULL pointer and is not a pointer to a valid [prepared statement]
+** object, then the behavior is undefined and probably undesirable.
+**
+** This interface can be used in combination [odbql_next_stmt()]
+** to locate all prepared statements associated with a database 
+** connection that are in need of being reset.  This can be used,
+** for example, in diagnostic routines to search for prepared 
+** statements that are holding a transaction open.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_stmt_busy(odbql_stmt*);
+
+/*
+** CAPI3REF: Dynamically Typed Value Object
+** KEYWORDS: {protected odbql_value} {unprotected odbql_value}
+**
+** SQLite uses the odbql_value object to represent all values
+** that can be stored in a database table. SQLite uses dynamic typing
+** for the values it stores.  ^Values stored in odbql_value objects
+** can be integers, floating point values, strings, BLOBs, or NULL.
+**
+** An odbql_value object may be either "protected" or "unprotected".
+** Some interfaces require a protected odbql_value.  Other interfaces
+** will accept either a protected or an unprotected odbql_value.
+** Every interface that accepts odbql_value arguments specifies
+** whether or not it requires a protected odbql_value.  The
+** [odbql_value_dup()] interface can be used to construct a new 
+** protected odbql_value from an unprotected odbql_value.
+**
+** The terms "protected" and "unprotected" refer to whether or not
+** a mutex is held.  An internal mutex is held for a protected
+** odbql_value object but no mutex is held for an unprotected
+** odbql_value object.  If SQLite is compiled to be single-threaded
+** (with [ODBQL_THREADSAFE=0] and with [odbql_threadsafe()] returning 0)
+** or if SQLite is run in one of reduced mutex modes 
+** [ODBQL_CONFIG_SINGLETHREAD] or [ODBQL_CONFIG_MULTITHREAD]
+** then there is no distinction between protected and unprotected
+** odbql_value objects and they can be used interchangeably.  However,
+** for maximum code portability it is recommended that applications
+** still make the distinction between protected and unprotected
+** odbql_value objects even when not strictly required.
+**
+** ^The odbql_value objects that are passed as parameters into the
+** implementation of [application-defined SQL functions] are protected.
+** ^The odbql_value object returned by
+** [odbql_column_value()] is unprotected.
+** Unprotected odbql_value objects may only be used with
+** [odbql_result_value()] and [odbql_bind_value()].
+** The [odbql_value_blob | odbql_value_type()] family of
+** interfaces require protected odbql_value objects.
+*/
+typedef struct Mem odbql_value;
+
+/*
+** CAPI3REF: SQL Function Context Object
+**
+** The context in which an SQL function executes is stored in an
+** odbql_context object.  ^A pointer to an odbql_context object
+** is always first parameter to [application-defined SQL functions].
+** The application-defined SQL function implementation will pass this
+** pointer through into calls to [odbql_result_int | odbql_result()],
+** [odbql_aggregate_context()], [odbql_user_data()],
+** [odbql_context_db_handle()], [odbql_get_auxdata()],
+** and/or [odbql_set_auxdata()].
+*/
+typedef struct odbql_context odbql_context;
+
+/*
+** CAPI3REF: Binding Values To Prepared Statements
+** KEYWORDS: {host parameter} {host parameters} {host parameter name}
+** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
+** METHOD: odbql_stmt
+**
+** ^(In the SQL statement text input to [odbql_prepare_v2()] and its variants,
+** literals may be replaced by a [parameter] that matches one of following
+** templates:
+**
+** <ul>
+** <li>  ?
+** <li>  ?NNN
+** <li>  :VVV
+** <li>  @VVV
+** <li>  $VVV
+** </ul>
+**
+** In the templates above, NNN represents an integer literal,
+** and VVV represents an alphanumeric identifier.)^  ^The values of these
+** parameters (also called "host parameter names" or "SQL parameters")
+** can be set using the odbql_bind_*() routines defined here.
+**
+** ^The first argument to the odbql_bind_*() routines is always
+** a pointer to the [odbql_stmt] object returned from
+** [odbql_prepare_v2()] or its variants.
+**
+** ^The second argument is the index of the SQL parameter to be set.
+** ^The leftmost SQL parameter has an index of 1.  ^When the same named
+** SQL parameter is used more than once, second and subsequent
+** occurrences have the same index as the first occurrence.
+** ^The index for named parameters can be looked up using the
+** [odbql_bind_parameter_index()] API if desired.  ^The index
+** for "?NNN" parameters is the value of NNN.
+** ^The NNN value must be between 1 and the [odbql_limit()]
+** parameter [ODBQL_LIMIT_VARIABLE_NUMBER] (default value: 999).
+**
+** ^The third argument is the value to bind to the parameter.
+** ^If the third parameter to odbql_bind_text() or odbql_bind_text16()
+** or odbql_bind_blob() is a NULL pointer then the fourth parameter
+** is ignored and the end result is the same as odbql_bind_null().
+**
+** ^(In those routines that have a fourth argument, its value is the
+** number of bytes in the parameter.  To be clear: the value is the
+** number of <u>bytes</u> in the value, not the number of characters.)^
+** ^If the fourth parameter to odbql_bind_text() or odbql_bind_text16()
+** is negative, then the length of the string is
+** the number of bytes up to the first zero terminator.
+** If the fourth parameter to odbql_bind_blob() is negative, then
+** the behavior is undefined.
+** If a non-negative fourth parameter is provided to odbql_bind_text()
+** or odbql_bind_text16() or odbql_bind_text64() then
+** that parameter must be the byte offset
+** where the NUL terminator would occur assuming the string were NUL
+** terminated.  If any NUL characters occur at byte offsets less than 
+** the value of the fourth parameter then the resulting string value will
+** contain embedded NULs.  The result of expressions involving strings
+** with embedded NULs is undefined.
+**
+** ^The fifth argument to the BLOB and string binding interfaces
+** is a destructor used to dispose of the BLOB or
+** string after SQLite has finished with it.  ^The destructor is called
+** to dispose of the BLOB or string even if the call to bind API fails.
+** ^If the fifth argument is
+** the special value [ODBQL_STATIC], then SQLite assumes that the
+** information is in static, unmanaged space and does not need to be freed.
+** ^If the fifth argument has the value [ODBQL_TRANSIENT], then
+** SQLite makes its own private copy of the data immediately, before
+** the odbql_bind_*() routine returns.
+**
+** ^The sixth argument to odbql_bind_text64() must be one of
+** [ODBQL_UTF8], [ODBQL_UTF16], [ODBQL_UTF16BE], or [ODBQL_UTF16LE]
+** to specify the encoding of the text in the third parameter.  If
+** the sixth argument to odbql_bind_text64() is not one of the
+** allowed values shown above, or if the text encoding is different
+** from the encoding specified by the sixth parameter, then the behavior
+** is undefined.
+**
+** ^The odbql_bind_zeroblob() routine binds a BLOB of length N that
+** is filled with zeroes.  ^A zeroblob uses a fixed amount of memory
+** (just an integer to hold its size) while it is being processed.
+** Zeroblobs are intended to serve as placeholders for BLOBs whose
+** content is later written using
+** [odbql_blob_open | incremental BLOB I/O] routines.
+** ^A negative value for the zeroblob results in a zero-length BLOB.
+**
+** ^If any of the odbql_bind_*() routines are called with a NULL pointer
+** for the [prepared statement] or with a prepared statement for which
+** [odbql_step()] has been called more recently than [odbql_reset()],
+** then the call will return [ODBQL_MISUSE].  If any odbql_bind_()
+** routine is passed a [prepared statement] that has been finalized, the
+** result is undefined and probably harmful.
+**
+** ^Bindings are not cleared by the [odbql_reset()] routine.
+** ^Unbound parameters are interpreted as NULL.
+**
+** ^The odbql_bind_* routines return [ODBQL_OK] on success or an
+** [error code] if anything goes wrong.
+** ^[ODBQL_TOOBIG] might be returned if the size of a string or BLOB
+** exceeds limits imposed by [odbql_limit]([ODBQL_LIMIT_LENGTH]) or
+** [ODBQL_MAX_LENGTH].
+** ^[ODBQL_RANGE] is returned if the parameter
+** index is out of range.  ^[ODBQL_NOMEM] is returned if malloc() fails.
+**
+** See also: [odbql_bind_parameter_count()],
+** [odbql_bind_parameter_name()], and [odbql_bind_parameter_index()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_bind_blob(odbql_stmt*, int, const void*, int n, void(*)(void*));
+ODBQL_API int ODBQL_STDCALL odbql_bind_blob64(odbql_stmt*, int, const void*, odbql_uint64,
+                        void(*)(void*));
+ODBQL_API int ODBQL_STDCALL odbql_bind_double(odbql_stmt*, int, double);
+ODBQL_API int ODBQL_STDCALL odbql_bind_int(odbql_stmt*, int, int);
+ODBQL_API int ODBQL_STDCALL odbql_bind_int64(odbql_stmt*, int, odbql_int64);
+ODBQL_API int ODBQL_STDCALL odbql_bind_null(odbql_stmt*, int);
+ODBQL_API int ODBQL_STDCALL odbql_bind_text(odbql_stmt*,int,const char*,int,void(*)(void*));
+ODBQL_API int ODBQL_STDCALL odbql_bind_text16(odbql_stmt*, int, const void*, int, void(*)(void*));
+ODBQL_API int ODBQL_STDCALL odbql_bind_text64(odbql_stmt*, int, const char*, odbql_uint64,
+                         void(*)(void*), unsigned char encoding);
+ODBQL_API int ODBQL_STDCALL odbql_bind_value(odbql_stmt*, int, const odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_bind_zeroblob(odbql_stmt*, int, int n);
+ODBQL_API int ODBQL_STDCALL odbql_bind_zeroblob64(odbql_stmt*, int, odbql_uint64);
+
+/*
+** CAPI3REF: Number Of SQL Parameters
+** METHOD: odbql_stmt
+**
+** ^This routine can be used to find the number of [SQL parameters]
+** in a [prepared statement].  SQL parameters are tokens of the
+** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as
+** placeholders for values that are [odbql_bind_blob | bound]
+** to the parameters at a later time.
+**
+** ^(This routine actually returns the index of the largest (rightmost)
+** parameter. For all forms except ?NNN, this will correspond to the
+** number of unique parameters.  If parameters of the ?NNN form are used,
+** there may be gaps in the list.)^
+**
+** See also: [odbql_bind_blob|odbql_bind()],
+** [odbql_bind_parameter_name()], and
+** [odbql_bind_parameter_index()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_bind_parameter_count(odbql_stmt*);
+
+/*
+** CAPI3REF: Name Of A Host Parameter
+** METHOD: odbql_stmt
+**
+** ^The odbql_bind_parameter_name(P,N) interface returns
+** the name of the N-th [SQL parameter] in the [prepared statement] P.
+** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA"
+** respectively.
+** In other words, the initial ":" or "$" or "@" or "?"
+** is included as part of the name.)^
+** ^Parameters of the form "?" without a following integer have no name
+** and are referred to as "nameless" or "anonymous parameters".
+**
+** ^The first host parameter has an index of 1, not 0.
+**
+** ^If the value N is out of range or if the N-th parameter is
+** nameless, then NULL is returned.  ^The returned string is
+** always in UTF-8 encoding even if the named parameter was
+** originally specified as UTF-16 in [odbql_prepare16()] or
+** [odbql_prepare16_v2()].
+**
+** See also: [odbql_bind_blob|odbql_bind()],
+** [odbql_bind_parameter_count()], and
+** [odbql_bind_parameter_index()].
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_bind_parameter_name(odbql_stmt*, int);
+
+/*
+** CAPI3REF: Index Of A Parameter With A Given Name
+** METHOD: odbql_stmt
+**
+** ^Return the index of an SQL parameter given its name.  ^The
+** index value returned is suitable for use as the second
+** parameter to [odbql_bind_blob|odbql_bind()].  ^A zero
+** is returned if no matching parameter is found.  ^The parameter
+** name must be given in UTF-8 even if the original statement
+** was prepared from UTF-16 text using [odbql_prepare16_v2()].
+**
+** See also: [odbql_bind_blob|odbql_bind()],
+** [odbql_bind_parameter_count()], and
+** [odbql_bind_parameter_name()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_bind_parameter_index(odbql_stmt*, const char *zName);
+
+/*
+** CAPI3REF: Reset All Bindings On A Prepared Statement
+** METHOD: odbql_stmt
+**
+** ^Contrary to the intuition of many, [odbql_reset()] does not reset
+** the [odbql_bind_blob | bindings] on a [prepared statement].
+** ^Use this routine to reset all host parameters to NULL.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_clear_bindings(odbql_stmt*);
+
+/*
+** CAPI3REF: Number Of Columns In A Result Set
+** METHOD: odbql_stmt
+**
+** ^Return the number of columns in the result set returned by the
+** [prepared statement]. ^This routine returns 0 if pStmt is an SQL
+** statement that does not return data (for example an [UPDATE]).
+**
+** See also: [odbql_data_count()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_column_count(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Column Names In A Result Set
+** METHOD: odbql_stmt
+**
+** ^These routines return the name assigned to a particular column
+** in the result set of a [SELECT] statement.  ^The odbql_column_name()
+** interface returns a pointer to a zero-terminated UTF-8 string
+** and odbql_column_name16() returns a pointer to a zero-terminated
+** UTF-16 string.  ^The first parameter is the [prepared statement]
+** that implements the [SELECT] statement. ^The second parameter is the
+** column number.  ^The leftmost column is number 0.
+**
+** ^The returned string pointer is valid until either the [prepared statement]
+** is destroyed by [odbql_finalize()] or until the statement is automatically
+** reprepared by the first call to [odbql_step()] for a particular run
+** or until the next call to
+** odbql_column_name() or odbql_column_name16() on the same column.
+**
+** ^If odbql_malloc() fails during the processing of either routine
+** (for example during a conversion from UTF-8 to UTF-16) then a
+** NULL pointer is returned.
+**
+** ^The name of a result column is the value of the "AS" clause for
+** that column, if there is an AS clause.  If there is no AS clause
+** then the name of the column is unspecified and may change from
+** one release of SQLite to the next.
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_column_name(odbql_stmt*, int N);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_name16(odbql_stmt*, int N);
+
+/*
+** CAPI3REF: Source Of Data In A Query Result
+** METHOD: odbql_stmt
+**
+** ^These routines provide a means to determine the database, table, and
+** table column that is the origin of a particular result column in
+** [SELECT] statement.
+** ^The name of the database or table or column can be returned as
+** either a UTF-8 or UTF-16 string.  ^The _database_ routines return
+** the database name, the _table_ routines return the table name, and
+** the origin_ routines return the column name.
+** ^The returned string is valid until the [prepared statement] is destroyed
+** using [odbql_finalize()] or until the statement is automatically
+** reprepared by the first call to [odbql_step()] for a particular run
+** or until the same information is requested
+** again in a different encoding.
+**
+** ^The names returned are the original un-aliased names of the
+** database, table, and column.
+**
+** ^The first argument to these interfaces is a [prepared statement].
+** ^These functions return information about the Nth result column returned by
+** the statement, where N is the second function argument.
+** ^The left-most column is column 0 for these routines.
+**
+** ^If the Nth column returned by the statement is an expression or
+** subquery and is not a column value, then all of these functions return
+** NULL.  ^These routine might also return NULL if a memory allocation error
+** occurs.  ^Otherwise, they return the name of the attached database, table,
+** or column that query result column was extracted from.
+**
+** ^As with all other SQLite APIs, those whose names end with "16" return
+** UTF-16 encoded strings and the other functions return UTF-8.
+**
+** ^These APIs are only available if the library was compiled with the
+** [ODBQL_ENABLE_COLUMN_METADATA] C-preprocessor symbol.
+**
+** If two or more threads call one or more of these routines against the same
+** prepared statement and column at the same time then the results are
+** undefined.
+**
+** If two or more threads call one or more
+** [odbql_column_database_name | column metadata interfaces]
+** for the same [prepared statement] and result column
+** at the same time then the results are undefined.
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_column_database_name(odbql_stmt*,int);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_database_name16(odbql_stmt*,int);
+ODBQL_API const char *ODBQL_STDCALL odbql_column_table_name(odbql_stmt*,int);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_table_name16(odbql_stmt*,int);
+ODBQL_API const char *ODBQL_STDCALL odbql_column_origin_name(odbql_stmt*,int);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_origin_name16(odbql_stmt*,int);
+
+/*
+** CAPI3REF: Declared Datatype Of A Query Result
+** METHOD: odbql_stmt
+**
+** ^(The first parameter is a [prepared statement].
+** If this statement is a [SELECT] statement and the Nth column of the
+** returned result set of that [SELECT] is a table column (not an
+** expression or subquery) then the declared type of the table
+** column is returned.)^  ^If the Nth column of the result set is an
+** expression or subquery, then a NULL pointer is returned.
+** ^The returned string is always UTF-8 encoded.
+**
+** ^(For example, given the database schema:
+**
+** CREATE TABLE t1(c1 VARIANT);
+**
+** and the following statement to be compiled:
+**
+** SELECT c1 + 1, c1 FROM t1;
+**
+** this routine would return the string "VARIANT" for the second result
+** column (i==1), and a NULL pointer for the first result column (i==0).)^
+**
+** ^SQLite uses dynamic run-time typing.  ^So just because a column
+** is declared to contain a particular type does not mean that the
+** data stored in that column is of the declared type.  SQLite is
+** strongly typed, but the typing is dynamic not static.  ^Type
+** is associated with individual values, not with the containers
+** used to hold those values.
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_column_decltype(odbql_stmt*,int);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_decltype16(odbql_stmt*,int);
+
+/*
+** CAPI3REF: Evaluate An SQL Statement
+** METHOD: odbql_stmt
+**
+** After a [prepared statement] has been prepared using either
+** [odbql_prepare_v2()] or [odbql_prepare16_v2()] or one of the legacy
+** interfaces [odbql_prepare()] or [odbql_prepare16()], this function
+** must be called one or more times to evaluate the statement.
+**
+** The details of the behavior of the odbql_step() interface depend
+** on whether the statement was prepared using the newer "v2" interface
+** [odbql_prepare_v2()] and [odbql_prepare16_v2()] or the older legacy
+** interface [odbql_prepare()] and [odbql_prepare16()].  The use of the
+** new "v2" interface is recommended for new applications but the legacy
+** interface will continue to be supported.
+**
+** ^In the legacy interface, the return value will be either [ODBQL_BUSY],
+** [ODBQL_DONE], [ODBQL_ROW], [ODBQL_ERROR], or [ODBQL_MISUSE].
+** ^With the "v2" interface, any of the other [result codes] or
+** [extended result codes] might be returned as well.
+**
+** ^[ODBQL_BUSY] means that the database engine was unable to acquire the
+** database locks it needs to do its job.  ^If the statement is a [COMMIT]
+** or occurs outside of an explicit transaction, then you can retry the
+** statement.  If the statement is not a [COMMIT] and occurs within an
+** explicit transaction then you should rollback the transaction before
+** continuing.
+**
+** ^[ODBQL_DONE] means that the statement has finished executing
+** successfully.  odbql_step() should not be called again on this virtual
+** machine without first calling [odbql_reset()] to reset the virtual
+** machine back to its initial state.
+**
+** ^If the SQL statement being executed returns any data, then [ODBQL_ROW]
+** is returned each time a new row of data is ready for processing by the
+** caller. The values may be accessed using the [column access functions].
+** odbql_step() is called again to retrieve the next row of data.
+**
+** ^[ODBQL_ERROR] means that a run-time error (such as a constraint
+** violation) has occurred.  odbql_step() should not be called again on
+** the VM. More information may be found by calling [odbql_errmsg()].
+** ^With the legacy interface, a more specific error code (for example,
+** [ODBQL_INTERRUPT], [ODBQL_SCHEMA], [ODBQL_CORRUPT], and so forth)
+** can be obtained by calling [odbql_reset()] on the
+** [prepared statement].  ^In the "v2" interface,
+** the more specific error code is returned directly by odbql_step().
+**
+** [ODBQL_MISUSE] means that the this routine was called inappropriately.
+** Perhaps it was called on a [prepared statement] that has
+** already been [odbql_finalize | finalized] or on one that had
+** previously returned [ODBQL_ERROR] or [ODBQL_DONE].  Or it could
+** be the case that the same database connection is being used by two or
+** more threads at the same moment in time.
+**
+** For all versions of SQLite up to and including 3.6.23.1, a call to
+** [odbql_reset()] was required after odbql_step() returned anything
+** other than [ODBQL_ROW] before any subsequent invocation of
+** odbql_step().  Failure to reset the prepared statement using 
+** [odbql_reset()] would result in an [ODBQL_MISUSE] return from
+** odbql_step().  But after version 3.6.23.1, odbql_step() began
+** calling [odbql_reset()] automatically in this circumstance rather
+** than returning [ODBQL_MISUSE].  This is not considered a compatibility
+** break because any application that ever receives an ODBQL_MISUSE error
+** is broken by definition.  The [ODBQL_OMIT_AUTORESET] compile-time option
+** can be used to restore the legacy behavior.
+**
+** <b>Goofy Interface Alert:</b> In the legacy interface, the odbql_step()
+** API always returns a generic error code, [ODBQL_ERROR], following any
+** error other than [ODBQL_BUSY] and [ODBQL_MISUSE].  You must call
+** [odbql_reset()] or [odbql_finalize()] in order to find one of the
+** specific [error codes] that better describes the error.
+** We admit that this is a goofy design.  The problem has been fixed
+** with the "v2" interface.  If you prepare all of your SQL statements
+** using either [odbql_prepare_v2()] or [odbql_prepare16_v2()] instead
+** of the legacy [odbql_prepare()] and [odbql_prepare16()] interfaces,
+** then the more specific [error codes] are returned directly
+** by odbql_step().  The use of the "v2" interface is recommended.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_step(odbql_stmt*);
+
+/*
+** CAPI3REF: Number of columns in a result set
+** METHOD: odbql_stmt
+**
+** ^The odbql_data_count(P) interface returns the number of columns in the
+** current row of the result set of [prepared statement] P.
+** ^If prepared statement P does not have results ready to return
+** (via calls to the [odbql_column_int | odbql_column_*()] of
+** interfaces) then odbql_data_count(P) returns 0.
+** ^The odbql_data_count(P) routine also returns 0 if P is a NULL pointer.
+** ^The odbql_data_count(P) routine returns 0 if the previous call to
+** [odbql_step](P) returned [ODBQL_DONE].  ^The odbql_data_count(P)
+** will return non-zero if previous call to [odbql_step](P) returned
+** [ODBQL_ROW], except in the case of the [PRAGMA incremental_vacuum]
+** where it always returns zero since each step of that multi-step
+** pragma returns 0 columns of data.
+**
+** See also: [odbql_column_count()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_data_count(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Fundamental Datatypes
+** KEYWORDS: ODBQL_TEXT
+**
+** ^(Every value in SQLite has one of five fundamental datatypes:
+**
+** <ul>
+** <li> 64-bit signed integer
+** <li> 64-bit IEEE floating point number
+** <li> string
+** <li> BLOB
+** <li> NULL
+** </ul>)^
+**
+** These constants are codes for each of those types.
+**
+** Note that the ODBQL_TEXT constant was also used in SQLite version 2
+** for a completely different meaning.  Software that links against both
+** SQLite version 2 and SQLite version 3 should use ODBQL_TEXT, not
+** ODBQL_TEXT.
+*/
+#define ODBQL_INTEGER  1
+#define ODBQL_FLOAT    2
+#define ODBQL_BLOB     4
+#define ODBQL_NULL     5
+
+///////// ODB API specific type:
+#define ODBQL_BITFIELD     6
+#ifdef ODBQL_TEXT
+# undef ODBQL_TEXT
+#else
+# define ODBQL_TEXT     3
+#endif
+#define ODBQL_TEXT     3
+
+/*
+** CAPI3REF: Result Values From A Query
+** KEYWORDS: {column access functions}
+** METHOD: odbql_stmt
+**
+** ^These routines return information about a single column of the current
+** result row of a query.  ^In every case the first argument is a pointer
+** to the [prepared statement] that is being evaluated (the [odbql_stmt*]
+** that was returned from [odbql_prepare_v2()] or one of its variants)
+** and the second argument is the index of the column for which information
+** should be returned. ^The leftmost column of the result set has the index 0.
+** ^The number of columns in the result can be determined using
+** [odbql_column_count()].
+**
+** If the SQL statement does not currently point to a valid row, or if the
+** column index is out of range, the result is undefined.
+** These routines may only be called when the most recent call to
+** [odbql_step()] has returned [ODBQL_ROW] and neither
+** [odbql_reset()] nor [odbql_finalize()] have been called subsequently.
+** If any of these routines are called after [odbql_reset()] or
+** [odbql_finalize()] or after [odbql_step()] has returned
+** something other than [ODBQL_ROW], the results are undefined.
+** If [odbql_step()] or [odbql_reset()] or [odbql_finalize()]
+** are called from a different thread while any of these routines
+** are pending, then the results are undefined.
+**
+** ^The odbql_column_type() routine returns the
+** [ODBQL_INTEGER | datatype code] for the initial data type
+** of the result column.  ^The returned value is one of [ODBQL_INTEGER],
+** [ODBQL_FLOAT], [ODBQL_TEXT], [ODBQL_BLOB], or [ODBQL_NULL].  The value
+** returned by odbql_column_type() is only meaningful if no type
+** conversions have occurred as described below.  After a type conversion,
+** the value returned by odbql_column_type() is undefined.  Future
+** versions of SQLite may change the behavior of odbql_column_type()
+** following a type conversion.
+**
+** ^If the result is a BLOB or UTF-8 string then the odbql_column_bytes()
+** routine returns the number of bytes in that BLOB or string.
+** ^If the result is a UTF-16 string, then odbql_column_bytes() converts
+** the string to UTF-8 and then returns the number of bytes.
+** ^If the result is a numeric value then odbql_column_bytes() uses
+** [odbql_snprintf()] to convert that value to a UTF-8 string and returns
+** the number of bytes in that string.
+** ^If the result is NULL, then odbql_column_bytes() returns zero.
+**
+** ^If the result is a BLOB or UTF-16 string then the odbql_column_bytes16()
+** routine returns the number of bytes in that BLOB or string.
+** ^If the result is a UTF-8 string, then odbql_column_bytes16() converts
+** the string to UTF-16 and then returns the number of bytes.
+** ^If the result is a numeric value then odbql_column_bytes16() uses
+** [odbql_snprintf()] to convert that value to a UTF-16 string and returns
+** the number of bytes in that string.
+** ^If the result is NULL, then odbql_column_bytes16() returns zero.
+**
+** ^The values returned by [odbql_column_bytes()] and 
+** [odbql_column_bytes16()] do not include the zero terminators at the end
+** of the string.  ^For clarity: the values returned by
+** [odbql_column_bytes()] and [odbql_column_bytes16()] are the number of
+** bytes in the string, not the number of characters.
+**
+** ^Strings returned by odbql_column_text() and odbql_column_text16(),
+** even empty strings, are always zero-terminated.  ^The return
+** value from odbql_column_blob() for a zero-length BLOB is a NULL pointer.
+**
+** <b>Warning:</b> ^The object returned by [odbql_column_value()] is an
+** [unprotected odbql_value] object.  In a multithreaded environment,
+** an unprotected odbql_value object may only be used safely with
+** [odbql_bind_value()] and [odbql_result_value()].
+** If the [unprotected odbql_value] object returned by
+** [odbql_column_value()] is used in any other way, including calls
+** to routines like [odbql_value_int()], [odbql_value_text()],
+** or [odbql_value_bytes()], the behavior is not threadsafe.
+**
+** These routines attempt to convert the value where appropriate.  ^For
+** example, if the internal representation is FLOAT and a text result
+** is requested, [odbql_snprintf()] is used internally to perform the
+** conversion automatically.  ^(The following table details the conversions
+** that are applied:
+**
+** <blockquote>
+** <table border="1">
+** <tr><th> Internal<br>Type <th> Requested<br>Type <th>  Conversion
+**
+** <tr><td>  NULL    <td> INTEGER   <td> Result is 0
+** <tr><td>  NULL    <td>  FLOAT    <td> Result is 0.0
+** <tr><td>  NULL    <td>   TEXT    <td> Result is a NULL pointer
+** <tr><td>  NULL    <td>   BLOB    <td> Result is a NULL pointer
+** <tr><td> INTEGER  <td>  FLOAT    <td> Convert from integer to float
+** <tr><td> INTEGER  <td>   TEXT    <td> ASCII rendering of the integer
+** <tr><td> INTEGER  <td>   BLOB    <td> Same as INTEGER->TEXT
+** <tr><td>  FLOAT   <td> INTEGER   <td> [CAST] to INTEGER
+** <tr><td>  FLOAT   <td>   TEXT    <td> ASCII rendering of the float
+** <tr><td>  FLOAT   <td>   BLOB    <td> [CAST] to BLOB
+** <tr><td>  TEXT    <td> INTEGER   <td> [CAST] to INTEGER
+** <tr><td>  TEXT    <td>  FLOAT    <td> [CAST] to REAL
+** <tr><td>  TEXT    <td>   BLOB    <td> No change
+** <tr><td>  BLOB    <td> INTEGER   <td> [CAST] to INTEGER
+** <tr><td>  BLOB    <td>  FLOAT    <td> [CAST] to REAL
+** <tr><td>  BLOB    <td>   TEXT    <td> Add a zero terminator if needed
+** </table>
+** </blockquote>)^
+**
+** Note that when type conversions occur, pointers returned by prior
+** calls to odbql_column_blob(), odbql_column_text(), and/or
+** odbql_column_text16() may be invalidated.
+** Type conversions and pointer invalidations might occur
+** in the following cases:
+**
+** <ul>
+** <li> The initial content is a BLOB and odbql_column_text() or
+**      odbql_column_text16() is called.  A zero-terminator might
+**      need to be added to the string.</li>
+** <li> The initial content is UTF-8 text and odbql_column_bytes16() or
+**      odbql_column_text16() is called.  The content must be converted
+**      to UTF-16.</li>
+** <li> The initial content is UTF-16 text and odbql_column_bytes() or
+**      odbql_column_text() is called.  The content must be converted
+**      to UTF-8.</li>
+** </ul>
+**
+** ^Conversions between UTF-16be and UTF-16le are always done in place and do
+** not invalidate a prior pointer, though of course the content of the buffer
+** that the prior pointer references will have been modified.  Other kinds
+** of conversion are done in place when it is possible, but sometimes they
+** are not possible and in those cases prior pointers are invalidated.
+**
+** The safest policy is to invoke these routines
+** in one of the following ways:
+**
+** <ul>
+**  <li>odbql_column_text() followed by odbql_column_bytes()</li>
+**  <li>odbql_column_blob() followed by odbql_column_bytes()</li>
+**  <li>odbql_column_text16() followed by odbql_column_bytes16()</li>
+** </ul>
+**
+** In other words, you should call odbql_column_text(),
+** odbql_column_blob(), or odbql_column_text16() first to force the result
+** into the desired format, then invoke odbql_column_bytes() or
+** odbql_column_bytes16() to find the size of the result.  Do not mix calls
+** to odbql_column_text() or odbql_column_blob() with calls to
+** odbql_column_bytes16(), and do not mix calls to odbql_column_text16()
+** with calls to odbql_column_bytes().
+**
+** ^The pointers returned are valid until a type conversion occurs as
+** described above, or until [odbql_step()] or [odbql_reset()] or
+** [odbql_finalize()] is called.  ^The memory space used to hold strings
+** and BLOBs is freed automatically.  Do <em>not</em> pass the pointers returned
+** from [odbql_column_blob()], [odbql_column_text()], etc. into
+** [odbql_free()].
+**
+** ^(If a memory allocation error occurs during the evaluation of any
+** of these routines, a default value is returned.  The default value
+** is either the integer 0, the floating point number 0.0, or a NULL
+** pointer.  Subsequent calls to [odbql_errcode()] will return
+** [ODBQL_NOMEM].)^
+*/
+ODBQL_API const void *ODBQL_STDCALL odbql_column_blob(odbql_stmt*, int iCol);
+ODBQL_API int ODBQL_STDCALL odbql_column_bytes(odbql_stmt*, int iCol);
+ODBQL_API int ODBQL_STDCALL odbql_column_bytes16(odbql_stmt*, int iCol);
+ODBQL_API double ODBQL_STDCALL odbql_column_double(odbql_stmt*, int iCol);
+ODBQL_API int ODBQL_STDCALL odbql_column_int(odbql_stmt*, int iCol);
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_column_int64(odbql_stmt*, int iCol);
+ODBQL_API const unsigned char *ODBQL_STDCALL odbql_column_text(odbql_stmt*, int iCol);
+ODBQL_API const void *ODBQL_STDCALL odbql_column_text16(odbql_stmt*, int iCol);
+ODBQL_API int ODBQL_STDCALL odbql_column_type(odbql_stmt*, int iCol);
+ODBQL_API odbql_value *ODBQL_STDCALL odbql_column_value(odbql_stmt*, int iCol);
+
+/*
+** CAPI3REF: Destroy A Prepared Statement Object
+** DESTRUCTOR: odbql_stmt
+**
+** ^The odbql_finalize() function is called to delete a [prepared statement].
+** ^If the most recent evaluation of the statement encountered no errors
+** or if the statement is never been evaluated, then odbql_finalize() returns
+** ODBQL_OK.  ^If the most recent evaluation of statement S failed, then
+** odbql_finalize(S) returns the appropriate [error code] or
+** [extended error code].
+**
+** ^The odbql_finalize(S) routine can be called at any point during
+** the life cycle of [prepared statement] S:
+** before statement S is ever evaluated, after
+** one or more calls to [odbql_reset()], or after any call
+** to [odbql_step()] regardless of whether or not the statement has
+** completed execution.
+**
+** ^Invoking odbql_finalize() on a NULL pointer is a harmless no-op.
+**
+** The application must finalize every [prepared statement] in order to avoid
+** resource leaks.  It is a grievous error for the application to try to use
+** a prepared statement after it has been finalized.  Any use of a prepared
+** statement after it has been finalized can result in undefined and
+** undesirable behavior such as segfaults and heap corruption.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_finalize(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Reset A Prepared Statement Object
+** METHOD: odbql_stmt
+**
+** The odbql_reset() function is called to reset a [prepared statement]
+** object back to its initial state, ready to be re-executed.
+** ^Any SQL statement variables that had values bound to them using
+** the [odbql_bind_blob | odbql_bind_*() API] retain their values.
+** Use [odbql_clear_bindings()] to reset the bindings.
+**
+** ^The [odbql_reset(S)] interface resets the [prepared statement] S
+** back to the beginning of its program.
+**
+** ^If the most recent call to [odbql_step(S)] for the
+** [prepared statement] S returned [ODBQL_ROW] or [ODBQL_DONE],
+** or if [odbql_step(S)] has never before been called on S,
+** then [odbql_reset(S)] returns [ODBQL_OK].
+**
+** ^If the most recent call to [odbql_step(S)] for the
+** [prepared statement] S indicated an error, then
+** [odbql_reset(S)] returns an appropriate [error code].
+**
+** ^The [odbql_reset(S)] interface does not change the values
+** of any [odbql_bind_blob|bindings] on the [prepared statement] S.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_reset(odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Create Or Redefine SQL Functions
+** KEYWORDS: {function creation routines}
+** KEYWORDS: {application-defined SQL function}
+** KEYWORDS: {application-defined SQL functions}
+** METHOD: odbql
+**
+** ^These functions (collectively known as "function creation routines")
+** are used to add SQL functions or aggregates or to redefine the behavior
+** of existing SQL functions or aggregates.  The only differences between
+** these routines are the text encoding expected for
+** the second parameter (the name of the function being created)
+** and the presence or absence of a destructor callback for
+** the application data pointer.
+**
+** ^The first parameter is the [database connection] to which the SQL
+** function is to be added.  ^If an application uses more than one database
+** connection then application-defined SQL functions must be added
+** to each database connection separately.
+**
+** ^The second parameter is the name of the SQL function to be created or
+** redefined.  ^The length of the name is limited to 255 bytes in a UTF-8
+** representation, exclusive of the zero-terminator.  ^Note that the name
+** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.  
+** ^Any attempt to create a function with a longer name
+** will result in [ODBQL_MISUSE] being returned.
+**
+** ^The third parameter (nArg)
+** is the number of arguments that the SQL function or
+** aggregate takes. ^If this parameter is -1, then the SQL function or
+** aggregate may take any number of arguments between 0 and the limit
+** set by [odbql_limit]([ODBQL_LIMIT_FUNCTION_ARG]).  If the third
+** parameter is less than -1 or greater than 127 then the behavior is
+** undefined.
+**
+** ^The fourth parameter, eTextRep, specifies what
+** [ODBQL_UTF8 | text encoding] this SQL function prefers for
+** its parameters.  The application should set this parameter to
+** [ODBQL_UTF16LE] if the function implementation invokes 
+** [odbql_value_text16le()] on an input, or [ODBQL_UTF16BE] if the
+** implementation invokes [odbql_value_text16be()] on an input, or
+** [ODBQL_UTF16] if [odbql_value_text16()] is used, or [ODBQL_UTF8]
+** otherwise.  ^The same SQL function may be registered multiple times using
+** different preferred text encodings, with different implementations for
+** each encoding.
+** ^When multiple implementations of the same function are available, SQLite
+** will pick the one that involves the least amount of data conversion.
+**
+** ^The fourth parameter may optionally be ORed with [ODBQL_DETERMINISTIC]
+** to signal that the function will always return the same result given
+** the same inputs within a single SQL statement.  Most SQL functions are
+** deterministic.  The built-in [random()] SQL function is an example of a
+** function that is not deterministic.  The SQLite query planner is able to
+** perform additional optimizations on deterministic functions, so use
+** of the [ODBQL_DETERMINISTIC] flag is recommended where possible.
+**
+** ^(The fifth parameter is an arbitrary pointer.  The implementation of the
+** function can gain access to this pointer using [odbql_user_data()].)^
+**
+** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are
+** pointers to C-language functions that implement the SQL function or
+** aggregate. ^A scalar SQL function requires an implementation of the xFunc
+** callback only; NULL pointers must be passed as the xStep and xFinal
+** parameters. ^An aggregate SQL function requires an implementation of xStep
+** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing
+** SQL function or aggregate, pass NULL pointers for all three function
+** callbacks.
+**
+** ^(If the ninth parameter to odbql_create_function_v2() is not NULL,
+** then it is destructor for the application data pointer. 
+** The destructor is invoked when the function is deleted, either by being
+** overloaded or when the database connection closes.)^
+** ^The destructor is also invoked if the call to
+** odbql_create_function_v2() fails.
+** ^When the destructor callback of the tenth parameter is invoked, it
+** is passed a single argument which is a copy of the application data 
+** pointer which was the fifth parameter to odbql_create_function_v2().
+**
+** ^It is permitted to register multiple implementations of the same
+** functions with the same name but with either differing numbers of
+** arguments or differing preferred text encodings.  ^SQLite will use
+** the implementation that most closely matches the way in which the
+** SQL function is used.  ^A function implementation with a non-negative
+** nArg parameter is a better match than a function implementation with
+** a negative nArg.  ^A function where the preferred text encoding
+** matches the database encoding is a better
+** match than a function where the encoding is different.  
+** ^A function where the encoding difference is between UTF16le and UTF16be
+** is a closer match than a function where the encoding difference is
+** between UTF8 and UTF16.
+**
+** ^Built-in functions may be overloaded by new application-defined functions.
+**
+** ^An application-defined function is permitted to call other
+** SQLite interfaces.  However, such calls must not
+** close the database connection nor finalize or reset the prepared
+** statement in which the function is running.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_create_function(
+  odbql *db,
+  const char *zFunctionName,
+  int nArg,
+  int eTextRep,
+  void *pApp,
+  void (*xFunc)(odbql_context*,int,odbql_value**),
+  void (*xStep)(odbql_context*,int,odbql_value**),
+  void (*xFinal)(odbql_context*)
+);
+ODBQL_API int ODBQL_STDCALL odbql_create_function16(
+  odbql *db,
+  const void *zFunctionName,
+  int nArg,
+  int eTextRep,
+  void *pApp,
+  void (*xFunc)(odbql_context*,int,odbql_value**),
+  void (*xStep)(odbql_context*,int,odbql_value**),
+  void (*xFinal)(odbql_context*)
+);
+ODBQL_API int ODBQL_STDCALL odbql_create_function_v2(
+  odbql *db,
+  const char *zFunctionName,
+  int nArg,
+  int eTextRep,
+  void *pApp,
+  void (*xFunc)(odbql_context*,int,odbql_value**),
+  void (*xStep)(odbql_context*,int,odbql_value**),
+  void (*xFinal)(odbql_context*),
+  void(*xDestroy)(void*)
+);
+
+/*
+** CAPI3REF: Text Encodings
+**
+** These constant define integer codes that represent the various
+** text encodings supported by SQLite.
+*/
+#define ODBQL_UTF8           1    /* IMP: R-37514-35566 */
+#define ODBQL_UTF16LE        2    /* IMP: R-03371-37637 */
+#define ODBQL_UTF16BE        3    /* IMP: R-51971-34154 */
+#define ODBQL_UTF16          4    /* Use native byte order */
+#define ODBQL_ANY            5    /* Deprecated */
+#define ODBQL_UTF16_ALIGNED  8    /* odbql_create_collation only */
+
+/*
+** CAPI3REF: Function Flags
+**
+** These constants may be ORed together with the 
+** [ODBQL_UTF8 | preferred text encoding] as the fourth argument
+** to [odbql_create_function()], [odbql_create_function16()], or
+** [odbql_create_function_v2()].
+*/
+#define ODBQL_DETERMINISTIC    0x800
+
+/*
+** CAPI3REF: Deprecated Functions
+** DEPRECATED
+**
+** These functions are [deprecated].  In order to maintain
+** backwards compatibility with older code, these functions continue 
+** to be supported.  However, new applications should avoid
+** the use of these functions.  To encourage programmers to avoid
+** these functions, we will not explain what they do.
+*/
+#ifndef ODBQL_OMIT_DEPRECATED
+ODBQL_API ODBQL_DEPRECATED int ODBQL_STDCALL odbql_aggregate_count(odbql_context*);
+ODBQL_API ODBQL_DEPRECATED int ODBQL_STDCALL odbql_expired(odbql_stmt*);
+ODBQL_API ODBQL_DEPRECATED int ODBQL_STDCALL odbql_transfer_bindings(odbql_stmt*, odbql_stmt*);
+ODBQL_API ODBQL_DEPRECATED int ODBQL_STDCALL odbql_global_recover(void);
+ODBQL_API ODBQL_DEPRECATED void ODBQL_STDCALL odbql_thread_cleanup(void);
+ODBQL_API ODBQL_DEPRECATED int ODBQL_STDCALL odbql_memory_alarm(void(*)(void*,odbql_int64,int),
+                      void*,odbql_int64);
+#endif
+
+/*
+** CAPI3REF: Obtaining SQL Values
+** METHOD: odbql_value
+**
+** The C-language implementation of SQL functions and aggregates uses
+** this set of interface routines to access the parameter values on
+** the function or aggregate.  
+**
+** The xFunc (for scalar functions) or xStep (for aggregates) parameters
+** to [odbql_create_function()] and [odbql_create_function16()]
+** define callbacks that implement the SQL functions and aggregates.
+** The 3rd parameter to these callbacks is an array of pointers to
+** [protected odbql_value] objects.  There is one [odbql_value] object for
+** each parameter to the SQL function.  These routines are used to
+** extract values from the [odbql_value] objects.
+**
+** These routines work only with [protected odbql_value] objects.
+** Any attempt to use these routines on an [unprotected odbql_value]
+** object results in undefined behavior.
+**
+** ^These routines work just like the corresponding [column access functions]
+** except that these routines take a single [protected odbql_value] object
+** pointer instead of a [odbql_stmt*] pointer and an integer column number.
+**
+** ^The odbql_value_text16() interface extracts a UTF-16 string
+** in the native byte-order of the host machine.  ^The
+** odbql_value_text16be() and odbql_value_text16le() interfaces
+** extract UTF-16 strings as big-endian and little-endian respectively.
+**
+** ^(The odbql_value_numeric_type() interface attempts to apply
+** numeric affinity to the value.  This means that an attempt is
+** made to convert the value to an integer or floating point.  If
+** such a conversion is possible without loss of information (in other
+** words, if the value is a string that looks like a number)
+** then the conversion is performed.  Otherwise no conversion occurs.
+** The [ODBQL_INTEGER | datatype] after conversion is returned.)^
+**
+** Please pay particular attention to the fact that the pointer returned
+** from [odbql_value_blob()], [odbql_value_text()], or
+** [odbql_value_text16()] can be invalidated by a subsequent call to
+** [odbql_value_bytes()], [odbql_value_bytes16()], [odbql_value_text()],
+** or [odbql_value_text16()].
+**
+** These routines must be called from the same thread as
+** the SQL function that supplied the [odbql_value*] parameters.
+*/
+ODBQL_API const void *ODBQL_STDCALL odbql_value_blob(odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_value_bytes(odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_value_bytes16(odbql_value*);
+ODBQL_API double ODBQL_STDCALL odbql_value_double(odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_value_int(odbql_value*);
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_value_int64(odbql_value*);
+ODBQL_API const unsigned char *ODBQL_STDCALL odbql_value_text(odbql_value*);
+ODBQL_API const void *ODBQL_STDCALL odbql_value_text16(odbql_value*);
+ODBQL_API const void *ODBQL_STDCALL odbql_value_text16le(odbql_value*);
+ODBQL_API const void *ODBQL_STDCALL odbql_value_text16be(odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_value_type(odbql_value*);
+ODBQL_API int ODBQL_STDCALL odbql_value_numeric_type(odbql_value*);
+
+/*
+** CAPI3REF: Finding The Subtype Of SQL Values
+** METHOD: odbql_value
+**
+** The odbql_value_subtype(V) function returns the subtype for
+** an [application-defined SQL function] argument V.  The subtype
+** information can be used to pass a limited amount of context from
+** one SQL function to another.  Use the [odbql_result_subtype()]
+** routine to set the subtype for the return value of an SQL function.
+**
+** SQLite makes no use of subtype itself.  It merely passes the subtype
+** from the result of one [application-defined SQL function] into the
+** input of another.
+*/
+ODBQL_API unsigned int ODBQL_STDCALL odbql_value_subtype(odbql_value*);
+
+/*
+** CAPI3REF: Copy And Free SQL Values
+** METHOD: odbql_value
+**
+** ^The odbql_value_dup(V) interface makes a copy of the [odbql_value]
+** object D and returns a pointer to that copy.  ^The [odbql_value] returned
+** is a [protected odbql_value] object even if the input is not.
+** ^The odbql_value_dup(V) interface returns NULL if V is NULL or if a
+** memory allocation fails.
+**
+** ^The odbql_value_free(V) interface frees an [odbql_value] object
+** previously obtained from [odbql_value_dup()].  ^If V is a NULL pointer
+** then odbql_value_free(V) is a harmless no-op.
+*/
+ODBQL_API odbql_value *ODBQL_STDCALL odbql_value_dup(const odbql_value*);
+ODBQL_API void ODBQL_STDCALL odbql_value_free(odbql_value*);
+
+/*
+** CAPI3REF: Obtain Aggregate Function Context
+** METHOD: odbql_context
+**
+** Implementations of aggregate SQL functions use this
+** routine to allocate memory for storing their state.
+**
+** ^The first time the odbql_aggregate_context(C,N) routine is called 
+** for a particular aggregate function, SQLite
+** allocates N of memory, zeroes out that memory, and returns a pointer
+** to the new memory. ^On second and subsequent calls to
+** odbql_aggregate_context() for the same aggregate function instance,
+** the same buffer is returned.  Sqlite3_aggregate_context() is normally
+** called once for each invocation of the xStep callback and then one
+** last time when the xFinal callback is invoked.  ^(When no rows match
+** an aggregate query, the xStep() callback of the aggregate function
+** implementation is never called and xFinal() is called exactly once.
+** In those cases, odbql_aggregate_context() might be called for the
+** first time from within xFinal().)^
+**
+** ^The odbql_aggregate_context(C,N) routine returns a NULL pointer 
+** when first called if N is less than or equal to zero or if a memory
+** allocate error occurs.
+**
+** ^(The amount of space allocated by odbql_aggregate_context(C,N) is
+** determined by the N parameter on first successful call.  Changing the
+** value of N in subsequent call to odbql_aggregate_context() within
+** the same aggregate function instance will not resize the memory
+** allocation.)^  Within the xFinal callback, it is customary to set
+** N=0 in calls to odbql_aggregate_context(C,N) so that no 
+** pointless memory allocations occur.
+**
+** ^SQLite automatically frees the memory allocated by 
+** odbql_aggregate_context() when the aggregate query concludes.
+**
+** The first parameter must be a copy of the
+** [odbql_context | SQL function context] that is the first parameter
+** to the xStep or xFinal callback routine that implements the aggregate
+** function.
+**
+** This routine must be called from the same thread in which
+** the aggregate SQL function is running.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_aggregate_context(odbql_context*, int nBytes);
+
+/*
+** CAPI3REF: User Data For Functions
+** METHOD: odbql_context
+**
+** ^The odbql_user_data() interface returns a copy of
+** the pointer that was the pUserData parameter (the 5th parameter)
+** of the [odbql_create_function()]
+** and [odbql_create_function16()] routines that originally
+** registered the application defined function.
+**
+** This routine must be called from the same thread in which
+** the application-defined function is running.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_user_data(odbql_context*);
+
+/*
+** CAPI3REF: Database Connection For Functions
+** METHOD: odbql_context
+**
+** ^The odbql_context_db_handle() interface returns a copy of
+** the pointer to the [database connection] (the 1st parameter)
+** of the [odbql_create_function()]
+** and [odbql_create_function16()] routines that originally
+** registered the application defined function.
+*/
+ODBQL_API odbql *ODBQL_STDCALL odbql_context_db_handle(odbql_context*);
+
+/*
+** CAPI3REF: Function Auxiliary Data
+** METHOD: odbql_context
+**
+** These functions may be used by (non-aggregate) SQL functions to
+** associate metadata with argument values. If the same value is passed to
+** multiple invocations of the same SQL function during query execution, under
+** some circumstances the associated metadata may be preserved.  An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.  
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
+**
+** ^The odbql_get_auxdata() interface returns a pointer to the metadata
+** associated by the odbql_set_auxdata() function with the Nth argument
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this odbql_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The odbql_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function.  ^Subsequent
+** calls to odbql_get_auxdata(C,N) return P from the most recent
+** odbql_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to odbql_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> when the corresponding function parameter changes, or
+** <li> when [odbql_reset()] or [odbql_finalize()] is called for the
+**      SQL statement, or
+** <li> when odbql_set_auxdata() is invoked again on the same parameter, or
+** <li> during the original odbql_set_auxdata() call when a memory 
+**      allocation error occurs. </ul>)^
+**
+** Note the last bullet in particular.  The destructor X in 
+** odbql_set_auxdata(C,N,P,X) might be called immediately, before the
+** odbql_set_auxdata() interface even returns.  Hence odbql_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** odbql_set_auxdata() has been called.
+**
+** ^(In practice, metadata is preserved between function calls for
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
+**
+** These routines must be called from the same thread in which
+** the SQL function is running.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_get_auxdata(odbql_context*, int N);
+ODBQL_API void ODBQL_STDCALL odbql_set_auxdata(odbql_context*, int N, void*, void (*)(void*));
+
+
+/*
+** CAPI3REF: Constants Defining Special Destructor Behavior
+**
+** These are special values for the destructor that is passed in as the
+** final argument to routines like [odbql_result_blob()].  ^If the destructor
+** argument is ODBQL_STATIC, it means that the content pointer is constant
+** and will never change.  It does not need to be destroyed.  ^The
+** ODBQL_TRANSIENT value means that the content will likely change in
+** the near future and that SQLite should make its own private copy of
+** the content before returning.
+**
+** The typedef is necessary to work around problems in certain
+** C++ compilers.
+*/
+typedef void (*odbql_destructor_type)(void*);
+#define ODBQL_STATIC      ((odbql_destructor_type)0)
+#define ODBQL_TRANSIENT   ((odbql_destructor_type)-1)
+
+/*
+** CAPI3REF: Setting The Result Of An SQL Function
+** METHOD: odbql_context
+**
+** These routines are used by the xFunc or xFinal callbacks that
+** implement SQL functions and aggregates.  See
+** [odbql_create_function()] and [odbql_create_function16()]
+** for additional information.
+**
+** These functions work very much like the [parameter binding] family of
+** functions used to bind values to host parameters in prepared statements.
+** Refer to the [SQL parameter] documentation for additional information.
+**
+** ^The odbql_result_blob() interface sets the result from
+** an application-defined function to be the BLOB whose content is pointed
+** to by the second parameter and which is N bytes long where N is the
+** third parameter.
+**
+** ^The odbql_result_zeroblob(C,N) and odbql_result_zeroblob64(C,N)
+** interfaces set the result of the application-defined function to be
+** a BLOB containing all zero bytes and N bytes in size.
+**
+** ^The odbql_result_double() interface sets the result from
+** an application-defined function to be a floating point value specified
+** by its 2nd argument.
+**
+** ^The odbql_result_error() and odbql_result_error16() functions
+** cause the implemented SQL function to throw an exception.
+** ^SQLite uses the string pointed to by the
+** 2nd parameter of odbql_result_error() or odbql_result_error16()
+** as the text of an error message.  ^SQLite interprets the error
+** message string from odbql_result_error() as UTF-8. ^SQLite
+** interprets the string from odbql_result_error16() as UTF-16 in native
+** byte order.  ^If the third parameter to odbql_result_error()
+** or odbql_result_error16() is negative then SQLite takes as the error
+** message all text up through the first zero character.
+** ^If the third parameter to odbql_result_error() or
+** odbql_result_error16() is non-negative then SQLite takes that many
+** bytes (not characters) from the 2nd parameter as the error message.
+** ^The odbql_result_error() and odbql_result_error16()
+** routines make a private copy of the error message text before
+** they return.  Hence, the calling function can deallocate or
+** modify the text after they return without harm.
+** ^The odbql_result_error_code() function changes the error code
+** returned by SQLite as a result of an error in a function.  ^By default,
+** the error code is ODBQL_ERROR.  ^A subsequent call to odbql_result_error()
+** or odbql_result_error16() resets the error code to ODBQL_ERROR.
+**
+** ^The odbql_result_error_toobig() interface causes SQLite to throw an
+** error indicating that a string or BLOB is too long to represent.
+**
+** ^The odbql_result_error_nomem() interface causes SQLite to throw an
+** error indicating that a memory allocation failed.
+**
+** ^The odbql_result_int() interface sets the return value
+** of the application-defined function to be the 32-bit signed integer
+** value given in the 2nd argument.
+** ^The odbql_result_int64() interface sets the return value
+** of the application-defined function to be the 64-bit signed integer
+** value given in the 2nd argument.
+**
+** ^The odbql_result_null() interface sets the return value
+** of the application-defined function to be NULL.
+**
+** ^The odbql_result_text(), odbql_result_text16(),
+** odbql_result_text16le(), and odbql_result_text16be() interfaces
+** set the return value of the application-defined function to be
+** a text string which is represented as UTF-8, UTF-16 native byte order,
+** UTF-16 little endian, or UTF-16 big endian, respectively.
+** ^The odbql_result_text64() interface sets the return value of an
+** application-defined function to be a text string in an encoding
+** specified by the fifth (and last) parameter, which must be one
+** of [ODBQL_UTF8], [ODBQL_UTF16], [ODBQL_UTF16BE], or [ODBQL_UTF16LE].
+** ^SQLite takes the text result from the application from
+** the 2nd parameter of the odbql_result_text* interfaces.
+** ^If the 3rd parameter to the odbql_result_text* interfaces
+** is negative, then SQLite takes result text from the 2nd parameter
+** through the first zero character.
+** ^If the 3rd parameter to the odbql_result_text* interfaces
+** is non-negative, then as many bytes (not characters) of the text
+** pointed to by the 2nd parameter are taken as the application-defined
+** function result.  If the 3rd parameter is non-negative, then it
+** must be the byte offset into the string where the NUL terminator would
+** appear if the string where NUL terminated.  If any NUL characters occur
+** in the string at a byte offset that is less than the value of the 3rd
+** parameter, then the resulting string will contain embedded NULs and the
+** result of expressions operating on strings with embedded NULs is undefined.
+** ^If the 4th parameter to the odbql_result_text* interfaces
+** or odbql_result_blob is a non-NULL pointer, then SQLite calls that
+** function as the destructor on the text or BLOB result when it has
+** finished using that result.
+** ^If the 4th parameter to the odbql_result_text* interfaces or to
+** odbql_result_blob is the special constant ODBQL_STATIC, then SQLite
+** assumes that the text or BLOB result is in constant space and does not
+** copy the content of the parameter nor call a destructor on the content
+** when it has finished using that result.
+** ^If the 4th parameter to the odbql_result_text* interfaces
+** or odbql_result_blob is the special constant ODBQL_TRANSIENT
+** then SQLite makes a copy of the result into space obtained from
+** from [odbql_malloc()] before it returns.
+**
+** ^The odbql_result_value() interface sets the result of
+** the application-defined function to be a copy of the
+** [unprotected odbql_value] object specified by the 2nd parameter.  ^The
+** odbql_result_value() interface makes a copy of the [odbql_value]
+** so that the [odbql_value] specified in the parameter may change or
+** be deallocated after odbql_result_value() returns without harm.
+** ^A [protected odbql_value] object may always be used where an
+** [unprotected odbql_value] object is required, so either
+** kind of [odbql_value] object can be used with this interface.
+**
+** If these routines are called from within the different thread
+** than the one containing the application-defined function that received
+** the [odbql_context] pointer, the results are undefined.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_result_blob(odbql_context*, const void*, int, void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_blob64(odbql_context*,const void*,
+                           odbql_uint64,void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_double(odbql_context*, double);
+ODBQL_API void ODBQL_STDCALL odbql_result_error(odbql_context*, const char*, int);
+ODBQL_API void ODBQL_STDCALL odbql_result_error16(odbql_context*, const void*, int);
+ODBQL_API void ODBQL_STDCALL odbql_result_error_toobig(odbql_context*);
+ODBQL_API void ODBQL_STDCALL odbql_result_error_nomem(odbql_context*);
+ODBQL_API void ODBQL_STDCALL odbql_result_error_code(odbql_context*, int);
+ODBQL_API void ODBQL_STDCALL odbql_result_int(odbql_context*, int);
+ODBQL_API void ODBQL_STDCALL odbql_result_int64(odbql_context*, odbql_int64);
+ODBQL_API void ODBQL_STDCALL odbql_result_null(odbql_context*);
+ODBQL_API void ODBQL_STDCALL odbql_result_text(odbql_context*, const char*, int, void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_text64(odbql_context*, const char*,odbql_uint64,
+                           void(*)(void*), unsigned char encoding);
+ODBQL_API void ODBQL_STDCALL odbql_result_text16(odbql_context*, const void*, int, void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_text16le(odbql_context*, const void*, int,void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_text16be(odbql_context*, const void*, int,void(*)(void*));
+ODBQL_API void ODBQL_STDCALL odbql_result_value(odbql_context*, odbql_value*);
+ODBQL_API void ODBQL_STDCALL odbql_result_zeroblob(odbql_context*, int n);
+ODBQL_API int ODBQL_STDCALL odbql_result_zeroblob64(odbql_context*, odbql_uint64 n);
+
+
+/*
+** CAPI3REF: Setting The Subtype Of An SQL Function
+** METHOD: odbql_context
+**
+** The odbql_result_subtype(C,T) function causes the subtype of
+** the result from the [application-defined SQL function] with 
+** [odbql_context] C to be the value T.  Only the lower 8 bits 
+** of the subtype T are preserved in current versions of SQLite;
+** higher order bits are discarded.
+** The number of subtype bytes preserved by SQLite might increase
+** in future releases of SQLite.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_result_subtype(odbql_context*,unsigned int);
+
+/*
+** CAPI3REF: Define New Collating Sequences
+** METHOD: odbql
+**
+** ^These functions add, remove, or modify a [collation] associated
+** with the [database connection] specified as the first argument.
+**
+** ^The name of the collation is a UTF-8 string
+** for odbql_create_collation() and odbql_create_collation_v2()
+** and a UTF-16 string in native byte order for odbql_create_collation16().
+** ^Collation names that compare equal according to [odbql_strnicmp()] are
+** considered to be the same name.
+**
+** ^(The third argument (eTextRep) must be one of the constants:
+** <ul>
+** <li> [ODBQL_UTF8],
+** <li> [ODBQL_UTF16LE],
+** <li> [ODBQL_UTF16BE],
+** <li> [ODBQL_UTF16], or
+** <li> [ODBQL_UTF16_ALIGNED].
+** </ul>)^
+** ^The eTextRep argument determines the encoding of strings passed
+** to the collating function callback, xCallback.
+** ^The [ODBQL_UTF16] and [ODBQL_UTF16_ALIGNED] values for eTextRep
+** force strings to be UTF16 with native byte order.
+** ^The [ODBQL_UTF16_ALIGNED] value for eTextRep forces strings to begin
+** on an even byte address.
+**
+** ^The fourth argument, pArg, is an application data pointer that is passed
+** through as the first argument to the collating function callback.
+**
+** ^The fifth argument, xCallback, is a pointer to the collating function.
+** ^Multiple collating functions can be registered using the same name but
+** with different eTextRep parameters and SQLite will use whichever
+** function requires the least amount of data transformation.
+** ^If the xCallback argument is NULL then the collating function is
+** deleted.  ^When all collating functions having the same name are deleted,
+** that collation is no longer usable.
+**
+** ^The collating function callback is invoked with a copy of the pArg 
+** application data pointer and with two strings in the encoding specified
+** by the eTextRep argument.  The collating function must return an
+** integer that is negative, zero, or positive
+** if the first string is less than, equal to, or greater than the second,
+** respectively.  A collating function must always return the same answer
+** given the same inputs.  If two or more collating functions are registered
+** to the same collation name (using different eTextRep values) then all
+** must give an equivalent answer when invoked with equivalent strings.
+** The collating function must obey the following properties for all
+** strings A, B, and C:
+**
+** <ol>
+** <li> If A==B then B==A.
+** <li> If A==B and B==C then A==C.
+** <li> If A<B THEN B>A.
+** <li> If A<B and B<C then A<C.
+** </ol>
+**
+** If a collating function fails any of the above constraints and that
+** collating function is  registered and used, then the behavior of SQLite
+** is undefined.
+**
+** ^The odbql_create_collation_v2() works like odbql_create_collation()
+** with the addition that the xDestroy callback is invoked on pArg when
+** the collating function is deleted.
+** ^Collating functions are deleted when they are overridden by later
+** calls to the collation creation functions or when the
+** [database connection] is closed using [odbql_close()].
+**
+** ^The xDestroy callback is <u>not</u> called if the 
+** odbql_create_collation_v2() function fails.  Applications that invoke
+** odbql_create_collation_v2() with a non-NULL xDestroy argument should 
+** check the return code and dispose of the application data pointer
+** themselves rather than expecting SQLite to deal with it for them.
+** This is different from every other SQLite interface.  The inconsistency 
+** is unfortunate but cannot be changed without breaking backwards 
+** compatibility.
+**
+** See also:  [odbql_collation_needed()] and [odbql_collation_needed16()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_create_collation(
+  odbql*, 
+  const char *zName, 
+  int eTextRep, 
+  void *pArg,
+  int(*xCompare)(void*,int,const void*,int,const void*)
+);
+ODBQL_API int ODBQL_STDCALL odbql_create_collation_v2(
+  odbql*, 
+  const char *zName, 
+  int eTextRep, 
+  void *pArg,
+  int(*xCompare)(void*,int,const void*,int,const void*),
+  void(*xDestroy)(void*)
+);
+ODBQL_API int ODBQL_STDCALL odbql_create_collation16(
+  odbql*, 
+  const void *zName,
+  int eTextRep, 
+  void *pArg,
+  int(*xCompare)(void*,int,const void*,int,const void*)
+);
+
+/*
+** CAPI3REF: Collation Needed Callbacks
+** METHOD: odbql
+**
+** ^To avoid having to register all collation sequences before a database
+** can be used, a single callback function may be registered with the
+** [database connection] to be invoked whenever an undefined collation
+** sequence is required.
+**
+** ^If the function is registered using the odbql_collation_needed() API,
+** then it is passed the names of undefined collation sequences as strings
+** encoded in UTF-8. ^If odbql_collation_needed16() is used,
+** the names are passed as UTF-16 in machine native byte order.
+** ^A call to either function replaces the existing collation-needed callback.
+**
+** ^(When the callback is invoked, the first argument passed is a copy
+** of the second argument to odbql_collation_needed() or
+** odbql_collation_needed16().  The second argument is the database
+** connection.  The third argument is one of [ODBQL_UTF8], [ODBQL_UTF16BE],
+** or [ODBQL_UTF16LE], indicating the most desirable form of the collation
+** sequence function required.  The fourth parameter is the name of the
+** required collation sequence.)^
+**
+** The callback function should register the desired collation using
+** [odbql_create_collation()], [odbql_create_collation16()], or
+** [odbql_create_collation_v2()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_collation_needed(
+  odbql*, 
+  void*, 
+  void(*)(void*,odbql*,int eTextRep,const char*)
+);
+ODBQL_API int ODBQL_STDCALL odbql_collation_needed16(
+  odbql*, 
+  void*,
+  void(*)(void*,odbql*,int eTextRep,const void*)
+);
+
+#ifdef ODBQL_HAS_CODEC
+/*
+** Specify the key for an encrypted database.  This routine should be
+** called right after odbql_open().
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_key(
+  odbql *db,                   /* Database to be rekeyed */
+  const void *pKey, int nKey     /* The key */
+);
+ODBQL_API int ODBQL_STDCALL odbql_key_v2(
+  odbql *db,                   /* Database to be rekeyed */
+  const char *zDbName,           /* Name of the database */
+  const void *pKey, int nKey     /* The key */
+);
+
+/*
+** Change the key on an open database.  If the current database is not
+** encrypted, this routine will encrypt it.  If pNew==0 or nNew==0, the
+** database is decrypted.
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_rekey(
+  odbql *db,                   /* Database to be rekeyed */
+  const void *pKey, int nKey     /* The new key */
+);
+ODBQL_API int ODBQL_STDCALL odbql_rekey_v2(
+  odbql *db,                   /* Database to be rekeyed */
+  const char *zDbName,           /* Name of the database */
+  const void *pKey, int nKey     /* The new key */
+);
+
+/*
+** Specify the activation key for a SEE database.  Unless 
+** activated, none of the SEE routines will work.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_activate_see(
+  const char *zPassPhrase        /* Activation phrase */
+);
+#endif
+
+#ifdef ODBQL_ENABLE_CEROD
+/*
+** Specify the activation key for a CEROD database.  Unless 
+** activated, none of the CEROD routines will work.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_activate_cerod(
+  const char *zPassPhrase        /* Activation phrase */
+);
+#endif
+
+/*
+** CAPI3REF: Suspend Execution For A Short Time
+**
+** The odbql_sleep() function causes the current thread to suspend execution
+** for at least a number of milliseconds specified in its parameter.
+**
+** If the operating system does not support sleep requests with
+** millisecond time resolution, then the time will be rounded up to
+** the nearest second. The number of milliseconds of sleep actually
+** requested from the operating system is returned.
+**
+** ^SQLite implements this interface by calling the xSleep()
+** method of the default [odbql_vfs] object.  If the xSleep() method
+** of the default VFS is not implemented correctly, or not implemented at
+** all, then the behavior of odbql_sleep() may deviate from the description
+** in the previous paragraphs.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_sleep(int);
+
+/*
+** CAPI3REF: Name Of The Folder Holding Temporary Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all temporary files
+** created by SQLite when using a built-in [odbql_vfs | VFS]
+** will be placed in that directory.)^  ^If this variable
+** is a NULL pointer, then SQLite performs a search for an appropriate
+** temporary file directory.
+**
+** Applications are strongly discouraged from using this global variable.
+** It is required to set a temporary folder on Windows Runtime (WinRT).
+** But for all other platforms, it is highly recommended that applications
+** neither read nor write this variable.  This global variable is a relic
+** that exists for backwards compatibility of legacy applications and should
+** be avoided in new projects.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time.  It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [temp_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [odbql_malloc].  ^Furthermore,
+** the [temp_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from 
+** [odbql_malloc] and the pragma may attempt to free that memory
+** using [odbql_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [odbql_malloc]
+** or else the use of the [temp_store_directory pragma] should be avoided.
+** Except when requested by the [temp_store_directory pragma], SQLite
+** does not free the memory that odbql_temp_directory points to.  If
+** the application wants that memory to be freed, it must do
+** so itself, taking care to only do so after all [database connection]
+** objects have been destroyed.
+**
+** <b>Note to Windows Runtime users:</b>  The temporary directory must be set
+** prior to calling [odbql_open] or [odbql_open_v2].  Otherwise, various
+** features that require the use of temporary files may fail.  Here is an
+** example of how to do this using C++ with the Windows Runtime:
+**
+** <blockquote><pre>
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+**       TemporaryFolder->Path->Data();
+** char zPathBuf[MAX_PATH + 1];
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+**       NULL, NULL);
+** odbql_temp_directory = odbql_mprintf("%s", zPathBuf);
+** </pre></blockquote>
+*/
+ODBQL_API ODBQL_EXTERN char *odbql_temp_directory;
+
+/*
+** CAPI3REF: Name Of The Folder Holding Database Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all database files
+** specified with a relative pathname and created or accessed by
+** SQLite when using a built-in windows [odbql_vfs | VFS] will be assumed
+** to be relative to that directory.)^ ^If this variable is a NULL
+** pointer, then SQLite assumes that all database files specified
+** with a relative pathname are relative to the current directory
+** for the process.  Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
+**
+** Changing the value of this variable while a database connection is
+** open can result in a corrupt database.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time.  It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [data_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [odbql_malloc].  ^Furthermore,
+** the [data_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from 
+** [odbql_malloc] and the pragma may attempt to free that memory
+** using [odbql_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [odbql_malloc]
+** or else the use of the [data_store_directory pragma] should be avoided.
+*/
+ODBQL_API ODBQL_EXTERN char *odbql_data_directory;
+
+/*
+** CAPI3REF: Test For Auto-Commit Mode
+** KEYWORDS: {autocommit mode}
+** METHOD: odbql
+**
+** ^The odbql_get_autocommit() interface returns non-zero or
+** zero if the given database connection is or is not in autocommit mode,
+** respectively.  ^Autocommit mode is on by default.
+** ^Autocommit mode is disabled by a [BEGIN] statement.
+** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK].
+**
+** If certain kinds of errors occur on a statement within a multi-statement
+** transaction (errors including [ODBQL_FULL], [ODBQL_IOERR],
+** [ODBQL_NOMEM], [ODBQL_BUSY], and [ODBQL_INTERRUPT]) then the
+** transaction might be rolled back automatically.  The only way to
+** find out whether SQLite automatically rolled back the transaction after
+** an error is to use this function.
+**
+** If another thread changes the autocommit status of the database
+** connection while this routine is running, then the return value
+** is undefined.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_get_autocommit(odbql*);
+
+/*
+** CAPI3REF: Find The Database Handle Of A Prepared Statement
+** METHOD: odbql_stmt
+**
+** ^The odbql_db_handle interface returns the [database connection] handle
+** to which a [prepared statement] belongs.  ^The [database connection]
+** returned by odbql_db_handle is the same [database connection]
+** that was the first argument
+** to the [odbql_prepare_v2()] call (or its variants) that was used to
+** create the statement in the first place.
+*/
+ODBQL_API odbql *ODBQL_STDCALL odbql_db_handle(odbql_stmt*);
+
+/*
+** CAPI3REF: Return The Filename For A Database Connection
+** METHOD: odbql
+**
+** ^The odbql_db_filename(D,N) interface returns a pointer to a filename
+** associated with database N of connection D.  ^The main database file
+** has the name "main".  If there is no attached database N on the database
+** connection D, or if database N is a temporary or in-memory database, then
+** a NULL pointer is returned.
+**
+** ^The filename returned by this function is the output of the
+** xFullPathname method of the [VFS].  ^In other words, the filename
+** will be an absolute pathname, even if the filename used
+** to open the database originally was a URI or relative pathname.
+*/
+ODBQL_API const char *ODBQL_STDCALL odbql_db_filename(odbql *db, const char *zDbName);
+
+/*
+** CAPI3REF: Determine if a database is read-only
+** METHOD: odbql
+**
+** ^The odbql_db_readonly(D,N) interface returns 1 if the database N
+** of connection D is read-only, 0 if it is read/write, or -1 if N is not
+** the name of a database on connection D.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_db_readonly(odbql *db, const char *zDbName);
+
+/*
+** CAPI3REF: Find the next prepared statement
+** METHOD: odbql
+**
+** ^This interface returns a pointer to the next [prepared statement] after
+** pStmt associated with the [database connection] pDb.  ^If pStmt is NULL
+** then this interface returns a pointer to the first prepared statement
+** associated with the database connection pDb.  ^If no prepared statement
+** satisfies the conditions of this routine, it returns NULL.
+**
+** The [database connection] pointer D in a call to
+** [odbql_next_stmt(D,S)] must refer to an open database
+** connection and in particular must not be a NULL pointer.
+*/
+ODBQL_API odbql_stmt *ODBQL_STDCALL odbql_next_stmt(odbql *pDb, odbql_stmt *pStmt);
+
+/*
+** CAPI3REF: Commit And Rollback Notification Callbacks
+** METHOD: odbql
+**
+** ^The odbql_commit_hook() interface registers a callback
+** function to be invoked whenever a transaction is [COMMIT | committed].
+** ^Any callback set by a previous call to odbql_commit_hook()
+** for the same database connection is overridden.
+** ^The odbql_rollback_hook() interface registers a callback
+** function to be invoked whenever a transaction is [ROLLBACK | rolled back].
+** ^Any callback set by a previous call to odbql_rollback_hook()
+** for the same database connection is overridden.
+** ^The pArg argument is passed through to the callback.
+** ^If the callback on a commit hook function returns non-zero,
+** then the commit is converted into a rollback.
+**
+** ^The odbql_commit_hook(D,C,P) and odbql_rollback_hook(D,C,P) functions
+** return the P argument from the previous call of the same function
+** on the same [database connection] D, or NULL for
+** the first call for each function on D.
+**
+** The commit and rollback hook callbacks are not reentrant.
+** The callback implementation must not do anything that will modify
+** the database connection that invoked the callback.  Any actions
+** to modify the database connection must be deferred until after the
+** completion of the [odbql_step()] call that triggered the commit
+** or rollback hook in the first place.
+** Note that running any other SQL statements, including SELECT statements,
+** or merely calling [odbql_prepare_v2()] and [odbql_step()] will modify
+** the database connections for the meaning of "modify" in this paragraph.
+**
+** ^Registering a NULL function disables the callback.
+**
+** ^When the commit hook callback routine returns zero, the [COMMIT]
+** operation is allowed to continue normally.  ^If the commit hook
+** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK].
+** ^The rollback hook is invoked on a rollback that results from a commit
+** hook returning non-zero, just as it would be with any other rollback.
+**
+** ^For the purposes of this API, a transaction is said to have been
+** rolled back if an explicit "ROLLBACK" statement is executed, or
+** an error or constraint causes an implicit rollback to occur.
+** ^The rollback callback is not invoked if a transaction is
+** automatically rolled back because the database connection is closed.
+**
+** See also the [odbql_update_hook()] interface.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_commit_hook(odbql*, int(*)(void*), void*);
+ODBQL_API void *ODBQL_STDCALL odbql_rollback_hook(odbql*, void(*)(void *), void*);
+
+/*
+** CAPI3REF: Data Change Notification Callbacks
+** METHOD: odbql
+**
+** ^The odbql_update_hook() interface registers a callback function
+** with the [database connection] identified by the first argument
+** to be invoked whenever a row is updated, inserted or deleted in
+** a [rowid table].
+** ^Any callback set by a previous call to this function
+** for the same database connection is overridden.
+**
+** ^The second argument is a pointer to the function to invoke when a
+** row is updated, inserted or deleted in a rowid table.
+** ^The first argument to the callback is a copy of the third argument
+** to odbql_update_hook().
+** ^The second callback argument is one of [ODBQL_INSERT], [ODBQL_DELETE],
+** or [ODBQL_UPDATE], depending on the operation that caused the callback
+** to be invoked.
+** ^The third and fourth arguments to the callback contain pointers to the
+** database and table name containing the affected row.
+** ^The final callback parameter is the [rowid] of the row.
+** ^In the case of an update, this is the [rowid] after the update takes place.
+**
+** ^(The update hook is not invoked when internal system tables are
+** modified (i.e. sqlite_master and sqlite_sequence).)^
+** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
+**
+** ^In the current implementation, the update hook
+** is not invoked when duplication rows are deleted because of an
+** [ON CONFLICT | ON CONFLICT REPLACE] clause.  ^Nor is the update hook
+** invoked when rows are deleted using the [truncate optimization].
+** The exceptions defined in this paragraph might change in a future
+** release of SQLite.
+**
+** The update hook implementation must not do anything that will modify
+** the database connection that invoked the update hook.  Any actions
+** to modify the database connection must be deferred until after the
+** completion of the [odbql_step()] call that triggered the update hook.
+** Note that [odbql_prepare_v2()] and [odbql_step()] both modify their
+** database connections for the meaning of "modify" in this paragraph.
+**
+** ^The odbql_update_hook(D,C,P) function
+** returns the P argument from the previous call
+** on the same [database connection] D, or NULL for
+** the first call on D.
+**
+** See also the [odbql_commit_hook()], [odbql_rollback_hook()],
+** and [odbql_preupdate_hook()] interfaces.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_update_hook(
+  odbql*, 
+  void(*)(void *,int ,char const *,char const *,odbql_int64),
+  void*
+);
+
+/*
+** CAPI3REF: Enable Or Disable Shared Pager Cache
+**
+** ^(This routine enables or disables the sharing of the database cache
+** and schema data structures between [database connection | connections]
+** to the same database. Sharing is enabled if the argument is true
+** and disabled if the argument is false.)^
+**
+** ^Cache sharing is enabled and disabled for an entire process.
+** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** sharing was enabled or disabled for each thread separately.
+**
+** ^(The cache sharing mode set by this interface effects all subsequent
+** calls to [odbql_open()], [odbql_open_v2()], and [odbql_open16()].
+** Existing database connections continue use the sharing mode
+** that was in effect at the time they were opened.)^
+**
+** ^(This routine returns [ODBQL_OK] if shared cache was enabled or disabled
+** successfully.  An [error code] is returned otherwise.)^
+**
+** ^Shared cache is disabled by default. But this might change in
+** future releases of SQLite.  Applications that care about shared
+** cache setting should set it explicitly.
+**
+** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0
+** and will always return ODBQL_MISUSE. On those systems, 
+** shared cache mode should be enabled per-database connection via 
+** [odbql_open_v2()] with [ODBQL_OPEN_SHAREDCACHE].
+**
+** This interface is threadsafe on processors where writing a
+** 32-bit integer is atomic.
+**
+** See Also:  [SQLite Shared-Cache Mode]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_enable_shared_cache(int);
+
+/*
+** CAPI3REF: Attempt To Free Heap Memory
+**
+** ^The odbql_release_memory() interface attempts to free N bytes
+** of heap memory by deallocating non-essential memory allocations
+** held by the database library.   Memory used to cache database
+** pages to improve performance is an example of non-essential memory.
+** ^odbql_release_memory() returns the number of bytes actually freed,
+** which might be more or less than the amount requested.
+** ^The odbql_release_memory() routine is a no-op returning zero
+** if SQLite is not compiled with [ODBQL_ENABLE_MEMORY_MANAGEMENT].
+**
+** See also: [odbql_db_release_memory()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_release_memory(int);
+
+/*
+** CAPI3REF: Free Memory Used By A Database Connection
+** METHOD: odbql
+**
+** ^The odbql_db_release_memory(D) interface attempts to free as much heap
+** memory as possible from database connection D. Unlike the
+** [odbql_release_memory()] interface, this interface is in effect even
+** when the [ODBQL_ENABLE_MEMORY_MANAGEMENT] compile-time option is
+** omitted.
+**
+** See also: [odbql_release_memory()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_db_release_memory(odbql*);
+
+/*
+** CAPI3REF: Impose A Limit On Heap Size
+**
+** ^The odbql_soft_heap_limit64() interface sets and/or queries the
+** soft limit on the amount of heap memory that may be allocated by SQLite.
+** ^SQLite strives to keep heap memory utilization below the soft heap
+** limit by reducing the number of pages held in the page cache
+** as heap memory usages approaches the limit.
+** ^The soft heap limit is "soft" because even though SQLite strives to stay
+** below the limit, it will exceed the limit rather than generate
+** an [ODBQL_NOMEM] error.  In other words, the soft heap limit 
+** is advisory only.
+**
+** ^The return value from odbql_soft_heap_limit64() is the size of
+** the soft heap limit prior to the call, or negative in the case of an
+** error.  ^If the argument N is negative
+** then no change is made to the soft heap limit.  Hence, the current
+** size of the soft heap limit can be determined by invoking
+** odbql_soft_heap_limit64() with a negative argument.
+**
+** ^If the argument N is zero then the soft heap limit is disabled.
+**
+** ^(The soft heap limit is not enforced in the current implementation
+** if one or more of following conditions are true:
+**
+** <ul>
+** <li> The soft heap limit is set to zero.
+** <li> Memory accounting is disabled using a combination of the
+**      [odbql_config]([ODBQL_CONFIG_MEMSTATUS],...) start-time option and
+**      the [ODBQL_DEFAULT_MEMSTATUS] compile-time option.
+** <li> An alternative page cache implementation is specified using
+**      [odbql_config]([ODBQL_CONFIG_PCACHE2],...).
+** <li> The page cache allocates from its own memory pool supplied
+**      by [odbql_config]([ODBQL_CONFIG_PAGECACHE],...) rather than
+**      from the heap.
+** </ul>)^
+**
+** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
+** regardless of whether or not the [ODBQL_ENABLE_MEMORY_MANAGEMENT]
+** compile-time option is invoked.  With [ODBQL_ENABLE_MEMORY_MANAGEMENT],
+** the soft heap limit is enforced on every memory allocation.  Without
+** [ODBQL_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced
+** when memory is allocated by the page cache.  Testing suggests that because
+** the page cache is the predominate memory user in SQLite, most
+** applications will achieve adequate soft heap limit enforcement without
+** the use of [ODBQL_ENABLE_MEMORY_MANAGEMENT].
+**
+** The circumstances under which SQLite will enforce the soft heap limit may
+** changes in future releases of SQLite.
+*/
+ODBQL_API odbql_int64 ODBQL_STDCALL odbql_soft_heap_limit64(odbql_int64 N);
+
+/*
+** CAPI3REF: Deprecated Soft Heap Limit Interface
+** DEPRECATED
+**
+** This is a deprecated version of the [odbql_soft_heap_limit64()]
+** interface.  This routine is provided for historical compatibility
+** only.  All new applications should use the
+** [odbql_soft_heap_limit64()] interface rather than this one.
+*/
+ODBQL_API ODBQL_DEPRECATED void ODBQL_STDCALL odbql_soft_heap_limit(int N);
+
+
+/*
+** CAPI3REF: Extract Metadata About A Column Of A Table
+** METHOD: odbql
+**
+** ^(The odbql_table_column_metadata(X,D,T,C,....) routine returns
+** information about column C of table T in database D
+** on [database connection] X.)^  ^The odbql_table_column_metadata()
+** interface returns ODBQL_OK and fills in the non-NULL pointers in
+** the final five arguments with appropriate values if the specified
+** column exists.  ^The odbql_table_column_metadata() interface returns
+** ODBQL_ERROR and if the specified column does not exist.
+** ^If the column-name parameter to odbql_table_column_metadata() is a
+** NULL pointer, then this routine simply checks for the existance of the
+** table and returns ODBQL_OK if the table exists and ODBQL_ERROR if it
+** does not.
+**
+** ^The column is identified by the second, third and fourth parameters to
+** this function. ^(The second parameter is either the name of the database
+** (i.e. "main", "temp", or an attached database) containing the specified
+** table or NULL.)^ ^If it is NULL, then all attached databases are searched
+** for the table using the same algorithm used by the database engine to
+** resolve unqualified table references.
+**
+** ^The third and fourth parameters to this function are the table and column
+** name of the desired column, respectively.
+**
+** ^Metadata is returned by writing to the memory locations passed as the 5th
+** and subsequent parameters to this function. ^Any of these arguments may be
+** NULL, in which case the corresponding element of metadata is omitted.
+**
+** ^(<blockquote>
+** <table border="1">
+** <tr><th> Parameter <th> Output<br>Type <th>  Description
+**
+** <tr><td> 5th <td> const char* <td> Data type
+** <tr><td> 6th <td> const char* <td> Name of default collation sequence
+** <tr><td> 7th <td> int         <td> True if column has a NOT NULL constraint
+** <tr><td> 8th <td> int         <td> True if column is part of the PRIMARY KEY
+** <tr><td> 9th <td> int         <td> True if column is [AUTOINCREMENT]
+** </table>
+** </blockquote>)^
+**
+** ^The memory pointed to by the character pointers returned for the
+** declaration type and collation sequence is valid until the next
+** call to any SQLite API function.
+**
+** ^If the specified table is actually a view, an [error code] is returned.
+**
+** ^If the specified column is "rowid", "oid" or "_rowid_" and the table 
+** is not a [WITHOUT ROWID] table and an
+** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
+** parameters are set for the explicitly declared column. ^(If there is no
+** [INTEGER PRIMARY KEY] column, then the outputs
+** for the [rowid] are set as follows:
+**
+** <pre>
+**     data type: "INTEGER"
+**     collation sequence: "BINARY"
+**     not null: 0
+**     primary key: 1
+**     auto increment: 0
+** </pre>)^
+**
+** ^This function causes all database schemas to be read from disk and
+** parsed, if that has not already been done, and returns an error if
+** any errors are encountered while loading the schema.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_table_column_metadata(
+  odbql *db,                /* Connection handle */
+  const char *zDbName,        /* Database name or NULL */
+  const char *zTableName,     /* Table name */
+  const char *zColumnName,    /* Column name */
+  char const **pzDataType,    /* OUTPUT: Declared data type */
+  char const **pzCollSeq,     /* OUTPUT: Collation sequence name */
+  int *pNotNull,              /* OUTPUT: True if NOT NULL constraint exists */
+  int *pPrimaryKey,           /* OUTPUT: True if column part of PK */
+  int *pAutoinc               /* OUTPUT: True if column is auto-increment */
+);
+
+/*
+** CAPI3REF: Load An Extension
+** METHOD: odbql
+**
+** ^This interface loads an SQLite extension library from the named file.
+**
+** ^The odbql_load_extension() interface attempts to load an
+** [SQLite extension] library contained in the file zFile.  If
+** the file cannot be loaded directly, attempts are made to load
+** with various operating-system specific extensions added.
+** So for example, if "samplelib" cannot be loaded, then names like
+** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might
+** be tried also.
+**
+** ^The entry point is zProc.
+** ^(zProc may be 0, in which case SQLite will try to come up with an
+** entry point name on its own.  It first tries "odbql_extension_init".
+** If that does not work, it constructs a name "odbql_X_init" where the
+** X is consists of the lower-case equivalent of all ASCII alphabetic
+** characters in the filename from the last "/" to the first following
+** "." and omitting any initial "lib".)^
+** ^The odbql_load_extension() interface returns
+** [ODBQL_OK] on success and [ODBQL_ERROR] if something goes wrong.
+** ^If an error occurs and pzErrMsg is not 0, then the
+** [odbql_load_extension()] interface shall attempt to
+** fill *pzErrMsg with error message text stored in memory
+** obtained from [odbql_malloc()]. The calling function
+** should free this memory by calling [odbql_free()].
+**
+** ^Extension loading must be enabled using
+** [odbql_enable_load_extension()] or
+** [odbql_db_config](db,[ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL)
+** prior to calling this API,
+** otherwise an error will be returned.
+**
+** <b>Security warning:</b> It is recommended that the 
+** [ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
+** interface.  The use of the [odbql_enable_load_extension()] interface
+** should be avoided.  This will keep the SQL function [load_extension()]
+** disabled and prevent SQL injections from giving attackers
+** access to extension loading capabilities.
+**
+** See also the [load_extension() SQL function].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_load_extension(
+  odbql *db,          /* Load the extension into this database connection */
+  const char *zFile,    /* Name of the shared library containing extension */
+  const char *zProc,    /* Entry point.  Derived from zFile if 0 */
+  char **pzErrMsg       /* Put error message here if not 0 */
+);
+
+/*
+** CAPI3REF: Enable Or Disable Extension Loading
+** METHOD: odbql
+**
+** ^So as not to open security holes in older applications that are
+** unprepared to deal with [extension loading], and as a means of disabling
+** [extension loading] while evaluating user-entered SQL, the following API
+** is provided to turn the [odbql_load_extension()] mechanism on and off.
+**
+** ^Extension loading is off by default.
+** ^Call the odbql_enable_load_extension() routine with onoff==1
+** to turn extension loading on and call it with onoff==0 to turn
+** it back off again.
+**
+** ^This interface enables or disables both the C-API
+** [odbql_load_extension()] and the SQL function [load_extension()].
+** Use [odbql_db_config](db,[ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION],..)
+** to enable or disable only the C-API.
+**
+** <b>Security warning:</b> It is recommended that extension loading
+** be disabled using the [ODBQL_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** rather than this interface, so the [load_extension()] SQL function
+** remains disabled. This will prevent SQL injections from giving attackers
+** access to extension loading capabilities.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_enable_load_extension(odbql *db, int onoff);
+
+/*
+** CAPI3REF: Automatically Load Statically Linked Extensions
+**
+** ^This interface causes the xEntryPoint() function to be invoked for
+** each new [database connection] that is created.  The idea here is that
+** xEntryPoint() is the entry point for a statically linked [SQLite extension]
+** that is to be automatically loaded into all new database connections.
+**
+** ^(Even though the function prototype shows that xEntryPoint() takes
+** no arguments and returns void, SQLite invokes xEntryPoint() with three
+** arguments and expects and integer result as if the signature of the
+** entry point where as follows:
+**
+** <blockquote><pre>
+**    int xEntryPoint(
+**      odbql *db,
+**      const char **pzErrMsg,
+**      const struct odbql_api_routines *pThunk
+**    );
+** </pre></blockquote>)^
+**
+** If the xEntryPoint routine encounters an error, it should make *pzErrMsg
+** point to an appropriate error message (obtained from [odbql_mprintf()])
+** and return an appropriate [error code].  ^SQLite ensures that *pzErrMsg
+** is NULL before calling the xEntryPoint().  ^SQLite will invoke
+** [odbql_free()] on *pzErrMsg after xEntryPoint() returns.  ^If any
+** xEntryPoint() returns an error, the [odbql_open()], [odbql_open16()],
+** or [odbql_open_v2()] call that provoked the xEntryPoint() will fail.
+**
+** ^Calling odbql_auto_extension(X) with an entry point X that is already
+** on the list of automatic extensions is a harmless no-op. ^No entry point
+** will be called more than once for each database connection that is opened.
+**
+** See also: [odbql_reset_auto_extension()]
+** and [odbql_cancel_auto_extension()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_auto_extension(void (*xEntryPoint)(void));
+
+/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [odbql_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [odbql_auto_extension(X)].  ^The [odbql_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully 
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_cancel_auto_extension(void (*xEntryPoint)(void));
+
+/*
+** CAPI3REF: Reset Automatic Extension Loading
+**
+** ^This interface disables all automatic extensions previously
+** registered using [odbql_auto_extension()].
+*/
+ODBQL_API void ODBQL_STDCALL odbql_reset_auto_extension(void);
+
+/*
+** The interface to the virtual-table mechanism is currently considered
+** to be experimental.  The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+*/
+
+/*
+** Structures used by the virtual table interface
+*/
+typedef struct odbql_vtab odbql_vtab;
+typedef struct odbql_index_info odbql_index_info;
+typedef struct odbql_vtab_cursor odbql_vtab_cursor;
+typedef struct odbql_module odbql_module;
+
+/*
+** CAPI3REF: Virtual Table Object
+** KEYWORDS: odbql_module {virtual table module}
+**
+** This structure, sometimes called a "virtual table module", 
+** defines the implementation of a [virtual tables].  
+** This structure consists mostly of methods for the module.
+**
+** ^A virtual table module is created by filling in a persistent
+** instance of this structure and passing a pointer to that instance
+** to [odbql_create_module()] or [odbql_create_module_v2()].
+** ^The registration remains valid until it is replaced by a different
+** module or until the [database connection] closes.  The content
+** of this structure must not change while it is registered with
+** any database connection.
+*/
+struct odbql_module {
+  int iVersion;
+  int (*xCreate)(odbql*, void *pAux,
+               int argc, const char *const*argv,
+               odbql_vtab **ppVTab, char**);
+  int (*xConnect)(odbql*, void *pAux,
+               int argc, const char *const*argv,
+               odbql_vtab **ppVTab, char**);
+  int (*xBestIndex)(odbql_vtab *pVTab, odbql_index_info*);
+  int (*xDisconnect)(odbql_vtab *pVTab);
+  int (*xDestroy)(odbql_vtab *pVTab);
+  int (*xOpen)(odbql_vtab *pVTab, odbql_vtab_cursor **ppCursor);
+  int (*xClose)(odbql_vtab_cursor*);
+  int (*xFilter)(odbql_vtab_cursor*, int idxNum, const char *idxStr,
+                int argc, odbql_value **argv);
+  int (*xNext)(odbql_vtab_cursor*);
+  int (*xEof)(odbql_vtab_cursor*);
+  int (*xColumn)(odbql_vtab_cursor*, odbql_context*, int);
+  int (*xRowid)(odbql_vtab_cursor*, odbql_int64 *pRowid);
+  int (*xUpdate)(odbql_vtab *, int, odbql_value **, odbql_int64 *);
+  int (*xBegin)(odbql_vtab *pVTab);
+  int (*xSync)(odbql_vtab *pVTab);
+  int (*xCommit)(odbql_vtab *pVTab);
+  int (*xRollback)(odbql_vtab *pVTab);
+  int (*xFindFunction)(odbql_vtab *pVtab, int nArg, const char *zName,
+                       void (**pxFunc)(odbql_context*,int,odbql_value**),
+                       void **ppArg);
+  int (*xRename)(odbql_vtab *pVtab, const char *zNew);
+  /* The methods above are in version 1 of the sqlite_module object. Those 
+  ** below are for version 2 and greater. */
+  int (*xSavepoint)(odbql_vtab *pVTab, int);
+  int (*xRelease)(odbql_vtab *pVTab, int);
+  int (*xRollbackTo)(odbql_vtab *pVTab, int);
+};
+
+/*
+** CAPI3REF: Virtual Table Indexing Information
+** KEYWORDS: odbql_index_info
+**
+** The odbql_index_info structure and its substructures is used as part
+** of the [virtual table] interface to
+** pass information into and receive the reply from the [xBestIndex]
+** method of a [virtual table module].  The fields under **Inputs** are the
+** inputs to xBestIndex and are read-only.  xBestIndex inserts its
+** results into the **Outputs** fields.
+**
+** ^(The aConstraint[] array records WHERE clause constraints of the form:
+**
+** <blockquote>column OP expr</blockquote>
+**
+** where OP is =, <, <=, >, or >=.)^  ^(The particular operator is
+** stored in aConstraint[].op using one of the
+** [ODBQL_INDEX_CONSTRAINT_EQ | ODBQL_INDEX_CONSTRAINT_ values].)^
+** ^(The index of the column is stored in
+** aConstraint[].iColumn.)^  ^(aConstraint[].usable is TRUE if the
+** expr on the right-hand side can be evaluated (and thus the constraint
+** is usable) and false if it cannot.)^
+**
+** ^The optimizer automatically inverts terms of the form "expr OP column"
+** and makes other simplifications to the WHERE clause in an attempt to
+** get as many WHERE clause terms into the form shown above as possible.
+** ^The aConstraint[] array only reports WHERE clause terms that are
+** relevant to the particular virtual table being queried.
+**
+** ^Information about the ORDER BY clause is stored in aOrderBy[].
+** ^Each term of aOrderBy records a column of the ORDER BY clause.
+**
+** The colUsed field indicates which columns of the virtual table may be
+** required by the current scan. Virtual table columns are numbered from
+** zero in the order in which they appear within the CREATE TABLE statement
+** passed to odbql_declare_vtab(). For the first 63 columns (columns 0-62),
+** the corresponding bit is set within the colUsed mask if the column may be
+** required by SQLite. If the table has at least 64 columns and any column
+** to the right of the first 63 is required, then bit 63 of colUsed is also
+** set. In other words, column iCol may be required if the expression
+** (colUsed & ((odbql_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to 
+** non-zero.
+**
+** The [xBestIndex] method must fill aConstraintUsage[] with information
+** about what parameters to pass to xFilter.  ^If argvIndex>0 then
+** the right-hand side of the corresponding aConstraint[] is evaluated
+** and becomes the argvIndex-th entry in argv.  ^(If aConstraintUsage[].omit
+** is true, then the constraint is assumed to be fully handled by the
+** virtual table and is not checked again by SQLite.)^
+**
+** ^The idxNum and idxPtr values are recorded and passed into the
+** [xFilter] method.
+** ^[odbql_free()] is used to free idxPtr if and only if
+** needToFreeIdxPtr is true.
+**
+** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
+** the correct order to satisfy the ORDER BY clause so that no separate
+** sorting step is required.
+**
+** ^The estimatedCost value is an estimate of the cost of a particular
+** strategy. A cost of N indicates that the cost of the strategy is similar
+** to a linear scan of an SQLite table with N rows. A cost of log(N) 
+** indicates that the expense of the operation is similar to that of a
+** binary search on a unique indexed field of an SQLite table with N rows.
+**
+** ^The estimatedRows value is an estimate of the number of rows that
+** will be returned by the strategy.
+**
+** The xBestIndex method may optionally populate the idxFlags field with a 
+** mask of ODBQL_INDEX_SCAN_* flags. Currently there is only one such flag -
+** ODBQL_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row. 
+**
+** Additionally, if xBestIndex sets the ODBQL_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns ODBQL_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** ODBQL_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if ODBQL_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns ODBQL_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
+** IMPORTANT: The estimatedRows field was added to the odbql_index_info
+** structure for SQLite version 3.8.2. If a virtual table extension is
+** used with an SQLite version earlier than 3.8.2, the results of attempting 
+** to read or write the estimatedRows field are undefined (but are likely 
+** to included crashing the application). The estimatedRows field should
+** therefore only be used if [odbql_libversion_number()] returns a
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for version 3.9.0. It may therefore only be used if
+** odbql_libversion_number() returns a value greater than or equal to
+** 3009000.
+*/
+struct odbql_index_info {
+  /* Inputs */
+  int nConstraint;           /* Number of entries in aConstraint */
+  struct odbql_index_constraint {
+     int iColumn;              /* Column constrained.  -1 for ROWID */
+     unsigned char op;         /* Constraint operator */
+     unsigned char usable;     /* True if this constraint is usable */
+     int iTermOffset;          /* Used internally - xBestIndex should ignore */
+  } *aConstraint;            /* Table of WHERE clause constraints */
+  int nOrderBy;              /* Number of terms in the ORDER BY clause */
+  struct odbql_index_orderby {
+     int iColumn;              /* Column number */
+     unsigned char desc;       /* True for DESC.  False for ASC. */
+  } *aOrderBy;               /* The ORDER BY clause */
+  /* Outputs */
+  struct odbql_index_constraint_usage {
+    int argvIndex;           /* if >0, constraint is part of argv to xFilter */
+    unsigned char omit;      /* Do not code a test for this constraint */
+  } *aConstraintUsage;
+  int idxNum;                /* Number used to identify the index */
+  char *idxStr;              /* String, possibly obtained from odbql_malloc */
+  int needToFreeIdxStr;      /* Free idxStr using odbql_free() if true */
+  int orderByConsumed;       /* True if output is already ordered */
+  double estimatedCost;           /* Estimated cost of using this index */
+  /* Fields below are only available in SQLite 3.8.2 and later */
+  odbql_int64 estimatedRows;    /* Estimated number of rows returned */
+  /* Fields below are only available in SQLite 3.9.0 and later */
+  int idxFlags;              /* Mask of ODBQL_INDEX_SCAN_* flags */
+  /* Fields below are only available in SQLite 3.10.0 and later */
+  odbql_uint64 colUsed;    /* Input: Mask of columns used by statement */
+};
+
+/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define ODBQL_INDEX_SCAN_UNIQUE      1     /* Scan visits at most 1 row */
+
+/*
+** CAPI3REF: Virtual Table Constraint Operator Codes
+**
+** These macros defined the allowed values for the
+** [odbql_index_info].aConstraint[].op field.  Each value represents
+** an operator that is part of a constraint term in the wHERE clause of
+** a query that uses a [virtual table].
+*/
+#define ODBQL_INDEX_CONSTRAINT_EQ      2
+#define ODBQL_INDEX_CONSTRAINT_GT      4
+#define ODBQL_INDEX_CONSTRAINT_LE      8
+#define ODBQL_INDEX_CONSTRAINT_LT     16
+#define ODBQL_INDEX_CONSTRAINT_GE     32
+#define ODBQL_INDEX_CONSTRAINT_MATCH  64
+#define ODBQL_INDEX_CONSTRAINT_LIKE   65
+#define ODBQL_INDEX_CONSTRAINT_GLOB   66
+#define ODBQL_INDEX_CONSTRAINT_REGEXP 67
+
+/*
+** CAPI3REF: Register A Virtual Table Implementation
+** METHOD: odbql
+**
+** ^These routines are used to register a new [virtual table module] name.
+** ^Module names must be registered before
+** creating a new [virtual table] using the module and before using a
+** preexisting [virtual table] for the module.
+**
+** ^The module name is registered on the [database connection] specified
+** by the first parameter.  ^The name of the module is given by the 
+** second parameter.  ^The third parameter is a pointer to
+** the implementation of the [virtual table module].   ^The fourth
+** parameter is an arbitrary client data pointer that is passed through
+** into the [xCreate] and [xConnect] methods of the virtual table module
+** when a new virtual table is be being created or reinitialized.
+**
+** ^The odbql_create_module_v2() interface has a fifth parameter which
+** is a pointer to a destructor for the pClientData.  ^SQLite will
+** invoke the destructor function (if it is not NULL) when SQLite
+** no longer needs the pClientData pointer.  ^The destructor will also
+** be invoked if the call to odbql_create_module_v2() fails.
+** ^The odbql_create_module()
+** interface is equivalent to odbql_create_module_v2() with a NULL
+** destructor.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_create_module(
+  odbql *db,               /* SQLite connection to register module with */
+  const char *zName,         /* Name of the module */
+  const odbql_module *p,   /* Methods for the module */
+  void *pClientData          /* Client data for xCreate/xConnect */
+);
+ODBQL_API int ODBQL_STDCALL odbql_create_module_v2(
+  odbql *db,               /* SQLite connection to register module with */
+  const char *zName,         /* Name of the module */
+  const odbql_module *p,   /* Methods for the module */
+  void *pClientData,         /* Client data for xCreate/xConnect */
+  void(*xDestroy)(void*)     /* Module destructor function */
+);
+
+/*
+** CAPI3REF: Virtual Table Instance Object
+** KEYWORDS: odbql_vtab
+**
+** Every [virtual table module] implementation uses a subclass
+** of this object to describe a particular instance
+** of the [virtual table].  Each subclass will
+** be tailored to the specific needs of the module implementation.
+** The purpose of this superclass is to define certain fields that are
+** common to all module implementations.
+**
+** ^Virtual tables methods can set an error message by assigning a
+** string obtained from [odbql_mprintf()] to zErrMsg.  The method should
+** take care that any prior string is freed by a call to [odbql_free()]
+** prior to assigning a new string to zErrMsg.  ^After the error message
+** is delivered up to the client application, the string will be automatically
+** freed by odbql_free() and the zErrMsg field will be zeroed.
+*/
+struct odbql_vtab {
+  const odbql_module *pModule;  /* The module for this virtual table */
+  int nRef;                       /* Number of open cursors */
+  char *zErrMsg;                  /* Error message from odbql_mprintf() */
+  /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Virtual Table Cursor Object
+** KEYWORDS: odbql_vtab_cursor {virtual table cursor}
+**
+** Every [virtual table module] implementation uses a subclass of the
+** following structure to describe cursors that point into the
+** [virtual table] and are used
+** to loop through the virtual table.  Cursors are created using the
+** [odbql_module.xOpen | xOpen] method of the module and are destroyed
+** by the [odbql_module.xClose | xClose] method.  Cursors are used
+** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods
+** of the module.  Each module implementation will define
+** the content of a cursor structure to suit its own needs.
+**
+** This superclass exists in order to define fields of the cursor that
+** are common to all implementations.
+*/
+struct odbql_vtab_cursor {
+  odbql_vtab *pVtab;      /* Virtual table of this cursor */
+  /* Virtual table implementations will typically add additional fields */
+};
+
+/*
+** CAPI3REF: Declare The Schema Of A Virtual Table
+**
+** ^The [xCreate] and [xConnect] methods of a
+** [virtual table module] call this interface
+** to declare the format (the names and datatypes of the columns) of
+** the virtual tables they implement.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_declare_vtab(odbql*, const char *zSQL);
+
+/*
+** CAPI3REF: Overload A Function For A Virtual Table
+** METHOD: odbql
+**
+** ^(Virtual tables can provide alternative implementations of functions
+** using the [xFindFunction] method of the [virtual table module].  
+** But global versions of those functions
+** must exist in order to be overloaded.)^
+**
+** ^(This API makes sure a global version of a function with a particular
+** name and number of parameters exists.  If no such function exists
+** before this API is called, a new function is created.)^  ^The implementation
+** of the new function always causes an exception to be thrown.  So
+** the new function is not good for anything by itself.  Its only
+** purpose is to be a placeholder function that can be overloaded
+** by a [virtual table].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_overload_function(odbql*, const char *zFuncName, int nArg);
+
+/*
+** The interface to the virtual-table mechanism defined above (back up
+** to a comment remarkably similar to this one) is currently considered
+** to be experimental.  The interface might change in incompatible ways.
+** If this is a problem for you, do not use the interface at this time.
+**
+** When the virtual-table mechanism stabilizes, we will declare the
+** interface fixed, support it indefinitely, and remove this comment.
+*/
+
+/*
+** CAPI3REF: A Handle To An Open BLOB
+** KEYWORDS: {BLOB handle} {BLOB handles}
+**
+** An instance of this object represents an open BLOB on which
+** [odbql_blob_open | incremental BLOB I/O] can be performed.
+** ^Objects of this type are created by [odbql_blob_open()]
+** and destroyed by [odbql_blob_close()].
+** ^The [odbql_blob_read()] and [odbql_blob_write()] interfaces
+** can be used to read or write small subsections of the BLOB.
+** ^The [odbql_blob_bytes()] interface returns the size of the BLOB in bytes.
+*/
+typedef struct odbql_blob odbql_blob;
+
+/*
+** CAPI3REF: Open A BLOB For Incremental I/O
+** METHOD: odbql
+** CONSTRUCTOR: odbql_blob
+**
+** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located
+** in row iRow, column zColumn, table zTable in database zDb;
+** in other words, the same BLOB that would be selected by:
+**
+** <pre>
+**     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
+** </pre>)^
+**
+** ^(Parameter zDb is not the filename that contains the database, but 
+** rather the symbolic name of the database. For attached databases, this is
+** the name that appears after the AS keyword in the [ATTACH] statement.
+** For the main database file, the database name is "main". For TEMP
+** tables, the database name is "temp".)^
+**
+** ^If the flags parameter is non-zero, then the BLOB is opened for read
+** and write access. ^If the flags parameter is zero, the BLOB is opened for
+** read-only access.
+**
+** ^(On success, [ODBQL_OK] is returned and the new [BLOB handle] is stored
+** in *ppBlob. Otherwise an [error code] is returned and, unless the error
+** code is ODBQL_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
+** the API is not misused, it is always safe to call [odbql_blob_close()] 
+** on *ppBlob after this function it returns.
+**
+** This function fails with ODBQL_ERROR if any of the following are true:
+** <ul>
+**   <li> ^(Database zDb does not exist)^, 
+**   <li> ^(Table zTable does not exist within database zDb)^, 
+**   <li> ^(Table zTable is a WITHOUT ROWID table)^, 
+**   <li> ^(Column zColumn does not exist)^,
+**   <li> ^(Row iRow is not present in the table)^,
+**   <li> ^(The specified column of row iRow contains a value that is not
+**         a TEXT or BLOB value)^,
+**   <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE 
+**         constraint and the blob is being opened for read/write access)^,
+**   <li> ^([foreign key constraints | Foreign key constraints] are enabled, 
+**         column zColumn is part of a [child key] definition and the blob is
+**         being opened for read/write access)^.
+** </ul>
+**
+** ^Unless it returns ODBQL_MISUSE, this function sets the 
+** [database connection] error code and message accessible via 
+** [odbql_errcode()] and [odbql_errmsg()] and related functions. 
+**
+**
+** ^(If the row that a BLOB handle points to is modified by an
+** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
+** then the BLOB handle is marked as "expired".
+** This is true if any column of the row is changed, even a column
+** other than the one the BLOB handle is open on.)^
+** ^Calls to [odbql_blob_read()] and [odbql_blob_write()] for
+** an expired BLOB handle fail with a return code of [ODBQL_ABORT].
+** ^(Changes written into a BLOB prior to the BLOB expiring are not
+** rolled back by the expiration of the BLOB.  Such changes will eventually
+** commit if the transaction continues to completion.)^
+**
+** ^Use the [odbql_blob_bytes()] interface to determine the size of
+** the opened blob.  ^The size of a blob may not be changed by this
+** interface.  Use the [UPDATE] SQL command to change the size of a
+** blob.
+**
+** ^The [odbql_bind_zeroblob()] and [odbql_result_zeroblob()] interfaces
+** and the built-in [zeroblob] SQL function may be used to create a 
+** zero-filled blob to read or write using the incremental-blob interface.
+**
+** To avoid a resource leak, every open [BLOB handle] should eventually
+** be released by a call to [odbql_blob_close()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_open(
+  odbql*,
+  const char *zDb,
+  const char *zTable,
+  const char *zColumn,
+  odbql_int64 iRow,
+  int flags,
+  odbql_blob **ppBlob
+);
+
+/*
+** CAPI3REF: Move a BLOB Handle to a New Row
+** METHOD: odbql_blob
+**
+** ^This function is used to move an existing blob handle so that it points
+** to a different row of the same database table. ^The new row is identified
+** by the rowid value passed as the second argument. Only the row can be
+** changed. ^The database, table and column on which the blob handle is open
+** remain the same. Moving an existing blob handle to a new row can be
+** faster than closing the existing handle and opening a new one.
+**
+** ^(The new row must meet the same criteria as for [odbql_blob_open()] -
+** it must exist and there must be either a blob or text value stored in
+** the nominated column.)^ ^If the new row is not present in the table, or if
+** it does not contain a blob or text value, or if another error occurs, an
+** SQLite error code is returned and the blob handle is considered aborted.
+** ^All subsequent calls to [odbql_blob_read()], [odbql_blob_write()] or
+** [odbql_blob_reopen()] on an aborted blob handle immediately return
+** ODBQL_ABORT. ^Calling [odbql_blob_bytes()] on an aborted blob handle
+** always returns zero.
+**
+** ^This function sets the database handle error code and message.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_reopen(odbql_blob *, odbql_int64);
+
+/*
+** CAPI3REF: Close A BLOB Handle
+** DESTRUCTOR: odbql_blob
+**
+** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
+** unconditionally.  Even if this routine returns an error code, the 
+** handle is still closed.)^
+**
+** ^If the blob handle being closed was opened for read-write access, and if
+** the database is in auto-commit mode and there are no other open read-write
+** blob handles or active write statements, the current transaction is
+** committed. ^If an error occurs while committing the transaction, an error
+** code is returned and the transaction rolled back.
+**
+** Calling this function with an argument that is not a NULL pointer or an
+** open blob handle results in undefined behaviour. ^Calling this routine 
+** with a null pointer (such as would be returned by a failed call to 
+** [odbql_blob_open()]) is a harmless no-op. ^Otherwise, if this function
+** is passed a valid open blob handle, the values returned by the 
+** odbql_errcode() and odbql_errmsg() functions are set before returning.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_close(odbql_blob *);
+
+/*
+** CAPI3REF: Return The Size Of An Open BLOB
+** METHOD: odbql_blob
+**
+** ^Returns the size in bytes of the BLOB accessible via the 
+** successfully opened [BLOB handle] in its only argument.  ^The
+** incremental blob I/O routines can only read or overwriting existing
+** blob content; they cannot change the size of a blob.
+**
+** This routine only works on a [BLOB handle] which has been created
+** by a prior successful call to [odbql_blob_open()] and which has not
+** been closed by [odbql_blob_close()].  Passing any other pointer in
+** to this routine results in undefined and probably undesirable behavior.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_bytes(odbql_blob *);
+
+/*
+** CAPI3REF: Read Data From A BLOB Incrementally
+** METHOD: odbql_blob
+**
+** ^(This function is used to read data from an open [BLOB handle] into a
+** caller-supplied buffer. N bytes of data are copied into buffer Z
+** from the open BLOB, starting at offset iOffset.)^
+**
+** ^If offset iOffset is less than N bytes from the end of the BLOB,
+** [ODBQL_ERROR] is returned and no data is read.  ^If N or iOffset is
+** less than zero, [ODBQL_ERROR] is returned and no data is read.
+** ^The size of the blob (and hence the maximum value of N+iOffset)
+** can be determined using the [odbql_blob_bytes()] interface.
+**
+** ^An attempt to read from an expired [BLOB handle] fails with an
+** error code of [ODBQL_ABORT].
+**
+** ^(On success, odbql_blob_read() returns ODBQL_OK.
+** Otherwise, an [error code] or an [extended error code] is returned.)^
+**
+** This routine only works on a [BLOB handle] which has been created
+** by a prior successful call to [odbql_blob_open()] and which has not
+** been closed by [odbql_blob_close()].  Passing any other pointer in
+** to this routine results in undefined and probably undesirable behavior.
+**
+** See also: [odbql_blob_write()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_read(odbql_blob *, void *Z, int N, int iOffset);
+
+/*
+** CAPI3REF: Write Data Into A BLOB Incrementally
+** METHOD: odbql_blob
+**
+** ^(This function is used to write data into an open [BLOB handle] from a
+** caller-supplied buffer. N bytes of data are copied from the buffer Z
+** into the open BLOB, starting at offset iOffset.)^
+**
+** ^(On success, odbql_blob_write() returns ODBQL_OK.
+** Otherwise, an  [error code] or an [extended error code] is returned.)^
+** ^Unless ODBQL_MISUSE is returned, this function sets the 
+** [database connection] error code and message accessible via 
+** [odbql_errcode()] and [odbql_errmsg()] and related functions. 
+**
+** ^If the [BLOB handle] passed as the first argument was not opened for
+** writing (the flags parameter to [odbql_blob_open()] was zero),
+** this function returns [ODBQL_READONLY].
+**
+** This function may only modify the contents of the BLOB; it is
+** not possible to increase the size of a BLOB using this API.
+** ^If offset iOffset is less than N bytes from the end of the BLOB,
+** [ODBQL_ERROR] is returned and no data is written. The size of the 
+** BLOB (and hence the maximum value of N+iOffset) can be determined 
+** using the [odbql_blob_bytes()] interface. ^If N or iOffset are less 
+** than zero [ODBQL_ERROR] is returned and no data is written.
+**
+** ^An attempt to write to an expired [BLOB handle] fails with an
+** error code of [ODBQL_ABORT].  ^Writes to the BLOB that occurred
+** before the [BLOB handle] expired are not rolled back by the
+** expiration of the handle, though of course those changes might
+** have been overwritten by the statement that expired the BLOB handle
+** or by other independent statements.
+**
+** This routine only works on a [BLOB handle] which has been created
+** by a prior successful call to [odbql_blob_open()] and which has not
+** been closed by [odbql_blob_close()].  Passing any other pointer in
+** to this routine results in undefined and probably undesirable behavior.
+**
+** See also: [odbql_blob_read()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_blob_write(odbql_blob *, const void *z, int n, int iOffset);
+
+/*
+** CAPI3REF: Virtual File System Objects
+**
+** A virtual filesystem (VFS) is an [odbql_vfs] object
+** that SQLite uses to interact
+** with the underlying operating system.  Most SQLite builds come with a
+** single default VFS that is appropriate for the host computer.
+** New VFSes can be registered and existing VFSes can be unregistered.
+** The following interfaces are provided.
+**
+** ^The odbql_vfs_find() interface returns a pointer to a VFS given its name.
+** ^Names are case sensitive.
+** ^Names are zero-terminated UTF-8 strings.
+** ^If there is no match, a NULL pointer is returned.
+** ^If zVfsName is NULL then the default VFS is returned.
+**
+** ^New VFSes are registered with odbql_vfs_register().
+** ^Each new VFS becomes the default VFS if the makeDflt flag is set.
+** ^The same VFS can be registered multiple times without injury.
+** ^To make an existing VFS into the default VFS, register it again
+** with the makeDflt flag set.  If two different VFSes with the
+** same name are registered, the behavior is undefined.  If a
+** VFS is registered with a name that is NULL or an empty string,
+** then the behavior is undefined.
+**
+** ^Unregister a VFS with the odbql_vfs_unregister() interface.
+** ^(If the default VFS is unregistered, another VFS is chosen as
+** the default.  The choice for the new VFS is arbitrary.)^
+*/
+ODBQL_API odbql_vfs *ODBQL_STDCALL odbql_vfs_find(const char *zVfsName);
+ODBQL_API int ODBQL_STDCALL odbql_vfs_register(odbql_vfs*, int makeDflt);
+ODBQL_API int ODBQL_STDCALL odbql_vfs_unregister(odbql_vfs*);
+
+/*
+** CAPI3REF: Mutexes
+**
+** The SQLite core uses these routines for thread
+** synchronization. Though they are intended for internal
+** use by SQLite, code that links against SQLite is
+** permitted to use any of these routines.
+**
+** The SQLite source code contains multiple implementations
+** of these mutex routines.  An appropriate implementation
+** is selected automatically at compile-time.  The following
+** implementations are available in the SQLite core:
+**
+** <ul>
+** <li>   ODBQL_MUTEX_PTHREADS
+** <li>   ODBQL_MUTEX_W32
+** <li>   ODBQL_MUTEX_NOOP
+** </ul>
+**
+** The ODBQL_MUTEX_NOOP implementation is a set of routines
+** that does no real locking and is appropriate for use in
+** a single-threaded application.  The ODBQL_MUTEX_PTHREADS and
+** ODBQL_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
+**
+** If SQLite is compiled with the ODBQL_MUTEX_APPDEF preprocessor
+** macro defined (with "-DODBQL_MUTEX_APPDEF=1"), then no mutex
+** implementation is included with the library. In this case the
+** application must supply a custom mutex implementation using the
+** [ODBQL_CONFIG_MUTEX] option of the odbql_config() function
+** before calling odbql_initialize() or any other public odbql_
+** function that calls odbql_initialize().
+**
+** ^The odbql_mutex_alloc() routine allocates a new
+** mutex and returns a pointer to it. ^The odbql_mutex_alloc()
+** routine returns NULL if it is unable to allocate the requested
+** mutex.  The argument to odbql_mutex_alloc() must one of these
+** integer constants:
+**
+** <ul>
+** <li>  ODBQL_MUTEX_FAST
+** <li>  ODBQL_MUTEX_RECURSIVE
+** <li>  ODBQL_MUTEX_STATIC_MASTER
+** <li>  ODBQL_MUTEX_STATIC_MEM
+** <li>  ODBQL_MUTEX_STATIC_OPEN
+** <li>  ODBQL_MUTEX_STATIC_PRNG
+** <li>  ODBQL_MUTEX_STATIC_LRU
+** <li>  ODBQL_MUTEX_STATIC_PMEM
+** <li>  ODBQL_MUTEX_STATIC_APP1
+** <li>  ODBQL_MUTEX_STATIC_APP2
+** <li>  ODBQL_MUTEX_STATIC_APP3
+** <li>  ODBQL_MUTEX_STATIC_VFS1
+** <li>  ODBQL_MUTEX_STATIC_VFS2
+** <li>  ODBQL_MUTEX_STATIC_VFS3
+** </ul>
+**
+** ^The first two constants (ODBQL_MUTEX_FAST and ODBQL_MUTEX_RECURSIVE)
+** cause odbql_mutex_alloc() to create
+** a new mutex.  ^The new mutex is recursive when ODBQL_MUTEX_RECURSIVE
+** is used but not necessarily so when ODBQL_MUTEX_FAST is used.
+** The mutex implementation does not need to make a distinction
+** between ODBQL_MUTEX_RECURSIVE and ODBQL_MUTEX_FAST if it does
+** not want to.  SQLite will only request a recursive mutex in
+** cases where it really needs one.  If a faster non-recursive mutex
+** implementation is available on the host platform, the mutex subsystem
+** might return such a mutex in response to ODBQL_MUTEX_FAST.
+**
+** ^The other allowed parameters to odbql_mutex_alloc() (anything other
+** than ODBQL_MUTEX_FAST and ODBQL_MUTEX_RECURSIVE) each return
+** a pointer to a static preexisting mutex.  ^Nine static mutexes are
+** used by the current version of SQLite.  Future versions of SQLite
+** may add additional static mutexes.  Static mutexes are for internal
+** use by SQLite only.  Applications that use SQLite mutexes should
+** use only the dynamic mutexes returned by ODBQL_MUTEX_FAST or
+** ODBQL_MUTEX_RECURSIVE.
+**
+** ^Note that if one of the dynamic mutex parameters (ODBQL_MUTEX_FAST
+** or ODBQL_MUTEX_RECURSIVE) is used then odbql_mutex_alloc()
+** returns a different mutex on every call.  ^For the static
+** mutex types, the same mutex is returned on every call that has
+** the same type number.
+**
+** ^The odbql_mutex_free() routine deallocates a previously
+** allocated dynamic mutex.  Attempting to deallocate a static
+** mutex results in undefined behavior.
+**
+** ^The odbql_mutex_enter() and odbql_mutex_try() routines attempt
+** to enter a mutex.  ^If another thread is already within the mutex,
+** odbql_mutex_enter() will block and odbql_mutex_try() will return
+** ODBQL_BUSY.  ^The odbql_mutex_try() interface returns [ODBQL_OK]
+** upon successful entry.  ^(Mutexes created using
+** ODBQL_MUTEX_RECURSIVE can be entered multiple times by the same thread.
+** In such cases, the
+** mutex must be exited an equal number of times before another thread
+** can enter.)^  If the same thread tries to enter any mutex other
+** than an ODBQL_MUTEX_RECURSIVE more than once, the behavior is undefined.
+**
+** ^(Some systems (for example, Windows 95) do not support the operation
+** implemented by odbql_mutex_try().  On those systems, odbql_mutex_try()
+** will always return ODBQL_BUSY. The SQLite core only ever uses
+** odbql_mutex_try() as an optimization so this is acceptable 
+** behavior.)^
+**
+** ^The odbql_mutex_leave() routine exits a mutex that was
+** previously entered by the same thread.   The behavior
+** is undefined if the mutex is not currently entered by the
+** calling thread or is not currently allocated.
+**
+** ^If the argument to odbql_mutex_enter(), odbql_mutex_try(), or
+** odbql_mutex_leave() is a NULL pointer, then all three routines
+** behave as no-ops.
+**
+** See also: [odbql_mutex_held()] and [odbql_mutex_notheld()].
+*/
+ODBQL_API odbql_mutex *ODBQL_STDCALL odbql_mutex_alloc(int);
+ODBQL_API void ODBQL_STDCALL odbql_mutex_free(odbql_mutex*);
+ODBQL_API void ODBQL_STDCALL odbql_mutex_enter(odbql_mutex*);
+ODBQL_API int ODBQL_STDCALL odbql_mutex_try(odbql_mutex*);
+ODBQL_API void ODBQL_STDCALL odbql_mutex_leave(odbql_mutex*);
+
+/*
+** CAPI3REF: Mutex Methods Object
+**
+** An instance of this structure defines the low-level routines
+** used to allocate and use mutexes.
+**
+** Usually, the default mutex implementations provided by SQLite are
+** sufficient, however the application has the option of substituting a custom
+** implementation for specialized deployments or systems for which SQLite
+** does not provide a suitable implementation. In this case, the application
+** creates and populates an instance of this structure to pass
+** to odbql_config() along with the [ODBQL_CONFIG_MUTEX] option.
+** Additionally, an instance of this structure can be used as an
+** output variable when querying the system for the current mutex
+** implementation, using the [ODBQL_CONFIG_GETMUTEX] option.
+**
+** ^The xMutexInit method defined by this structure is invoked as
+** part of system initialization by the odbql_initialize() function.
+** ^The xMutexInit routine is called by SQLite exactly once for each
+** effective call to [odbql_initialize()].
+**
+** ^The xMutexEnd method defined by this structure is invoked as
+** part of system shutdown by the odbql_shutdown() function. The
+** implementation of this method is expected to release all outstanding
+** resources obtained by the mutex methods implementation, especially
+** those obtained by the xMutexInit method.  ^The xMutexEnd()
+** interface is invoked exactly once for each call to [odbql_shutdown()].
+**
+** ^(The remaining seven methods defined by this structure (xMutexAlloc,
+** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and
+** xMutexNotheld) implement the following interfaces (respectively):
+**
+** <ul>
+**   <li>  [odbql_mutex_alloc()] </li>
+**   <li>  [odbql_mutex_free()] </li>
+**   <li>  [odbql_mutex_enter()] </li>
+**   <li>  [odbql_mutex_try()] </li>
+**   <li>  [odbql_mutex_leave()] </li>
+**   <li>  [odbql_mutex_held()] </li>
+**   <li>  [odbql_mutex_notheld()] </li>
+** </ul>)^
+**
+** The only difference is that the public odbql_XXX functions enumerated
+** above silently ignore any invocations that pass a NULL pointer instead
+** of a valid mutex handle. The implementations of the methods defined
+** by this structure are not required to handle this case, the results
+** of passing a NULL pointer instead of a valid mutex handle are undefined
+** (i.e. it is acceptable to provide an implementation that segfaults if
+** it is passed a NULL pointer).
+**
+** The xMutexInit() method must be threadsafe.  It must be harmless to
+** invoke xMutexInit() multiple times within the same process and without
+** intervening calls to xMutexEnd().  Second and subsequent calls to
+** xMutexInit() must be no-ops.
+**
+** xMutexInit() must not use SQLite memory allocation ([odbql_malloc()]
+** and its associates).  Similarly, xMutexAlloc() must not use SQLite memory
+** allocation for a static mutex.  ^However xMutexAlloc() may use SQLite
+** memory allocation for a fast or recursive mutex.
+**
+** ^SQLite will invoke the xMutexEnd() method when [odbql_shutdown()] is
+** called, but only if the prior call to xMutexInit returned ODBQL_OK.
+** If xMutexInit fails in any way, it is expected to clean up after itself
+** prior to returning.
+*/
+typedef struct odbql_mutex_methods odbql_mutex_methods;
+struct odbql_mutex_methods {
+  int (*xMutexInit)(void);
+  int (*xMutexEnd)(void);
+  odbql_mutex *(*xMutexAlloc)(int);
+  void (*xMutexFree)(odbql_mutex *);
+  void (*xMutexEnter)(odbql_mutex *);
+  int (*xMutexTry)(odbql_mutex *);
+  void (*xMutexLeave)(odbql_mutex *);
+  int (*xMutexHeld)(odbql_mutex *);
+  int (*xMutexNotheld)(odbql_mutex *);
+};
+
+/*
+** CAPI3REF: Mutex Verification Routines
+**
+** The odbql_mutex_held() and odbql_mutex_notheld() routines
+** are intended for use inside assert() statements.  The SQLite core
+** never uses these routines except inside an assert() and applications
+** are advised to follow the lead of the core.  The SQLite core only
+** provides implementations for these routines when it is compiled
+** with the ODBQL_DEBUG flag.  External mutex implementations
+** are only required to provide these routines if ODBQL_DEBUG is
+** defined and if NDEBUG is not defined.
+**
+** These routines should return true if the mutex in their argument
+** is held or not held, respectively, by the calling thread.
+**
+** The implementation is not required to provide versions of these
+** routines that actually work. If the implementation does not provide working
+** versions of these routines, it should at least provide stubs that always
+** return true so that one does not get spurious assertion failures.
+**
+** If the argument to odbql_mutex_held() is a NULL pointer then
+** the routine should return 1.   This seems counter-intuitive since
+** clearly the mutex cannot be held if it does not exist.  But
+** the reason the mutex does not exist is because the build is not
+** using mutexes.  And we do not want the assert() containing the
+** call to odbql_mutex_held() to fail, so a non-zero return is
+** the appropriate thing to do.  The odbql_mutex_notheld()
+** interface should also return 1 when given a NULL pointer.
+*/
+#ifndef NDEBUG
+ODBQL_API int ODBQL_STDCALL odbql_mutex_held(odbql_mutex*);
+ODBQL_API int ODBQL_STDCALL odbql_mutex_notheld(odbql_mutex*);
+#endif
+
+/*
+** CAPI3REF: Mutex Types
+**
+** The [odbql_mutex_alloc()] interface takes a single argument
+** which is one of these integer constants.
+**
+** The set of static mutexes may change from one SQLite release to the
+** next.  Applications that override the built-in mutex logic must be
+** prepared to accommodate additional static mutexes.
+*/
+#define ODBQL_MUTEX_FAST             0
+#define ODBQL_MUTEX_RECURSIVE        1
+#define ODBQL_MUTEX_STATIC_MASTER    2
+#define ODBQL_MUTEX_STATIC_MEM       3  /* odbql_malloc() */
+#define ODBQL_MUTEX_STATIC_MEM2      4  /* NOT USED */
+#define ODBQL_MUTEX_STATIC_OPEN      4  /* odbqlBtreeOpen() */
+#define ODBQL_MUTEX_STATIC_PRNG      5  /* odbql_random() */
+#define ODBQL_MUTEX_STATIC_LRU       6  /* lru page list */
+#define ODBQL_MUTEX_STATIC_LRU2      7  /* NOT USED */
+#define ODBQL_MUTEX_STATIC_PMEM      7  /* odbqlPageMalloc() */
+#define ODBQL_MUTEX_STATIC_APP1      8  /* For use by application */
+#define ODBQL_MUTEX_STATIC_APP2      9  /* For use by application */
+#define ODBQL_MUTEX_STATIC_APP3     10  /* For use by application */
+#define ODBQL_MUTEX_STATIC_VFS1     11  /* For use by built-in VFS */
+#define ODBQL_MUTEX_STATIC_VFS2     12  /* For use by extension VFS */
+#define ODBQL_MUTEX_STATIC_VFS3     13  /* For use by application VFS */
+
+/*
+** CAPI3REF: Retrieve the mutex for a database connection
+** METHOD: odbql
+**
+** ^This interface returns a pointer the [odbql_mutex] object that 
+** serializes access to the [database connection] given in the argument
+** when the [threading mode] is Serialized.
+** ^If the [threading mode] is Single-thread or Multi-thread then this
+** routine returns a NULL pointer.
+*/
+ODBQL_API odbql_mutex *ODBQL_STDCALL odbql_db_mutex(odbql*);
+
+/*
+** CAPI3REF: Low-Level Control Of Database Files
+** METHOD: odbql
+**
+** ^The [odbql_file_control()] interface makes a direct call to the
+** xFileControl method for the [odbql_io_methods] object associated
+** with a particular database identified by the second argument. ^The
+** name of the database is "main" for the main database or "temp" for the
+** TEMP database, or the name that appears after the AS keyword for
+** databases that are added using the [ATTACH] SQL command.
+** ^A NULL pointer can be used in place of "main" to refer to the
+** main database file.
+** ^The third and fourth parameters to this routine
+** are passed directly through to the second and third parameters of
+** the xFileControl method.  ^The return value of the xFileControl
+** method becomes the return value of this routine.
+**
+** ^The ODBQL_FCNTL_FILE_POINTER value for the op parameter causes
+** a pointer to the underlying [odbql_file] object to be written into
+** the space pointed to by the 4th parameter.  ^The ODBQL_FCNTL_FILE_POINTER
+** case is a short-circuit path which does not actually invoke the
+** underlying odbql_io_methods.xFileControl method.
+**
+** ^If the second parameter (zDbName) does not match the name of any
+** open database file, then ODBQL_ERROR is returned.  ^This error
+** code is not remembered and will not be recalled by [odbql_errcode()]
+** or [odbql_errmsg()].  The underlying xFileControl method might
+** also return ODBQL_ERROR.  There is no way to distinguish between
+** an incorrect zDbName and an ODBQL_ERROR return from the underlying
+** xFileControl method.
+**
+** See also: [ODBQL_FCNTL_LOCKSTATE]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_file_control(odbql*, const char *zDbName, int op, void*);
+
+/*
+** CAPI3REF: Testing Interface
+**
+** ^The odbql_test_control() interface is used to read out internal
+** state of SQLite and to inject faults into SQLite for testing
+** purposes.  ^The first parameter is an operation code that determines
+** the number, meaning, and operation of all subsequent parameters.
+**
+** This interface is not for use by applications.  It exists solely
+** for verifying the correct operation of the SQLite library.  Depending
+** on how the SQLite library is compiled, this interface might not exist.
+**
+** The details of the operation codes, their meanings, the parameters
+** they take, and what they do are all subject to change without notice.
+** Unlike most of the SQLite API, this function is not guaranteed to
+** operate consistently from one release to the next.
+*/
+ODBQL_API int ODBQL_CDECL odbql_test_control(int op, ...);
+
+/*
+** CAPI3REF: Testing Interface Operation Codes
+**
+** These constants are the valid operation code parameters used
+** as the first argument to [odbql_test_control()].
+**
+** These parameters and their meanings are subject to change
+** without notice.  These values are for testing purposes only.
+** Applications should not use any of these parameters or the
+** [odbql_test_control()] interface.
+*/
+#define ODBQL_TESTCTRL_FIRST                    5
+#define ODBQL_TESTCTRL_PRNG_SAVE                5
+#define ODBQL_TESTCTRL_PRNG_RESTORE             6
+#define ODBQL_TESTCTRL_PRNG_RESET               7
+#define ODBQL_TESTCTRL_BITVEC_TEST              8
+#define ODBQL_TESTCTRL_FAULT_INSTALL            9
+#define ODBQL_TESTCTRL_BENIGN_MALLOC_HOOKS     10
+#define ODBQL_TESTCTRL_PENDING_BYTE            11
+#define ODBQL_TESTCTRL_ASSERT                  12
+#define ODBQL_TESTCTRL_ALWAYS                  13
+#define ODBQL_TESTCTRL_RESERVE                 14
+#define ODBQL_TESTCTRL_OPTIMIZATIONS           15
+#define ODBQL_TESTCTRL_ISKEYWORD               16
+#define ODBQL_TESTCTRL_SCRATCHMALLOC           17
+#define ODBQL_TESTCTRL_LOCALTIME_FAULT         18
+#define ODBQL_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
+#define ODBQL_TESTCTRL_NEVER_CORRUPT           20
+#define ODBQL_TESTCTRL_VDBE_COVERAGE           21
+#define ODBQL_TESTCTRL_BYTEORDER               22
+#define ODBQL_TESTCTRL_ISINIT                  23
+#define ODBQL_TESTCTRL_SORTER_MMAP             24
+#define ODBQL_TESTCTRL_IMPOSTER                25
+#define ODBQL_TESTCTRL_LAST                    25
+
+/*
+** CAPI3REF: SQLite Runtime Status
+**
+** ^These interfaces are used to retrieve runtime status information
+** about the performance of SQLite, and optionally to reset various
+** highwater marks.  ^The first argument is an integer code for
+** the specific parameter to measure.  ^(Recognized integer codes
+** are of the form [status parameters | ODBQL_STATUS_...].)^
+** ^The current value of the parameter is returned into *pCurrent.
+** ^The highest recorded value is returned in *pHighwater.  ^If the
+** resetFlag is true, then the highest record value is reset after
+** *pHighwater is written.  ^(Some parameters do not record the highest
+** value.  For those parameters
+** nothing is written into *pHighwater and the resetFlag is ignored.)^
+** ^(Other parameters record only the highwater mark and not the current
+** value.  For these latter parameters nothing is written into *pCurrent.)^
+**
+** ^The odbql_status() and odbql_status64() routines return
+** ODBQL_OK on success and a non-zero [error code] on failure.
+**
+** If either the current value or the highwater mark is too large to
+** be represented by a 32-bit integer, then the values returned by
+** odbql_status() are undefined.
+**
+** See also: [odbql_db_status()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+ODBQL_API int ODBQL_STDCALL odbql_status64(
+  int op,
+  odbql_int64 *pCurrent,
+  odbql_int64 *pHighwater,
+  int resetFlag
+);
+
+
+/*
+** CAPI3REF: Status Parameters
+** KEYWORDS: {status parameters}
+**
+** These integer constants designate various run-time status parameters
+** that can be returned by [odbql_status()].
+**
+** <dl>
+** [[ODBQL_STATUS_MEMORY_USED]] ^(<dt>ODBQL_STATUS_MEMORY_USED</dt>
+** <dd>This parameter is the current amount of memory checked out
+** using [odbql_malloc()], either directly or indirectly.  The
+** figure includes calls made to [odbql_malloc()] by the application
+** and internal memory usage by the SQLite library.  Scratch memory
+** controlled by [ODBQL_CONFIG_SCRATCH] and auxiliary page-cache
+** memory controlled by [ODBQL_CONFIG_PAGECACHE] is not included in
+** this parameter.  The amount returned is the sum of the allocation
+** sizes as reported by the xSize method in [odbql_mem_methods].</dd>)^
+**
+** [[ODBQL_STATUS_MALLOC_SIZE]] ^(<dt>ODBQL_STATUS_MALLOC_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [odbql_malloc()] or [odbql_realloc()] (or their
+** internal equivalents).  Only the value returned in the
+** *pHighwater parameter to [odbql_status()] is of interest.  
+** The value written into the *pCurrent parameter is undefined.</dd>)^
+**
+** [[ODBQL_STATUS_MALLOC_COUNT]] ^(<dt>ODBQL_STATUS_MALLOC_COUNT</dt>
+** <dd>This parameter records the number of separate memory allocations
+** currently checked out.</dd>)^
+**
+** [[ODBQL_STATUS_PAGECACHE_USED]] ^(<dt>ODBQL_STATUS_PAGECACHE_USED</dt>
+** <dd>This parameter returns the number of pages used out of the
+** [pagecache memory allocator] that was configured using 
+** [ODBQL_CONFIG_PAGECACHE].  The
+** value returned is in pages, not in bytes.</dd>)^
+**
+** [[ODBQL_STATUS_PAGECACHE_OVERFLOW]] 
+** ^(<dt>ODBQL_STATUS_PAGECACHE_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of page cache
+** allocation which could not be satisfied by the [ODBQL_CONFIG_PAGECACHE]
+** buffer and where forced to overflow to [odbql_malloc()].  The
+** returned value includes allocations that overflowed because they
+** where too large (they were larger than the "sz" parameter to
+** [ODBQL_CONFIG_PAGECACHE]) and allocations that overflowed because
+** no space was left in the page cache.</dd>)^
+**
+** [[ODBQL_STATUS_PAGECACHE_SIZE]] ^(<dt>ODBQL_STATUS_PAGECACHE_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [pagecache memory allocator].  Only the value returned in the
+** *pHighwater parameter to [odbql_status()] is of interest.  
+** The value written into the *pCurrent parameter is undefined.</dd>)^
+**
+** [[ODBQL_STATUS_SCRATCH_USED]] ^(<dt>ODBQL_STATUS_SCRATCH_USED</dt>
+** <dd>This parameter returns the number of allocations used out of the
+** [scratch memory allocator] configured using
+** [ODBQL_CONFIG_SCRATCH].  The value returned is in allocations, not
+** in bytes.  Since a single thread may only have one scratch allocation
+** outstanding at time, this parameter also reports the number of threads
+** using scratch memory at the same time.</dd>)^
+**
+** [[ODBQL_STATUS_SCRATCH_OVERFLOW]] ^(<dt>ODBQL_STATUS_SCRATCH_OVERFLOW</dt>
+** <dd>This parameter returns the number of bytes of scratch memory
+** allocation which could not be satisfied by the [ODBQL_CONFIG_SCRATCH]
+** buffer and where forced to overflow to [odbql_malloc()].  The values
+** returned include overflows because the requested allocation was too
+** larger (that is, because the requested allocation was larger than the
+** "sz" parameter to [ODBQL_CONFIG_SCRATCH]) and because no scratch buffer
+** slots were available.
+** </dd>)^
+**
+** [[ODBQL_STATUS_SCRATCH_SIZE]] ^(<dt>ODBQL_STATUS_SCRATCH_SIZE</dt>
+** <dd>This parameter records the largest memory allocation request
+** handed to [scratch memory allocator].  Only the value returned in the
+** *pHighwater parameter to [odbql_status()] is of interest.  
+** The value written into the *pCurrent parameter is undefined.</dd>)^
+**
+** [[ODBQL_STATUS_PARSER_STACK]] ^(<dt>ODBQL_STATUS_PARSER_STACK</dt>
+** <dd>The *pHighwater parameter records the deepest parser stack. 
+** The *pCurrent value is undefined.  The *pHighwater value is only
+** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
+** </dl>
+**
+** New status parameters may be added from time to time.
+*/
+#define ODBQL_STATUS_MEMORY_USED          0
+#define ODBQL_STATUS_PAGECACHE_USED       1
+#define ODBQL_STATUS_PAGECACHE_OVERFLOW   2
+#define ODBQL_STATUS_SCRATCH_USED         3
+#define ODBQL_STATUS_SCRATCH_OVERFLOW     4
+#define ODBQL_STATUS_MALLOC_SIZE          5
+#define ODBQL_STATUS_PARSER_STACK         6
+#define ODBQL_STATUS_PAGECACHE_SIZE       7
+#define ODBQL_STATUS_SCRATCH_SIZE         8
+#define ODBQL_STATUS_MALLOC_COUNT         9
+
+/*
+** CAPI3REF: Database Connection Status
+** METHOD: odbql
+**
+** ^This interface is used to retrieve runtime status information 
+** about a single [database connection].  ^The first argument is the
+** database connection object to be interrogated.  ^The second argument
+** is an integer constant, taken from the set of
+** [ODBQL_DBSTATUS options], that
+** determines the parameter to interrogate.  The set of 
+** [ODBQL_DBSTATUS options] is likely
+** to grow in future releases of SQLite.
+**
+** ^The current value of the requested parameter is written into *pCur
+** and the highest instantaneous value is written into *pHiwtr.  ^If
+** the resetFlg is true, then the highest instantaneous value is
+** reset back down to the current value.
+**
+** ^The odbql_db_status() routine returns ODBQL_OK on success and a
+** non-zero [error code] on failure.
+**
+** See also: [odbql_status()] and [odbql_stmt_status()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_db_status(odbql*, int op, int *pCur, int *pHiwtr, int resetFlg);
+
+/*
+** CAPI3REF: Status Parameters for database connections
+** KEYWORDS: {ODBQL_DBSTATUS options}
+**
+** These constants are the available integer "verbs" that can be passed as
+** the second argument to the [odbql_db_status()] interface.
+**
+** New verbs may be added in future releases of SQLite. Existing verbs
+** might be discontinued. Applications should check the return code from
+** [odbql_db_status()] to make sure that the call worked.
+** The [odbql_db_status()] interface will return a non-zero error code
+** if a discontinued or unsupported verb is invoked.
+**
+** <dl>
+** [[ODBQL_DBSTATUS_LOOKASIDE_USED]] ^(<dt>ODBQL_DBSTATUS_LOOKASIDE_USED</dt>
+** <dd>This parameter returns the number of lookaside memory slots currently
+** checked out.</dd>)^
+**
+** [[ODBQL_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>ODBQL_DBSTATUS_LOOKASIDE_HIT</dt>
+** <dd>This parameter returns the number malloc attempts that were 
+** satisfied using lookaside memory. Only the high-water value is meaningful;
+** the current value is always zero.)^
+**
+** [[ODBQL_DBSTATUS_LOOKASIDE_MISS_SIZE]]
+** ^(<dt>ODBQL_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
+** <dd>This parameter returns the number malloc attempts that might have
+** been satisfied using lookaside memory but failed due to the amount of
+** memory requested being larger than the lookaside slot size.
+** Only the high-water value is meaningful;
+** the current value is always zero.)^
+**
+** [[ODBQL_DBSTATUS_LOOKASIDE_MISS_FULL]]
+** ^(<dt>ODBQL_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
+** <dd>This parameter returns the number malloc attempts that might have
+** been satisfied using lookaside memory but failed due to all lookaside
+** memory already being in use.
+** Only the high-water value is meaningful;
+** the current value is always zero.)^
+**
+** [[ODBQL_DBSTATUS_CACHE_USED]] ^(<dt>ODBQL_DBSTATUS_CACHE_USED</dt>
+** <dd>This parameter returns the approximate number of bytes of heap
+** memory used by all pager caches associated with the database connection.)^
+** ^The highwater mark associated with ODBQL_DBSTATUS_CACHE_USED is always 0.
+**
+** [[ODBQL_DBSTATUS_SCHEMA_USED]] ^(<dt>ODBQL_DBSTATUS_SCHEMA_USED</dt>
+** <dd>This parameter returns the approximate number of bytes of heap
+** memory used to store the schema for all databases associated
+** with the connection - main, temp, and any [ATTACH]-ed databases.)^ 
+** ^The full amount of memory used by the schemas is reported, even if the
+** schema memory is shared with other database connections due to
+** [shared cache mode] being enabled.
+** ^The highwater mark associated with ODBQL_DBSTATUS_SCHEMA_USED is always 0.
+**
+** [[ODBQL_DBSTATUS_STMT_USED]] ^(<dt>ODBQL_DBSTATUS_STMT_USED</dt>
+** <dd>This parameter returns the approximate number of bytes of heap
+** and lookaside memory used by all prepared statements associated with
+** the database connection.)^
+** ^The highwater mark associated with ODBQL_DBSTATUS_STMT_USED is always 0.
+** </dd>
+**
+** [[ODBQL_DBSTATUS_CACHE_HIT]] ^(<dt>ODBQL_DBSTATUS_CACHE_HIT</dt>
+** <dd>This parameter returns the number of pager cache hits that have
+** occurred.)^ ^The highwater mark associated with ODBQL_DBSTATUS_CACHE_HIT 
+** is always 0.
+** </dd>
+**
+** [[ODBQL_DBSTATUS_CACHE_MISS]] ^(<dt>ODBQL_DBSTATUS_CACHE_MISS</dt>
+** <dd>This parameter returns the number of pager cache misses that have
+** occurred.)^ ^The highwater mark associated with ODBQL_DBSTATUS_CACHE_MISS 
+** is always 0.
+** </dd>
+**
+** [[ODBQL_DBSTATUS_CACHE_WRITE]] ^(<dt>ODBQL_DBSTATUS_CACHE_WRITE</dt>
+** <dd>This parameter returns the number of dirty cache entries that have
+** been written to disk. Specifically, the number of pages written to the
+** wal file in wal mode databases, or the number of pages written to the
+** database file in rollback mode databases. Any pages written as part of
+** transaction rollback or database recovery operations are not included.
+** If an IO or other error occurs while writing a page to disk, the effect
+** on subsequent ODBQL_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
+** highwater mark associated with ODBQL_DBSTATUS_CACHE_WRITE is always 0.
+** </dd>
+**
+** [[ODBQL_DBSTATUS_DEFERRED_FKS]] ^(<dt>ODBQL_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^  ^The highwater mark is always 0.
+** </dd>
+** </dl>
+*/
+#define ODBQL_DBSTATUS_LOOKASIDE_USED       0
+#define ODBQL_DBSTATUS_CACHE_USED           1
+#define ODBQL_DBSTATUS_SCHEMA_USED          2
+#define ODBQL_DBSTATUS_STMT_USED            3
+#define ODBQL_DBSTATUS_LOOKASIDE_HIT        4
+#define ODBQL_DBSTATUS_LOOKASIDE_MISS_SIZE  5
+#define ODBQL_DBSTATUS_LOOKASIDE_MISS_FULL  6
+#define ODBQL_DBSTATUS_CACHE_HIT            7
+#define ODBQL_DBSTATUS_CACHE_MISS           8
+#define ODBQL_DBSTATUS_CACHE_WRITE          9
+#define ODBQL_DBSTATUS_DEFERRED_FKS        10
+#define ODBQL_DBSTATUS_MAX                 10   /* Largest defined DBSTATUS */
+
+
+/*
+** CAPI3REF: Prepared Statement Status
+** METHOD: odbql_stmt
+**
+** ^(Each prepared statement maintains various
+** [ODBQL_STMTSTATUS counters] that measure the number
+** of times it has performed specific operations.)^  These counters can
+** be used to monitor the performance characteristics of the prepared
+** statements.  For example, if the number of table steps greatly exceeds
+** the number of table searches or result rows, that would tend to indicate
+** that the prepared statement is using a full table scan rather than
+** an index.  
+**
+** ^(This interface is used to retrieve and reset counter values from
+** a [prepared statement].  The first argument is the prepared statement
+** object to be interrogated.  The second argument
+** is an integer code for a specific [ODBQL_STMTSTATUS counter]
+** to be interrogated.)^
+** ^The current value of the requested counter is returned.
+** ^If the resetFlg is true, then the counter is reset to zero after this
+** interface call returns.
+**
+** See also: [odbql_status()] and [odbql_db_status()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_stmt_status(odbql_stmt*, int op,int resetFlg);
+
+/*
+** CAPI3REF: Status Parameters for prepared statements
+** KEYWORDS: {ODBQL_STMTSTATUS counter} {ODBQL_STMTSTATUS counters}
+**
+** These preprocessor macros define integer codes that name counter
+** values associated with the [odbql_stmt_status()] interface.
+** The meanings of the various counters are as follows:
+**
+** <dl>
+** [[ODBQL_STMTSTATUS_FULLSCAN_STEP]] <dt>ODBQL_STMTSTATUS_FULLSCAN_STEP</dt>
+** <dd>^This is the number of times that SQLite has stepped forward in
+** a table as part of a full table scan.  Large numbers for this counter
+** may indicate opportunities for performance improvement through 
+** careful use of indices.</dd>
+**
+** [[ODBQL_STMTSTATUS_SORT]] <dt>ODBQL_STMTSTATUS_SORT</dt>
+** <dd>^This is the number of sort operations that have occurred.
+** A non-zero value in this counter may indicate an opportunity to
+** improvement performance through careful use of indices.</dd>
+**
+** [[ODBQL_STMTSTATUS_AUTOINDEX]] <dt>ODBQL_STMTSTATUS_AUTOINDEX</dt>
+** <dd>^This is the number of rows inserted into transient indices that
+** were created automatically in order to help joins run faster.
+** A non-zero value in this counter may indicate an opportunity to
+** improvement performance by adding permanent indices that do not
+** need to be reinitialized each time the statement is run.</dd>
+**
+** [[ODBQL_STMTSTATUS_VM_STEP]] <dt>ODBQL_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647.  The number of virtual machine operations can be 
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
+** </dl>
+*/
+#define ODBQL_STMTSTATUS_FULLSCAN_STEP     1
+#define ODBQL_STMTSTATUS_SORT              2
+#define ODBQL_STMTSTATUS_AUTOINDEX         3
+#define ODBQL_STMTSTATUS_VM_STEP           4
+
+/*
+** CAPI3REF: Custom Page Cache Object
+**
+** The odbql_pcache type is opaque.  It is implemented by
+** the pluggable module.  The SQLite core has no knowledge of
+** its size or internal structure and never deals with the
+** odbql_pcache object except by holding and passing pointers
+** to the object.
+**
+** See [odbql_pcache_methods2] for additional information.
+*/
+typedef struct odbql_pcache odbql_pcache;
+
+/*
+** CAPI3REF: Custom Page Cache Object
+**
+** The odbql_pcache_page object represents a single page in the
+** page cache.  The page cache will allocate instances of this
+** object.  Various methods of the page cache use pointers to instances
+** of this object as parameters or as their return value.
+**
+** See [odbql_pcache_methods2] for additional information.
+*/
+typedef struct odbql_pcache_page odbql_pcache_page;
+struct odbql_pcache_page {
+  void *pBuf;        /* The content of the page */
+  void *pExtra;      /* Extra information associated with the page */
+};
+
+/*
+** CAPI3REF: Application Defined Page Cache.
+** KEYWORDS: {page cache}
+**
+** ^(The [odbql_config]([ODBQL_CONFIG_PCACHE2], ...) interface can
+** register an alternative page cache implementation by passing in an 
+** instance of the odbql_pcache_methods2 structure.)^
+** In many applications, most of the heap memory allocated by 
+** SQLite is used for the page cache.
+** By implementing a 
+** custom page cache using this API, an application can better control
+** the amount of memory consumed by SQLite, the way in which 
+** that memory is allocated and released, and the policies used to 
+** determine exactly which parts of a database file are cached and for 
+** how long.
+**
+** The alternative page cache mechanism is an
+** extreme measure that is only needed by the most demanding applications.
+** The built-in page cache is recommended for most uses.
+**
+** ^(The contents of the odbql_pcache_methods2 structure are copied to an
+** internal buffer by SQLite within the call to [odbql_config].  Hence
+** the application may discard the parameter after the call to
+** [odbql_config()] returns.)^
+**
+** [[the xInit() page cache method]]
+** ^(The xInit() method is called once for each effective 
+** call to [odbql_initialize()])^
+** (usually only once during the lifetime of the process). ^(The xInit()
+** method is passed a copy of the odbql_pcache_methods2.pArg value.)^
+** The intent of the xInit() method is to set up global data structures 
+** required by the custom page cache implementation. 
+** ^(If the xInit() method is NULL, then the 
+** built-in default page cache is used instead of the application defined
+** page cache.)^
+**
+** [[the xShutdown() page cache method]]
+** ^The xShutdown() method is called by [odbql_shutdown()].
+** It can be used to clean up 
+** any outstanding resources before process shutdown, if required.
+** ^The xShutdown() method may be NULL.
+**
+** ^SQLite automatically serializes calls to the xInit method,
+** so the xInit method need not be threadsafe.  ^The
+** xShutdown method is only called from [odbql_shutdown()] so it does
+** not need to be threadsafe either.  All other methods must be threadsafe
+** in multithreaded applications.
+**
+** ^SQLite will never invoke xInit() more than once without an intervening
+** call to xShutdown().
+**
+** [[the xCreate() page cache methods]]
+** ^SQLite invokes the xCreate() method to construct a new cache instance.
+** SQLite will typically create one cache instance for each open database file,
+** though this is not guaranteed. ^The
+** first parameter, szPage, is the size in bytes of the pages that must
+** be allocated by the cache.  ^szPage will always a power of two.  ^The
+** second parameter szExtra is a number of bytes of extra storage 
+** associated with each page cache entry.  ^The szExtra parameter will
+** a number less than 250.  SQLite will use the
+** extra szExtra bytes on each page to store metadata about the underlying
+** database page on disk.  The value passed into szExtra depends
+** on the SQLite version, the target platform, and how SQLite was compiled.
+** ^The third argument to xCreate(), bPurgeable, is true if the cache being
+** created will be used to cache database pages of a file stored on disk, or
+** false if it is used for an in-memory database. The cache implementation
+** does not have to do anything special based with the value of bPurgeable;
+** it is purely advisory.  ^On a cache where bPurgeable is false, SQLite will
+** never invoke xUnpin() except to deliberately delete a page.
+** ^In other words, calls to xUnpin() on a cache with bPurgeable set to
+** false will always have the "discard" flag set to true.  
+** ^Hence, a cache created with bPurgeable false will
+** never contain any unpinned pages.
+**
+** [[the xCachesize() page cache method]]
+** ^(The xCachesize() method may be called at any time by SQLite to set the
+** suggested maximum cache-size (number of pages stored by) the cache
+** instance passed as the first argument. This is the value configured using
+** the SQLite "[PRAGMA cache_size]" command.)^  As with the bPurgeable
+** parameter, the implementation is not required to do anything with this
+** value; it is advisory only.
+**
+** [[the xPagecount() page cache methods]]
+** The xPagecount() method must return the number of pages currently
+** stored in the cache, both pinned and unpinned.
+** 
+** [[the xFetch() page cache methods]]
+** The xFetch() method locates a page in the cache and returns a pointer to 
+** an odbql_pcache_page object associated with that page, or a NULL pointer.
+** The pBuf element of the returned odbql_pcache_page object will be a
+** pointer to a buffer of szPage bytes used to store the content of a 
+** single database page.  The pExtra element of odbql_pcache_page will be
+** a pointer to the szExtra bytes of extra storage that SQLite has requested
+** for each entry in the page cache.
+**
+** The page to be fetched is determined by the key. ^The minimum key value
+** is 1.  After it has been retrieved using xFetch, the page is considered
+** to be "pinned".
+**
+** If the requested page is already in the page cache, then the page cache
+** implementation must return a pointer to the page buffer with its content
+** intact.  If the requested page is not already in the cache, then the
+** cache implementation should use the value of the createFlag
+** parameter to help it determined what action to take:
+**
+** <table border=1 width=85% align=center>
+** <tr><th> createFlag <th> Behavior when page is not already in cache
+** <tr><td> 0 <td> Do not allocate a new page.  Return NULL.
+** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
+**                 Otherwise return NULL.
+** <tr><td> 2 <td> Make every effort to allocate a new page.  Only return
+**                 NULL if allocating a new page is effectively impossible.
+** </table>
+**
+** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1.  SQLite
+** will only use a createFlag of 2 after a prior call with a createFlag of 1
+** failed.)^  In between the to xFetch() calls, SQLite may
+** attempt to unpin one or more cache pages by spilling the content of
+** pinned pages to disk and synching the operating system disk cache.
+**
+** [[the xUnpin() page cache method]]
+** ^xUnpin() is called by SQLite with a pointer to a currently pinned page
+** as its second argument.  If the third parameter, discard, is non-zero,
+** then the page must be evicted from the cache.
+** ^If the discard parameter is
+** zero, then the page may be discarded or retained at the discretion of
+** page cache implementation. ^The page cache implementation
+** may choose to evict unpinned pages at any time.
+**
+** The cache must not perform any reference counting. A single 
+** call to xUnpin() unpins the page regardless of the number of prior calls 
+** to xFetch().
+**
+** [[the xRekey() page cache methods]]
+** The xRekey() method is used to change the key value associated with the
+** page passed as the second argument. If the cache
+** previously contains an entry associated with newKey, it must be
+** discarded. ^Any prior cache entry associated with newKey is guaranteed not
+** to be pinned.
+**
+** When SQLite calls the xTruncate() method, the cache must discard all
+** existing cache entries with page numbers (keys) greater than or equal
+** to the value of the iLimit parameter passed to xTruncate(). If any
+** of these pages are pinned, they are implicitly unpinned, meaning that
+** they can be safely discarded.
+**
+** [[the xDestroy() page cache method]]
+** ^The xDestroy() method is used to delete a cache allocated by xCreate().
+** All resources associated with the specified cache should be freed. ^After
+** calling the xDestroy() method, SQLite considers the [odbql_pcache*]
+** handle invalid, and will not use it with any other odbql_pcache_methods2
+** functions.
+**
+** [[the xShrink() page cache method]]
+** ^SQLite invokes the xShrink() method when it wants the page cache to
+** free up as much of heap memory as possible.  The page cache implementation
+** is not obligated to free any memory, but well-behaved implementations should
+** do their best.
+*/
+typedef struct odbql_pcache_methods2 odbql_pcache_methods2;
+struct odbql_pcache_methods2 {
+  int iVersion;
+  void *pArg;
+  int (*xInit)(void*);
+  void (*xShutdown)(void*);
+  odbql_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
+  void (*xCachesize)(odbql_pcache*, int nCachesize);
+  int (*xPagecount)(odbql_pcache*);
+  odbql_pcache_page *(*xFetch)(odbql_pcache*, unsigned key, int createFlag);
+  void (*xUnpin)(odbql_pcache*, odbql_pcache_page*, int discard);
+  void (*xRekey)(odbql_pcache*, odbql_pcache_page*, 
+      unsigned oldKey, unsigned newKey);
+  void (*xTruncate)(odbql_pcache*, unsigned iLimit);
+  void (*xDestroy)(odbql_pcache*);
+  void (*xShrink)(odbql_pcache*);
+};
+
+/*
+** This is the obsolete pcache_methods object that has now been replaced
+** by odbql_pcache_methods2.  This object is not used by SQLite.  It is
+** retained in the header file for backwards compatibility only.
+*/
+typedef struct odbql_pcache_methods odbql_pcache_methods;
+struct odbql_pcache_methods {
+  void *pArg;
+  int (*xInit)(void*);
+  void (*xShutdown)(void*);
+  odbql_pcache *(*xCreate)(int szPage, int bPurgeable);
+  void (*xCachesize)(odbql_pcache*, int nCachesize);
+  int (*xPagecount)(odbql_pcache*);
+  void *(*xFetch)(odbql_pcache*, unsigned key, int createFlag);
+  void (*xUnpin)(odbql_pcache*, void*, int discard);
+  void (*xRekey)(odbql_pcache*, void*, unsigned oldKey, unsigned newKey);
+  void (*xTruncate)(odbql_pcache*, unsigned iLimit);
+  void (*xDestroy)(odbql_pcache*);
+};
+
+
+/*
+** CAPI3REF: Online Backup Object
+**
+** The odbql_backup object records state information about an ongoing
+** online backup operation.  ^The odbql_backup object is created by
+** a call to [odbql_backup_init()] and is destroyed by a call to
+** [odbql_backup_finish()].
+**
+** See Also: [Using the SQLite Online Backup API]
+*/
+typedef struct odbql_backup odbql_backup;
+
+/*
+** CAPI3REF: Online Backup API.
+**
+** The backup API copies the content of one database into another.
+** It is useful either for creating backups of databases or
+** for copying in-memory databases to or from persistent files. 
+**
+** See Also: [Using the SQLite Online Backup API]
+**
+** ^SQLite holds a write transaction open on the destination database file
+** for the duration of the backup operation.
+** ^The source database is read-locked only while it is being read;
+** it is not locked continuously for the entire backup operation.
+** ^Thus, the backup may be performed on a live source database without
+** preventing other database connections from
+** reading or writing to the source database while the backup is underway.
+** 
+** ^(To perform a backup operation: 
+**   <ol>
+**     <li><b>odbql_backup_init()</b> is called once to initialize the
+**         backup, 
+**     <li><b>odbql_backup_step()</b> is called one or more times to transfer 
+**         the data between the two databases, and finally
+**     <li><b>odbql_backup_finish()</b> is called to release all resources 
+**         associated with the backup operation. 
+**   </ol>)^
+** There should be exactly one call to odbql_backup_finish() for each
+** successful call to odbql_backup_init().
+**
+** [[odbql_backup_init()]] <b>odbql_backup_init()</b>
+**
+** ^The D and N arguments to odbql_backup_init(D,N,S,M) are the 
+** [database connection] associated with the destination database 
+** and the database name, respectively.
+** ^The database name is "main" for the main database, "temp" for the
+** temporary database, or the name specified after the AS keyword in
+** an [ATTACH] statement for an attached database.
+** ^The S and M arguments passed to 
+** odbql_backup_init(D,N,S,M) identify the [database connection]
+** and database name of the source database, respectively.
+** ^The source and destination [database connections] (parameters S and D)
+** must be different or else odbql_backup_init(D,N,S,M) will fail with
+** an error.
+**
+** ^A call to odbql_backup_init() will fail, returning NULL, if 
+** there is already a read or read-write transaction open on the 
+** destination database.
+**
+** ^If an error occurs within odbql_backup_init(D,N,S,M), then NULL is
+** returned and an error code and error message are stored in the
+** destination [database connection] D.
+** ^The error code and message for the failed call to odbql_backup_init()
+** can be retrieved using the [odbql_errcode()], [odbql_errmsg()], and/or
+** [odbql_errmsg16()] functions.
+** ^A successful call to odbql_backup_init() returns a pointer to an
+** [odbql_backup] object.
+** ^The [odbql_backup] object may be used with the odbql_backup_step() and
+** odbql_backup_finish() functions to perform the specified backup 
+** operation.
+**
+** [[odbql_backup_step()]] <b>odbql_backup_step()</b>
+**
+** ^Function odbql_backup_step(B,N) will copy up to N pages between 
+** the source and destination databases specified by [odbql_backup] object B.
+** ^If N is negative, all remaining source pages are copied. 
+** ^If odbql_backup_step(B,N) successfully copies N pages and there
+** are still more pages to be copied, then the function returns [ODBQL_OK].
+** ^If odbql_backup_step(B,N) successfully finishes copying all pages
+** from source to destination, then it returns [ODBQL_DONE].
+** ^If an error occurs while running odbql_backup_step(B,N),
+** then an [error code] is returned. ^As well as [ODBQL_OK] and
+** [ODBQL_DONE], a call to odbql_backup_step() may return [ODBQL_READONLY],
+** [ODBQL_NOMEM], [ODBQL_BUSY], [ODBQL_LOCKED], or an
+** [ODBQL_IOERR_ACCESS | ODBQL_IOERR_XXX] extended error code.
+**
+** ^(The odbql_backup_step() might return [ODBQL_READONLY] if
+** <ol>
+** <li> the destination database was opened read-only, or
+** <li> the destination database is using write-ahead-log journaling
+** and the destination and source page sizes differ, or
+** <li> the destination database is an in-memory database and the
+** destination and source page sizes differ.
+** </ol>)^
+**
+** ^If odbql_backup_step() cannot obtain a required file-system lock, then
+** the [odbql_busy_handler | busy-handler function]
+** is invoked (if one is specified). ^If the 
+** busy-handler returns non-zero before the lock is available, then 
+** [ODBQL_BUSY] is returned to the caller. ^In this case the call to
+** odbql_backup_step() can be retried later. ^If the source
+** [database connection]
+** is being used to write to the source database when odbql_backup_step()
+** is called, then [ODBQL_LOCKED] is returned immediately. ^Again, in this
+** case the call to odbql_backup_step() can be retried later on. ^(If
+** [ODBQL_IOERR_ACCESS | ODBQL_IOERR_XXX], [ODBQL_NOMEM], or
+** [ODBQL_READONLY] is returned, then 
+** there is no point in retrying the call to odbql_backup_step(). These 
+** errors are considered fatal.)^  The application must accept 
+** that the backup operation has failed and pass the backup operation handle 
+** to the odbql_backup_finish() to release associated resources.
+**
+** ^The first call to odbql_backup_step() obtains an exclusive lock
+** on the destination file. ^The exclusive lock is not released until either 
+** odbql_backup_finish() is called or the backup operation is complete 
+** and odbql_backup_step() returns [ODBQL_DONE].  ^Every call to
+** odbql_backup_step() obtains a [shared lock] on the source database that
+** lasts for the duration of the odbql_backup_step() call.
+** ^Because the source database is not locked between calls to
+** odbql_backup_step(), the source database may be modified mid-way
+** through the backup process.  ^If the source database is modified by an
+** external process or via a database connection other than the one being
+** used by the backup operation, then the backup will be automatically
+** restarted by the next call to odbql_backup_step(). ^If the source 
+** database is modified by the using the same database connection as is used
+** by the backup operation, then the backup database is automatically
+** updated at the same time.
+**
+** [[odbql_backup_finish()]] <b>odbql_backup_finish()</b>
+**
+** When odbql_backup_step() has returned [ODBQL_DONE], or when the 
+** application wishes to abandon the backup operation, the application
+** should destroy the [odbql_backup] by passing it to odbql_backup_finish().
+** ^The odbql_backup_finish() interfaces releases all
+** resources associated with the [odbql_backup] object. 
+** ^If odbql_backup_step() has not yet returned [ODBQL_DONE], then any
+** active write-transaction on the destination database is rolled back.
+** The [odbql_backup] object is invalid
+** and may not be used following a call to odbql_backup_finish().
+**
+** ^The value returned by odbql_backup_finish is [ODBQL_OK] if no
+** odbql_backup_step() errors occurred, regardless or whether or not
+** odbql_backup_step() completed.
+** ^If an out-of-memory condition or IO error occurred during any prior
+** odbql_backup_step() call on the same [odbql_backup] object, then
+** odbql_backup_finish() returns the corresponding [error code].
+**
+** ^A return of [ODBQL_BUSY] or [ODBQL_LOCKED] from odbql_backup_step()
+** is not a permanent error and does not affect the return value of
+** odbql_backup_finish().
+**
+** [[odbql_backup_remaining()]] [[odbql_backup_pagecount()]]
+** <b>odbql_backup_remaining() and odbql_backup_pagecount()</b>
+**
+** ^The odbql_backup_remaining() routine returns the number of pages still
+** to be backed up at the conclusion of the most recent odbql_backup_step().
+** ^The odbql_backup_pagecount() routine returns the total number of pages
+** in the source database at the conclusion of the most recent
+** odbql_backup_step().
+** ^(The values returned by these functions are only updated by
+** odbql_backup_step(). If the source database is modified in a way that
+** changes the size of the source database or the number of pages remaining,
+** those changes are not reflected in the output of odbql_backup_pagecount()
+** and odbql_backup_remaining() until after the next
+** odbql_backup_step().)^
+**
+** <b>Concurrent Usage of Database Handles</b>
+**
+** ^The source [database connection] may be used by the application for other
+** purposes while a backup operation is underway or being initialized.
+** ^If SQLite is compiled and configured to support threadsafe database
+** connections, then the source database connection may be used concurrently
+** from within other threads.
+**
+** However, the application must guarantee that the destination 
+** [database connection] is not passed to any other API (by any thread) after 
+** odbql_backup_init() is called and before the corresponding call to
+** odbql_backup_finish().  SQLite does not currently check to see
+** if the application incorrectly accesses the destination [database connection]
+** and so no error code is reported, but the operations may malfunction
+** nevertheless.  Use of the destination database connection while a
+** backup is in progress might also also cause a mutex deadlock.
+**
+** If running in [shared cache mode], the application must
+** guarantee that the shared cache used by the destination database
+** is not accessed while the backup is running. In practice this means
+** that the application must guarantee that the disk file being 
+** backed up to is not accessed by any connection within the process,
+** not just the specific connection that was passed to odbql_backup_init().
+**
+** The [odbql_backup] object itself is partially threadsafe. Multiple 
+** threads may safely make multiple concurrent calls to odbql_backup_step().
+** However, the odbql_backup_remaining() and odbql_backup_pagecount()
+** APIs are not strictly speaking threadsafe. If they are invoked at the
+** same time as another thread is invoking odbql_backup_step() it is
+** possible that they return invalid values.
+*/
+ODBQL_API odbql_backup *ODBQL_STDCALL odbql_backup_init(
+  odbql *pDest,                        /* Destination database handle */
+  const char *zDestName,                 /* Destination database name */
+  odbql *pSource,                      /* Source database handle */
+  const char *zSourceName                /* Source database name */
+);
+ODBQL_API int ODBQL_STDCALL odbql_backup_step(odbql_backup *p, int nPage);
+ODBQL_API int ODBQL_STDCALL odbql_backup_finish(odbql_backup *p);
+ODBQL_API int ODBQL_STDCALL odbql_backup_remaining(odbql_backup *p);
+ODBQL_API int ODBQL_STDCALL odbql_backup_pagecount(odbql_backup *p);
+
+/*
+** CAPI3REF: Unlock Notification
+** METHOD: odbql
+**
+** ^When running in shared-cache mode, a database operation may fail with
+** an [ODBQL_LOCKED] error if the required locks on the shared-cache or
+** individual tables within the shared-cache cannot be obtained. See
+** [SQLite Shared-Cache Mode] for a description of shared-cache locking. 
+** ^This API may be used to register a callback that SQLite will invoke 
+** when the connection currently holding the required lock relinquishes it.
+** ^This API is only available if the library was compiled with the
+** [ODBQL_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined.
+**
+** See Also: [Using the SQLite Unlock Notification Feature].
+**
+** ^Shared-cache locks are released when a database connection concludes
+** its current transaction, either by committing it or rolling it back. 
+**
+** ^When a connection (known as the blocked connection) fails to obtain a
+** shared-cache lock and ODBQL_LOCKED is returned to the caller, the
+** identity of the database connection (the blocking connection) that
+** has locked the required resource is stored internally. ^After an 
+** application receives an ODBQL_LOCKED error, it may call the
+** odbql_unlock_notify() method with the blocked connection handle as 
+** the first argument to register for a callback that will be invoked
+** when the blocking connections current transaction is concluded. ^The
+** callback is invoked from within the [odbql_step] or [odbql_close]
+** call that concludes the blocking connections transaction.
+**
+** ^(If odbql_unlock_notify() is called in a multi-threaded application,
+** there is a chance that the blocking connection will have already
+** concluded its transaction by the time odbql_unlock_notify() is invoked.
+** If this happens, then the specified callback is invoked immediately,
+** from within the call to odbql_unlock_notify().)^
+**
+** ^If the blocked connection is attempting to obtain a write-lock on a
+** shared-cache table, and more than one other connection currently holds
+** a read-lock on the same table, then SQLite arbitrarily selects one of 
+** the other connections to use as the blocking connection.
+**
+** ^(There may be at most one unlock-notify callback registered by a 
+** blocked connection. If odbql_unlock_notify() is called when the
+** blocked connection already has a registered unlock-notify callback,
+** then the new callback replaces the old.)^ ^If odbql_unlock_notify() is
+** called with a NULL pointer as its second argument, then any existing
+** unlock-notify callback is canceled. ^The blocked connections 
+** unlock-notify callback may also be canceled by closing the blocked
+** connection using [odbql_close()].
+**
+** The unlock-notify callback is not reentrant. If an application invokes
+** any odbql_xxx API functions from within an unlock-notify callback, a
+** crash or deadlock may be the result.
+**
+** ^Unless deadlock is detected (see below), odbql_unlock_notify() always
+** returns ODBQL_OK.
+**
+** <b>Callback Invocation Details</b>
+**
+** When an unlock-notify callback is registered, the application provides a 
+** single void* pointer that is passed to the callback when it is invoked.
+** However, the signature of the callback function allows SQLite to pass
+** it an array of void* context pointers. The first argument passed to
+** an unlock-notify callback is a pointer to an array of void* pointers,
+** and the second is the number of entries in the array.
+**
+** When a blocking connections transaction is concluded, there may be
+** more than one blocked connection that has registered for an unlock-notify
+** callback. ^If two or more such blocked connections have specified the
+** same callback function, then instead of invoking the callback function
+** multiple times, it is invoked once with the set of void* context pointers
+** specified by the blocked connections bundled together into an array.
+** This gives the application an opportunity to prioritize any actions 
+** related to the set of unblocked database connections.
+**
+** <b>Deadlock Detection</b>
+**
+** Assuming that after registering for an unlock-notify callback a 
+** database waits for the callback to be issued before taking any further
+** action (a reasonable assumption), then using this API may cause the
+** application to deadlock. For example, if connection X is waiting for
+** connection Y's transaction to be concluded, and similarly connection
+** Y is waiting on connection X's transaction, then neither connection
+** will proceed and the system may remain deadlocked indefinitely.
+**
+** To avoid this scenario, the odbql_unlock_notify() performs deadlock
+** detection. ^If a given call to odbql_unlock_notify() would put the
+** system in a deadlocked state, then ODBQL_LOCKED is returned and no
+** unlock-notify callback is registered. The system is said to be in
+** a deadlocked state if connection A has registered for an unlock-notify
+** callback on the conclusion of connection B's transaction, and connection
+** B has itself registered for an unlock-notify callback when connection
+** A's transaction is concluded. ^Indirect deadlock is also detected, so
+** the system is also considered to be deadlocked if connection B has
+** registered for an unlock-notify callback on the conclusion of connection
+** C's transaction, where connection C is waiting on connection A. ^Any
+** number of levels of indirection are allowed.
+**
+** <b>The "DROP TABLE" Exception</b>
+**
+** When a call to [odbql_step()] returns ODBQL_LOCKED, it is almost 
+** always appropriate to call odbql_unlock_notify(). There is however,
+** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement,
+** SQLite checks if there are any currently executing SELECT statements
+** that belong to the same connection. If there are, ODBQL_LOCKED is
+** returned. In this case there is no "blocking connection", so invoking
+** odbql_unlock_notify() results in the unlock-notify callback being
+** invoked immediately. If the application then re-attempts the "DROP TABLE"
+** or "DROP INDEX" query, an infinite loop might be the result.
+**
+** One way around this problem is to check the extended error code returned
+** by an odbql_step() call. ^(If there is a blocking connection, then the
+** extended error code is set to ODBQL_LOCKED_SHAREDCACHE. Otherwise, in
+** the special "DROP TABLE/INDEX" case, the extended error code is just 
+** ODBQL_LOCKED.)^
+*/
+ODBQL_API int ODBQL_STDCALL odbql_unlock_notify(
+  odbql *pBlocked,                          /* Waiting connection */
+  void (*xNotify)(void **apArg, int nArg),    /* Callback function to invoke */
+  void *pNotifyArg                            /* Argument to pass to xNotify */
+);
+
+
+/*
+** CAPI3REF: String Comparison
+**
+** ^The [odbql_stricmp()] and [odbql_strnicmp()] APIs allow applications
+** and extensions to compare the contents of two buffers containing UTF-8
+** strings in a case-independent fashion, using the same definition of "case
+** independence" that SQLite uses internally when comparing identifiers.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_stricmp(const char *, const char *);
+ODBQL_API int ODBQL_STDCALL odbql_strnicmp(const char *, const char *, int);
+
+/*
+** CAPI3REF: String Globbing
+*
+** ^The [odbql_strglob(P,X)] interface returns zero if and only if
+** string X matches the [GLOB] pattern P.
+** ^The definition of [GLOB] pattern matching used in
+** [odbql_strglob(P,X)] is the same as for the "X GLOB P" operator in the
+** SQL dialect understood by SQLite.  ^The [odbql_strglob(P,X)] function
+** is case sensitive.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [odbql_stricmp()] and [odbql_strnicmp()].
+**
+** See also: [odbql_strlike()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_strglob(const char *zGlob, const char *zStr);
+
+/*
+** CAPI3REF: String LIKE Matching
+*
+** ^The [odbql_strlike(P,X,E)] interface returns zero if and only if
+** string X matches the [LIKE] pattern P with escape character E.
+** ^The definition of [LIKE] pattern matching used in
+** [odbql_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
+** operator in the SQL dialect understood by SQLite.  ^For "X LIKE P" without
+** the ESCAPE clause, set the E parameter of [odbql_strlike(P,X,E)] to 0.
+** ^As with the LIKE operator, the [odbql_strlike(P,X,E)] function is case
+** insensitive - equivalent upper and lower case ASCII characters match
+** one another.
+**
+** ^The [odbql_strlike(P,X,E)] function matches Unicode characters, though
+** only ASCII characters are case folded.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [odbql_stricmp()] and [odbql_strnicmp()].
+**
+** See also: [odbql_strglob()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
+
+/*
+** CAPI3REF: Error Logging Interface
+**
+** ^The [odbql_log()] interface writes a message into the [error log]
+** established by the [ODBQL_CONFIG_LOG] option to [odbql_config()].
+** ^If logging is enabled, the zFormat string and subsequent arguments are
+** used with [odbql_snprintf()] to generate the final output string.
+**
+** The odbql_log() interface is intended for use by extensions such as
+** virtual tables, collating functions, and SQL functions.  While there is
+** nothing to prevent an application from calling odbql_log(), doing so
+** is considered bad form.
+**
+** The zFormat string must not be NULL.
+**
+** To avoid deadlocks and other threading problems, the odbql_log() routine
+** will not use dynamically allocated memory.  The log message is stored in
+** a fixed-length buffer on the stack.  If the log message is longer than
+** a few hundred characters, it will be truncated to the length of the
+** buffer.
+*/
+ODBQL_API void ODBQL_CDECL odbql_log(int iErrCode, const char *zFormat, ...);
+
+/*
+** CAPI3REF: Write-Ahead Log Commit Hook
+** METHOD: odbql
+**
+** ^The [odbql_wal_hook()] function is used to register a callback that
+** is invoked each time data is committed to a database in wal mode.
+**
+** ^(The callback is invoked by SQLite after the commit has taken place and 
+** the associated write-lock on the database released)^, so the implementation 
+** may read, write or [checkpoint] the database as required.
+**
+** ^The first parameter passed to the callback function when it is invoked
+** is a copy of the third parameter passed to odbql_wal_hook() when
+** registering the callback. ^The second is a copy of the database handle.
+** ^The third parameter is the name of the database that was written to -
+** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter
+** is the number of pages currently in the write-ahead log file,
+** including those that were just committed.
+**
+** The callback function should normally return [ODBQL_OK].  ^If an error
+** code is returned, that error will propagate back up through the
+** SQLite code base to cause the statement that provoked the callback
+** to report an error, though the commit will have still occurred. If the
+** callback returns [ODBQL_ROW] or [ODBQL_DONE], or if it returns a value
+** that does not correspond to any valid SQLite error code, the results
+** are undefined.
+**
+** A single database handle may have at most a single write-ahead log callback 
+** registered at one time. ^Calling [odbql_wal_hook()] replaces any
+** previously registered write-ahead log callback. ^Note that the
+** [odbql_wal_autocheckpoint()] interface and the
+** [wal_autocheckpoint pragma] both invoke [odbql_wal_hook()] and will
+** overwrite any prior [odbql_wal_hook()] settings.
+*/
+ODBQL_API void *ODBQL_STDCALL odbql_wal_hook(
+  odbql*, 
+  int(*)(void *,odbql*,const char*,int),
+  void*
+);
+
+/*
+** CAPI3REF: Configure an auto-checkpoint
+** METHOD: odbql
+**
+** ^The [odbql_wal_autocheckpoint(D,N)] is a wrapper around
+** [odbql_wal_hook()] that causes any database on [database connection] D
+** to automatically [checkpoint]
+** after committing a transaction if there are N or
+** more frames in the [write-ahead log] file.  ^Passing zero or 
+** a negative value as the nFrame parameter disables automatic
+** checkpoints entirely.
+**
+** ^The callback registered by this function replaces any existing callback
+** registered using [odbql_wal_hook()].  ^Likewise, registering a callback
+** using [odbql_wal_hook()] disables the automatic checkpoint mechanism
+** configured by this function.
+**
+** ^The [wal_autocheckpoint pragma] can be used to invoke this interface
+** from SQL.
+**
+** ^Checkpoints initiated by this mechanism are
+** [odbql_wal_checkpoint_v2|PASSIVE].
+**
+** ^Every new [database connection] defaults to having the auto-checkpoint
+** enabled with a threshold of 1000 or [ODBQL_DEFAULT_WAL_AUTOCHECKPOINT]
+** pages.  The use of this interface
+** is only necessary if the default setting is found to be suboptimal
+** for a particular application.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_wal_autocheckpoint(odbql *db, int N);
+
+/*
+** CAPI3REF: Checkpoint a database
+** METHOD: odbql
+**
+** ^(The odbql_wal_checkpoint(D,X) is equivalent to
+** [odbql_wal_checkpoint_v2](D,X,[ODBQL_CHECKPOINT_PASSIVE],0,0).)^
+**
+** In brief, odbql_wal_checkpoint(D,X) causes the content in the 
+** [write-ahead log] for database X on [database connection] D to be
+** transferred into the database file and for the write-ahead log to
+** be reset.  See the [checkpointing] documentation for addition
+** information.
+**
+** This interface used to be the only way to cause a checkpoint to
+** occur.  But then the newer and more powerful [odbql_wal_checkpoint_v2()]
+** interface was added.  This interface is retained for backwards
+** compatibility and as a convenience for applications that need to manually
+** start a callback but which do not need the full power (and corresponding
+** complication) of [odbql_wal_checkpoint_v2()].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_wal_checkpoint(odbql *db, const char *zDb);
+
+/*
+** CAPI3REF: Checkpoint a database
+** METHOD: odbql
+**
+** ^(The odbql_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint
+** operation on database X of [database connection] D in mode M.  Status
+** information is written back into integers pointed to by L and C.)^
+** ^(The M parameter must be a valid [checkpoint mode]:)^
+**
+** <dl>
+** <dt>ODBQL_CHECKPOINT_PASSIVE<dd>
+**   ^Checkpoint as many frames as possible without waiting for any database 
+**   readers or writers to finish, then sync the database file if all frames 
+**   in the log were checkpointed. ^The [busy-handler callback]
+**   is never invoked in the ODBQL_CHECKPOINT_PASSIVE mode.  
+**   ^On the other hand, passive mode might leave the checkpoint unfinished
+**   if there are concurrent readers or writers.
+**
+** <dt>ODBQL_CHECKPOINT_FULL<dd>
+**   ^This mode blocks (it invokes the
+**   [odbql_busy_handler|busy-handler callback]) until there is no
+**   database writer and all readers are reading from the most recent database
+**   snapshot. ^It then checkpoints all frames in the log file and syncs the
+**   database file. ^This mode blocks new database writers while it is pending,
+**   but new database readers are allowed to continue unimpeded.
+**
+** <dt>ODBQL_CHECKPOINT_RESTART<dd>
+**   ^This mode works the same way as ODBQL_CHECKPOINT_FULL with the addition
+**   that after checkpointing the log file it blocks (calls the 
+**   [busy-handler callback])
+**   until all readers are reading from the database file only. ^This ensures 
+**   that the next writer will restart the log file from the beginning.
+**   ^Like ODBQL_CHECKPOINT_FULL, this mode blocks new
+**   database writer attempts while it is pending, but does not impede readers.
+**
+** <dt>ODBQL_CHECKPOINT_TRUNCATE<dd>
+**   ^This mode works the same way as ODBQL_CHECKPOINT_RESTART with the
+**   addition that it also truncates the log file to zero bytes just prior
+**   to a successful return.
+** </dl>
+**
+** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
+** the log file or to -1 if the checkpoint could not run because
+** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
+** NULL,then *pnCkpt is set to the total number of checkpointed frames in the
+** log file (including any that were already checkpointed before the function
+** was called) or to -1 if the checkpoint could not run due to an error or
+** because the database is not in WAL mode. ^Note that upon successful
+** completion of an ODBQL_CHECKPOINT_TRUNCATE, the log file will have been
+** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero.
+**
+** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If
+** any other process is running a checkpoint operation at the same time, the 
+** lock cannot be obtained and ODBQL_BUSY is returned. ^Even if there is a 
+** busy-handler configured, it will not be invoked in this case.
+**
+** ^The ODBQL_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the 
+** exclusive "writer" lock on the database file. ^If the writer lock cannot be
+** obtained immediately, and a busy-handler is configured, it is invoked and
+** the writer lock retried until either the busy-handler returns 0 or the lock
+** is successfully obtained. ^The busy-handler is also invoked while waiting for
+** database readers as described above. ^If the busy-handler returns 0 before
+** the writer lock is obtained or while waiting for database readers, the
+** checkpoint operation proceeds from that point in the same way as 
+** ODBQL_CHECKPOINT_PASSIVE - checkpointing as many frames as possible 
+** without blocking any further. ^ODBQL_BUSY is returned in this case.
+**
+** ^If parameter zDb is NULL or points to a zero length string, then the
+** specified operation is attempted on all WAL databases [attached] to 
+** [database connection] db.  In this case the
+** values written to output parameters *pnLog and *pnCkpt are undefined. ^If 
+** an ODBQL_BUSY error is encountered when processing one or more of the 
+** attached WAL databases, the operation is still attempted on any remaining 
+** attached databases and ODBQL_BUSY is returned at the end. ^If any other 
+** error occurs while processing an attached database, processing is abandoned 
+** and the error code is returned to the caller immediately. ^If no error 
+** (ODBQL_BUSY or otherwise) is encountered while processing the attached 
+** databases, ODBQL_OK is returned.
+**
+** ^If database zDb is the name of an attached database that is not in WAL
+** mode, ODBQL_OK is returned and both *pnLog and *pnCkpt set to -1. ^If
+** zDb is not NULL (or a zero length string) and is not the name of any
+** attached database, ODBQL_ERROR is returned to the caller.
+**
+** ^Unless it returns ODBQL_MISUSE,
+** the odbql_wal_checkpoint_v2() interface
+** sets the error information that is queried by
+** [odbql_errcode()] and [odbql_errmsg()].
+**
+** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface
+** from SQL.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_wal_checkpoint_v2(
+  odbql *db,                    /* Database handle */
+  const char *zDb,                /* Name of attached database (or NULL) */
+  int eMode,                      /* ODBQL_CHECKPOINT_* value */
+  int *pnLog,                     /* OUT: Size of WAL log in frames */
+  int *pnCkpt                     /* OUT: Total number of frames checkpointed */
+);
+
+/*
+** CAPI3REF: Checkpoint Mode Values
+** KEYWORDS: {checkpoint mode}
+**
+** These constants define all valid values for the "checkpoint mode" passed
+** as the third parameter to the [odbql_wal_checkpoint_v2()] interface.
+** See the [odbql_wal_checkpoint_v2()] documentation for details on the
+** meaning of each of these checkpoint modes.
+*/
+#define ODBQL_CHECKPOINT_PASSIVE  0  /* Do as much as possible w/o blocking */
+#define ODBQL_CHECKPOINT_FULL     1  /* Wait for writers, then checkpoint */
+#define ODBQL_CHECKPOINT_RESTART  2  /* Like FULL but wait for for readers */
+#define ODBQL_CHECKPOINT_TRUNCATE 3  /* Like RESTART but also truncate WAL */
+
+/*
+** CAPI3REF: Virtual Table Interface Configuration
+**
+** This function may be called by either the [xConnect] or [xCreate] method
+** of a [virtual table] implementation to configure
+** various facets of the virtual table interface.
+**
+** If this interface is invoked outside the context of an xConnect or
+** xCreate virtual table method then the behavior is undefined.
+**
+** At present, there is only one option that may be configured using
+** this function. (See [ODBQL_VTAB_CONSTRAINT_SUPPORT].)  Further options
+** may be added in the future.
+*/
+ODBQL_API int ODBQL_CDECL odbql_vtab_config(odbql*, int op, ...);
+
+/*
+** CAPI3REF: Virtual Table Configuration Options
+**
+** These macros define the various options to the
+** [odbql_vtab_config()] interface that [virtual table] implementations
+** can use to customize and optimize their behavior.
+**
+** <dl>
+** <dt>ODBQL_VTAB_CONSTRAINT_SUPPORT
+** <dd>Calls of the form
+** [odbql_vtab_config](db,ODBQL_VTAB_CONSTRAINT_SUPPORT,X) are supported,
+** where X is an integer.  If X is zero, then the [virtual table] whose
+** [xCreate] or [xConnect] method invoked [odbql_vtab_config()] does not
+** support constraints.  In this configuration (which is the default) if
+** a call to the [xUpdate] method returns [ODBQL_CONSTRAINT], then the entire
+** statement is rolled back as if [ON CONFLICT | OR ABORT] had been
+** specified as part of the users SQL statement, regardless of the actual
+** ON CONFLICT mode specified.
+**
+** If X is non-zero, then the virtual table implementation guarantees
+** that if [xUpdate] returns [ODBQL_CONSTRAINT], it will do so before
+** any modifications to internal or persistent data structures have been made.
+** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite 
+** is able to roll back a statement or database transaction, and abandon
+** or continue processing the current SQL statement as appropriate. 
+** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
+** [ODBQL_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
+** had been ABORT.
+**
+** Virtual table implementations that are required to handle OR REPLACE
+** must do so within the [xUpdate] method. If a call to the 
+** [odbql_vtab_on_conflict()] function indicates that the current ON 
+** CONFLICT policy is REPLACE, the virtual table implementation should 
+** silently replace the appropriate rows within the xUpdate callback and
+** return ODBQL_OK. Or, if this is not possible, it may return
+** ODBQL_CONSTRAINT, in which case SQLite falls back to OR ABORT 
+** constraint handling.
+** </dl>
+*/
+#define ODBQL_VTAB_CONSTRAINT_SUPPORT 1
+
+/*
+** CAPI3REF: Determine The Virtual Table Conflict Policy
+**
+** This function may only be called from within a call to the [xUpdate] method
+** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
+** value returned is one of [ODBQL_ROLLBACK], [ODBQL_IGNORE], [ODBQL_FAIL],
+** [ODBQL_ABORT], or [ODBQL_REPLACE], according to the [ON CONFLICT] mode
+** of the SQL statement that triggered the call to the [xUpdate] method of the
+** [virtual table].
+*/
+ODBQL_API int ODBQL_STDCALL odbql_vtab_on_conflict(odbql *);
+
+/*
+** CAPI3REF: Conflict resolution modes
+** KEYWORDS: {conflict resolution mode}
+**
+** These constants are returned by [odbql_vtab_on_conflict()] to
+** inform a [virtual table] implementation what the [ON CONFLICT] mode
+** is for the SQL statement being evaluated.
+**
+** Note that the [ODBQL_IGNORE] constant is also used as a potential
+** return value from the [odbql_set_authorizer()] callback and that
+** [ODBQL_ABORT] is also a [result code].
+*/
+#define ODBQL_ROLLBACK 1
+/* #define ODBQL_IGNORE 2 // Also used by odbql_authorizer() callback */
+#define ODBQL_FAIL     3
+/* #define ODBQL_ABORT 4  // Also an error code */
+#define ODBQL_REPLACE  5
+
+/*
+** CAPI3REF: Prepared Statement Scan Status Opcodes
+** KEYWORDS: {scanstatus options}
+**
+** The following constants can be used for the T parameter to the
+** [odbql_stmt_scanstatus(S,X,T,V)] interface.  Each constant designates a
+** different metric for odbql_stmt_scanstatus() to return.
+**
+** When the value returned to V is a string, space to hold that string is
+** managed by the prepared statement S and will be automatically freed when
+** S is finalized.
+**
+** <dl>
+** [[ODBQL_SCANSTAT_NLOOP]] <dt>ODBQL_SCANSTAT_NLOOP</dt>
+** <dd>^The [odbql_int64] variable pointed to by the T parameter will be
+** set to the total number of times that the X-th loop has run.</dd>
+**
+** [[ODBQL_SCANSTAT_NVISIT]] <dt>ODBQL_SCANSTAT_NVISIT</dt>
+** <dd>^The [odbql_int64] variable pointed to by the T parameter will be set
+** to the total number of rows examined by all iterations of the X-th loop.</dd>
+**
+** [[ODBQL_SCANSTAT_EST]] <dt>ODBQL_SCANSTAT_EST</dt>
+** <dd>^The "double" variable pointed to by the T parameter will be set to the
+** query planner's estimate for the average number of rows output from each
+** iteration of the X-th loop.  If the query planner's estimates was accurate,
+** then this value will approximate the quotient NVISIT/NLOOP and the
+** product of this value for all prior loops with the same SELECTID will
+** be the NLOOP value for the current loop.
+**
+** [[ODBQL_SCANSTAT_NAME]] <dt>ODBQL_SCANSTAT_NAME</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the name of the index or table
+** used for the X-th loop.
+**
+** [[ODBQL_SCANSTAT_EXPLAIN]] <dt>ODBQL_SCANSTAT_EXPLAIN</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
+** description for the X-th loop.
+**
+** [[ODBQL_SCANSTAT_SELECTID]] <dt>ODBQL_SCANSTAT_SELECT</dt>
+** <dd>^The "int" variable pointed to by the T parameter will be set to the
+** "select-id" for the X-th loop.  The select-id identifies which query or
+** subquery the loop is part of.  The main query has a select-id of zero.
+** The select-id is the same value as is output in the first column
+** of an [EXPLAIN QUERY PLAN] query.
+** </dl>
+*/
+#define ODBQL_SCANSTAT_NLOOP    0
+#define ODBQL_SCANSTAT_NVISIT   1
+#define ODBQL_SCANSTAT_EST      2
+#define ODBQL_SCANSTAT_NAME     3
+#define ODBQL_SCANSTAT_EXPLAIN  4
+#define ODBQL_SCANSTAT_SELECTID 5
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** METHOD: odbql_stmt
+**
+** This interface returns information about the predicted and measured
+** performance for pStmt.  Advanced applications can use this
+** interface to compare the predicted and the measured performance and
+** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
+**
+** Since this interface is expected to be rarely used, it is only
+** available if SQLite is compiled using the [ODBQL_ENABLE_STMT_SCANSTATUS]
+** compile-time option.
+**
+** The "iScanStatusOp" parameter determines which status information to return.
+** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
+** of this interface is undefined.
+** ^The requested measurement is written into a variable pointed to by
+** the "pOut" parameter.
+** Parameter "idx" identifies the specific loop to retrieve statistics for.
+** Loops are numbered starting from zero. ^If idx is out of range - less than
+** zero or greater than or equal to the total number of loops used to implement
+** the statement - a non-zero value is returned and the variable that pOut
+** points to is unchanged.
+**
+** ^Statistics might not be available for all loops in all statements. ^In cases
+** where there exist loops with no available statistics, this function behaves
+** as if the loop did not exist - it returns non-zero and leave the variable
+** that pOut points to unchanged.
+**
+** See also: [odbql_stmt_scanstatus_reset()]
+*/
+ODBQL_API int ODBQL_STDCALL odbql_stmt_scanstatus(
+  odbql_stmt *pStmt,      /* Prepared statement for which info desired */
+  int idx,                  /* Index of loop to report on */
+  int iScanStatusOp,        /* Information desired.  ODBQL_SCANSTAT_* */
+  void *pOut                /* Result written here */
+);     
+
+/*
+** CAPI3REF: Zero Scan-Status Counters
+** METHOD: odbql_stmt
+**
+** ^Zero all [odbql_stmt_scanstatus()] related event counters.
+**
+** This API is only available if the library is built with pre-processor
+** symbol [ODBQL_ENABLE_STMT_SCANSTATUS] defined.
+*/
+ODBQL_API void ODBQL_STDCALL odbql_stmt_scanstatus_reset(odbql_stmt*);
+
+/*
+** CAPI3REF: Flush caches to disk mid-transaction
+**
+** ^If a write-transaction is open on [database connection] D when the
+** [odbql_db_cacheflush(D)] interface invoked, any dirty
+** pages in the pager-cache that are not currently in use are written out 
+** to disk. A dirty page may be in use if a database cursor created by an
+** active SQL statement is reading from it, or if it is page 1 of a database
+** file (page 1 is always "in use").  ^The [odbql_db_cacheflush(D)]
+** interface flushes caches for all schemas - "main", "temp", and
+** any [attached] databases.
+**
+** ^If this function needs to obtain extra database locks before dirty pages 
+** can be flushed to disk, it does so. ^If those locks cannot be obtained 
+** immediately and there is a busy-handler callback configured, it is invoked
+** in the usual manner. ^If the required lock still cannot be obtained, then
+** the database is skipped and an attempt made to flush any dirty pages
+** belonging to the next (if any) database. ^If any databases are skipped
+** because locks cannot be obtained, but no other error occurs, this
+** function returns ODBQL_BUSY.
+**
+** ^If any other error occurs while flushing dirty pages to disk (for
+** example an IO error or out-of-memory condition), then processing is
+** abandoned and an SQLite [error code] is returned to the caller immediately.
+**
+** ^Otherwise, if no error occurs, [odbql_db_cacheflush()] returns ODBQL_OK.
+**
+** ^This function does not set the database handle error code or message
+** returned by the [odbql_errcode()] and [odbql_errmsg()] functions.
+*/
+ODBQL_API int ODBQL_STDCALL odbql_db_cacheflush(odbql*);
+
+/*
+** CAPI3REF: The pre-update hook.
+**
+** ^These interfaces are only available if SQLite is compiled using the
+** [ODBQL_ENABLE_PREUPDATE_HOOK] compile-time option.
+**
+** ^The [odbql_preupdate_hook()] interface registers a callback function
+** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
+** on a [rowid table].
+** ^At most one preupdate hook may be registered at a time on a single
+** [database connection]; each call to [odbql_preupdate_hook()] overrides
+** the previous setting.
+** ^The preupdate hook is disabled by invoking [odbql_preupdate_hook()]
+** with a NULL pointer as the second parameter.
+** ^The third parameter to [odbql_preupdate_hook()] is passed through as
+** the first parameter to callbacks.
+**
+** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
+** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
+** tables.
+**
+** ^The second parameter to the preupdate callback is a pointer to
+** the [database connection] that registered the preupdate hook.
+** ^The third parameter to the preupdate callback is one of the constants
+** [ODBQL_INSERT], [ODBQL_DELETE], or [ODBQL_UPDATE] to indentify the
+** kind of update operation that is about to occur.
+** ^(The fourth parameter to the preupdate callback is the name of the
+** database within the database connection that is being modified.  This
+** will be "main" for the main database or "temp" for TEMP tables or 
+** the name given after the AS keyword in the [ATTACH] statement for attached
+** databases.)^
+** ^The fifth parameter to the preupdate callback is the name of the
+** table that is being modified.
+** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
+** row being changes for ODBQL_UPDATE and ODBQL_DELETE changes and is
+** undefined for ODBQL_INSERT changes.
+** ^The seventh parameter to the preupdate callback is the final [rowid] of
+** the row being changed for ODBQL_UPDATE and ODBQL_INSERT changes and is
+** undefined for ODBQL_DELETE changes.
+**
+** The [odbql_preupdate_old()], [odbql_preupdate_new()],
+** [odbql_preupdate_count()], and [odbql_preupdate_depth()] interfaces
+** provide additional information about a preupdate event. These routines
+** may only be called from within a preupdate callback.  Invoking any of
+** these routines from outside of a preupdate callback or with a
+** [database connection] pointer that is different from the one supplied
+** to the preupdate callback results in undefined and probably undesirable
+** behavior.
+**
+** ^The [odbql_preupdate_count(D)] interface returns the number of columns
+** in the row that is being inserted, updated, or deleted.
+**
+** ^The [odbql_preupdate_old(D,N,P)] interface writes into P a pointer to
+** a [protected odbql_value] that contains the value of the Nth column of
+** the table row before it is updated.  The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within ODBQL_UPDATE and ODBQL_DELETE
+** preupdate callbacks; if it is used by an ODBQL_INSERT callback then the
+** behavior is undefined.  The [odbql_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [odbql_preupdate_new(D,N,P)] interface writes into P a pointer to
+** a [protected odbql_value] that contains the value of the Nth column of
+** the table row after it is updated.  The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within ODBQL_INSERT and ODBQL_UPDATE
+** preupdate callbacks; if it is used by an ODBQL_DELETE callback then the
+** behavior is undefined.  The [odbql_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [odbql_preupdate_depth(D)] interface returns 0 if the preupdate
+** callback was invoked as a result of a direct insert, update, or delete
+** operation; or 1 for inserts, updates, or deletes invoked by top-level 
+** triggers; or 2 for changes resulting from triggers called by top-level
+** triggers; and so forth.
+**
+** See also:  [odbql_update_hook()]
+*/
+ODBQL_API ODBQL_EXPERIMENTAL void *ODBQL_STDCALL odbql_preupdate_hook(
+  odbql *db,
+  void(*xPreUpdate)(
+    void *pCtx,                   /* Copy of third arg to preupdate_hook() */
+    odbql *db,                  /* Database handle */
+    int op,                       /* ODBQL_UPDATE, DELETE or INSERT */
+    char const *zDb,              /* Database name */
+    char const *zName,            /* Table name */
+    odbql_int64 iKey1,          /* Rowid of row about to be deleted/updated */
+    odbql_int64 iKey2           /* New rowid value (for a rowid UPDATE) */
+  ),
+  void*
+);
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_preupdate_old(odbql *, int, odbql_value **);
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_preupdate_count(odbql *);
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_preupdate_depth(odbql *);
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_preupdate_new(odbql *, int, odbql_value **);
+
+/*
+** CAPI3REF: Low-level system error code
+**
+** ^Attempt to return the underlying operating system error code or error
+** number that caused the most recent I/O error or failure to open a file.
+** The return value is OS-dependent.  For example, on unix systems, after
+** [odbql_open_v2()] returns [ODBQL_CANTOPEN], this interface could be
+** called to get back the underlying "errno" that caused the problem, such
+** as ENOSPC, EAUTH, EISDIR, and so forth.  
+*/
+ODBQL_API int ODBQL_STDCALL odbql_system_errno(odbql*);
+
+/*
+** CAPI3REF: Database Snapshot
+** KEYWORDS: {snapshot}
+** EXPERIMENTAL
+**
+** An instance of the snapshot object records the state of a [WAL mode]
+** database for some specific point in history.
+**
+** In [WAL mode], multiple [database connections] that are open on the
+** same database file can each be reading a different historical version
+** of the database file.  When a [database connection] begins a read
+** transaction, that connection sees an unchanging copy of the database
+** as it existed for the point in time when the transaction first started.
+** Subsequent changes to the database from other connections are not seen
+** by the reader until a new read transaction is started.
+**
+** The odbql_snapshot object records state information about an historical
+** version of the database file so that it is possible to later open a new read
+** transaction that sees that historical version of the database rather than
+** the most recent version.
+**
+** The constructor for this object is [odbql_snapshot_get()].  The
+** [odbql_snapshot_open()] method causes a fresh read transaction to refer
+** to an historical snapshot (if possible).  The destructor for 
+** odbql_snapshot objects is [odbql_snapshot_free()].
+*/
+typedef struct odbql_snapshot odbql_snapshot;
+
+/*
+** CAPI3REF: Record A Database Snapshot
+** EXPERIMENTAL
+**
+** ^The [odbql_snapshot_get(D,S,P)] interface attempts to make a
+** new [odbql_snapshot] object that records the current state of
+** schema S in database connection D.  ^On success, the
+** [odbql_snapshot_get(D,S,P)] interface writes a pointer to the newly
+** created [odbql_snapshot] object into *P and returns ODBQL_OK.
+** ^If schema S of [database connection] D is not a [WAL mode] database
+** that is in a read transaction, then [odbql_snapshot_get(D,S,P)]
+** leaves the *P value unchanged and returns an appropriate [error code].
+**
+** The [odbql_snapshot] object returned from a successful call to
+** [odbql_snapshot_get()] must be freed using [odbql_snapshot_free()]
+** to avoid a memory leak.
+**
+** The [odbql_snapshot_get()] interface is only available when the
+** ODBQL_ENABLE_SNAPSHOT compile-time option is used.
+*/
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_snapshot_get(
+  odbql *db,
+  const char *zSchema,
+  odbql_snapshot **ppSnapshot
+);
+
+/*
+** CAPI3REF: Start a read transaction on an historical snapshot
+** EXPERIMENTAL
+**
+** ^The [odbql_snapshot_open(D,S,P)] interface starts a
+** read transaction for schema S of
+** [database connection] D such that the read transaction
+** refers to historical [snapshot] P, rather than the most
+** recent change to the database.
+** ^The [odbql_snapshot_open()] interface returns ODBQL_OK on success
+** or an appropriate [error code] if it fails.
+**
+** ^In order to succeed, a call to [odbql_snapshot_open(D,S,P)] must be
+** the first operation following the [BEGIN] that takes the schema S
+** out of [autocommit mode].
+** ^In other words, schema S must not currently be in
+** a transaction for [odbql_snapshot_open(D,S,P)] to work, but the
+** database connection D must be out of [autocommit mode].
+** ^A [snapshot] will fail to open if it has been overwritten by a
+** [checkpoint].
+** ^(A call to [odbql_snapshot_open(D,S,P)] will fail if the
+** database connection D does not know that the database file for
+** schema S is in [WAL mode].  A database connection might not know
+** that the database file is in [WAL mode] if there has been no prior
+** I/O on that database connection, or if the database entered [WAL mode] 
+** after the most recent I/O on the database connection.)^
+** (Hint: Run "[PRAGMA application_id]" against a newly opened
+** database connection in order to make it ready to use snapshots.)
+**
+** The [odbql_snapshot_open()] interface is only available when the
+** ODBQL_ENABLE_SNAPSHOT compile-time option is used.
+*/
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_snapshot_open(
+  odbql *db,
+  const char *zSchema,
+  odbql_snapshot *pSnapshot
+);
+
+/*
+** CAPI3REF: Destroy a snapshot
+** EXPERIMENTAL
+**
+** ^The [odbql_snapshot_free(P)] interface destroys [odbql_snapshot] P.
+** The application must eventually free every [odbql_snapshot] object
+** using this routine to avoid a memory leak.
+**
+** The [odbql_snapshot_free()] interface is only available when the
+** ODBQL_ENABLE_SNAPSHOT compile-time option is used.
+*/
+ODBQL_API ODBQL_EXPERIMENTAL void ODBQL_STDCALL odbql_snapshot_free(odbql_snapshot*);
+
+/*
+** CAPI3REF: Compare the ages of two snapshot handles.
+** EXPERIMENTAL
+**
+** The odbql_snapshot_cmp(P1, P2) interface is used to compare the ages
+** of two valid snapshot handles. 
+**
+** If the two snapshot handles are not associated with the same database 
+** file, the result of the comparison is undefined. 
+**
+** Additionally, the result of the comparison is only valid if both of the
+** snapshot handles were obtained by calling odbql_snapshot_get() since the
+** last time the wal file was deleted. The wal file is deleted when the
+** database is changed back to rollback mode or when the number of database
+** clients drops to zero. If either snapshot handle was obtained before the 
+** wal file was last deleted, the value returned by this function 
+** is undefined.
+**
+** Otherwise, this API returns a negative value if P1 refers to an older
+** snapshot than P2, zero if the two handles refer to the same database
+** snapshot, and a positive value if P1 is a newer snapshot than P2.
+*/
+ODBQL_API ODBQL_EXPERIMENTAL int ODBQL_STDCALL odbql_snapshot_cmp(
+  odbql_snapshot *p1,
+  odbql_snapshot *p2
+);
+
+/*
+** Undo the hack that converts floating point types to integer for
+** builds on processors without floating point support.
+*/
+#ifdef ODBQL_OMIT_FLOATING_POINT
+# undef double
+#endif
+
+#ifdef __cplusplus
+}  /* End of the 'extern "C"' block */
+#endif
+#endif /* _ODBQL_H_ */
+
+/******** Begin file odbqlrtree.h *********/
+/*
+** 2010 August 30
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+*/
+
+#ifndef _ODBQLRTREE_H_
+#define _ODBQLRTREE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct odbql_rtree_geometry odbql_rtree_geometry;
+typedef struct odbql_rtree_query_info odbql_rtree_query_info;
+
+/* The double-precision datatype used by RTree depends on the
+** ODBQL_RTREE_INT_ONLY compile-time option.
+*/
+#ifdef ODBQL_RTREE_INT_ONLY
+  typedef odbql_int64 odbql_rtree_dbl;
+#else
+  typedef double odbql_rtree_dbl;
+#endif
+
+/*
+** Register a geometry callback named zGeom that can be used as part of an
+** R-Tree geometry query as follows:
+**
+**   SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...)
+*/
+ODBQL_API int ODBQL_STDCALL odbql_rtree_geometry_callback(
+  odbql *db,
+  const char *zGeom,
+  int (*xGeom)(odbql_rtree_geometry*, int, odbql_rtree_dbl*,int*),
+  void *pContext
+);
+
+
+/*
+** A pointer to a structure of the following type is passed as the first
+** argument to callbacks registered using rtree_geometry_callback().
+*/
+struct odbql_rtree_geometry {
+  void *pContext;                 /* Copy of pContext passed to s_r_g_c() */
+  int nParam;                     /* Size of array aParam[] */
+  odbql_rtree_dbl *aParam;      /* Parameters passed to SQL geom function */
+  void *pUser;                    /* Callback implementation user data */
+  void (*xDelUser)(void *);       /* Called by SQLite to clean up pUser */
+};
+
+/*
+** Register a 2nd-generation geometry callback named zScore that can be 
+** used as part of an R-Tree geometry query as follows:
+**
+**   SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
+*/
+ODBQL_API int ODBQL_STDCALL odbql_rtree_query_callback(
+  odbql *db,
+  const char *zQueryFunc,
+  int (*xQueryFunc)(odbql_rtree_query_info*),
+  void *pContext,
+  void (*xDestructor)(void*)
+);
+
+
+/*
+** A pointer to a structure of the following type is passed as the 
+** argument to scored geometry callback registered using
+** odbql_rtree_query_callback().
+**
+** Note that the first 5 fields of this structure are identical to
+** odbql_rtree_geometry.  This structure is a subclass of
+** odbql_rtree_geometry.
+*/
+struct odbql_rtree_query_info {
+  void *pContext;                   /* pContext from when function registered */
+  int nParam;                       /* Number of function parameters */
+  odbql_rtree_dbl *aParam;        /* value of function parameters */
+  void *pUser;                      /* callback can use this, if desired */
+  void (*xDelUser)(void*);          /* function to free pUser */
+  odbql_rtree_dbl *aCoord;        /* Coordinates of node or entry to check */
+  unsigned int *anQueue;            /* Number of pending entries in the queue */
+  int nCoord;                       /* Number of coordinates */
+  int iLevel;                       /* Level of current node or entry */
+  int mxLevel;                      /* The largest iLevel value in the tree */
+  odbql_int64 iRowid;             /* Rowid for current entry */
+  odbql_rtree_dbl rParentScore;   /* Score of parent node */
+  int eParentWithin;                /* Visibility of parent node */
+  int eWithin;                      /* OUT: Visiblity */
+  odbql_rtree_dbl rScore;         /* OUT: Write the score here */
+  /* The following fields are only available in 3.8.11 and later */
+  odbql_value **apSqlParam;       /* Original SQL values of parameters */
+};
+
+/*
+** Allowed values for odbql_rtree_query.eWithin and .eParentWithin.
+*/
+#define NOT_WITHIN       0   /* Object completely outside of query region */
+#define PARTLY_WITHIN    1   /* Object partially overlaps query region */
+#define FULLY_WITHIN     2   /* Object fully contained within query region */
+
+
+#ifdef __cplusplus
+}  /* end of the 'extern "C"' block */
+#endif
+
+#endif  /* ifndef _ODBQLRTREE_H_ */
+
+/******** End of odbqlrtree.h *********/
+/******** Begin file odbqlsession.h *********/
+
+#if !defined(__ODBQLSESSION_H_) && defined(ODBQL_ENABLE_SESSION)
+#define __ODBQLSESSION_H_ 1
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** CAPI3REF: Session Object Handle
+*/
+typedef struct odbql_session odbql_session;
+
+/*
+** CAPI3REF: Changeset Iterator Handle
+*/
+typedef struct odbql_changeset_iter odbql_changeset_iter;
+
+/*
+** CAPI3REF: Create A New Session Object
+**
+** Create a new session object attached to database handle db. If successful,
+** a pointer to the new object is written to *ppSession and ODBQL_OK is
+** returned. If an error occurs, *ppSession is set to NULL and an SQLite
+** error code (e.g. ODBQL_NOMEM) is returned.
+**
+** It is possible to create multiple session objects attached to a single
+** database handle.
+**
+** Session objects created using this function should be deleted using the
+** [odbqlsession_delete()] function before the database handle that they
+** are attached to is itself closed. If the database handle is closed before
+** the session object is deleted, then the results of calling any session
+** module function, including [odbqlsession_delete()] on the session object
+** are undefined.
+**
+** Because the session module uses the [odbql_preupdate_hook()] API, it
+** is not possible for an application to register a pre-update hook on a
+** database handle that has one or more session objects attached. Nor is
+** it possible to create a session object attached to a database handle for
+** which a pre-update hook is already defined. The results of attempting 
+** either of these things are undefined.
+**
+** The session object will be used to create changesets for tables in
+** database zDb, where zDb is either "main", or "temp", or the name of an
+** attached database. It is not an error if database zDb is not attached
+** to the database when the session object is created.
+*/
+int odbqlsession_create(
+  odbql *db,                    /* Database handle */
+  const char *zDb,                /* Name of db (e.g. "main") */
+  odbql_session **ppSession     /* OUT: New session object */
+);
+
+/*
+** CAPI3REF: Delete A Session Object
+**
+** Delete a session object previously allocated using 
+** [odbqlsession_create()]. Once a session object has been deleted, the
+** results of attempting to use pSession with any other session module
+** function are undefined.
+**
+** Session objects must be deleted before the database handle to which they
+** are attached is closed. Refer to the documentation for 
+** [odbqlsession_create()] for details.
+*/
+void odbqlsession_delete(odbql_session *pSession);
+
+
+/*
+** CAPI3REF: Enable Or Disable A Session Object
+**
+** Enable or disable the recording of changes by a session object. When
+** enabled, a session object records changes made to the database. When
+** disabled - it does not. A newly created session object is enabled.
+** Refer to the documentation for [odbqlsession_changeset()] for further
+** details regarding how enabling and disabling a session object affects
+** the eventual changesets.
+**
+** Passing zero to this function disables the session. Passing a value
+** greater than zero enables it. Passing a value less than zero is a 
+** no-op, and may be used to query the current state of the session.
+**
+** The return value indicates the final state of the session object: 0 if 
+** the session is disabled, or 1 if it is enabled.
+*/
+int odbqlsession_enable(odbql_session *pSession, int bEnable);
+
+/*
+** CAPI3REF: Set Or Clear the Indirect Change Flag
+**
+** Each change recorded by a session object is marked as either direct or
+** indirect. A change is marked as indirect if either:
+**
+** <ul>
+**   <li> The session object "indirect" flag is set when the change is
+**        made, or
+**   <li> The change is made by an SQL trigger or foreign key action 
+**        instead of directly as a result of a users SQL statement.
+** </ul>
+**
+** If a single row is affected by more than one operation within a session,
+** then the change is considered indirect if all operations meet the criteria
+** for an indirect change above, or direct otherwise.
+**
+** This function is used to set, clear or query the session object indirect
+** flag.  If the second argument passed to this function is zero, then the
+** indirect flag is cleared. If it is greater than zero, the indirect flag
+** is set. Passing a value less than zero does not modify the current value
+** of the indirect flag, and may be used to query the current state of the 
+** indirect flag for the specified session object.
+**
+** The return value indicates the final state of the indirect flag: 0 if 
+** it is clear, or 1 if it is set.
+*/
+int odbqlsession_indirect(odbql_session *pSession, int bIndirect);
+
+/*
+** CAPI3REF: Attach A Table To A Session Object
+**
+** If argument zTab is not NULL, then it is the name of a table to attach
+** to the session object passed as the first argument. All subsequent changes 
+** made to the table while the session object is enabled will be recorded. See 
+** documentation for [odbqlsession_changeset()] for further details.
+**
+** Or, if argument zTab is NULL, then changes are recorded for all tables
+** in the database. If additional tables are added to the database (by 
+** executing "CREATE TABLE" statements) after this call is made, changes for 
+** the new tables are also recorded.
+**
+** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
+** defined as part of their CREATE TABLE statement. It does not matter if the 
+** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
+** KEY may consist of a single column, or may be a composite key.
+** 
+** It is not an error if the named table does not exist in the database. Nor
+** is it an error if the named table does not have a PRIMARY KEY. However,
+** no changes will be recorded in either of these scenarios.
+**
+** Changes are not recorded for individual rows that have NULL values stored
+** in one or more of their PRIMARY KEY columns.
+**
+** ODBQL_OK is returned if the call completes without error. Or, if an error 
+** occurs, an SQLite error code (e.g. ODBQL_NOMEM) is returned.
+*/
+int odbqlsession_attach(
+  odbql_session *pSession,      /* Session object */
+  const char *zTab                /* Table name */
+);
+
+/*
+** CAPI3REF: Set a table filter on a Session Object.
+**
+** The second argument (xFilter) is the "filter callback". For changes to rows 
+** in tables that are not attached to the Session oject, the filter is called
+** to determine whether changes to the table's rows should be tracked or not. 
+** If xFilter returns 0, changes is not tracked. Note that once a table is 
+** attached, xFilter will not be called again.
+*/
+void odbqlsession_table_filter(
+  odbql_session *pSession,      /* Session object */
+  int(*xFilter)(
+    void *pCtx,                   /* Copy of third arg to _filter_table() */
+    const char *zTab              /* Table name */
+  ),
+  void *pCtx                      /* First argument passed to xFilter */
+);
+
+/*
+** CAPI3REF: Generate A Changeset From A Session Object
+**
+** Obtain a changeset containing changes to the tables attached to the 
+** session object passed as the first argument. If successful, 
+** set *ppChangeset to point to a buffer containing the changeset 
+** and *pnChangeset to the size of the changeset in bytes before returning
+** ODBQL_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
+** zero and return an SQLite error code.
+**
+** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes,
+** each representing a change to a single row of an attached table. An INSERT
+** change contains the values of each field of a new database row. A DELETE
+** contains the original values of each field of a deleted database row. An
+** UPDATE change contains the original values of each field of an updated
+** database row along with the updated values for each updated non-primary-key
+** column. It is not possible for an UPDATE change to represent a change that
+** modifies the values of primary key columns. If such a change is made, it
+** is represented in a changeset as a DELETE followed by an INSERT.
+**
+** Changes are not recorded for rows that have NULL values stored in one or 
+** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
+** no corresponding change is present in the changesets returned by this
+** function. If an existing row with one or more NULL values stored in
+** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL,
+** only an INSERT is appears in the changeset. Similarly, if an existing row
+** with non-NULL PRIMARY KEY values is updated so that one or more of its
+** PRIMARY KEY columns are set to NULL, the resulting changeset contains a
+** DELETE change only.
+**
+** The contents of a changeset may be traversed using an iterator created
+** using the [odbqlchangeset_start()] API. A changeset may be applied to
+** a database with a compatible schema using the [odbqlchangeset_apply()]
+** API.
+**
+** Within a changeset generated by this function, all changes related to a
+** single table are grouped together. In other words, when iterating through
+** a changeset or when applying a changeset to a database, all changes related
+** to a single table are processed before moving on to the next table. Tables
+** are sorted in the same order in which they were attached (or auto-attached)
+** to the odbql_session object. The order in which the changes related to
+** a single table are stored is undefined.
+**
+** Following a successful call to this function, it is the responsibility of
+** the caller to eventually free the buffer that *ppChangeset points to using
+** [odbql_free()].
+**
+** <h3>Changeset Generation</h3>
+**
+** Once a table has been attached to a session object, the session object
+** records the primary key values of all new rows inserted into the table.
+** It also records the original primary key and other column values of any
+** deleted or updated rows. For each unique primary key value, data is only
+** recorded once - the first time a row with said primary key is inserted,
+** updated or deleted in the lifetime of the session.
+**
+** There is one exception to the previous paragraph: when a row is inserted,
+** updated or deleted, if one or more of its primary key columns contain a
+** NULL value, no record of the change is made.
+**
+** The session object therefore accumulates two types of records - those
+** that consist of primary key values only (created when the user inserts
+** a new record) and those that consist of the primary key values and the
+** original values of other table columns (created when the users deletes
+** or updates a record).
+**
+** When this function is called, the requested changeset is created using
+** both the accumulated records and the current contents of the database
+** file. Specifically:
+**
+** <ul>
+**   <li> For each record generated by an insert, the database is queried
+**        for a row with a matching primary key. If one is found, an INSERT
+**        change is added to the changeset. If no such row is found, no change 
+**        is added to the changeset.
+**
+**   <li> For each record generated by an update or delete, the database is 
+**        queried for a row with a matching primary key. If such a row is
+**        found and one or more of the non-primary key fields have been
+**        modified from their original values, an UPDATE change is added to 
+**        the changeset. Or, if no such row is found in the table, a DELETE 
+**        change is added to the changeset. If there is a row with a matching
+**        primary key in the database, but all fields contain their original
+**        values, no change is added to the changeset.
+** </ul>
+**
+** This means, amongst other things, that if a row is inserted and then later
+** deleted while a session object is active, neither the insert nor the delete
+** will be present in the changeset. Or if a row is deleted and then later a 
+** row with the same primary key values inserted while a session object is
+** active, the resulting changeset will contain an UPDATE change instead of
+** a DELETE and an INSERT.
+**
+** When a session object is disabled (see the [odbqlsession_enable()] API),
+** it does not accumulate records when rows are inserted, updated or deleted.
+** This may appear to have some counter-intuitive effects if a single row
+** is written to more than once during a session. For example, if a row
+** is inserted while a session object is enabled, then later deleted while 
+** the same session object is disabled, no INSERT record will appear in the
+** changeset, even though the delete took place while the session was disabled.
+** Or, if one field of a row is updated while a session is disabled, and 
+** another field of the same row is updated while the session is enabled, the
+** resulting changeset will contain an UPDATE change that updates both fields.
+*/
+int odbqlsession_changeset(
+  odbql_session *pSession,      /* Session object */
+  int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
+  void **ppChangeset              /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Load The Difference Between Tables Into A Session 
+**
+** If it is not already attached to the session object passed as the first
+** argument, this function attaches table zTbl in the same manner as the
+** [odbqlsession_attach()] function. If zTbl does not exist, or if it
+** does not have a primary key, this function is a no-op (but does not return
+** an error).
+**
+** Argument zFromDb must be the name of a database ("main", "temp" etc.)
+** attached to the same database handle as the session object that contains 
+** a table compatible with the table attached to the session by this function.
+** A table is considered compatible if it:
+**
+** <ul>
+**   <li> Has the same name,
+**   <li> Has the same set of columns declared in the same order, and
+**   <li> Has the same PRIMARY KEY definition.
+** </ul>
+**
+** If the tables are not compatible, ODBQL_SCHEMA is returned. If the tables
+** are compatible but do not have any PRIMARY KEY columns, it is not an error
+** but no changes are added to the session object. As with other session
+** APIs, tables without PRIMARY KEYs are simply ignored.
+**
+** This function adds a set of changes to the session object that could be
+** used to update the table in database zFrom (call this the "from-table") 
+** so that its content is the same as the table attached to the session 
+** object (call this the "to-table"). Specifically:
+**
+** <ul>
+**   <li> For each row (primary key) that exists in the to-table but not in 
+**     the from-table, an INSERT record is added to the session object.
+**
+**   <li> For each row (primary key) that exists in the to-table but not in 
+**     the from-table, a DELETE record is added to the session object.
+**
+**   <li> For each row (primary key) that exists in both tables, but features 
+**     different in each, an UPDATE record is added to the session.
+** </ul>
+**
+** To clarify, if this function is called and then a changeset constructed
+** using [odbqlsession_changeset()], then after applying that changeset to 
+** database zFrom the contents of the two compatible tables would be 
+** identical.
+**
+** It an error if database zFrom does not exist or does not contain the
+** required compatible table.
+**
+** If the operation successful, ODBQL_OK is returned. Otherwise, an SQLite
+** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
+** may be set to point to a buffer containing an English language error 
+** message. It is the responsibility of the caller to free this buffer using
+** odbql_free().
+*/
+int odbqlsession_diff(
+  odbql_session *pSession,
+  const char *zFromDb,
+  const char *zTbl,
+  char **pzErrMsg
+);
+
+
+/*
+** CAPI3REF: Generate A Patchset From A Session Object
+**
+** The differences between a patchset and a changeset are that:
+**
+** <ul>
+**   <li> DELETE records consist of the primary key fields only. The 
+**        original values of other fields are omitted.
+**   <li> The original values of any modified fields are omitted from 
+**        UPDATE records.
+** </ul>
+**
+** A patchset blob may be used with up to date versions of all 
+** odbqlchangeset_xxx API functions except for odbqlchangeset_invert(), 
+** which returns ODBQL_CORRUPT if it is passed a patchset. Similarly,
+** attempting to use a patchset blob with old versions of the
+** odbqlchangeset_xxx APIs also provokes an ODBQL_CORRUPT error. 
+**
+** Because the non-primary key "old.*" fields are omitted, no 
+** ODBQL_CHANGESET_DATA conflicts can be detected or reported if a patchset
+** is passed to the odbqlchangeset_apply() API. Other conflict types work
+** in the same way as for changesets.
+**
+** Changes within a patchset are ordered in the same way as for changesets
+** generated by the odbqlsession_changeset() function (i.e. all changes for
+** a single table are grouped together, tables appear in the order in which
+** they were attached to the session object).
+*/
+int odbqlsession_patchset(
+  odbql_session *pSession,      /* Session object */
+  int *pnPatchset,                /* OUT: Size of buffer at *ppChangeset */
+  void **ppPatchset               /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Test if a changeset has recorded any changes.
+**
+** Return non-zero if no changes to attached tables have been recorded by 
+** the session object passed as the first argument. Otherwise, if one or 
+** more changes have been recorded, return zero.
+**
+** Even if this function returns zero, it is possible that calling
+** [odbqlsession_changeset()] on the session handle may still return a
+** changeset that contains no changes. This can happen when a row in 
+** an attached table is modified and then later on the original values 
+** are restored. However, if this function returns non-zero, then it is
+** guaranteed that a call to odbqlsession_changeset() will return a 
+** changeset containing zero changes.
+*/
+int odbqlsession_isempty(odbql_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset 
+**
+** Create an iterator used to iterate through the contents of a changeset.
+** If successful, *pp is set to point to the iterator handle and ODBQL_OK
+** is returned. Otherwise, if an error occurs, *pp is set to zero and an
+** SQLite error code is returned.
+**
+** The following functions can be used to advance and query a changeset 
+** iterator created by this function:
+**
+** <ul>
+**   <li> [odbqlchangeset_next()]
+**   <li> [odbqlchangeset_op()]
+**   <li> [odbqlchangeset_new()]
+**   <li> [odbqlchangeset_old()]
+** </ul>
+**
+** It is the responsibility of the caller to eventually destroy the iterator
+** by passing it to [odbqlchangeset_finalize()]. The buffer containing the
+** changeset (pChangeset) must remain valid until after the iterator is
+** destroyed.
+**
+** Assuming the changeset blob was created by one of the
+** [odbqlsession_changeset()], [odbqlchangeset_concat()] or
+** [odbqlchangeset_invert()] functions, all changes within the changeset 
+** that apply to a single table are grouped together. This means that when 
+** an application iterates through a changeset using an iterator created by 
+** this function, all changes that relate to a single table are visted 
+** consecutively. There is no chance that the iterator will visit a change 
+** the applies to table X, then one for table Y, and then later on visit 
+** another change for table X.
+*/
+int odbqlchangeset_start(
+  odbql_changeset_iter **pp,    /* OUT: New changeset iterator handle */
+  int nChangeset,                 /* Size of changeset blob in bytes */
+  void *pChangeset                /* Pointer to blob containing changeset */
+);
+
+
+/*
+** CAPI3REF: Advance A Changeset Iterator
+**
+** This function may only be used with iterators created by function
+** [odbqlchangeset_start()]. If it is called on an iterator passed to
+** a conflict-handler callback by [odbqlchangeset_apply()], ODBQL_MISUSE
+** is returned and the call has no effect.
+**
+** Immediately after an iterator is created by odbqlchangeset_start(), it
+** does not point to any change in the changeset. Assuming the changeset
+** is not empty, the first call to this function advances the iterator to
+** point to the first change in the changeset. Each subsequent call advances
+** the iterator to point to the next change in the changeset (if any). If
+** no error occurs and the iterator points to a valid change after a call
+** to odbqlchangeset_next() has advanced it, ODBQL_ROW is returned. 
+** Otherwise, if all changes in the changeset have already been visited,
+** ODBQL_DONE is returned.
+**
+** If an error occurs, an SQLite error code is returned. Possible error 
+** codes include ODBQL_CORRUPT (if the changeset buffer is corrupt) or 
+** ODBQL_NOMEM.
+*/
+int odbqlchangeset_next(odbql_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [odbqlchangeset_apply()], or an iterator
+** created by [odbqlchangeset_start()]. In the latter case, the most recent
+** call to [odbqlchangeset_next()] must have returned [ODBQL_ROW]. If this
+** is not the case, this function returns [ODBQL_MISUSE].
+**
+** If argument pzTab is not NULL, then *pzTab is set to point to a
+** nul-terminated utf-8 encoded string containing the name of the table
+** affected by the current change. The buffer remains valid until either
+** odbqlchangeset_next() is called on the iterator or until the 
+** conflict-handler function returns. If pnCol is not NULL, then *pnCol is 
+** set to the number of columns in the table affected by the change. If
+** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** is an indirect change, or false (0) otherwise. See the documentation for
+** [odbqlsession_indirect()] for a description of direct and indirect
+** changes. Finally, if pOp is not NULL, then *pOp is set to one of 
+** [ODBQL_INSERT], [ODBQL_DELETE] or [ODBQL_UPDATE], depending on the 
+** type of change that the iterator currently points to.
+**
+** If no error occurs, ODBQL_OK is returned. If an error does occur, an
+** SQLite error code is returned. The values of the output variables may not
+** be trusted in this case.
+*/
+int odbqlchangeset_op(
+  odbql_changeset_iter *pIter,  /* Iterator object */
+  const char **pzTab,             /* OUT: Pointer to table name */
+  int *pnCol,                     /* OUT: Number of columns in table */
+  int *pOp,                       /* OUT: ODBQL_INSERT, DELETE or UPDATE */
+  int *pbIndirect                 /* OUT: True for an 'indirect' change */
+);
+
+/*
+** CAPI3REF: Obtain The Primary Key Definition Of A Table
+**
+** For each modified table, a changeset includes the following:
+**
+** <ul>
+**   <li> The number of columns in the table, and
+**   <li> Which of those columns make up the tables PRIMARY KEY.
+** </ul>
+**
+** This function is used to find which columns comprise the PRIMARY KEY of
+** the table modified by the change that iterator pIter currently points to.
+** If successful, *pabPK is set to point to an array of nCol entries, where
+** nCol is the number of columns in the table. Elements of *pabPK are set to
+** 0x01 if the corresponding column is part of the tables primary key, or
+** 0x00 if it is not.
+**
+** If argumet pnCol is not NULL, then *pnCol is set to the number of columns
+** in the table.
+**
+** If this function is called when the iterator does not point to a valid
+** entry, ODBQL_MISUSE is returned and the output variables zeroed. Otherwise,
+** ODBQL_OK is returned and the output variables populated as described
+** above.
+*/
+int odbqlchangeset_pk(
+  odbql_changeset_iter *pIter,  /* Iterator object */
+  unsigned char **pabPK,          /* OUT: Array of boolean - true for PK cols */
+  int *pnCol                      /* OUT: Number of entries in output array */
+);
+
+/*
+** CAPI3REF: Obtain old.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [odbqlchangeset_apply()], or an iterator
+** created by [odbqlchangeset_start()]. In the latter case, the most recent
+** call to [odbqlchangeset_next()] must have returned ODBQL_ROW. 
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [ODBQL_DELETE] or [ODBQL_UPDATE]. Otherwise,
+** this function returns [ODBQL_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [ODBQL_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** odbql_value object containing the iVal'th value from the vector of 
+** original row values stored as part of the UPDATE or DELETE change and
+** returns ODBQL_OK. The name of the function comes from the fact that this 
+** is similar to the "old.*" columns available to update or delete triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int odbqlchangeset_old(
+  odbql_changeset_iter *pIter,  /* Changeset iterator */
+  int iVal,                       /* Column number */
+  odbql_value **ppValue         /* OUT: Old value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain new.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [odbqlchangeset_apply()], or an iterator
+** created by [odbqlchangeset_start()]. In the latter case, the most recent
+** call to [odbqlchangeset_next()] must have returned ODBQL_ROW. 
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [ODBQL_UPDATE] or [ODBQL_INSERT]. Otherwise,
+** this function returns [ODBQL_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [ODBQL_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** odbql_value object containing the iVal'th value from the vector of 
+** new row values stored as part of the UPDATE or INSERT change and
+** returns ODBQL_OK. If the change is an UPDATE and does not include
+** a new value for the requested column, *ppValue is set to NULL and 
+** ODBQL_OK returned. The name of the function comes from the fact that 
+** this is similar to the "new.*" columns available to update or delete 
+** triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int odbqlchangeset_new(
+  odbql_changeset_iter *pIter,  /* Changeset iterator */
+  int iVal,                       /* Column number */
+  odbql_value **ppValue         /* OUT: New value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
+**
+** This function should only be used with iterator objects passed to a
+** conflict-handler callback by [odbqlchangeset_apply()] with either
+** [ODBQL_CHANGESET_DATA] or [ODBQL_CHANGESET_CONFLICT]. If this function
+** is called on any other iterator, [ODBQL_MISUSE] is returned and *ppValue
+** is set to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [ODBQL_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** odbql_value object containing the iVal'th value from the 
+** "conflicting row" associated with the current conflict-handler callback
+** and returns ODBQL_OK.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int odbqlchangeset_conflict(
+  odbql_changeset_iter *pIter,  /* Changeset iterator */
+  int iVal,                       /* Column number */
+  odbql_value **ppValue         /* OUT: Value from conflicting row */
+);
+
+/*
+** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
+**
+** This function may only be called with an iterator passed to an
+** ODBQL_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns ODBQL_OK.
+**
+** In all other cases this function returns ODBQL_MISUSE.
+*/
+int odbqlchangeset_fk_conflicts(
+  odbql_changeset_iter *pIter,  /* Changeset iterator */
+  int *pnOut                      /* OUT: Number of FK violations */
+);
+
+
+/*
+** CAPI3REF: Finalize A Changeset Iterator
+**
+** This function is used to finalize an iterator allocated with
+** [odbqlchangeset_start()].
+**
+** This function should only be called on iterators created using the
+** [odbqlchangeset_start()] function. If an application calls this
+** function with an iterator passed to a conflict-handler by
+** [odbqlchangeset_apply()], [ODBQL_MISUSE] is immediately returned and the
+** call has no effect.
+**
+** If an error was encountered within a call to an odbqlchangeset_xxx()
+** function (for example an [ODBQL_CORRUPT] in [odbqlchangeset_next()] or an 
+** [ODBQL_NOMEM] in [odbqlchangeset_new()]) then an error code corresponding
+** to that error is returned by this function. Otherwise, ODBQL_OK is
+** returned. This is to allow the following pattern (pseudo-code):
+**
+**   odbqlchangeset_start();
+**   while( ODBQL_ROW==odbqlchangeset_next() ){
+**     // Do something with change.
+**   }
+**   rc = odbqlchangeset_finalize();
+**   if( rc!=ODBQL_OK ){
+**     // An error has occurred 
+**   }
+*/
+int odbqlchangeset_finalize(odbql_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Invert A Changeset
+**
+** This function is used to "invert" a changeset object. Applying an inverted
+** changeset to a database reverses the effects of applying the uninverted
+** changeset. Specifically:
+**
+** <ul>
+**   <li> Each DELETE change is changed to an INSERT, and
+**   <li> Each INSERT change is changed to a DELETE, and
+**   <li> For each UPDATE change, the old.* and new.* values are exchanged.
+** </ul>
+**
+** This function does not change the order in which changes appear within
+** the changeset. It merely reverses the sense of each individual change.
+**
+** If successful, a pointer to a buffer containing the inverted changeset
+** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and
+** ODBQL_OK is returned. If an error occurs, both *pnOut and *ppOut are
+** zeroed and an SQLite error code returned.
+**
+** It is the responsibility of the caller to eventually call odbql_free()
+** on the *ppOut pointer to free the buffer allocation following a successful 
+** call to this function.
+**
+** WARNING/TODO: This function currently assumes that the input is a valid
+** changeset. If it is not, the results are undefined.
+*/
+int odbqlchangeset_invert(
+  int nIn, const void *pIn,       /* Input changeset */
+  int *pnOut, void **ppOut        /* OUT: Inverse of input */
+);
+
+/*
+** CAPI3REF: Concatenate Two Changeset Objects
+**
+** This function is used to concatenate two changesets, A and B, into a 
+** single changeset. The result is a changeset equivalent to applying
+** changeset A followed by changeset B. 
+**
+** This function combines the two input changesets using an 
+** odbql_changegroup object. Calling it produces similar results as the
+** following code fragment:
+**
+**   odbql_changegroup *pGrp;
+**   rc = odbql_changegroup_new(&pGrp);
+**   if( rc==ODBQL_OK ) rc = odbqlchangegroup_add(pGrp, nA, pA);
+**   if( rc==ODBQL_OK ) rc = odbqlchangegroup_add(pGrp, nB, pB);
+**   if( rc==ODBQL_OK ){
+**     rc = odbqlchangegroup_output(pGrp, pnOut, ppOut);
+**   }else{
+**     *ppOut = 0;
+**     *pnOut = 0;
+**   }
+**
+** Refer to the odbql_changegroup documentation below for details.
+*/
+int odbqlchangeset_concat(
+  int nA,                         /* Number of bytes in buffer pA */
+  void *pA,                       /* Pointer to buffer containing changeset A */
+  int nB,                         /* Number of bytes in buffer pB */
+  void *pB,                       /* Pointer to buffer containing changeset B */
+  int *pnOut,                     /* OUT: Number of bytes in output changeset */
+  void **ppOut                    /* OUT: Buffer containing output changeset */
+);
+
+
+/*
+** Changegroup handle.
+*/
+typedef struct odbql_changegroup odbql_changegroup;
+
+/*
+** CAPI3REF: Combine two or more changesets into a single changeset.
+**
+** An odbql_changegroup object is used to combine two or more changesets
+** (or patchsets) into a single changeset (or patchset). A single changegroup
+** object may combine changesets or patchsets, but not both. The output is
+** always in the same format as the input.
+**
+** If successful, this function returns ODBQL_OK and populates (*pp) with
+** a pointer to a new odbql_changegroup object before returning. The caller
+** should eventually free the returned object using a call to 
+** odbqlchangegroup_delete(). If an error occurs, an SQLite error code
+** (i.e. ODBQL_NOMEM) is returned and *pp is set to NULL.
+**
+** The usual usage pattern for an odbql_changegroup object is as follows:
+**
+** <ul>
+**   <li> It is created using a call to odbqlchangegroup_new().
+**
+**   <li> Zero or more changesets (or patchsets) are added to the object
+**        by calling odbqlchangegroup_add().
+**
+**   <li> The result of combining all input changesets together is obtained 
+**        by the application via a call to odbqlchangegroup_output().
+**
+**   <li> The object is deleted using a call to odbqlchangegroup_delete().
+** </ul>
+**
+** Any number of calls to add() and output() may be made between the calls to
+** new() and delete(), and in any order.
+**
+** As well as the regular odbqlchangegroup_add() and 
+** odbqlchangegroup_output() functions, also available are the streaming
+** versions odbqlchangegroup_add_strm() and odbqlchangegroup_output_strm().
+*/
+int odbqlchangegroup_new(odbql_changegroup **pp);
+
+/*
+** Add all changes within the changeset (or patchset) in buffer pData (size
+** nData bytes) to the changegroup. 
+**
+** If the buffer contains a patchset, then all prior calls to this function
+** on the same changegroup object must also have specified patchsets. Or, if
+** the buffer contains a changeset, so must have the earlier calls to this
+** function. Otherwise, ODBQL_ERROR is returned and no changes are added
+** to the changegroup.
+**
+** Rows within the changeset and changegroup are identified by the values in
+** their PRIMARY KEY columns. A change in the changeset is considered to
+** apply to the same row as a change already present in the changegroup if
+** the two rows have the same primary key.
+**
+** Changes to rows that that do not already appear in the changegroup are
+** simply copied into it. Or, if both the new changeset and the changegroup
+** contain changes that apply to a single row, the final contents of the
+** changegroup depends on the type of each change, as follows:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+**   <tr><th style="white-space:pre">Existing Change  </th>
+**       <th style="white-space:pre">New Change       </th>
+**       <th>Output Change
+**   <tr><td>INSERT <td>INSERT <td>
+**       The new change is ignored. This case does not occur if the new
+**       changeset was recorded immediately after the changesets already
+**       added to the changegroup.
+**   <tr><td>INSERT <td>UPDATE <td>
+**       The INSERT change remains in the changegroup. The values in the 
+**       INSERT change are modified as if the row was inserted by the
+**       existing change and then updated according to the new change.
+**   <tr><td>INSERT <td>DELETE <td>
+**       The existing INSERT is removed from the changegroup. The DELETE is
+**       not added.
+**   <tr><td>UPDATE <td>INSERT <td>
+**       The new change is ignored. This case does not occur if the new
+**       changeset was recorded immediately after the changesets already
+**       added to the changegroup.
+**   <tr><td>UPDATE <td>UPDATE <td>
+**       The existing UPDATE remains within the changegroup. It is amended 
+**       so that the accompanying values are as if the row was updated once 
+**       by the existing change and then again by the new change.
+**   <tr><td>UPDATE <td>DELETE <td>
+**       The existing UPDATE is replaced by the new DELETE within the
+**       changegroup.
+**   <tr><td>DELETE <td>INSERT <td>
+**       If one or more of the column values in the row inserted by the
+**       new change differ from those in the row deleted by the existing 
+**       change, the existing DELETE is replaced by an UPDATE within the
+**       changegroup. Otherwise, if the inserted row is exactly the same 
+**       as the deleted row, the existing DELETE is simply discarded.
+**   <tr><td>DELETE <td>UPDATE <td>
+**       The new change is ignored. This case does not occur if the new
+**       changeset was recorded immediately after the changesets already
+**       added to the changegroup.
+**   <tr><td>DELETE <td>DELETE <td>
+**       The new change is ignored. This case does not occur if the new
+**       changeset was recorded immediately after the changesets already
+**       added to the changegroup.
+** </table>
+**
+** If the new changeset contains changes to a table that is already present
+** in the changegroup, then the number of columns and the position of the
+** primary key columns for the table must be consistent. If this is not the
+** case, this function fails with ODBQL_SCHEMA. If the input changeset
+** appears to be corrupt and the corruption is detected, ODBQL_CORRUPT is
+** returned. Or, if an out-of-memory condition occurs during processing, this
+** function returns ODBQL_NOMEM. In all cases, if an error occurs the
+** final contents of the changegroup is undefined.
+**
+** If no error occurs, ODBQL_OK is returned.
+*/
+int odbqlchangegroup_add(odbql_changegroup*, int nData, void *pData);
+
+/*
+** Obtain a buffer containing a changeset (or patchset) representing the
+** current contents of the changegroup. If the inputs to the changegroup
+** were themselves changesets, the output is a changeset. Or, if the
+** inputs were patchsets, the output is also a patchset.
+**
+** As with the output of the odbqlsession_changeset() and
+** odbqlsession_patchset() functions, all changes related to a single
+** table are grouped together in the output of this function. Tables appear
+** in the same order as for the very first changeset added to the changegroup.
+** If the second or subsequent changesets added to the changegroup contain
+** changes for tables that do not appear in the first changeset, they are
+** appended onto the end of the output changeset, again in the order in
+** which they are first encountered.
+**
+** If an error occurs, an SQLite error code is returned and the output
+** variables (*pnData) and (*ppData) are set to 0. Otherwise, ODBQL_OK
+** is returned and the output variables are set to the size of and a 
+** pointer to the output buffer, respectively. In this case it is the
+** responsibility of the caller to eventually free the buffer using a
+** call to odbql_free().
+*/
+int odbqlchangegroup_output(
+  odbql_changegroup*,
+  int *pnData,                    /* OUT: Size of output buffer in bytes */
+  void **ppData                   /* OUT: Pointer to output buffer */
+);
+
+/*
+** Delete a changegroup object.
+*/
+void odbqlchangegroup_delete(odbql_changegroup*);
+
+/*
+** CAPI3REF: Apply A Changeset To A Database
+**
+** Apply a changeset to a database. This function attempts to update the
+** "main" database attached to handle db with the changes found in the
+** changeset passed via the second and third arguments.
+**
+** The fourth argument (xFilter) passed to this function is the "filter
+** callback". If it is not NULL, then for each table affected by at least one
+** change in the changeset, the filter callback is invoked with
+** the table name as the second argument, and a copy of the context pointer
+** passed as the sixth argument to this function as the first. If the "filter
+** callback" returns zero, then no attempt is made to apply any changes to 
+** the table. Otherwise, if the return value is non-zero or the xFilter
+** argument to this function is NULL, all changes related to the table are
+** attempted.
+**
+** For each table that is not excluded by the filter callback, this function 
+** tests that the target database contains a compatible table. A table is 
+** considered compatible if all of the following are true:
+**
+** <ul>
+**   <li> The table has the same name as the name recorded in the 
+**        changeset, and
+**   <li> The table has the same number of columns as recorded in the 
+**        changeset, and
+**   <li> The table has primary key columns in the same position as 
+**        recorded in the changeset.
+** </ul>
+**
+** If there is no compatible table, it is not an error, but none of the
+** changes associated with the table are applied. A warning message is issued
+** via the odbql_log() mechanism with the error code ODBQL_SCHEMA. At most
+** one such warning is issued for each table in the changeset.
+**
+** For each change for which there is a compatible table, an attempt is made 
+** to modify the table contents according to the UPDATE, INSERT or DELETE 
+** change. If a change cannot be applied cleanly, the conflict handler 
+** function passed as the fifth argument to odbqlchangeset_apply() may be 
+** invoked. A description of exactly when the conflict handler is invoked for 
+** each type of change is below.
+**
+** Unlike the xFilter argument, xConflict may not be passed NULL. The results
+** of passing anything other than a valid function pointer as the xConflict
+** argument are undefined.
+**
+** Each time the conflict handler function is invoked, it must return one
+** of [ODBQL_CHANGESET_OMIT], [ODBQL_CHANGESET_ABORT] or 
+** [ODBQL_CHANGESET_REPLACE]. ODBQL_CHANGESET_REPLACE may only be returned
+** if the second argument passed to the conflict handler is either
+** ODBQL_CHANGESET_DATA or ODBQL_CHANGESET_CONFLICT. If the conflict-handler
+** returns an illegal value, any changes already made are rolled back and
+** the call to odbqlchangeset_apply() returns ODBQL_MISUSE. Different 
+** actions are taken by odbqlchangeset_apply() depending on the value
+** returned by each invocation of the conflict-handler function. Refer to
+** the documentation for the three 
+** [ODBQL_CHANGESET_OMIT|available return values] for details.
+**
+** <dl>
+** <dt>DELETE Changes<dd>
+**   For each DELETE change, this function checks if the target database 
+**   contains a row with the same primary key value (or values) as the 
+**   original row values stored in the changeset. If it does, and the values 
+**   stored in all non-primary key columns also match the values stored in 
+**   the changeset the row is deleted from the target database.
+**
+**   If a row with matching primary key values is found, but one or more of
+**   the non-primary key fields contains a value different from the original
+**   row value stored in the changeset, the conflict-handler function is
+**   invoked with [ODBQL_CHANGESET_DATA] as the second argument.
+**
+**   If no row with matching primary key values is found in the database,
+**   the conflict-handler function is invoked with [ODBQL_CHANGESET_NOTFOUND]
+**   passed as the second argument.
+**
+**   If the DELETE operation is attempted, but SQLite returns ODBQL_CONSTRAINT
+**   (which can only happen if a foreign key constraint is violated), the
+**   conflict-handler function is invoked with [ODBQL_CHANGESET_CONSTRAINT]
+**   passed as the second argument. This includes the case where the DELETE
+**   operation is attempted because an earlier call to the conflict handler
+**   function returned [ODBQL_CHANGESET_REPLACE].
+**
+** <dt>INSERT Changes<dd>
+**   For each INSERT change, an attempt is made to insert the new row into
+**   the database.
+**
+**   If the attempt to insert the row fails because the database already 
+**   contains a row with the same primary key values, the conflict handler
+**   function is invoked with the second argument set to 
+**   [ODBQL_CHANGESET_CONFLICT].
+**
+**   If the attempt to insert the row fails because of some other constraint
+**   violation (e.g. NOT NULL or UNIQUE), the conflict handler function is 
+**   invoked with the second argument set to [ODBQL_CHANGESET_CONSTRAINT].
+**   This includes the case where the INSERT operation is re-attempted because 
+**   an earlier call to the conflict handler function returned 
+**   [ODBQL_CHANGESET_REPLACE].
+**
+** <dt>UPDATE Changes<dd>
+**   For each UPDATE change, this function checks if the target database 
+**   contains a row with the same primary key value (or values) as the 
+**   original row values stored in the changeset. If it does, and the values 
+**   stored in all non-primary key columns also match the values stored in 
+**   the changeset the row is updated within the target database.
+**
+**   If a row with matching primary key values is found, but one or more of
+**   the non-primary key fields contains a value different from an original
+**   row value stored in the changeset, the conflict-handler function is
+**   invoked with [ODBQL_CHANGESET_DATA] as the second argument. Since
+**   UPDATE changes only contain values for non-primary key fields that are
+**   to be modified, only those fields need to match the original values to
+**   avoid the ODBQL_CHANGESET_DATA conflict-handler callback.
+**
+**   If no row with matching primary key values is found in the database,
+**   the conflict-handler function is invoked with [ODBQL_CHANGESET_NOTFOUND]
+**   passed as the second argument.
+**
+**   If the UPDATE operation is attempted, but SQLite returns 
+**   ODBQL_CONSTRAINT, the conflict-handler function is invoked with 
+**   [ODBQL_CHANGESET_CONSTRAINT] passed as the second argument.
+**   This includes the case where the UPDATE operation is attempted after 
+**   an earlier call to the conflict handler function returned
+**   [ODBQL_CHANGESET_REPLACE].  
+** </dl>
+**
+** It is safe to execute SQL statements, including those that write to the
+** table that the callback related to, from within the xConflict callback.
+** This can be used to further customize the applications conflict
+** resolution strategy.
+**
+** All changes made by this function are enclosed in a savepoint transaction.
+** If any other error (aside from a constraint failure when attempting to
+** write to the target database) occurs, then the savepoint transaction is
+** rolled back, restoring the target database to its original state, and an 
+** SQLite error code returned.
+*/
+int odbqlchangeset_apply(
+  odbql *db,                    /* Apply change to "main" db of this handle */
+  int nChangeset,                 /* Size of changeset in bytes */
+  void *pChangeset,               /* Changeset blob */
+  int(*xFilter)(
+    void *pCtx,                   /* Copy of sixth arg to _apply() */
+    const char *zTab              /* Table name */
+  ),
+  int(*xConflict)(
+    void *pCtx,                   /* Copy of sixth arg to _apply() */
+    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
+    odbql_changeset_iter *p     /* Handle describing change and conflict */
+  ),
+  void *pCtx                      /* First argument passed to xConflict */
+);
+
+/* 
+** CAPI3REF: Constants Passed To The Conflict Handler
+**
+** Values that may be passed as the second argument to a conflict-handler.
+**
+** <dl>
+** <dt>ODBQL_CHANGESET_DATA<dd>
+**   The conflict handler is invoked with CHANGESET_DATA as the second argument
+**   when processing a DELETE or UPDATE change if a row with the required
+**   PRIMARY KEY fields is present in the database, but one or more other 
+**   (non primary-key) fields modified by the update do not contain the 
+**   expected "before" values.
+** 
+**   The conflicting row, in this case, is the database row with the matching
+**   primary key.
+** 
+** <dt>ODBQL_CHANGESET_NOTFOUND<dd>
+**   The conflict handler is invoked with CHANGESET_NOTFOUND as the second
+**   argument when processing a DELETE or UPDATE change if a row with the
+**   required PRIMARY KEY fields is not present in the database.
+** 
+**   There is no conflicting row in this case. The results of invoking the
+**   odbqlchangeset_conflict() API are undefined.
+** 
+** <dt>ODBQL_CHANGESET_CONFLICT<dd>
+**   CHANGESET_CONFLICT is passed as the second argument to the conflict
+**   handler while processing an INSERT change if the operation would result 
+**   in duplicate primary key values.
+** 
+**   The conflicting row in this case is the database row with the matching
+**   primary key.
+**
+** <dt>ODBQL_CHANGESET_FOREIGN_KEY<dd>
+**   If foreign key handling is enabled, and applying a changeset leaves the
+**   database in a state containing foreign key violations, the conflict 
+**   handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
+**   exactly once before the changeset is committed. If the conflict handler
+**   returns CHANGESET_OMIT, the changes, including those that caused the
+**   foreign key constraint violation, are committed. Or, if it returns
+**   CHANGESET_ABORT, the changeset is rolled back.
+**
+**   No current or conflicting row information is provided. The only function
+**   it is possible to call on the supplied odbql_changeset_iter handle
+**   is odbqlchangeset_fk_conflicts().
+** 
+** <dt>ODBQL_CHANGESET_CONSTRAINT<dd>
+**   If any other constraint violation occurs while applying a change (i.e. 
+**   a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is 
+**   invoked with CHANGESET_CONSTRAINT as the second argument.
+** 
+**   There is no conflicting row in this case. The results of invoking the
+**   odbqlchangeset_conflict() API are undefined.
+**
+** </dl>
+*/
+#define ODBQL_CHANGESET_DATA        1
+#define ODBQL_CHANGESET_NOTFOUND    2
+#define ODBQL_CHANGESET_CONFLICT    3
+#define ODBQL_CHANGESET_CONSTRAINT  4
+#define ODBQL_CHANGESET_FOREIGN_KEY 5
+
+/* 
+** CAPI3REF: Constants Returned By The Conflict Handler
+**
+** A conflict handler callback must return one of the following three values.
+**
+** <dl>
+** <dt>ODBQL_CHANGESET_OMIT<dd>
+**   If a conflict handler returns this value no special action is taken. The
+**   change that caused the conflict is not applied. The session module 
+**   continues to the next change in the changeset.
+**
+** <dt>ODBQL_CHANGESET_REPLACE<dd>
+**   This value may only be returned if the second argument to the conflict
+**   handler was ODBQL_CHANGESET_DATA or ODBQL_CHANGESET_CONFLICT. If this
+**   is not the case, any changes applied so far are rolled back and the 
+**   call to odbqlchangeset_apply() returns ODBQL_MISUSE.
+**
+**   If CHANGESET_REPLACE is returned by an ODBQL_CHANGESET_DATA conflict
+**   handler, then the conflicting row is either updated or deleted, depending
+**   on the type of change.
+**
+**   If CHANGESET_REPLACE is returned by an ODBQL_CHANGESET_CONFLICT conflict
+**   handler, then the conflicting row is removed from the database and a
+**   second attempt to apply the change is made. If this second attempt fails,
+**   the original row is restored to the database before continuing.
+**
+** <dt>ODBQL_CHANGESET_ABORT<dd>
+**   If this value is returned, any changes applied so far are rolled back 
+**   and the call to odbqlchangeset_apply() returns ODBQL_ABORT.
+** </dl>
+*/
+#define ODBQL_CHANGESET_OMIT       0
+#define ODBQL_CHANGESET_REPLACE    1
+#define ODBQL_CHANGESET_ABORT      2
+
+/*
+** CAPI3REF: Streaming Versions of API functions.
+**
+** The six streaming API xxx_strm() functions serve similar purposes to the 
+** corresponding non-streaming API functions:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+**   <tr><th>Streaming function<th>Non-streaming equivalent</th>
+**   <tr><td>odbqlchangeset_apply_str<td>[odbqlchangeset_apply] 
+**   <tr><td>odbqlchangeset_concat_str<td>[odbqlchangeset_concat] 
+**   <tr><td>odbqlchangeset_invert_str<td>[odbqlchangeset_invert] 
+**   <tr><td>odbqlchangeset_start_str<td>[odbqlchangeset_start] 
+**   <tr><td>odbqlsession_changeset_str<td>[odbqlsession_changeset] 
+**   <tr><td>odbqlsession_patchset_str<td>[odbqlsession_patchset] 
+** </table>
+**
+** Non-streaming functions that accept changesets (or patchsets) as input
+** require that the entire changeset be stored in a single buffer in memory. 
+** Similarly, those that return a changeset or patchset do so by returning 
+** a pointer to a single large buffer allocated using odbql_malloc(). 
+** Normally this is convenient. However, if an application running in a 
+** low-memory environment is required to handle very large changesets, the
+** large contiguous memory allocations required can become onerous.
+**
+** In order to avoid this problem, instead of a single large buffer, input
+** is passed to a streaming API functions by way of a callback function that
+** the sessions module invokes to incrementally request input data as it is
+** required. In all cases, a pair of API function parameters such as
+**
+**  <pre>
+**        int nChangeset,
+**        void *pChangeset,
+**  </pre>
+**
+** Is replaced by:
+**
+**  <pre>
+**        int (*xInput)(void *pIn, void *pData, int *pnData),
+**        void *pIn,
+**  </pre>
+**
+** Each time the xInput callback is invoked by the sessions module, the first
+** argument passed is a copy of the supplied pIn context pointer. The second 
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no 
+** error occurs the xInput method should copy up to (*pnData) bytes of data 
+** into the buffer and set (*pnData) to the actual number of bytes copied 
+** before returning ODBQL_OK. If the input is completely exhausted, (*pnData) 
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite 
+** error code should be returned. In all cases, if an xInput callback returns
+** an error, all processing is abandoned and the streaming API function
+** returns a copy of the error code to the caller.
+**
+** In the case of odbqlchangeset_start_strm(), the xInput callback may be
+** invoked by the sessions module at any point during the lifetime of the
+** iterator. If such an xInput callback returns an error, the iterator enters
+** an error state, whereby all subsequent calls to iterator functions 
+** immediately fail with the same error code as returned by xInput.
+**
+** Similarly, streaming API functions that return changesets (or patchsets)
+** return them in chunks by way of a callback function instead of via a
+** pointer to a single large buffer. In this case, a pair of parameters such
+** as:
+**
+**  <pre>
+**        int *pnChangeset,
+**        void **ppChangeset,
+**  </pre>
+**
+** Is replaced by:
+**
+**  <pre>
+**        int (*xOutput)(void *pOut, const void *pData, int nData),
+**        void *pOut
+**  </pre>
+**
+** The xOutput callback is invoked zero or more times to return data to
+** the application. The first parameter passed to each call is a copy of the
+** pOut pointer supplied by the application. The second parameter, pData,
+** points to a buffer nData bytes in size containing the chunk of output
+** data being returned. If the xOutput callback successfully processes the
+** supplied data, it should return ODBQL_OK to indicate success. Otherwise,
+** it should return some other SQLite error code. In this case processing
+** is immediately abandoned and the streaming API function returns a copy
+** of the xOutput error code to the application.
+**
+** The sessions module never invokes an xOutput callback with the third 
+** parameter set to a value less than or equal to zero. Other than this,
+** no guarantees are made as to the size of the chunks of data returned.
+*/
+int odbqlchangeset_apply_strm(
+  odbql *db,                    /* Apply change to "main" db of this handle */
+  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+  void *pIn,                                          /* First arg for xInput */
+  int(*xFilter)(
+    void *pCtx,                   /* Copy of sixth arg to _apply() */
+    const char *zTab              /* Table name */
+  ),
+  int(*xConflict)(
+    void *pCtx,                   /* Copy of sixth arg to _apply() */
+    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
+    odbql_changeset_iter *p     /* Handle describing change and conflict */
+  ),
+  void *pCtx                      /* First argument passed to xConflict */
+);
+int odbqlchangeset_concat_strm(
+  int (*xInputA)(void *pIn, void *pData, int *pnData),
+  void *pInA,
+  int (*xInputB)(void *pIn, void *pData, int *pnData),
+  void *pInB,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+);
+int odbqlchangeset_invert_strm(
+  int (*xInput)(void *pIn, void *pData, int *pnData),
+  void *pIn,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+);
+int odbqlchangeset_start_strm(
+  odbql_changeset_iter **pp,
+  int (*xInput)(void *pIn, void *pData, int *pnData),
+  void *pIn
+);
+int odbqlsession_changeset_strm(
+  odbql_session *pSession,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+);
+int odbqlsession_patchset_strm(
+  odbql_session *pSession,
+  int (*xOutput)(void *pOut, const void *pData, int nData),
+  void *pOut
+);
+int odbqlchangegroup_add_strm(odbql_changegroup*, 
+    int (*xInput)(void *pIn, void *pData, int *pnData),
+    void *pIn
+);
+int odbqlchangegroup_output_strm(odbql_changegroup*,
+    int (*xOutput)(void *pOut, const void *pData, int nData), 
+    void *pOut
+);
+
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* !defined(__ODBQLSESSION_H_) && defined(ODBQL_ENABLE_SESSION) */
+
+/******** End of odbqlsession.h *********/
+/******** Begin file fts5.h *********/
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file, 
+** FTS5 may be extended with:
+**
+**     * custom tokenizers, and
+**     * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the odbql_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+  const Fts5ExtensionApi *pApi,   /* API offered by current FTS version */
+  Fts5Context *pFts,              /* First arg to pass to pApi functions */
+  odbql_context *pCtx,          /* Context for returning result/error */
+  int nVal,                       /* Number of values in apVal[] array */
+  odbql_value **apVal           /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+  const unsigned char *a;
+  const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+**   Return a copy of the context pointer the extension function was 
+**   registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+**   If parameter iCol is less than zero, set output variable *pnToken
+**   to the total number of tokens in the FTS5 table. Or, if iCol is
+**   non-negative but less than the number of columns in the table, return
+**   the total number of tokens in column iCol, considering all rows in 
+**   the FTS5 table.
+**
+**   If parameter iCol is greater than or equal to the number of columns
+**   in the table, ODBQL_RANGE is returned. Or, if an error occurs (e.g.
+**   an OOM condition or IO error), an appropriate SQLite error code is 
+**   returned.
+**
+** xColumnCount(pFts):
+**   Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+**   If parameter iCol is less than zero, set output variable *pnToken
+**   to the total number of tokens in the current row. Or, if iCol is
+**   non-negative but less than the number of columns in the table, set
+**   *pnToken to the number of tokens in column iCol of the current row.
+**
+**   If parameter iCol is greater than or equal to the number of columns
+**   in the table, ODBQL_RANGE is returned. Or, if an error occurs (e.g.
+**   an OOM condition or IO error), an appropriate SQLite error code is 
+**   returned.
+**
+**   This function may be quite inefficient if used with an FTS5 table
+**   created with the "columnsize=0" option.
+**
+** xColumnText:
+**   This function attempts to retrieve the text of column iCol of the
+**   current document. If successful, (*pz) is set to point to a buffer
+**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+**   (not characters) of the buffer and ODBQL_OK is returned. Otherwise,
+**   if an error occurs, an SQLite error code is returned and the final values
+**   of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+**   Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+**   Returns the number of tokens in phrase iPhrase of the query. Phrases
+**   are numbered starting from zero.
+**
+** xInstCount:
+**   Set *pnInst to the total number of occurrences of all phrases within
+**   the query within the current row. Return ODBQL_OK if successful, or
+**   an error code (i.e. ODBQL_NOMEM) if an error occurs.
+**
+**   This API can be quite slow if used with an FTS5 table created with the
+**   "detail=none" or "detail=column" option. If the FTS5 table is created 
+**   with either "detail=none" or "detail=column" and "content=" option 
+**   (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+**   Query for the details of phrase match iIdx within the current row.
+**   Phrase matches are numbered starting from zero, so the iIdx argument
+**   should be greater than or equal to zero and smaller than the value
+**   output by xInstCount().
+**
+**   Usually, output parameter *piPhrase is set to the phrase number, *piCol
+**   to the column in which it occurs and *piOff the token offset of the
+**   first token of the phrase. The exception is if the table was created
+**   with the offsets=0 option specified. In this case *piOff is always
+**   set to -1.
+**
+**   Returns ODBQL_OK if successful, or an error code (i.e. ODBQL_NOMEM) 
+**   if an error occurs.
+**
+**   This API can be quite slow if used with an FTS5 table created with the
+**   "detail=none" or "detail=column" option. 
+**
+** xRowid:
+**   Returns the rowid of the current row.
+**
+** xTokenize:
+**   Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+**   This API function is used to query the FTS table for phrase iPhrase
+**   of the current query. Specifically, a query equivalent to:
+**
+**       ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+**   with $p set to a phrase equivalent to the phrase iPhrase of the
+**   current query is executed. Any column filter that applies to
+**   phrase iPhrase of the current query is included in $p. For each 
+**   row visited, the callback function passed as the fourth argument 
+**   is invoked. The context and API objects passed to the callback 
+**   function may be used to access the properties of each matched row.
+**   Invoking Api.xUserData() returns a copy of the pointer passed as 
+**   the third argument to pUserData.
+**
+**   If the callback function returns any value other than ODBQL_OK, the
+**   query is abandoned and the xQueryPhrase function returns immediately.
+**   If the returned value is ODBQL_DONE, xQueryPhrase returns ODBQL_OK.
+**   Otherwise, the error code is propagated upwards.
+**
+**   If the query runs to completion without incident, ODBQL_OK is returned.
+**   Or, if some error occurs before the query completes or is aborted by
+**   the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+**   Save the pointer passed as the second argument as the extension functions 
+**   "auxiliary data". The pointer may then be retrieved by the current or any
+**   future invocation of the same fts5 extension function made as part of
+**   of the same MATCH query using the xGetAuxdata() API.
+**
+**   Each extension function is allocated a single auxiliary data slot for
+**   each FTS query (MATCH expression). If the extension function is invoked 
+**   more than once for a single FTS query, then all invocations share a 
+**   single auxiliary data context.
+**
+**   If there is already an auxiliary data pointer when this function is
+**   invoked, then it is replaced by the new pointer. If an xDelete callback
+**   was specified along with the original pointer, it is invoked at this
+**   point.
+**
+**   The xDelete callback, if one is specified, is also invoked on the
+**   auxiliary data pointer after the FTS5 query has finished.
+**
+**   If an error (e.g. an OOM condition) occurs within this function, an
+**   the auxiliary data is set to NULL and an error code returned. If the
+**   xDelete parameter was not NULL, it is invoked on the auxiliary data
+**   pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+**   Returns the current auxiliary data pointer for the fts5 extension 
+**   function. See the xSetAuxdata() method for details.
+**
+**   If the bClear argument is non-zero, then the auxiliary data is cleared
+**   (set to NULL) before this function returns. In this case the xDelete,
+**   if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+**   This function is used to retrieve the total number of rows in the table.
+**   In other words, the same value that would be returned by:
+**
+**        SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+**   This function is used, along with type Fts5PhraseIter and the xPhraseNext
+**   method, to iterate through all instances of a single query phrase within
+**   the current row. This is the same information as is accessible via the
+**   xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+**   to use, this API may be faster under some circumstances. To iterate 
+**   through instances of phrase iPhrase, use the following code:
+**
+**       Fts5PhraseIter iter;
+**       int iCol, iOff;
+**       for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+**           iCol>=0;
+**           pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+**       ){
+**         // An instance of phrase iPhrase at offset iOff of column iCol
+**       }
+**
+**   The Fts5PhraseIter structure is defined above. Applications should not
+**   modify this structure directly - it should only be used as shown above
+**   with the xPhraseFirst() and xPhraseNext() API methods (and by
+**   xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+**   This API can be quite slow if used with an FTS5 table created with the
+**   "detail=none" or "detail=column" option. If the FTS5 table is created 
+**   with either "detail=none" or "detail=column" and "content=" option 
+**   (i.e. if it is a contentless table), then this API always iterates
+**   through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+**   See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+**   This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+**   and xPhraseNext() APIs described above. The difference is that instead
+**   of iterating through all instances of a phrase in the current row, these
+**   APIs are used to iterate through the set of columns in the current row
+**   that contain one or more instances of a specified phrase. For example:
+**
+**       Fts5PhraseIter iter;
+**       int iCol;
+**       for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+**           iCol>=0;
+**           pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+**       ){
+**         // Column iCol contains at least one instance of phrase iPhrase
+**       }
+**
+**   This API can be quite slow if used with an FTS5 table created with the
+**   "detail=none" option. If the FTS5 table is created with either 
+**   "detail=none" "content=" option (i.e. if it is a contentless table), 
+**   then this API always iterates through an empty set (all calls to 
+**   xPhraseFirstColumn() set iCol to -1).
+**
+**   The information accessed using this API and its companion
+**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+**   (or xInst/xInstCount). The chief advantage of this API is that it is
+**   significantly more efficient than those alternatives when used with
+**   "detail=column" tables.  
+**
+** xPhraseNextColumn()
+**   See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+  int iVersion;                   /* Currently always set to 3 */
+
+  void *(*xUserData)(Fts5Context*);
+
+  int (*xColumnCount)(Fts5Context*);
+  int (*xRowCount)(Fts5Context*, odbql_int64 *pnRow);
+  int (*xColumnTotalSize)(Fts5Context*, int iCol, odbql_int64 *pnToken);
+
+  int (*xTokenize)(Fts5Context*, 
+    const char *pText, int nText, /* Text to tokenize */
+    void *pCtx,                   /* Context passed to xToken() */
+    int (*xToken)(void*, int, const char*, int, int, int)       /* Callback */
+  );
+
+  int (*xPhraseCount)(Fts5Context*);
+  int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+  int (*xInstCount)(Fts5Context*, int *pnInst);
+  int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+  odbql_int64 (*xRowid)(Fts5Context*);
+  int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+  int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+  int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+    int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+  );
+  int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+  void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/* 
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer 
+** is registered by providing fts5 with a populated instance of the 
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+**   This function is used to allocate and inititalize a tokenizer instance.
+**   A tokenizer instance is required to actually tokenize text.
+**
+**   The first argument passed to this function is a copy of the (void*)
+**   pointer provided by the application when the fts5_tokenizer object
+**   was registered with FTS5 (the third argument to xCreateTokenizer()). 
+**   The second and third arguments are an array of nul-terminated strings
+**   containing the tokenizer arguments, if any, specified following the
+**   tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+**   to create the FTS5 table.
+**
+**   The final argument is an output variable. If successful, (*ppOut) 
+**   should be set to point to the new tokenizer handle and ODBQL_OK
+**   returned. If an error occurs, some value other than ODBQL_OK should
+**   be returned. In this case, fts5 assumes that the final value of *ppOut 
+**   is undefined.
+**
+** xDelete:
+**   This function is invoked to delete a tokenizer handle previously
+**   allocated using xCreate(). Fts5 guarantees that this function will
+**   be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+**   This function is expected to tokenize the nText byte string indicated 
+**   by argument pText. pText may or may not be nul-terminated. The first
+**   argument passed to this function is a pointer to an Fts5Tokenizer object
+**   returned by an earlier call to xCreate().
+**
+**   The second argument indicates the reason that FTS5 is requesting
+**   tokenization of the supplied text. This is always one of the following
+**   four values:
+**
+**   <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+**            or removed from the FTS table. The tokenizer is being invoked to
+**            determine the set of tokens to add to (or delete from) the
+**            FTS index.
+**
+**       <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed 
+**            against the FTS index. The tokenizer is being called to tokenize 
+**            a bareword or quoted string specified as part of the query.
+**
+**       <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+**            FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+**            followed by a "*" character, indicating that the last token
+**            returned by the tokenizer will be treated as a token prefix.
+**
+**       <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to 
+**            satisfy an fts5_api.xTokenize() request made by an auxiliary
+**            function. Or an fts5_api.xColumnSize() request made by the same
+**            on a columnsize=0 database.  
+**   </ul>
+**
+**   For each token in the input string, the supplied callback xToken() must
+**   be invoked. The first argument to it should be a copy of the pointer
+**   passed as the second argument to xTokenize(). The third and fourth
+**   arguments are a pointer to a buffer containing the token text, and the
+**   size of the token in bytes. The 4th and 5th arguments are the byte offsets
+**   of the first byte of and first byte immediately following the text from
+**   which the token is derived within the input.
+**
+**   The second argument passed to the xToken() callback ("tflags") should
+**   normally be set to 0. The exception is if the tokenizer supports 
+**   synonyms. In this case see the discussion below for details.
+**
+**   FTS5 assumes the xToken() callback is invoked for each token in the 
+**   order that they occur within the input text.
+**
+**   If an xToken() callback returns any value other than ODBQL_OK, then
+**   the tokenization should be abandoned and the xTokenize() method should
+**   immediately return a copy of the xToken() return value. Or, if the
+**   input buffer is exhausted, xTokenize() should return ODBQL_OK. Finally,
+**   if an error occurs with the xTokenize() implementation itself, it
+**   may abandon the tokenization and return any error code other than
+**   ODBQL_OK or ODBQL_DONE.
+**
+** SYNONYM SUPPORT
+**
+**   Custom tokenizers may also support synonyms. Consider a case in which a
+**   user wishes to query for a phrase such as "first place". Using the 
+**   built-in tokenizers, the FTS5 query 'first + place' will match instances
+**   of "first place" within the document set, but not alternative forms
+**   such as "1st place". In some applications, it would be better to match
+**   all instances of "first place" or "1st place" regardless of which form
+**   the user specified in the MATCH query text.
+**
+**   There are several ways to approach this in FTS5:
+**
+**   <ol><li> By mapping all synonyms to a single token. In this case, the 
+**            In the above example, this means that the tokenizer returns the
+**            same token for inputs "first" and "1st". Say that token is in
+**            fact "first", so that when the user inserts the document "I won
+**            1st place" entries are added to the index for tokens "i", "won",
+**            "first" and "place". If the user then queries for '1st + place',
+**            the tokenizer substitutes "first" for "1st" and the query works
+**            as expected.
+**
+**       <li> By adding multiple synonyms for a single term to the FTS index.
+**            In this case, when tokenizing query text, the tokenizer may 
+**            provide multiple synonyms for a single term within the document.
+**            FTS5 then queries the index for each synonym individually. For
+**            example, faced with the query:
+**
+**   <codeblock>
+**     ... MATCH 'first place'</codeblock>
+**
+**            the tokenizer offers both "1st" and "first" as synonyms for the
+**            first token in the MATCH query and FTS5 effectively runs a query 
+**            similar to:
+**
+**   <codeblock>
+**     ... MATCH '(first OR 1st) place'</codeblock>
+**
+**            except that, for the purposes of auxiliary functions, the query
+**            still appears to contain just two phrases - "(first OR 1st)" 
+**            being treated as a single phrase.
+**
+**       <li> By adding multiple synonyms for a single term to the FTS index.
+**            Using this method, when tokenizing document text, the tokenizer
+**            provides multiple synonyms for each token. So that when a 
+**            document such as "I won first place" is tokenized, entries are
+**            added to the FTS index for "i", "won", "first", "1st" and
+**            "place".
+**
+**            This way, even if the tokenizer does not provide synonyms
+**            when tokenizing query text (it should not - to do would be
+**            inefficient), it doesn't matter if the user queries for 
+**            'first + place' or '1st + place', as there are entires in the
+**            FTS index corresponding to both forms of the first token.
+**   </ol>
+**
+**   Whether it is parsing document or query text, any call to xToken that
+**   specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+**   is considered to supply a synonym for the previous token. For example,
+**   when parsing the document "I won first place", a tokenizer that supports
+**   synonyms would call xToken() 5 times, as follows:
+**
+**   <codeblock>
+**       xToken(pCtx, 0, "i",                      1,  0,  1);
+**       xToken(pCtx, 0, "won",                    3,  2,  5);
+**       xToken(pCtx, 0, "first",                  5,  6, 11);
+**       xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3,  6, 11);
+**       xToken(pCtx, 0, "place",                  5, 12, 17);
+**</codeblock>
+**
+**   It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+**   xToken() is called. Multiple synonyms may be specified for a single token
+**   by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence. 
+**   There is no limit to the number of synonyms that may be provided for a
+**   single token.
+**
+**   In many cases, method (1) above is the best approach. It does not add 
+**   extra data to the FTS index or require FTS5 to query for multiple terms,
+**   so it is efficient in terms of disk space and query speed. However, it
+**   does not support prefix queries very well. If, as suggested above, the
+**   token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+**   <codeblock>
+**     ... MATCH '1s*'</codeblock>
+**
+**   will not match documents that contain the token "1st" (as the tokenizer
+**   will probably not map "1s" to any prefix of "first").
+**
+**   For full prefix support, method (3) may be preferred. In this case, 
+**   because the index contains entries for both "first" and "1st", prefix
+**   queries such as 'fi*' or '1s*' will match correctly. However, because
+**   extra entries are added to the FTS index, this method uses more space
+**   within the database.
+**
+**   Method (2) offers a midpoint between (1) and (3). Using this method,
+**   a query such as '1s*' will match documents that contain the literal 
+**   token "1st", but not "first" (assuming the tokenizer is not able to
+**   provide synonyms for prefixes). However, a non-prefix query like '1st'
+**   will match against "1st" and "first". This method does not require
+**   extra disk space, as no extra entries are added to the FTS index. 
+**   On the other hand, it may require more CPU cycles to run MATCH queries,
+**   as separate queries of the FTS index are required for each synonym.
+**
+**   When using methods (2) or (3), it is important that the tokenizer only
+**   provide synonyms when tokenizing document text (method (2)) or query
+**   text (method (3)), not both. Doing so will not cause any errors, but is
+**   inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+  int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+  void (*xDelete)(Fts5Tokenizer*);
+  int (*xTokenize)(Fts5Tokenizer*, 
+      void *pCtx,
+      int flags,            /* Mask of FTS5_TOKENIZE_* flags */
+      const char *pText, int nText, 
+      int (*xToken)(
+        void *pCtx,         /* Copy of 2nd argument to xTokenize() */
+        int tflags,         /* Mask of FTS5_TOKEN_* flags */
+        const char *pToken, /* Pointer to buffer containing token */
+        int nToken,         /* Size of token in bytes */
+        int iStart,         /* Byte offset of token within input text */
+        int iEnd            /* Byte offset of end of token within input text */
+      )
+  );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY     0x0001
+#define FTS5_TOKENIZE_PREFIX    0x0002
+#define FTS5_TOKENIZE_DOCUMENT  0x0004
+#define FTS5_TOKENIZE_AUX       0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED    0x0001      /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+  int iVersion;                   /* Currently always set to 2 */
+
+  /* Create a new tokenizer */
+  int (*xCreateTokenizer)(
+    fts5_api *pApi,
+    const char *zName,
+    void *pContext,
+    fts5_tokenizer *pTokenizer,
+    void (*xDestroy)(void*)
+  );
+
+  /* Find an existing tokenizer */
+  int (*xFindTokenizer)(
+    fts5_api *pApi,
+    const char *zName,
+    void **ppContext,
+    fts5_tokenizer *pTokenizer
+  );
+
+  /* Create a new auxiliary function */
+  int (*xCreateFunction)(
+    fts5_api *pApi,
+    const char *zName,
+    void *pContext,
+    fts5_extension_function xFunction,
+    void (*xDestroy)(void*)
+  );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#ifdef __cplusplus
+}  /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+
+/******** End of fts5.h *********/
diff --git a/odb_api/src/odb_api/piconst.h b/odb_api/src/odb_api/piconst.h
new file mode 100644
index 0000000..548ce0b
--- /dev/null
+++ b/odb_api/src/odb_api/piconst.h
@@ -0,0 +1,29 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file piconst.h
+/// ECMWF July 2010
+
+#ifndef piconst_H
+#define piconst_H
+
+#include <cmath>
+
+namespace piconst {
+//  const double  pi                = ((double)3.14159265358979323844e0);
+  const double  pi                = atan2(1.0,1.0)*4;
+  const double  half_pi           = (pi / 2);
+  const double  two_pi            = (2 * pi);
+  const double  four_pi           = (4 * pi);
+  const double  pi_over_180       = (pi/180);
+  const double  recip_pi_over_180 = (180/pi);
+  const double  sphere_area       = four_pi; /* Actually: 4 * pi * R^2 */
+}
+#endif
diff --git a/odb_api/src/odb_api/pyodbapi.h b/odb_api/src/odb_api/pyodbapi.h
new file mode 100644
index 0000000..eba8748
--- /dev/null
+++ b/odb_api/src/odb_api/pyodbapi.h
@@ -0,0 +1,19 @@
+// Classes user by swig
+
+#include "eckit/eckit.h"
+
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/Column.h"
+#include "odb_api/IteratorProxy.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/Select.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Reader.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/WriterBufferingIterator.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/TextReaderIterator.h"
+
+using namespace odb;
diff --git a/odb_api/src/odb_api/sqll.l b/odb_api/src/odb_api/sqll.l
new file mode 100755
index 0000000..2e7de31
--- /dev/null
+++ b/odb_api/src/odb_api/sqll.l
@@ -0,0 +1,269 @@
+%option reentrant
+%option extra-type="void *"
+%option bison-bridge
+%option yylineno
+%{
+
+// %option noyywrap // few lines higher!
+// #define YY_USER_ACTION yyset_column(yyget_column(yyscanner) + yyget_leng(yyscanner), yyscanner);
+//#define YY_USER_ACTION \
+//    printf("yyget_column(yyscanner): %i, yyget_leng(yyscanner): %i\n", yyget_column(yyscanner), yyget_leng(yyscanner)); \
+//    yyset_column(yyget_column(yyscanner) + yyget_leng(yyscanner), yyscanner);
+/*
+static void update_loc(void* yyscanner)
+{
+    struct ::yyguts_t * yyg ((struct yyguts_t*) yyscanner);
+
+    int curr_line = 1;
+    int curr_col  = 1;
+
+    yyg->yylloc.first_line = 1;
+
+    yylloc.first_line   = curr_line;
+    yylloc.first_column = curr_col;
+
+    {
+        char * s; 
+        for(s = yytext; *s != '\0'; s++)
+        {
+            if(*s == '\n') {
+                curr_line++;
+                curr_col = 1;
+            } else {
+                curr_col++;
+            }
+        }
+    }
+
+    yylloc.last_line   = curr_line;
+    yylloc.last_column = curr_col-1;
+}
+*/
+
+#define YY_USER_ACTION do { \
+        struct yyguts_t * yyg ((struct yyguts_t*) yyscanner); \
+        /* fprintf(stderr, "location <%d,%d>,<%d,%d>\n", yyg->yylloc_r.first_line, yyg->yylloc_r.first_column, yyg->yylloc_r.last_line, yyg->yylloc_r.last_column); */ \
+    } while (0);
+
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+
+#include "odb_api/odblib_lex.h"
+
+// http://flex.sourceforge.net/manual/Multiple-Input-Buffers.html#Multiple-Input-Buffers
+
+class Stack {
+    static const size_t maxIncludeDepth = 2000;
+public:
+    void push(const std::string& sql, const std::string& yypath, YY_BUFFER_STATE state, odblib_scan_t scanner)
+    {
+        if (stack_.size() > maxIncludeDepth)
+            throw eckit::UserError("Includes nested too deeply");
+
+        stack_.push_back(state);
+        odblib__switch_to_buffer(odblib__scan_bytes(sql.c_str(), sql.size(), scanner), scanner);
+    }
+
+    int pop(odblib_scan_t scanner)
+    {
+        if (stack_.size() <= 1)
+            return 1; 
+
+        YY_BUFFER_STATE currentState (stack_.back());
+        stack_.pop_back();
+
+        odblib__switch_to_buffer(currentState, scanner);
+        return 0;
+    }
+
+private:
+    std::vector<YY_BUFFER_STATE> stack_;
+};
+
+typedef Stack include_stack;
+
+/*
+%option noyywrap
+*/
+
+%}
+IDENT   [_A-Za-z]+[_0-9A-Za-z]*
+VAR     [$][_0-9A-Za-z]+
+NUMB    [-]?[0-9]+(\.[0-9]*)?
+SEMICOLON [;]
+
+
+%s LEX_ORDERBY
+%s LEX_CREATE
+%x incl
+
+%%
+#include                          BEGIN(incl);
+
+@[lL][iI][nN][kK]                 return LINK;
+[sS][eE][tT]                      return SET;
+[dD][aA][tT][aA][bB][aA][sS][eE]  return DATABASE;
+[sS][eE][lL][eE][cC][tT]          return SELECT;
+[iI][nN][tT][oO]                  return INTO;
+[fF][rR][oO][mM]                  return FROM;
+[wW][hH][eE][rR][eE]              return WHERE;
+[iI][nN][sS][eE][rR][tT]          return INSERT;
+[vV][aA][lL][uU][eE][sS]          return VALUES;
+[aA][nN][dD]                      return AND;
+[oO][rR]                          return OR;
+[oO][nN]                          return ON;
+[iI][sS]                          return IS;
+[nN][uU][lL][lL]                  return NIL;
+[nN][oO][tT]                      return NOT;
+[cC][oO][uU][nN][tT]              return COUNT;
+[eE][xX][iI][tT]                  return EXIT;
+[vV][iI][eE][wW]                  { BEGIN 0; return VIEW; }
+[cC][rR][eE][aA][tT][eE]          { BEGIN LEX_CREATE; return CREATE; }
+[sS][cC][hH][eE][mM][aA]          { BEGIN 0; return SCHEMA; }
+<LEX_CREATE>[iI][nN][dD][eE][xX]  { BEGIN 0; return INDEX; }
+<LEX_CREATE>[tT][aA][bB][lL][eE]  { BEGIN 0; return TABLE; }
+<LEX_CREATE>[tT][yY][pP][eE]      { BEGIN 0; return TYPE; }
+[tT][yY][pP][eE][dD][eE][fF]      { BEGIN 0; return TYPEDEF; }
+[tT][yY][pP][eE][oO][fF]          return TYPEOF;
+[bB][eE][tT][wW][eE][eE][nN]      return BETWEEN;
+[dD][iI][sS][tT][iI][nN][cC][tT]  return DISTINCT;
+[aA][lL][lL]                      return ALL;
+[mM][iI][sS][sS][iI][nN][gG]      return NIL;
+[gG][rR][oO][uU][pP]              return GROUP;
+[oO][rR][dD][eE][rR]              { BEGIN LEX_ORDERBY; return ORDER; }
+[bB][yY]                          return BY;
+[iI][nN]                          return IN;
+[rR][eE][aA][dD][oO][nN][lL][yY]  return READONLY;
+[uU][pP][dD][aA][tT][eE][dD]      return UPDATED;
+[nN][oO][rR][eE][oO][rR][dD][eE][rR] return NOREORDER;
+[sS][aA][fF][eE][gG][uU][aA][rR][dD] return SAFEGUARD;
+[tT][eE][mM][pP][oO][rR][aA][rR][yY] return TEMPORARY;
+<LEX_ORDERBY>[aA][sS][cC]         return ASC;
+<LEX_ORDERBY>[dD][eE][sS][cC]     return DESC;
+{SEMICOLON}	                      { BEGIN 0; return ';'; }
+[aA][sS]                          return AS;
+\#                                return HASH; 
+[cC][oO][nN][sS][tT][rR][aA][iI][nN][tT] return CONSTRAINT;
+[uU][nN][iI][qQ][uU][eE]          return UNIQUE;
+[pP][rR][iI][mM][aA][rR][yY]      return PRIMARY;
+[fF][oO][rR][eE][iI][gG][nN]      return FOREIGN;
+[kK][eE][yY]                      return KEY;
+[rR][eE][fF][eE][rR][eE][nN][cC][eE][sS] return REFERENCES;
+[dD][eE][fF][aA][uU][lL][tT]      return DEFAULT;
+[iI][nN][hH][eE][rR][iI][tT][sS]  { BEGIN 0; return INHERITS; }
+[mM][aA][tT][cC][hH]              return MATCH;
+[qQ][uU][eE][rR][yY]              return QUERY;
+[lL][iI][kK][eE]                  return LIKE;
+[rR][lL][iI][kK][eE]              return RLIKE;
+[aA][lL][iI][gG][nN]              return ALIGN;
+[rR][eE][sS][eE][tT]              return RESET;
+[oO][nN][eE][lL][oO][oO][pP][eE][rR] return ONELOOPER;
+<incl>[ \t]*                      /* eat the whitespace */
+<incl>[^ \t\n]+ {                 /* name of the file to be included */
+    //cerr << "include " << yytext << std::endl;
+
+    eckit::PathName fileName(StringTool::unQuote(yytext));
+
+    //cerr << "Opening " << fileName << std::endl;
+    std::string text (SQLSession::readIncludeFile(fileName));
+
+    Stack* includeStack (static_cast<Stack*>(((struct SQLYacc::odblib_guts_t*) odblib_scanner)->odblib_extra_r));
+    ASSERT(includeStack);
+    includeStack->push(text, fileName, YY_CURRENT_BUFFER, odblib_scanner);
+    BEGIN(INITIAL);
+}
+
+<<EOF>> { 
+    //cerr << "** EOF **" << std::endl;
+    odblib_terminate();
+}
+
+\"|\'   {
+            int c, end = yytext[0];
+
+            yyleng = 0;
+            while ((c = yyinput(yyscanner)) && c != end)
+            {
+                yytext[yyleng++] = (c == '\\') ? yyinput(yyscanner) : c;
+                if (c == '\n')
+                {
+                    struct yyguts_t * yyg = (struct yyguts_t*) yyscanner;
+                    ++ yyg->yylineno_r;
+                }
+            }
+
+            yytext[yyleng++] = 0;
+            yylval->val = yytext;
+            return STRING;
+        }
+
+\{      {
+            int c, end = '}';
+
+            yyleng = 0;
+            while ((c = yyinput(yyscanner)) && c != end)
+            {
+                yytext[yyleng++] = (c == '\\') ? yyinput(yyscanner) : c;
+                if (c == '\n') 
+                {
+                    struct yyguts_t * yyg = (struct yyguts_t*) yyscanner;
+                    ++ yyg->yylineno_r;
+                }
+            }
+
+            yytext[yyleng++] = 0;
+            yylval->val = yytext;
+            return EMBEDDED_CODE;
+        }
+
+{IDENT}	{ 
+			char *p = yytext; while(*p) { *p = tolower(*p); p++; }
+			yylval->val = yytext; 
+			//cerr << "****I " << yylval.val << " ****" << std::endl; 
+			return IDENT; 
+		}
+{VAR}   { 
+			char *p = yytext; while(*p) { *p = tolower(*p); p++; }
+			yylval->val = yytext; 
+			//cerr << "****V " << yylval.val << " ****" << std::endl; 
+			return VAR; 
+		}
+
+{NUMB}	{
+			yylval->num = atof(yytext);
+			//cerr << "****N " << yylval.num << " ****" << std::endl; 
+			return DOUBLE;
+		}
+
+"--".*  ;
+"//".*  ;
+
+[ \t]*  ;
+\n      {
+            struct yyguts_t * yyg = (struct yyguts_t*) yyscanner;
+            ++ yyg->yylineno_r;
+        }
+
+"="       return EQ;
+"=="      return EQ;
+"!="      return NE;
+"<>"      return NE;
+">="      return GE;
+"<="      return LE;
+"!"       return NOT;
+
+.		return *yytext;
+
+%%
diff --git a/odb_api/src/odb_api/sqly.y b/odb_api/src/odb_api/sqly.y
new file mode 100755
index 0000000..cede223
--- /dev/null
+++ b/odb_api/src/odb_api/sqly.y
@@ -0,0 +1,768 @@
+%pure-parser
+%lex-param {yyscan_t scanner}
+%lex-param {odb::sql::SQLSession* session}
+%parse-param {yyscan_t scanner}
+%parse-param {odb::sql::SQLSession* session}
+
+%{
+
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#define YYMAXDEPTH 500
+
+#include <unistd.h>
+
+#include "eckit/eckit.h"
+#include "odb_api/SQLSession.h"
+#include "odb_api/SQLStatement.h"
+
+#include <iostream>
+
+Expressions emptyExpressionList;
+
+%}
+
+%token <val>STRING
+%token <val>EMBEDDED_CODE
+%token <val>IDENT
+%token <val>VAR
+
+%token <num>DOUBLE
+
+%token SEMICOLON
+%token LINK
+%token TYPEOF
+%token READONLY
+%token UPDATED
+%token NOREORDER
+%token SAFEGUARD
+
+%token EXIT
+
+%token SELECT
+%token INTO
+%token FROM
+%token SET
+%token DATABASE
+%token COUNT
+%token WHERE
+%token GROUP
+%token ORDER
+%token BY
+%token INSERT
+%token VALUES
+
+%token CREATE
+%token SCHEMA
+%token VIEW
+%token INDEX
+%token TABLE
+%token TYPE
+%token TYPEDEF
+%token TEMPORARY
+%token INHERITS
+%token DEFAULT
+
+%token CONSTRAINT
+%token UNIQUE
+%token PRIMARY
+%token FOREIGN
+%token KEY
+%token REFERENCES
+
+%token EQ
+%token GE
+%token LE
+%token NE
+%token IN
+%token NOT
+%token AND
+%token OR
+%token ON
+%token IS
+%token AS
+%token NIL
+%token ALL
+%token DISTINCT
+%token BETWEEN
+
+%token ASC
+%token DESC
+
+%token HASH
+%token LIKE
+%token RLIKE
+%token MATCH
+%token QUERY
+%token ALIGN
+%token RESET
+%token ONELOOPER
+
+%type <exp>column;
+%type <exp>vector_index;
+
+%type <exp>condition;
+%type <exp>atom_or_number;
+%type <exp>expression;
+
+%type <exp>factor;
+%type <exp>power;
+%type <exp>term;
+%type <exp>conjonction;
+%type <exp>disjonction;
+%type <exp>optional_hash;
+
+%type <exp>where;
+
+%type <exp>assignment_rhs;
+%type <explist>expression_list;
+
+%type <tablist>table_list;
+%type <table>table
+%type <val>table_name;
+%type <val>table_reference;
+%type <tablist>from;
+
+%type <explist>group_by;
+
+%type <orderlist>order_by;
+%type <orderlist>order_list;
+%type <orderexp>order;
+
+%type <explist>select_list;
+%type <explist>select_list_;
+%type <exp>select;
+%type <exp>select_;
+
+%type <bol>distinct;
+%type <bol>all;
+%type <val>into;
+%type <val>func;
+%type <val>relational_operator;
+
+%type <r>vector_range_decl;
+%type <val>column_name;
+%type <val>bitfield_ref;
+%type <coldefs>column_def_list;
+%type <coldefs>column_def_list_;
+%type <coldef>column_def;
+%type <coldefs>bitfield_def_list;
+%type <coldefs>bitfield_def_list_;
+%type <coldef>bitfield_def;
+%type <val>data_type;
+%type <val>default_value;
+%type <bol>temporary;
+%type <val>location;
+%type <tablemd>table_md;
+%type <list>inherits;
+%type <list>inheritance_list;
+%type <list>inheritance_list_;
+%type <list>optional_columns;
+%type <list>columns;
+%type <list>values;
+%type <list>values_list;
+
+%type <condefs>constraint_list;
+%type <condefs>constraint_list_;
+%type <condef>constraint;
+%type <val>constraint_name;
+%type <condef>primary_key;
+%type <condef>foreign_key;
+%type <list>column_reference_list;
+%type <val>column_reference;
+%type <select_statement>select_statement;
+%type <insert_statement>insert_statement;
+%type <select_statement>create_view_statement;
+%type <embedded_statement>embedded_statement;
+
+%%
+
+start : statements { session->currentDatabase().setLinks(); }
+	;
+
+statements : statement ';'
+		   | statements statement ';'
+		   ;
+
+statement: select_statement        { session->statement($1); } 
+		 | create_view_statement   { session->statement($1); } 
+		 | insert_statement        { session->statement(session->insertFactory().create(*session, /*InsertAST* */ ($1))); }
+		 | embedded_statement      { session->statement(new SQLEmbedded($1)); }
+		 | set_statement
+		 | create_schema_statement
+		 | create_index_statement 
+		 | create_type_statement
+		 | create_table_statement
+		 | readonly_statement
+		 | updated_statement
+		 | noreorder_statement
+		 | safeguard_statement
+		 | exit_statement
+		 | align_statement
+		 | reset_statement
+		 | onelooper_statement
+		 | error
+		 | empty
+	;
+
+exit_statement: EXIT { return 0; }
+	;
+
+readonly_statement: READONLY
+	;
+
+updated_statement: UPDATED
+	;
+
+noreorder_statement: NOREORDER
+	;
+
+safeguard_statement: SAFEGUARD
+	;
+
+align_statement: ALIGN '(' IDENT ',' IDENT ')' ;
+onelooper_statement: ONELOOPER '(' IDENT ',' IDENT ')' ;
+reset_statement: RESET ALIGN ;
+reset_statement: RESET ONELOOPER ;
+
+create_schema_statement: CREATE SCHEMA schema_name schema_element_list
+        {
+            session->currentDatabase().schemaAnalyzer().endSchema();
+        }
+        ;
+
+schema_name: IDENT
+        {
+           session->currentDatabase().schemaAnalyzer().beginSchema($1);
+        }
+       ;
+
+schema_element_list: schema_element
+                   | schema_element_list schema_element
+                   ;
+
+schema_element: create_table_statement
+              | create_view_statement
+              | empty
+              ;
+
+create_index_statement: CREATE INDEX IDENT TABLE IDENT
+	{
+        session->createIndex($3,$5);
+	}
+	| CREATE INDEX IDENT '@' IDENT
+	{
+        session->createIndex($3,$5);
+	}
+	;
+
+create_type_statement: create_type IDENT as_or_eq '(' bitfield_def_list ')'
+	{
+        std::string typeName ($2);
+        ColumnDefs	colDefs ($5);
+        FieldNames	fields;
+        Sizes		sizes;
+        for (ColumnDefs::size_type i (0); i < colDefs.size(); ++i)
+        {
+            std::string name (colDefs[i].name());
+            std::string memberType (colDefs[i].type());
+
+            fields.push_back(name);
+
+            int size (::atof(memberType.c_str() + 3)); // bit[0-9]+
+			ASSERT(size > 0);
+
+            sizes.push_back(size);
+        }
+        std::string typeSignature (type::SQLBitfield::make("Bitfield", fields, sizes, typeName.c_str()));
+
+        session->currentDatabase()
+        .schemaAnalyzer().addBitfieldType(typeName, fields, sizes, typeSignature);
+		
+		//cout << "CREATE TYPE " << typeName << " AS " << typeSignature << ";" << std::endl;		
+	}
+	;
+
+create_type_statement: create_type IDENT as_or_eq IDENT { type::SQLType::createAlias($4, $2); }
+	;
+
+create_type: CREATE TYPE
+           | TYPEDEF
+           ;
+
+as_or_eq: AS
+        | EQ
+        ;
+
+bitfield_def_list: bitfield_def_list_ { $$ = $1; }
+                 | bitfield_def_list_ ',' { $$ = $1; }
+	;
+
+bitfield_def_list_: bitfield_def { $$ = ColumnDefs(1, $1); }
+                  | bitfield_def_list_ ',' bitfield_def { $$ = $1; $$.push_back($3); }
+	;
+
+bitfield_def: column_def
+	{
+		/* Type should look like bit[0-9]+ '*/
+		$$ = $1;
+	}
+	;
+
+temporary: TEMPORARY  { $$ = true; }
+         | empty      { $$ = false; }
+         ;
+
+inheritance_list: inheritance_list_       { $$ = $1; }
+                 | inheritance_list_ ','  { $$ = $1; }
+                 ;
+
+inheritance_list_: IDENT                         { $$ = std::vector<std::string>(); $$.insert($$.begin(), $1); }
+                 | inheritance_list_ ',' IDENT   { $$ = $1; $$.push_back($3); }
+
+inherits: INHERITS '(' inheritance_list ')'   { $$ = $3;               }
+        | empty                               { $$ = std::vector<std::string>(); }
+        ;
+
+constraint_list: constraint_list_ { $$ = $1; }
+               | constraint_list_ ',' { $$ = $1; }
+               ;
+
+constraint_list_: constraint { $$ = ConstraintDefs(1, $1); }
+                | constraint_list_ ',' constraint { $$ = $1; $$.push_back($3); }
+                | empty { $$ = ConstraintDefs(0); }
+                ;
+
+constraint: primary_key { $$ = $1; }
+          | foreign_key { $$ = $1; }
+          ;
+
+primary_key: constraint_name UNIQUE '(' column_reference_list ')' { $$ = ConstraintDef($1, $4); }
+           | constraint_name PRIMARY KEY '(' column_reference_list ')' { $$ = ConstraintDef($1, $5); }
+           ;
+
+foreign_key: constraint_name FOREIGN KEY '(' column_reference_list ')' REFERENCES IDENT '(' column_reference_list ')'
+           {
+                $$ = ConstraintDef($1, $5, $8, $10);
+           }
+           ;
+
+constraint_name: CONSTRAINT IDENT { $$ = $2; }
+               | empty { $$ = std::string(); }
+               ;
+
+column_reference_list: column_reference { $$ = std::vector<std::string>(1, $1); }
+                | column_reference_list ',' column_reference { $$ = $1; $$.push_back($3); }
+                ;
+
+column_reference: IDENT table_reference { $$ = $1 + $2; }
+           ;
+
+location: ON STRING { $$ = $2; }
+        | empty     { $$ = ""; }
+        ;
+
+table_md: AS '(' column_def_list constraint_list ')' { $$ = make_pair($3, $4); }
+        | empty                                      { $$ = make_pair(ColumnDefs(), ConstraintDefs()); }
+        ;
+
+create_table_statement: CREATE temporary TABLE table_name table_md inherits location
+	{
+        bool temporary ($2);
+		std::string name ($4);
+
+        ColumnDefs cols ($5.first);
+        ConstraintDefs constraints ($5.second);
+		std::vector<std::string> inheritance($6);
+        std::string location($7);
+        TableDef tableDef(name, cols, constraints, inheritance, location);
+
+        session->currentDatabase().schemaAnalyzer().addTable(tableDef);
+	}
+	;
+
+table_name: IDENT { $$ = $1; }
+          | IDENT '.' IDENT { $$ = $1 + std::string(".") + $3; }
+          | expression { SQLExpression* e($1); $$ = e->title(); }
+          ;
+
+column_def_list: column_def_list_     { $$ = $1; }
+               | column_def_list_ ',' { $$ = $1; }
+               | empty                { $$ = ColumnDefs(); }
+               ;
+	 
+column_def_list_: column_def                      { $$ = ColumnDefs(1, $1); }
+                | column_def_list_ ',' column_def { $$ = $1; $$.push_back($3); }
+                ;
+
+column_def: column_name vector_range_decl data_type default_value
+	{
+		$$ = ColumnDef($1, $3, $2, $4);
+	}
+	;
+
+vector_range_decl: '[' DOUBLE ']'            { $$ = std::make_pair(1, $2); }
+                 | '[' DOUBLE ':' DOUBLE ']' { $$ = std::make_pair($2, $4); }
+                 | empty                     { $$ = std::make_pair(0, 0); }
+                 ;
+
+column_name: IDENT { $$ = $1; }
+           ;
+
+data_type: IDENT                 { $$ = $1; }
+         | LINK                  { $$ = "@LINK"; } 
+         | TYPEOF '(' column ')' {
+                                    std::stringstream ss;
+                                    ss << *($3);
+                                    std::string columnName (ss.str());
+                                    std::string type (session->currentDatabase().schemaAnalyzer().findColumnType(columnName));
+                                    Log::debug() << "TYPEOF(" << *($3) << ") => " << type << std::endl;
+                                    $$ = type;
+                                 }
+         ;
+
+default_value: DEFAULT expression { SQLExpression* e($2); $$ = e->title(); }
+             | empty { $$ = std::string(); }
+             ;
+
+create_view_statement: CREATE VIEW IDENT AS select_statement { $$ = $5; }
+	;
+
+embedded_statement: EMBEDDED_CODE { $$ = EmbeddedAST($1); }
+    ;
+
+select_statement: SELECT distinct all select_list into from where group_by order_by
+                {   
+                    bool                                      distinct($2);
+                    bool                                      all($3);
+                    Expressions                               select_list($4);
+                    std::string                               into($5);
+                    std::vector<Table>                        from ($6);
+                    SQLExpression*                            where($7);
+                    Expressions                               group_by($8);
+                    std::pair<Expressions,std::vector<bool> > order_by($9);
+
+                    $$ = SelectAST(distinct, all, select_list, into, from, where, group_by, order_by);
+                }
+                ;
+
+distinct: DISTINCT { $$ = true; }
+		| empty    { $$ = false; }
+		;
+
+all: ALL      { $$ = true; }
+   | empty    { $$ = false; }
+   ;
+
+
+into: INTO IDENT   { $$ = $2; }
+    | INTO STRING  { $$ = $2; }
+    | empty        { $$ = ""; }
+    ;
+
+from : FROM table_list   { $$ = $2; }
+     | FROM EMBEDDED_CODE 
+        { 
+            std::cerr << "FROM EMBEDDED_CODE: " << $2 << std::endl;
+            $$ = std::vector<Table>();
+            $$.push_back(Table($2, "", true));
+        } 
+     | empty             { $$ = std::vector<Table>(); }
+	 ;
+
+where : WHERE expression { $$ = $2; }
+	  |                  { $$ = 0;  } 
+	  ;
+
+insert_statement: INSERT INTO table optional_columns VALUES values
+                { $$ = InsertAST($3, $4, $6); }
+                ;
+
+optional_columns: '(' columns ')'   { $$ = $2; }
+                | empty             { $$ = std::vector<std::string>(); } 
+                ;
+
+columns: IDENT              { $$ = std::vector<std::string>(); $$.insert($$.begin(), $1); }
+       | columns ',' IDENT  { $$ = $1; $$.push_back($3); }
+       ;
+
+values: '(' values_list ')' { $$ = $2; }
+      ;
+
+values_list: '?'                  { $$ = std::vector<std::string>(); $$.insert($$.begin(), "?" /*$1*/); }
+           | values_list ',' '?'  { $$ = $1; $$.push_back("?"/*$3*/); }
+           ;
+
+assignment_rhs	: expression
+		;
+
+set_statement : SET DATABASE STRING { session->openDatabase($3); }
+	; 
+
+set_statement : SET DATABASE STRING AS IDENT { session->openDatabase($3,$5); }
+	; 
+
+set_statement : SET VAR EQ assignment_rhs
+	{ 
+        //using namespace std;
+		//cout << "== set variable " << $2 << " to "; if ($4) cout << *($4) << std::endl; else cout << "NULL" << std::endl;
+		session->currentDatabase().setVariable($2, $4);
+	}
+	; 
+
+bitfield_ref: '.' IDENT  { $$ = $2; }
+            |            { $$ = std::string(); }
+
+column: IDENT vector_index table_reference optional_hash
+		  {
+
+			std::string columnName($1);
+			SQLExpression* vectorIndex($2);
+			Table table($3, "", false); // TODO: table_reference should handle .<database> suffix
+			SQLExpression* pshift($4);
+
+            bool missing;
+			std::string bitfieldName;
+			$$ = session->selectFactory().createColumn(columnName, bitfieldName, vectorIndex, table /*TODO: handle .<database> */, pshift);
+		  }
+	   | IDENT bitfield_ref table_reference optional_hash
+		{
+
+			std::string columnName      ($1);
+			std::string bitfieldName    ($2);
+			SQLExpression* vectorIndex  (0); 
+			Table table($3, "", false); // TODO: table_reference should handle .<database> suffix
+			SQLExpression* pshift       ($4);
+
+            bool missing;
+			$$ = session->selectFactory().createColumn(columnName, bitfieldName, vectorIndex, table /*TODO: handle .<database> */, pshift);
+		 }
+	  ;
+
+vector_index : '[' expression ']'    { $$ = $2; }
+             | empty                 { $$ = NULL; }
+             ;
+
+table_reference: '@' IDENT   { $$ = std::string("@") + $2; }
+               | empty       { $$ = std::string(""); }
+               ;
+
+table : IDENT '.' IDENT { $$ = Table(std::string($1), std::string($3), false); } 
+      | IDENT           { $$ = Table(std::string($1), std::string(""), false); } 
+      | STRING			{ $$ = Table(std::string($1), std::string(""), false, true); } 
+      ;
+
+table_list : table                  { $$ = std::vector<Table>(); $$.push_back($1); }
+	       | table_list  ',' table  { $$ = $1; $$.push_back($3); }
+	       ;
+
+/*================= SELECT =========================================*/
+
+select_list: select_list_     { $$ = $1; }
+           | select_list_ ',' { $$ = $1; }
+           ;
+
+select_list_ : select         { $$ = Expressions(1, $1); }
+	| select_list_ ',' select { $$ = $1; $$.push_back($3); }
+	;
+
+select: select_ access_decl { $$ = $1; }
+      ;
+
+select_: '*' table_reference                             { $$ = new ColumnExpression("*", $2);  } 
+	   | IDENT '.' '*' table_reference                   { $$ = new BitColumnExpression($1, "*", $4); }
+	   | IDENT '[' expression ':' expression ']' table
+		{
+			// TODO: Add simillar rule for BitColumnExpression.
+			bool missing (false);
+			int begin ($3->eval(missing)); //ASSERT(!missing);
+			int end ($5->eval(missing)); //ASSERT(!missing);
+            Table table ($7);
+			$$ = new ColumnExpression($1, table.name /*TODO: handle .<database> */, begin, end);
+		}
+	   | expression AS IDENT table_reference { $$ = $1; $$->title($3 + $4); }
+	   | expression
+	   ;
+
+access_decl: UPDATED
+           | READONLY
+           | empty
+           ;
+
+/*================= GROUP BY ======================================*/
+group_by: GROUP BY expression_list { $$ = $3;                       }
+        | empty                    { $$ = Expressions(); }
+	    ;
+
+/*================= ORDER =========================================*/
+
+order_by: ORDER BY order_list { $$ = $3;                       }
+        | empty               { $$ = std::make_pair(Expressions(),std::vector<bool>()); }
+	    ;
+
+
+order_list : order                  { $$ = std::make_pair(Expressions(1, $1 .first),std::vector<bool>(1, $1 .second)); }
+           | order_list ',' order   { $$ = $1; $$.first.push_back($3 .first); $$.second.push_back($3 .second); }
+		   ;
+
+order : expression DESC      { $$ = std::make_pair($1, false); }
+      | expression ASC       { $$ = std::make_pair($1, true); }
+      | expression			 { $$ = std::make_pair($1, true); }
+	  ;
+
+
+/*================= EXPRESSION =========================================*/
+
+expression_list : expression         {  $$ = Expressions(1, $1); }
+				| expression_list ',' expression { $$ = $1; $$.push_back($3); }
+				;
+
+optional_hash : HASH DOUBLE { $$ = new NumberExpression($2); }
+			  |             { $$ = new NumberExpression(0); }
+			  ;
+
+
+atom_or_number : '(' expression ')'           { $$ = $2; }
+			   | '-' expression               { $$ = ast("-",$2); }
+			   | DOUBLE                       { $$ = new NumberExpression($1); }
+			   | column                   
+			   | VAR                          { $$ = session->currentDatabase().getVariable($1); } 
+			   | '?' DOUBLE                   { $$ = new ParameterExpression($2); }
+			   | func '(' expression_list ')' { $$ = ast($1, $3); }
+			   | func '(' empty ')'           { $$ = ast($1, emptyExpressionList); }
+			   | func '(' '*' ')'             
+				{
+                    if (std::string("count") != $1)
+                        throw eckit::UserError(std::string("Only function COUNT can accept '*' as parameter (") + $1 + ")");
+
+                    $$ = ast("count", new NumberExpression(1.0));
+				}
+			   | STRING                       { $$ = new StringExpression($1); }
+			   | EMBEDDED_CODE                { $$ = new EmbeddedCodeExpression($1); }
+			   ;
+
+
+func : IDENT { $$ = $1;      }
+	 | COUNT { $$ = "count"; }
+	 ;
+
+/* note: a^b^c -> a^(b^c) as in fortran */
+
+power       : atom_or_number
+			;
+
+factor      : factor '*' power          { $$ = ast("*",$1,$3);   }
+            | factor '/' power          { $$ = ast("/",$1,$3); }
+            /* | factor '%' power          { $$ = new CondMOD($1,$3); } */
+            | power
+            ;
+
+term        : term '+' factor           { $$ = ast("+",$1,$3);   }
+            | term '-' factor           { $$ = ast("-",$1,$3);   }
+            /* | term '&' factor */
+            | factor
+            ;
+
+relational_operator: '>' { $$ = ">"; }
+                   | EQ  { $$ = "="; }
+                   | '<' { $$ = "<"; }
+                   | GE  { $$ = ">="; }
+                   | LE  { $$ = "<="; }
+                   | NE  { $$ = "<>"; }
+                   ;
+
+condition   : term relational_operator term relational_operator term { $$ = ast("and", ast($2,$1,$3), ast($4,$3,$5)); }
+            | term relational_operator term                          { $$ = ast($2, $1, $3); }
+            | MATCH '(' expression_list ')' IN QUERY '(' select_statement ')'
+            { 
+                const Expressions& matchList ($3);
+                const SelectAST& subquery ($8);
+                $$ = ast("match", matchList, subquery);
+            }
+            | condition  IN '(' expression_list ')'                  { $4.push_back($1); $$ = ast("in",$4);   }
+            | condition  IN VAR         
+			{ 
+                SQLExpression* v = session->currentDatabase().getVariable($3);
+                ASSERT(v && v->isVector());
+                Expressions e(v->vector());
+                e.push_back($1);
+                $$ = ast("in", e);
+			}
+            | condition  NOT IN '(' expression_list ')'  { $5.push_back($1); $$ = ast("not_in",$5);   }
+            | condition  NOT IN VAR  
+			{ 
+                // This has not been implemented yet.
+                throw UserError("Syntax: 'condition NOT IN VAR' not yet supported");
+
+				SQLExpression* v = session->currentDatabase().getVariable($4);
+				ASSERT(v && v->isVector());
+				Expressions e(v->vector());
+				e.push_back($1);
+				$$ = ast("not_in", e);
+			}
+
+            | NOT condition             { $$ = ast("not",$2);   }
+			| condition IS NIL          { $$ = ast("null",$1);   }
+			| condition IS NOT NIL      { $$ = ast("not_null",$1);   }
+			| condition BETWEEN term AND term { $$ = ast("between",$1,$3,$5); }
+			| condition NOT BETWEEN term AND term { $$ = ast("not_between",$1,$4,$6); }
+            | condition LIKE term       { $$ = ast("like", $1, $3); }
+            | condition RLIKE term      { $$ = ast("rlike", $1, $3); }
+            | term
+            ;
+
+conjonction : conjonction AND condition       { $$ = ast("and",$1,$3);   }
+            | condition
+            ;
+
+disjonction : disjonction OR conjonction      { $$ = ast("or",$1,$3);   }
+            | conjonction
+            ;
+
+expression  : disjonction
+			| expression '[' expression ']' 
+			{
+                // This has not been implemented yet.
+                throw UserError("Syntax: 'expression [ expression ]' not yet supported");
+
+				SQLExpression* container = $1;
+				SQLExpression* index = $3;
+
+				bool missing = false;
+				long i = index->eval(missing);
+				ASSERT(! missing);
+				if (container->isVector())
+				{
+					//cerr << "==== index: '" << i << "'" << std::endl;
+					$$ = container->vector()[i];
+				}
+				else
+				if (container->isDictionary())
+				{
+                    std::string key (index->title());
+                    if (container->dictionary().find(key) == container->dictionary().end())
+                    {
+                        std::stringstream ss;
+                        ss << "Key '" << key << "' not found.";
+                        throw eckit::UserError(ss.str());
+                    }
+					
+                    $$ = container->dictionary()[key];
+				}
+			}
+            ;
+
+empty :
+      ;	
+
+%%
+#include "sqll.c"
+
diff --git a/odb_api/src/odb_api/test.py b/odb_api/src/odb_api/test.py
new file mode 100644
index 0000000..08ee5c5
--- /dev/null
+++ b/odb_api/src/odb_api/test.py
@@ -0,0 +1,58 @@
+import odb
+import pyodbapi
+import unittest
+
+class TestPython(unittest.TestCase):
+
+	def setUp(self):
+		self.fn = '../oda/some_conv_2011-01-08.odb'
+		self.bitfields=[19, 20, 21, 30, 31, 32, 33,]
+
+	def testSelectFromFile(self):
+		sql = 'select lat,lon,obsvalue from "' + self.fn + '"'
+		self.assertTrue( 15719 == len([r for r in odb.sql(sql)]) )
+
+	def testOpenFile(self):
+		o = odb.open(self.fn)
+		allRows = 0
+
+		for r in o:
+			stream1, lat1, lon1 = r[0, 'lat','lon']
+
+			stream2, (lat2, lon2, obsvalue2) = r[(0, ('lat','lon', 'obsvalue'))]
+			self.assertTrue( (stream1, lat1, lon1) == (stream2, lat2, lon2) )
+
+			allRows += 1
+			if allRows > 50: break
+
+		rr = r[:]
+		self.assertTrue (rr[1:10:2] == r[1:10:2]) 
+		self.assertTrue (rr[2:10:3] == r[2:10:3])
+		self.assertTrue (rr[10:1:-1] == r[10:1:-1])
+		self.assertTrue (rr[10:1:1] == r[10:1:1])
+		self.assertTrue (rr[-1:1:1] == r[-1:1:1])
+
+		md = r.columns()
+		self.assertTrue(49, len(md))
+		for c in md:
+			print c.name(), c.type(), c.missingValue()
+			self.assertTrue(c.name() == 'expver')
+			self.assertTrue(c.type() == 3)
+			self.assertTrue(c.missingValue() == -2147483647.0)
+			break
+
+	def testSyntaxError(self):
+		def rows(s): return [r[:] for r in odb.sql(s)]
+		self.assertRaises(SyntaxError, rows, 'foo bar')
+
+	def testBitfields(self):
+		"""
+		TODO:
+		"""
+		for r in odb.open(self.fn):
+			print 'elements: ', ",".join([ str(i) + ':' + str(r[i]) for i in self.bitfields])
+			print r[0:44]
+			break
+
+if __name__ == '__main__':
+	unittest.main()
diff --git a/odb_api/src/odb_api/tools/CAPIExamples.cc b/odb_api/src/odb_api/tools/CAPIExamples.cc
new file mode 100644
index 0000000..4436188
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CAPIExamples.cc
@@ -0,0 +1,184 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file CAPIExamples.cc
+///
+/// This file contains examples of usage of public APIs.
+///
+/// @author Piotr Kuchta, ECMWF, September 2015
+
+#include <string>
+#include <iostream>
+#include <vector>
+#include <math.h>
+
+#include "odb_api/odb_api.h"
+
+#include "odb_api/odbcapi.h"
+#include "odb_api/tools/odb_api_tools_c.h"
+
+#include "TestCase.h"
+
+namespace {
+
+TEST(c_api_example_select_data_read_results)
+{
+    const int maxCols = 4;
+    int err;
+    double buffer[maxCols];
+    double* data = buffer;
+    int newDataset = 0;
+    int nRows = 0;
+    oda_ptr oh;
+    oda_select_iterator* it;
+    size_t i;
+
+    // Prepare input data
+    import_text("x:INTEGER,y:INTEGER,v:DOUBLE,f:BITFIELD[a:1;b:2]]\n" 
+                "1,1,0.3,0\n"
+                "1,1,0.2,1\n" 
+                "2,2,0.4,2\n" 
+                "2,2,0.1,3\n", 
+                "c_example_select_data_read_results.odb");
+
+    oh = odb_select_create("", &err);
+    ASSERT(0 == err);
+    it = odb_create_select_iterator(oh, "select x, min(v), max(v)"
+                                        " from \"c_example_select_data_read_results.odb\"" 
+                                        " order by x;", &err);
+    ASSERT(0 == err);
+    for (i = 0; 0 == odb_select_iterator_get_next_row(it, 3, data, &newDataset); ++i)
+    {
+        switch (i) {
+            case 0: ASSERT(data[0] == 1 && fabs(data[1] - 0.2) < 0.0000001); break;
+            case 1: ASSERT(data[0] == 2 && fabs(data[1] - 0.1) < 0.0000001); break;
+        }
+    }
+    ASSERT(i == 2);
+    ASSERT(0 == odb_select_iterator_destroy(it));
+
+    // Select some bitfields
+    it = odb_create_select_iterator(oh, "select f, f.a, f.b from \"c_example_select_data_read_results.odb\";", &err);
+    ASSERT(0 == err);
+
+    for (i = 0; 0 == odb_select_iterator_get_next_row(it, 3, data, &newDataset); ++i)
+    {
+        double f = data[0], a = data[1], b = data[2];
+        switch (i) {
+            case 0: ASSERT(f == 0 && a == 0 && b == 0); break;
+            case 1: ASSERT(f == 1 && a == 1 && b == 0); break;
+            case 2: ASSERT(f == 2 && a == 0 && b == 1); break;
+            case 3: ASSERT(f == 3 && a == 1 && b == 1); break;
+        }
+    }
+    ASSERT(i == 4);
+
+    ASSERT(0 == odb_select_iterator_destroy(it));
+    ASSERT(0 == odb_select_destroy(oh));
+}
+
+
+TEST(c_api_example_read_data)
+{
+    const int numberOfColumns = 4;
+    int err, nCols, type, nameLength;
+    char *name;
+    oda_ptr oh;
+    oda_read_iterator* it;
+    double buffer[numberOfColumns];
+    double* data;
+    int newDataset;
+    int nRows;
+
+    // Prepare input data
+    import_text("x:INTEGER,y:REAL,v:DOUBLE,f:BITFIELD[a:1;b:2]\n" 
+                "1,1,0.3,0\n" 
+                "2,1,0.2,1\n" 
+                "3,2,0.4,2\n" 
+                "4,2,0.1,3\n",
+                "c_api_example_read_data.odb");
+
+    oh = odb_read_create("", &err);
+    it = odb_create_read_iterator(oh, "c_api_example_read_data.odb", &err);
+    ASSERT(0 == err);
+    ASSERT(0 != it);
+
+    odb_read_iterator_get_no_of_columns(it, &nCols);
+    ASSERT(nCols == numberOfColumns);
+
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 0, &type));
+    ASSERT(type == 1 /*INTEGER*/);
+
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 1, &type));
+    ASSERT(type == 2 /*REAL*/);
+
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 2, &type));
+    ASSERT(type == 5 /*DOUBLE*/);
+
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 3, &type));
+    ASSERT(type == 4 /*BITFIELD*/);
+
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 0, &name, &nameLength));
+    ASSERT(0 == strncmp("x", name, strlen("x")));
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 1, &name, &nameLength));
+    ASSERT(0 == strncmp("y", name, strlen("y")));
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 2, &name, &nameLength));
+    ASSERT(0 == strncmp("v", name, strlen("v")));
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 3, &name, &nameLength));
+    ASSERT(0 == strncmp("f", name, strlen("f")));
+
+    data = buffer;
+    newDataset = 0;
+    for (nRows = 0; 0 == odb_read_iterator_get_next_row(it, numberOfColumns, data, &newDataset); ++nRows)
+    {
+        int x = int(data[0]);
+        int f = int(data[3]);
+
+        std::cout << "Read row " << nRows << ", x=" << x << std::endl;
+
+        ASSERT(x == nRows + 1 && f == nRows);
+    }
+
+    ASSERT(nRows == 4);
+
+    ASSERT(0 == odb_read_iterator_destroy(it));
+    ASSERT(0 == odb_read_destroy(oh));
+}
+
+TEST(c_api_example_write_data)
+{
+    int err;
+    oda_writer* writer = odb_writer_create("", &err);
+    oda_write_iterator* wi = odb_create_write_iterator(writer, "c_api_example_write_data.odb", &err);
+    ASSERT(0 == odb_write_iterator_set_no_of_columns(wi, 4));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 0, odb::INTEGER, "x"));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 1, odb::REAL, "y"));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 2, odb::DOUBLE, "v"));
+    // Define three fields: a (1 bit only), b (2 bits), c (1 bit)
+    ASSERT(0 == odb_write_iterator_set_bitfield(wi, 3, odb::BITFIELD, "bf", "a:b:c", "1:2:1"));
+    ASSERT(0 == odb_write_iterator_write_header(wi));
+
+    double data[4];
+    for (int i = 1; i <= 10; ++i)
+    {
+        data[0] = i;
+        data[1] = i * 10;
+        data[2] = i * 100;
+        data[3] = i;
+
+        ASSERT(0 == odb_write_iterator_set_next_row(wi, data, 4));
+    }
+
+    ASSERT(0 == odb_write_iterator_destroy(wi));
+    ASSERT(0 == odb_writer_destroy(writer));
+}
+
+} // namespace
+
diff --git a/odb_api/src/odb_api/tools/CMakeLists.txt b/odb_api/src/odb_api/tools/CMakeLists.txt
new file mode 100644
index 0000000..419c86d
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CMakeLists.txt
@@ -0,0 +1,145 @@
+list( APPEND odbtools_src_files
+ECMLTool.cc
+ECMLTool.h
+TestCase.h 
+TestCase.cc 
+TestRunner.cc 
+TestRunner.h 
+TestRunnerApplication.cc
+TestRunnerApplication.h
+TestRunnerApplication.cfg
+CompactTool.cc
+CompactTool.h
+CompareTool.cc
+CompareTool.h
+CountTool.cc
+CountTool.h
+IndexTool.cc
+IndexTool.h
+FixedSizeRowTool.cc
+FixedSizeRowTool.h
+ImportTool.cc
+ImportTool.h
+LSTool.cc
+LSTool.h
+MDSetTool.cc
+MDSetTool.h
+MergeTool.cc
+MergeTool.h
+ODA2RequestTool.cc
+ODA2RequestTool.h
+ODAHeaderTool.cc
+ODAHeaderTool.h
+SQLTool.cc
+SQLTool.h
+SetTool.cc
+SetTool.h
+SplitTool.cc
+SplitTool.h
+Tool.cc
+Tool.h
+ToolFactory.cc
+ToolFactory.h
+ToolRunnerApplication.cc
+ToolRunnerApplication.h
+XYVTool.cc
+XYVTool.h
+odb_api_tools_c.cc
+odb_api_tools_c.h
+)
+
+list( APPEND odbtest_src_files
+Examples.cc
+CAPIExamples.cc
+UnitTests.cc
+TestAAAImportODB.cc
+TestAAAImportODBDispatching.cc
+TestAggregateFunctions.cc
+TestAggregateFunctions.sql
+TestAggregateFunctions2.cc
+TestAggregateFunctions3.cc
+TestAtTableInTheOutput.cc
+TestBitfields.cc
+TestCatFiles.cc
+TestCodec.cc
+TestCodecOptimization.cc
+TestCommandLineParsing.cc
+TestConstCodec.cc
+TestConstIntegerCodec.cc
+TestDataJoin.cc
+TestDataLink.cc
+TestDataLoader.cc
+TestDataPage.cc
+TestDataRow.cc
+TestDataSelect.cc
+TestDataSet.cc
+TestDataTable.cc
+TestDecoding.cc
+TestDispatchingWriter.cc
+TestDistinct.cc
+TestFastODA2Request.cc
+TestFastODA2Request2.cc
+TestFastODA2Request3.cc
+TestFunctionCircle.cc
+TestFunctionDateAndTime.cc
+TestFunctionDistance.cc
+TestFunctionDotp.cc
+TestFunctionEqBox.cc
+TestFunctionNorm.cc
+TestFunctionRggBox.cc
+TestFunctionTdiff.cc
+TestFunctionThin.cc
+TestFunctionTypeConversion.cc
+TestFunctionsForAngleConversion.cc
+TestFunctionsForTemperatureConversion.cc
+TestInMemoryDataHandle.cc
+TestInt16_MissingCodec.cc
+TestIntegerValues.cc
+TestMetaData.cc
+TestMetaDataReader.cc
+TestMetaDataReader.ksh
+TestMinMax.cc
+TestMissingValue.cc
+TestOdaCAPI.cc
+TestOdaCAPI.h
+TestOrderBy.cc
+TestSQLFunctionsInfo.cc
+TestSelectDataHandle.cc
+TestSelectIterator.cc
+TestSelectIterator2.cc
+TestSelectIterator3.cc
+TestSelectStarAt.cc
+TestSelectTwoFiles.cc
+TestSetvbuffer.cc
+TestStar.cc
+TestTEMPLATE.cc
+TestTextSelect.cc
+TestTextSelect.txt
+TestTextSelect2.cc
+TestTextSelect2.txt
+TestTextSelect3.cc
+TestWriteCatFiles.cc
+MockReader.h
+MockReader.cc
+TestODBModule.cc
+)
+
+include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../odb_api )
+
+ecbuild_add_library( TARGET     odbtools
+                     INSTALL_HEADERS LISTED
+                     HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api/tools
+                     SOURCES    ${odbtools_src_files}
+                     TEMPLATES  ${odbtools_templates}
+                     LIBS       Odb )
+
+ecbuild_add_library( TARGET     odbtest
+                     #INSTALL_HEADERS LISTED
+                     #HEADER_DESTINATION ${INSTALL_INCLUDE_DIR}/odb_api/tools
+                     SOURCES    ${odbtest_src_files}
+                     TEMPLATES  ${odbtest_templates}
+                     LIBS       Odb odbtools )
+
+ecbuild_add_executable( TARGET    odb
+                        SOURCES   odb.cc
+                        LIBS      Odb odbtools odbtest )
diff --git a/odb_api/src/odb_api/tools/CompactTool.cc b/odb_api/src/odb_api/tools/CompactTool.cc
new file mode 100644
index 0000000..9b9aebf
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CompactTool.cc
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+#include "odb_api/tools/CompactTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+CompactTool::CompactTool (int argc, char *argv[]) : Tool(argc, argv) { }
+
+void CompactTool::run()
+{
+	if (parameters().size() != 3)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+	PathName inFile = parameters(1);
+	PathName outFile = parameters(2);
+
+	odb::Reader in(inFile);
+	odb::Writer<> out(outFile);
+
+	odb::Reader::iterator it(in.begin());
+	odb::Reader::iterator end(in.end());
+
+	odb::Writer<>::iterator writer(out.begin());
+	writer->pass1(it, end);
+	
+	odb::Reader outReader(outFile);
+	Log::info() << "Verifying." << std::endl;	
+	odb::Reader::iterator it1 = in.begin();
+	odb::Reader::iterator end1 = in.end();
+
+	odb::Reader::iterator it2 = outReader.begin();
+	odb::Reader::iterator end2 = outReader.end();
+
+	odb::Comparator comparator;
+	comparator.compare(it1, end1, it2, end2, inFile, outFile);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/CompactTool.h b/odb_api/src/odb_api/tools/CompactTool.h
new file mode 100644
index 0000000..d062a83
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CompactTool.h
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+#ifndef CompactTool_H
+#define CompactTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class CompactTool : public Tool {
+public:
+	CompactTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{ o << "Tries to compress a file"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{ o << name << " <input.odb> <output.odb>"; }
+
+private:
+// No copy allowed
+
+    CompactTool(const CompactTool&);
+    CompactTool& operator=(const CompactTool&);
+};
+
+template <> struct ExperimentalTool<CompactTool> { enum { experimental = true }; };
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/CompareTool.cc b/odb_api/src/odb_api/tools/CompareTool.cc
new file mode 100644
index 0000000..e4eb526
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CompareTool.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/tools/CompareTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+CompareTool::CompareTool (int argc, char *argv[])
+: Tool(argc, argv) 
+{
+	registerOptionWithArgument("-excludeColumnsTypes");
+	if (parameters().size() != 3)
+	{
+		Log::error() << "Usage:";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		throw Exception("Wrong number of parameters.");
+	}
+
+
+	PathName p;
+	if (! (p = PathName(parameters()[1])).exists()
+		|| ! (p = PathName(parameters()[2])).exists())
+	{
+		stringstream s;
+		s << "File " << p << " does not exist.";
+		throw Exception(s.str());
+	}
+
+	file1 = new PathName(parameters()[1]);
+	file2 = new PathName(parameters()[2]);
+}
+
+
+void CompareTool::run()
+{
+	Timer t(std::string("Comparing files ") + *file1 + " and " + *file2);
+	odb::Reader oda1(*file1);
+	odb::Reader oda2(*file2);
+
+	odb::Reader::iterator it1(oda1.begin());
+	odb::Reader::iterator end1(oda1.end());
+	odb::Reader::iterator it2(oda2.begin());
+	odb::Reader::iterator end2(oda2.end());
+
+	std::vector<std::string> excludedColumnsTypes = StringTools::split(",", optionArgument("-excludeColumnsTypes", std::string("")));
+
+	if (excludedColumnsTypes.size())
+		Log::info() << "excludedColumnsTypes:" << excludedColumnsTypes << std::endl;
+	
+	bool checkMissing = ! optionIsSet("-dontCheckMissing");
+	odb::Comparator(checkMissing).compare(it1, end1, it2, end2, *file1, *file2, excludedColumnsTypes);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/CompareTool.h b/odb_api/src/odb_api/tools/CompareTool.h
new file mode 100644
index 0000000..8fe7795
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CompareTool.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef CompareTool_H
+#define CompareTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+
+class RowsReaderIterator;
+
+namespace tool {
+
+class CompareTool : public Tool {
+public:
+	CompareTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{
+		o << "Compares two ODB files";
+	}
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " [-excludeColumnsTypes <list-of-columns>] [-dontCheckMissing] <file1.odb> <file2.odb>";
+	}
+
+private:
+// No copy allowed
+
+    CompareTool(const CompareTool&);
+    CompareTool& operator=(const CompareTool&);
+
+	static char* dummyArgv_[];
+
+	eckit::PathName* file1;
+	eckit::PathName* file2;
+
+	odb::RowsReaderIterator* reader1_;
+	odb::RowsReaderIterator* reader2_;
+
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/CountTool.cc b/odb_api/src/odb_api/tools/CountTool.cc
new file mode 100644
index 0000000..a9cdd5f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CountTool.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/tools/CountTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+CountTool::CountTool (int argc, char *argv[]) : Tool(argc, argv) { }
+
+unsigned long long CountTool::fastRowCount(const PathName &db)
+{
+	unsigned long long n = 0;
+
+	typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+	MDR mdReader(db);
+	MDR::iterator it = mdReader.begin();
+	MDR::iterator end = mdReader.end();
+	for (; it != end; ++it)
+		n += it->columns().rowsNumber();
+	return n;
+}
+
+unsigned long long CountTool::rowCount(const PathName &db)
+{
+	odb::Reader oda(db);
+	odb::Reader::iterator i = oda.begin();
+	odb::Reader::iterator end = oda.end();
+
+	unsigned long long n = 0;
+	for ( ; i != end; ++i)
+		++n;
+	return n;
+}
+
+void CountTool::run()
+{
+	if (parameters().size() < 2)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+	unsigned long long n (0);
+    for (size_t i (1); i < parameters().size(); ++i)
+    {
+        const std::string fileName (parameters(i));
+
+        Log::debug() << "CountTool: counting " << fileName << endl;
+
+        n += fastRowCount(fileName);
+    }
+	
+	std::cout << n << std::endl;
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/CountTool.h b/odb_api/src/odb_api/tools/CountTool.h
new file mode 100644
index 0000000..1fe8e61
--- /dev/null
+++ b/odb_api/src/odb_api/tools/CountTool.h
@@ -0,0 +1,47 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_CountTool_H
+#define odb_api_CountTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class CountTool : public Tool {
+public:
+	CountTool (int argc, char *argv[]); 
+
+	static unsigned long long rowCount(const eckit::PathName &);
+	static unsigned long long fastRowCount(const eckit::PathName &);
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{
+		o << "Counts number of rows in files";
+	}
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " <file.odb>";
+	}
+
+private:
+// No copy allowed
+    CountTool(const CountTool&);
+    CountTool& operator=(const CountTool&);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/DefineFunctionHandler.cc b/odb_api/src/odb_api/tools/DefineFunctionHandler.cc
new file mode 100644
index 0000000..b74dbc8
--- /dev/null
+++ b/odb_api/src/odb_api/tools/DefineFunctionHandler.cc
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/ExecutionContext.h"
+#include "ecml/Environment.h"
+#include "ecml/Interpreter.h"
+
+#include "DefineFunctionHandler.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+DefineFunctionHandler::DefineFunctionHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request DefineFunctionHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "function");
+    Request r (request->rest());
+
+    Request params (0);
+    if (r && r->text() == "of")
+    {
+        params = r->value(); 
+        r = r->rest();
+    }
+
+    ASSERT(r->tag() == "" && r->text().size());
+    string name (r->text());
+    Request code (r->value()->value());
+    ASSERT(code->tag() == "_requests");
+
+    Log::debug() << "Defining function " << name << "(" << params << "): " << code << endl; 
+
+    Request function (new Cell("_function", name, params, code));
+    Request frame (new Cell("_frame", "definition", 0, 0));
+    frame->append(new Cell("", name, function, 0));
+
+    context.pushEnvironmentFrame(frame);
+
+    return Cell::clone(function);
+}
+
+} // namespace tool
+} // namespace odb
diff --git a/odb_api/src/odb_api/tools/DefineFunctionHandler.h b/odb_api/src/odb_api/tools/DefineFunctionHandler.h
new file mode 100644
index 0000000..0e87bb0
--- /dev/null
+++ b/odb_api/src/odb_api/tools/DefineFunctionHandler.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_DefineFunctionHandler_H
+#define odb_api_DefineFunctionHandler_H
+
+#include "eckit/filesystem/PathName.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/SpecialFormHandler.h"
+#include "ecml/ExecutionContext.h"
+
+namespace odb { 
+namespace tool {
+
+class DefineFunctionHandler : public ecml::SpecialFormHandler {
+public:
+    DefineFunctionHandler(const std::string&);
+    virtual ecml::Request handle(const ecml::Request, ecml::ExecutionContext&);
+};
+
+} // namespace tool
+
+} //namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/tools/ECMLTool.cc b/odb_api/src/odb_api/tools/ECMLTool.cc
new file mode 100755
index 0000000..6eabaf1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ECMLTool.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/log/Log.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "ecml/core/ExecutionContext.h"
+#include "ecml/prelude/REPLHandler.h"
+
+#include "odb_api/odb_api.h"
+#include "odb_api/ODBModule.h"
+#include "odb_api/tools/ECMLTool.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb::sql;
+
+namespace odb {
+namespace tool {
+
+ECMLTool::ECMLTool(int argc, char **argv) : Tool(argc, argv) {}
+
+ECMLTool::~ECMLTool() {}
+
+void ECMLTool::executeRC(ecml::ExecutionContext& context)
+{
+    const PathName ecmlrc (string(getenv("HOME")) + "/.ecmlrc");
+    if (ecmlrc.exists())
+        try { 
+            Log::info() << "Executing " << ecmlrc << endl;
+            context.executeScriptFile(ecmlrc);
+        } catch (std::exception e)
+        {
+            Log::info() << "Exception while trying to execute " << ecmlrc << ":" << e.what() << endl;
+        }
+}
+
+void ECMLTool::run()
+{
+    ecml::ExecutionContext context;
+    ODBModule odbModule;
+    context.import(odbModule);
+
+    executeRC(context);
+
+    if (parameters().size() < 2)
+    {
+        ecml::REPLHandler::repl(context);
+        return;
+    }
+
+    for (size_t i (1); i < argc(); ++i)
+    {
+        const string param (argv()[i]);
+        if (param == "-e") 
+        {
+            const string& e (argv()[++i]);
+
+            Log::info() << "Trying to execute expression '" << e << "':" << endl;
+            context.execute(e);
+        }
+        else
+            context.executeScriptFile(param);
+    }
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ECMLTool.h b/odb_api/src/odb_api/tools/ECMLTool.h
new file mode 100755
index 0000000..7cdf2f1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ECMLTool.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef odb_api_ECMLTool_H
+#define odb_api_ECMLTool_H
+
+#include "ecml/core/ExecutionContext.h"
+
+#include "Tool.h"
+
+namespace odb {
+
+namespace tool {
+
+class ECMLTool : public Tool {
+
+public:
+	ECMLTool(int argc, char **argv);
+	~ECMLTool();
+
+	virtual void run();
+
+	static void help(std::ostream &o) { o << "Executes ECML script(s) with ODB module imported"; }
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " <script-file> ..." << std::endl;
+	}
+
+    static void executeRC(ecml::ExecutionContext&);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/Examples.cc b/odb_api/src/odb_api/tools/Examples.cc
new file mode 100644
index 0000000..7e5b19c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/Examples.cc
@@ -0,0 +1,222 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file Examples.cc
+///
+/// This file contains examples of usage of public APIs.
+///
+/// @author Piotr Kuchta, ECMWF, June 2015
+
+#include <string>
+#include <iostream>
+#include <vector>
+
+#include "odb_api/odb_api.h"
+
+#include "TestCase.h"
+
+namespace {
+
+TEST(example_select_data_read_results)
+{
+    // Prepare input data
+    const char *data= "x:INTEGER,y:INTEGER,v:DOUBLE\n" "1,1,0.3\n" "1,1,0.2\n" "2,2,0.4\n" "2,2,0.1\n";
+    odb::tool::ImportTool::importText(data, "example_select_data_read_results.odb");
+
+    odb::Select select("select x,min(v),max(v);", "example_select_data_read_results.odb");
+
+    for (odb::Select::iterator it (select.begin()),
+                               end (select.end());
+         it != end;
+         ++it)
+    {
+        double r0 = (*it)[0],
+               r1 = (*it)[1],
+               r2 = (*it)[2];
+
+        std::cout << r0 << ", " << r1 << ", " << r2 << std::endl;
+    }
+}
+
+
+TEST(example_read_data)
+{
+    // Prepare input data
+    const char *data = "x:INTEGER,y:INTEGER,v:DOUBLE\n" "1,1,0.3\n" "1,1,0.2\n" "2,2,0.4\n" "2,2,0.1\n";
+    odb::tool::ImportTool::importText(data, "example_read_data.odb");
+
+    odb::Reader o("example_read_data.odb");
+    for (odb::Reader::iterator it (o.begin()),
+                               end (o.end());
+         it != end;
+         ++it)
+    {
+        double r0 = (*it)[0],
+               r1 = (*it)[1],
+               r2 = (*it)[2];
+
+        std::cout << r0 << ", " << r1 << ", " << r2 << std::endl;
+    }
+}
+
+TEST(example_write_data)
+{
+    odb::MetaData metaData;
+    metaData
+        .addColumn("x", "INTEGER")
+        .addColumn("y", "INTEGER")
+        .addColumn("v", "DOUBLE");
+
+    odb::Writer<> writer("example_write_data.odb");
+    odb::Writer<>::iterator it (writer.begin());
+    it->columns(metaData);
+    it->writeHeader();
+
+    for (size_t i (1); i <= 1000; ++i)
+    {
+        (*it)[0] = i;
+        (*it)[1] = i*2;
+        (*it)[2] = i*3;
+
+        // Incrementing iterator moves coursor to the next row.
+        ++it;
+    }
+}
+
+class ExampleCallback : public ecml::RequestHandler {
+public:
+    ExampleCallback(): ecml::RequestHandler("example_callback") {}
+    virtual ecml::Values handle(ecml::ExecutionContext&);
+};
+
+ecml::Values ExampleCallback::handle(ecml::ExecutionContext& context)
+{
+    // result_set is a variable SQL engine left in the environment for the callback
+    std::string resultSetId (context.environment().lookup("result_set", "", context));
+    std::cout << "ExampleCallback::handle: result_set: " << resultSetId << std::endl;
+    if (! resultSetId.size())
+        throw eckit::UserError("result_set not set");
+
+    // callback may need some extra parameters. These can be set on the script level, see code of the example below.
+    std::vector<std::string> extraParameters (context.environment().lookupList("extra_parameters", context));
+    for (size_t i(0); i < extraParameters.size(); ++i)
+        std::cout << "ExampleCallback::handle: extra_parameters[" << i << "]: " << extraParameters[i] << std::endl;
+
+    odb::sql::ResultSet& resultSet (odb::sql::ResultSetStore::get(resultSetId));
+    std::cout << "resultSet:" << resultSet << std::endl;
+    std::vector<std::vector<double> >& rows (resultSet.rows());
+
+    std::cout << "ExampleCallback:" << std::endl;
+    for (size_t i(0); i < rows.size(); ++i)
+    {
+        std::vector<double>& row (rows[i]);
+        for (size_t c(0); c < row.size(); ++c)
+            std::cout << row[c] << ", ";
+        std::cout << std::endl;
+    }
+
+    return new ecml::Cell("_list", "", 0, 0);
+}
+
+// Define a callback function by implementing and registering a class derived from ecml::RequestHandler
+// Execute SQL SELECT statement using SQL verb, passing the new callback as a requests's parameter.
+TEST(example_sql_select_callback)
+{
+    odb::tool::ImportTool::importText("x:INTEGER,y:INTEGER,v:DOUBLE\n"
+                                       "1,1,0.3\n"
+                                       "1,1,0.2\n"
+                                       "2,2,0.4\n"
+                                       "2,2,0.1\n",
+                                       "example_sql_select_callback_input.odb");
+
+    ecml::ExecutionContext context;
+    odb::ODBModule odbModule;
+    context.import(odbModule);
+
+    ExampleCallback exampleCallback;
+    context.registerHandler("example_callback", exampleCallback);
+    
+    context.execute(
+    // This variable will be used by the calback
+    "let, extra_parameters = 2015 / 2016 / 2017\n"
+
+    "sql,"
+    "  filter = 'select *',"
+    "  source = 'example_sql_select_callback_input.odb',"
+    "  target = 'example_sql_select_callback_output.odb',"
+    "  callback = example_callback"
+    );
+}
+
+// Run callback by registering it and executing as a verb in MARS script
+TEST(example_sql_select_callback_invoked_as_a_request)
+{
+    odb::tool::ImportTool::importText("x:INTEGER,y:INTEGER,v:DOUBLE\n"
+                                       "1,1,0.3\n"
+                                       "1,1,0.2\n"
+                                       "2,2,0.4\n"
+                                       "2,2,0.1\n",
+                                       "example_sql_select_callback_input.odb");
+
+    ecml::ExecutionContext context;
+    odb::ODBModule odbModule;
+    context.import(odbModule);
+
+    ExampleCallback exampleCallback;
+    context.registerHandler("example_callback", exampleCallback);
+
+    bool exceptionThrown (false);
+    try {
+        context.execute("example_callback, extra_parameters=1/2/3");
+    } 
+    catch (const eckit::UserError& e) {
+        std::cout << " *** example_sql_select_callback: Exception thrown: " << e.what() << std::endl;
+        exceptionThrown = true;
+    }
+    ASSERT(exceptionThrown);
+}
+
+// This example shows that a SQL callback can be a function defined in the MARS language.
+// Our MARS function will call the example native function twice,
+// each time with with different extra_parameters.
+TEST(example_sql_select_and_a_mars_verb_as_a_callback)
+{
+    odb::tool::ImportTool::importText("x:INTEGER,y:INTEGER,v:DOUBLE\n"
+                                       "1,1,0.3\n"
+                                       "1,1,0.2\n"
+                                       "2,2,0.4\n"
+                                       "2,2,0.1\n",
+                                       "example_sql_select_callback_input.odb");
+
+    ecml::ExecutionContext context;
+    odb::ODBModule odbModule;
+    context.import(odbModule);
+
+    ExampleCallback exampleCallback;
+    context.registerHandler("example_callback", exampleCallback);
+    
+    context.execute(
+
+    "function, of = result_set, \n"
+    "          example_mars_language_callback = ("
+    "            example_callback, result_set = (value,of=result_set), extra_parameters = 1 / 2 / 3 \n"
+    "            example_callback, result_set = (value,of=result_set), extra_parameters = 1000 / 2000 / 3000 \n"
+    "          ) \n"
+
+    "sql,"
+    "  filter = 'select *',"
+    "  source = 'example_sql_select_callback_input.odb',"
+    "  target = 'example_sql_select_callback_output.odb',"
+    "  callback = example_mars_language_callback"
+    );
+}
+
+} // namespace
+
diff --git a/odb_api/src/odb_api/tools/FixedSizeRowTool.cc b/odb_api/src/odb_api/tools/FixedSizeRowTool.cc
new file mode 100644
index 0000000..a3acd65
--- /dev/null
+++ b/odb_api/src/odb_api/tools/FixedSizeRowTool.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+#include "odb_api/tools/FixedSizeRowTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+FixedSizeRowTool::FixedSizeRowTool (int argc, char *argv[]) : Tool(argc, argv) { }
+
+void FixedSizeRowTool::run()
+{
+	if (parameters().size() != 3)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+	PathName inFile = parameters(1);
+	PathName outFile = parameters(2);
+
+	odb::Reader in(inFile);
+	odb::Writer<> out(outFile);
+
+	odb::Reader::iterator it = in.begin();
+	odb::Reader::iterator end = in.end();
+	odb::Writer<>::iterator outIt(out.begin());
+	outIt->pass1(it, end);
+	
+	odb::Reader outReader(outFile);
+    Log::info() << "Verifying." << std::endl;
+    odb::Reader::iterator it1 = in.begin();
+    odb::Reader::iterator end1 = in.end();
+    odb::Reader::iterator it2 = outReader.begin();
+    odb::Reader::iterator end2 = outReader.end();
+    odb::Comparator comparator;
+    comparator.compare(it1, end1, it2, end2, inFile, outFile);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/FixedSizeRowTool.h b/odb_api/src/odb_api/tools/FixedSizeRowTool.h
new file mode 100644
index 0000000..9a6ee14
--- /dev/null
+++ b/odb_api/src/odb_api/tools/FixedSizeRowTool.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef FixedSizeRowTool_H
+#define FixedSizeRowTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class FixedSizeRowTool : public Tool {
+public:
+	FixedSizeRowTool (int argc, char *argv[]); 
+
+	void run(); 
+	
+	static void help(std::ostream &o)
+	{ o << "Converts file to a format with fixed size rows"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{ o << name << " <input.odb> <output.odb>"; }
+
+private:
+// No copy allowed
+
+    FixedSizeRowTool(const FixedSizeRowTool&);
+    FixedSizeRowTool& operator=(const FixedSizeRowTool&);
+};
+
+template <> struct ExperimentalTool<FixedSizeRowTool> { enum { experimental = true }; };
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/ImportTool.cc b/odb_api/src/odb_api/tools/ImportTool.cc
new file mode 100644
index 0000000..c10540d
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ImportTool.cc
@@ -0,0 +1,98 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/Select.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/Writer.h"
+#include "odb_api/tools/ImportTool.h"
+#include "odb_api/SQLInteractiveSession.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+ImportTool::ImportTool(int argc, char *parameters[])
+: Tool(argc, parameters)
+{
+	registerOptionWithArgument("-d"); // Delimiter
+	registerOptionWithArgument("-sql"); // SQL to filter input CSV with
+}
+
+void ImportTool::run()
+{
+    if (parameters().size() != 3)
+    {
+        Log::error() << "Usage: ";
+        usage(parameters(0), Log::error());
+        Log::error() << std::endl;
+        return;
+    }
+
+    PathName inFile (parameters(1)),
+             outFile (parameters(2));
+
+    Log::info() << "ImportTool::run: inFile: " << inFile << ", outFile: " << outFile << std::endl;
+
+    std::string delimiter (StringTools::upper(optionArgument("-d", defaultDelimiter())));
+    delimiter = delimiter == "TAB" ? "\t" 
+              : delimiter == "SPACE" ? " "
+              : delimiter;
+
+    std::string sql (optionArgument("-sql", std::string("select *;")));
+
+    filterAndImportFile (inFile, outFile, sql, delimiter);
+}
+
+void ImportTool::importFile(const PathName& in, const PathName& out, const std::string& delimiter)
+{
+    filterAndImportFile(in, out, "select *;", delimiter);
+}
+
+void ImportTool::filterAndImportFile(const PathName& in, const PathName& out, const std::string& sql, const std::string& delimiter)
+{
+    odb::sql::SQLInteractiveSession session;
+	session.selectFactory().csvDelimiter(delimiter);
+
+	ifstream fs( in.asString().c_str() );
+	odb::Select input(sql, fs, delimiter);
+
+	odb::Writer<> writer(out);
+	odb::Writer<>::iterator output(writer.begin());
+	unsigned long long n = output->pass1(input.begin(), input.end());
+
+    Log::info() << "ImportTool::importFile: Copied " << n << " rows." << std::endl;
+}
+
+void ImportTool::importText(const std::string& s, const PathName& out, const std::string& delimiter)
+{
+    odb::sql::SQLInteractiveSession session;
+	session.selectFactory().csvDelimiter(delimiter);
+
+	stringstream fs(s);
+	odb::Select input("select *;", fs, delimiter);
+
+	odb::Writer<> writer(out);
+	odb::Writer<>::iterator output(writer.begin());
+
+	unsigned long long n = output->pass1(input.begin(), input.end());
+
+    Log::info() << "ImportTool::importText: Copied " << n << " rows." << std::endl;
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ImportTool.h b/odb_api/src/odb_api/tools/ImportTool.h
new file mode 100644
index 0000000..1eaa149
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ImportTool.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ImportTool_H
+#define ImportTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class ImportTool : public Tool {
+public:
+	ImportTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{ o << "Imports data from a text file"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " [-d delimiter] <input.file> <output.file>" << std::endl;
+		o << " delimiter can be a single character (e.g.: ',') or TAB";
+	}
+
+	static void importFile(const eckit::PathName& in, const eckit::PathName& out, const std::string& delimiter = defaultDelimiter());
+	static void filterAndImportFile(const eckit::PathName& in, const eckit::PathName& out, const std::string& sql, const std::string& delimiter = defaultDelimiter());
+	static void importText(const std::string& s, const eckit::PathName& out, const std::string& delimiter = defaultDelimiter());
+
+	static std::string defaultDelimiter() { return ","; };
+private:
+// No copy allowed
+    ImportTool(const ImportTool&);
+    ImportTool& operator=(const ImportTool&);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
+
diff --git a/odb_api/src/odb_api/tools/IndexTool.cc b/odb_api/src/odb_api/tools/IndexTool.cc
new file mode 100644
index 0000000..aa88b9f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/IndexTool.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Indexer.h"
+#include "odb_api/tools/IndexTool.h"
+#include "odb_api/tools/CountTool.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/Offset.h"
+#include "eckit/io/Length.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+IndexTool::IndexTool (int argc, char *argv[]) : Tool(argc, argv) { }
+
+void IndexTool::run()
+{
+	if (! (parameters().size() == 2 || parameters().size() == 3))
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+    PathName dataFile (parameters(1));
+    PathName indexFile (parameters().size() == 3 
+                        ? parameters(2) 
+                        : parameters(1) + ".idx");
+
+	Indexer::createIndex(dataFile, indexFile);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/IndexTool.h b/odb_api/src/odb_api/tools/IndexTool.h
new file mode 100644
index 0000000..3c7e730
--- /dev/null
+++ b/odb_api/src/odb_api/tools/IndexTool.h
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// @author Piotr Kuchta, ECMWF, Oct 2015
+
+#ifndef IndexTool_H
+#define IndexTool_H
+
+#include "odb_api/Partition.h"
+#include "odb_api/Partitions.h"
+
+namespace odb {
+namespace tool {
+
+typedef std::vector<std::pair<eckit::Offset,eckit::Length> > BlockOffsets;
+typedef unsigned long long ullong;
+
+class IndexTool : public Tool {
+public:
+	IndexTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{
+		o << "Creates index of reports for a given file";
+	}
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " <file.odb> [<file.odb.idx>] ";
+	}
+
+private:
+// No copy allowed
+    IndexTool(const IndexTool&);
+    IndexTool& operator=(const IndexTool&);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/LSTool.cc b/odb_api/src/odb_api/tools/LSTool.cc
new file mode 100644
index 0000000..cf74975
--- /dev/null
+++ b/odb_api/src/odb_api/tools/LSTool.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "odb_api/Reader.h"
+#include "odb_api/tools/LSTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+LSTool::LSTool (int argc, char *argv[]) : Tool(argc, argv)
+{
+	registerOptionWithArgument("-o"); // Text Output
+}
+
+const std::string LSTool::nullString;
+
+unsigned long long LSTool::printData(const std::string &db, std::ostream &out)
+{
+	odb::Reader f(db);
+	odb::Reader::iterator it = f.begin();
+	odb::Reader::iterator end = f.end();
+
+	odb::MetaData md(0);
+	// Formatting of real values:
+    out << std::fixed;
+	unsigned long long n = 0;
+	for ( ; it != end; ++it, ++n)
+	{
+		if (md != it->columns())
+		{
+			md = it->columns();
+			for (size_t i = 0; i < md.size(); ++i)
+				out << md[i]->name() << "\t";
+			out << std::endl;
+		}
+		for (size_t i = 0; i < md.size(); ++i)
+		{
+			switch(md[i]->type())
+			{
+				case odb::INTEGER:
+				case odb::BITFIELD:
+					out << static_cast<int>((*it)[i]);
+					break;
+				case odb::REAL:
+				case odb::DOUBLE:
+					out << (*it)[i];
+					break;
+				case odb::STRING:
+					out << "'" << (*it).string(i) << "'";
+					break;
+				case odb::IGNORE:
+				default:
+					ASSERT("Unknown type" && false);
+					break;
+			}
+			out << "\t";
+		}
+		out << std::endl;
+	}
+	return n;
+}
+
+void LSTool::run()
+{
+	if (parameters().size() < 2)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+    std::string db = parameters(1);
+
+    std::auto_ptr<std::ofstream> foutPtr;
+	if (optionIsSet("-o"))
+        foutPtr.reset(new std::ofstream(optionArgument("-o", std::string("")).c_str()));
+    std::ostream& out = optionIsSet("-o") ? *foutPtr : std::cout;
+
+	unsigned long long n = 0;
+	n = printData(db, out);
+	Log::info() << "Selected " << n << " row(s)." << std::endl;
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/LSTool.h b/odb_api/src/odb_api/tools/LSTool.h
new file mode 100644
index 0000000..07b06d3
--- /dev/null
+++ b/odb_api/src/odb_api/tools/LSTool.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef LSTool_H
+#define LSTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class LSTool : public Tool {
+public:
+	LSTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{ o << "Shows file's contents"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+    { o << name << " [-o <output-file>] <file-name>" << std::endl << std::endl; }
+
+	unsigned long long printData(const std::string &db, std::ostream &out);
+
+private:
+// No copy allowed
+    LSTool(const LSTool&);
+    LSTool& operator=(const LSTool&);
+
+	static const std::string nullString;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/LetHandler.cc b/odb_api/src/odb_api/tools/LetHandler.cc
new file mode 100644
index 0000000..f193e84
--- /dev/null
+++ b/odb_api/src/odb_api/tools/LetHandler.cc
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/types/Types.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/parser/StringTools.h"
+#include "ecml/parser/Request.h"
+
+#include "ecml/ExecutionContext.h"
+#include "ecml/Environment.h"
+#include "ecml/Interpreter.h"
+#include "ecml/SpecialFormHandler.h"
+
+#include "odb_api/tools/LetHandler.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+LetHandler::LetHandler(const string& name)
+: SpecialFormHandler(name)
+{}
+
+Request LetHandler::handle(const Request request, ExecutionContext& context)
+{
+    ASSERT(request->tag() == "_verb" && request->text() == "let");
+
+    Request evaluatedAttributes (context.interpreter().evalAttributes(request, context));
+    Request frame (new Cell("_frame", "let", 0, 0));
+    for (Request e(evaluatedAttributes->rest()); e; e = e->rest())
+    {
+        ASSERT(e->tag() == "");
+
+        const string& name (e->text());
+        Values values (context.interpreter().evalList(e->value(), context));
+
+        frame->append(new Cell("", name, values, 0));
+    }
+    context.pushEnvironmentFrame(frame);
+    return Cell::clone(frame);
+}
+
+} // namespace tool
+} // namespace odb
+
diff --git a/odb_api/src/odb_api/tools/LetHandler.h b/odb_api/src/odb_api/tools/LetHandler.h
new file mode 100644
index 0000000..525dac7
--- /dev/null
+++ b/odb_api/src/odb_api/tools/LetHandler.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, February 2015
+
+#ifndef odb_api_LetHandler_H
+#define odb_api_LetHandler_H
+
+#include "eckit/filesystem/PathName.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/SpecialFormHandler.h"
+
+namespace eckit { class ExecutionContext; }
+
+namespace odb { 
+namespace tool {  
+
+class LetHandler : public ecml::SpecialFormHandler {
+public:
+    LetHandler(const std::string&);
+
+    virtual ecml::Request handle(const ecml::Request, ecml::ExecutionContext&);
+};
+
+} //namespace tool
+} // namespace odb
+
+#endif
diff --git a/odb_api/src/odb_api/tools/ListHandler.cc b/odb_api/src/odb_api/tools/ListHandler.cc
new file mode 100644
index 0000000..5f7114b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ListHandler.cc
@@ -0,0 +1,27 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ListHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/ExecutionContext.h"
+#include "ecml/Environment.h"
+
+using namespace std;
+using namespace eckit;
+
+ListHandler::ListHandler(const string& name) : RequestHandler(name) {}
+
+Values ListHandler::handle(ExecutionContext& context)
+{
+    Values r (Cell::clone(context.environment().lookup("values")));
+    return r;
+}
+
diff --git a/odb_api/src/odb_api/tools/ListHandler.h b/odb_api/src/odb_api/tools/ListHandler.h
new file mode 100644
index 0000000..2e50072
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ListHandler.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef ListHandler_H
+#define ListHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/RequestHandler.h"
+
+class ListHandler : public ecml::RequestHandler {
+public:
+    ListHandler(const std::string&);
+    virtual eckit::Values handle(ecml::ExecutionContext&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/tools/MDSetTool.cc b/odb_api/src/odb_api/tools/MDSetTool.cc
new file mode 100644
index 0000000..3536267
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MDSetTool.cc
@@ -0,0 +1,144 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/parser/Tokenizer.h"
+#include "odb_api/Header.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/Types.h"
+#include "odb_api/tools/MDSetTool.h"
+
+using namespace eckit;
+using namespace std;
+typedef eckit::StringTools S;
+
+namespace odb {
+namespace tool {
+
+MDSetTool::MDSetTool (int argc, char *parameters[]) : Tool(argc, parameters) { }
+
+void MDSetTool::run()
+{
+    if (parameters().size() != 4)
+    {
+        Log::error() << "Usage: ";
+        usage(parameters(0), Log::error());
+        Log::error() << std::endl;
+        return;
+    }
+
+    PathName inFile = parameters(2), outFile = parameters(3);
+    std::auto_ptr<DataHandle> outHandle(ODBAPISettings::instance().writeToFile(outFile));
+
+    std::vector<std::string> columns, types, values;
+    std::vector<BitfieldDef> bitfieldDefs;
+    parseUpdateList(parameters(1), columns, types, values, bitfieldDefs);
+
+    typedef odb::MetaDataReader<odb::MetaDataReaderIterator> R;
+    R reader(inFile, false);
+
+    for (R::iterator it = reader.begin(), end = reader.end();
+        it != end;
+        ++it)
+    {
+        ASSERT(it->isNewDataset());
+        const MetaData& md (it->columns());
+        for (size_t i = 0; i < columns.size(); ++i)
+        {
+            Column& c (*md[md.columnIndex(columns[i])]);
+            Log::info() << "" << columns[i]  << ": " << c << endl;
+
+            if (types[i].size() && types[i] != "NONE") c.type(Column::type(types[i]));
+            if (bitfieldDefs[i].first.size()) c.bitfieldDef(bitfieldDefs[i]);
+            if (values[i].size() && values[i] != "NONE")
+            {
+                odb::codec::Codec& codec (c.coder());
+                if (codec.name().find("constant") == std::string::npos)
+                {
+                    stringstream ss;
+                    ss << "Column '" << columns[i] << "' is not constant (codec: " << codec.name() << ")" << endl;
+                    throw UserError(ss.str());
+                }
+                double v (StringTool::translate(values[i]));
+                c.min(v);
+                c.max(v);
+            }
+        }
+
+		size_t sizeOfEncodedData = (**it).sizeOfEncodedData(); 
+		Properties props;
+	    // See if the file was created on a different order architecture
+		if ((**it).byteOrder() == BYTE_ORDER_INDICATOR)
+		{
+			Log::info() << "MDSetTool::run: SAME ORDER " << sizeOfEncodedData << std::endl;
+
+			serializeHeader<SameByteOrder,DataHandle>(*outHandle, sizeOfEncodedData, md.rowsNumber(), props, md);
+			DataStream<SameByteOrder,DataHandle>(*outHandle).writeBytes((**it).encodedData(), sizeOfEncodedData);	
+		}
+		else
+		{
+			Log::info() << "MDSetTool::run: OTHER ORDER " << sizeOfEncodedData << std::endl;
+			
+			serializeHeader<OtherByteOrder,DataHandle>(*outHandle, sizeOfEncodedData, md.rowsNumber(), props, md);
+			DataStream<OtherByteOrder,DataHandle>(*outHandle).writeBytes((**it).encodedData(), sizeOfEncodedData);	
+		}
+	}
+}
+
+// 
+//static std::vector<std::string> split(const std::string& delim, const std::string& text);
+
+void MDSetTool::parseUpdateList(const std::string& s,
+                                std::vector<std::string>& columns,
+                                std::vector<std::string>& types,
+                                std::vector<std::string>& values,
+                                std::vector<BitfieldDef>& bitfieldDefs)
+{
+    std::vector<std::string> assignments(S::split(",", s));
+	for (size_t i = 0; i < assignments.size(); ++i)
+	{
+		vector<string> assignment(S::split("=", assignments[i]));
+        string value (assignment.size() == 2 ? assignment[1] : "NONE");
+        vector<string> columnNameAndType(S::split(":", assignment[0]));
+        string type (columnNameAndType.size() == 2 ? columnNameAndType[1] : "NONE");
+        string column (assignment[0]);
+	
+        BitfieldDef bf; 
+        if (type.size() && type[0] == '[' && type[type.size() - 1] == ']')
+        {
+            std::vector<std::string> parts(StringTools::split(";", type.substr(1, type.size() - 2)));
+            for (size_t p = 0; p < parts.size(); ++p)
+            {   
+                std::vector<std::string> field (S::split(":", parts[p]));
+                bf.first.push_back(field[0]);
+                bf.second.push_back(atoi(field[1].c_str()));
+            }   
+        }
+
+		Log::info() << "MDSetTool::parseUpdateList: " << column << " : " << type << " = '" << value << "'" << std::endl;
+
+		columns.push_back(column);
+		types.push_back(type);
+		values.push_back(value);
+        bitfieldDefs.push_back(bf);
+	}
+    ASSERT(columns.size() == types.size());
+    ASSERT(columns.size() == values.size());
+    ASSERT(columns.size() == bitfieldDefs.size());
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/MDSetTool.h b/odb_api/src/odb_api/tools/MDSetTool.h
new file mode 100644
index 0000000..e0de183
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MDSetTool.h
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+#ifndef MDSetTool_H
+#define MDSetTool_H
+
+#include "odb_api/tools/Tool.h"
+#include "odb_api/Types.h"
+
+namespace odb {
+namespace tool {
+
+class MDSetTool : public Tool {
+public:
+	MDSetTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{ o << "Creates a new file resetting types or values (constants only) of columns."; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{ 
+        using namespace std;
+
+        o << "Creates a new file resetting types or values (constants only) of columns." << endl << endl
+
+          << "Syntax:" << endl
+          << name << " <update-list> <input.odb> <output.odb>" << endl << endl
+
+          << "Syntax of the <update-list> is a comma separated list of expressions of the form:" << endl
+          << "  <column-name> : <type> = <value>" << endl << endl
+          << "<type> can be one of: integer, real, double, string. If ommited, the existing type of the column will not be changed." << endl
+          << "Both type and value are optional; at least one of the two should be present. For example:" << endl
+          << "  odb mdset \"expver='    0008'\" input.odb patched.odb " << endl
+          << "" << endl
+          << "" << endl
+          ;
+    }
+
+private:
+// No copy allowed
+
+    MDSetTool(const MDSetTool&);
+    MDSetTool& operator=(const MDSetTool&);
+
+	void parseUpdateList(const std::string& s,
+                         std::vector<std::string>& columns,
+                         std::vector<std::string>& types,
+                         std::vector<std::string>& values,
+                         std::vector<BitfieldDef>& bitfieldDefs);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
+
diff --git a/odb_api/src/odb_api/tools/Makefile b/odb_api/src/odb_api/tools/Makefile
new file mode 100755
index 0000000..3b2036e
--- /dev/null
+++ b/odb_api/src/odb_api/tools/Makefile
@@ -0,0 +1,2 @@
+all:
+	m
diff --git a/odb_api/src/odb_api/tools/MergeTool.cc b/odb_api/src/odb_api/tools/MergeTool.cc
new file mode 100644
index 0000000..f5ace46
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MergeTool.cc
@@ -0,0 +1,151 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Writer.h"
+#include "odb_api/tools/MergeTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+MergeTool::MergeTool (int ac, char *av[])
+: Tool(ac, av),
+  inputFiles_(),
+  sql_(),
+  outputFile_(),
+  sqlFiltering_(false)
+{
+	registerOptionWithArgument("-o");
+	if (parameters().size() < 3)
+	{
+		Log::error() << "Usage:";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+    sqlFiltering_ = optionIsSet("-S");
+	std::string o(optionArgument("-o", std::string("<no-default>")));
+	if (o == "<no-default>")
+		UserError("Output file is obligatory (option -o)");
+	outputFile_ = o;
+
+	for (size_t i = 1; i < parameters().size(); ++i) 
+    {
+		inputFiles_.push_back(PathName(parameters()[i]));
+        if (sqlFiltering_) {
+            std::string s(parameters()[++i]);
+            sql_.push_back(StringTool::isSelectStatement(s) ? s : StringTool::readFile(s));
+        }
+    }
+}
+
+
+void MergeTool::run()
+{
+    if (inputFiles_.size() == 0)
+        return;
+    std::stringstream s;
+	for (size_t i = 0; i < inputFiles_.size(); ++i)
+		s << inputFiles_[i] << ",";
+	Timer t(std::string("Merging files '") + s.str() + "' into '" + outputFile_ + "'");
+    if(! sqlFiltering_)
+        merge(inputFiles_, outputFile_);
+    else
+        merge(inputFiles_, sql_, outputFile_);
+}
+
+template <typename T, typename I>
+void doMerge(std::vector<std::pair<I, I> >& iterators, const PathName& outputFile)
+{
+	odb::Writer<> writer(outputFile);
+	odb::Writer<>::iterator out(writer.begin());
+
+	for (size_t i = 0; i < iterators.size(); ++i)
+	{
+		MetaData columns (iterators[i].first->columns());
+
+		for (size_t i = 0; i < columns.size(); ++i)
+			if (out->columns().hasColumn(columns[i]->name()))
+				throw eckit::UserError(std::string("Column '") + columns[i]->name()
+					+ "' occurs in more than one input file of merge.");
+        MetaData md (out->columns());
+        md += columns;
+        out->columns(md);
+		//out->columns() += columns;
+	}
+
+	out->writeHeader();
+	Log::info() << "MergeTool::merge: output metadata: " << out->columns() << std::endl;
+
+	for(;;)
+	{
+		for (size_t i = 0, ii = 0; ii < iterators.size(); ++ii)
+		{
+			I& in(iterators[ii].first);
+			I& inEnd(iterators[ii].second);
+			if(! (in != inEnd))
+                return (void) (Log::info() << "Input file number " << ii << " ended." << std::endl);
+
+			for (size_t cn = 0; cn < in->columns().size(); ++cn)
+			{
+				ASSERT(i < out->columns().size());
+				out->data(i++) = (*in)[cn];
+			}
+			++in;
+		}
+
+		++out;
+	}
+}
+
+template <typename T>
+struct AutoR : public std::vector<T*> { ~AutoR() { for (size_t i = 0; i < this->size(); ++i) delete this->at(i); } }; 
+
+void MergeTool::merge(const std::vector<PathName>& inputFiles, const PathName& outputFile)
+{
+	typedef odb::Reader R;
+	typedef R::iterator I;
+
+    AutoR<R>  readers;
+    std::vector<std::pair<I, I> > iterators;
+
+	for (size_t i = 0; i < inputFiles.size(); ++i)
+	{
+		readers.push_back(new odb::Reader(inputFiles[i]));
+        iterators.push_back(std::make_pair(readers[i]->begin(), readers[i]->end()));
+	}
+    doMerge<R, I>(iterators, outputFile);
+}
+
+void MergeTool::merge(const std::vector<PathName>& inputFiles, const std::vector<std::string>& sqls, const PathName& outputFile)
+{
+    typedef odb::Select S;
+    AutoR<S> readers;
+    AutoR<eckit::FileHandle> fhs;
+    std::vector<std::pair<S::iterator, S::iterator> > iterators;
+	for (size_t i = 0; i < inputFiles.size(); ++i)
+	{
+        FileHandle* fh = new FileHandle(inputFiles[i]);
+        fh->openForRead();
+        fhs.push_back(fh);
+		readers.push_back(new S(sqls[i], *fhs[i]));
+        iterators.push_back(std::make_pair(readers[i]->begin(), readers[i]->end()));
+	}
+    doMerge<S, S::iterator>(iterators, outputFile);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/MergeTool.h b/odb_api/src/odb_api/tools/MergeTool.h
new file mode 100644
index 0000000..130489f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MergeTool.h
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MergeTool_H
+#define MergeTool_H
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class MergeTool : public Tool {
+public:
+	MergeTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o) { o << "Merges rows from files"; }
+	static void usage(const std::string& name, std::ostream &o)
+	{
+        o << std::endl
+          << name << " -o <output-file.odb> <input1.odb> <input2.odb> ..." << std::endl
+                  << ""                                                    << std::endl
+                  << " or "                                                << std::endl
+                  << ""                                                    << std::endl
+          << name << " -S -o <output-file.odb> <input1.odb> <sql-select1> <input2.odb> <sql-select2> ..." << std::endl
+                  ;
+	}
+
+	static void merge(const std::vector<eckit::PathName>& inputFiles, const eckit::PathName& outputFileName);
+	static void merge(const std::vector<eckit::PathName>& inputFiles, const std::vector<std::string>& sqls, const eckit::PathName& outputFileName);
+
+private:
+// No copy allowed
+
+    MergeTool(const MergeTool&);
+    MergeTool& operator=(const MergeTool&);
+
+	static char* dummyArgv_[];
+
+	std::vector<eckit::PathName> inputFiles_;
+    std::vector<std::string> sql_;
+	eckit::PathName outputFile_;
+    bool sqlFiltering_;
+
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/MockReader.cc b/odb_api/src/odb_api/tools/MockReader.cc
new file mode 100644
index 0000000..bbce5b0
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MockReader.cc
@@ -0,0 +1,12 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "MockReader.h"
+
diff --git a/odb_api/src/odb_api/tools/MockReader.h b/odb_api/src/odb_api/tools/MockReader.h
new file mode 100644
index 0000000..1551975
--- /dev/null
+++ b/odb_api/src/odb_api/tools/MockReader.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef MockReader_H
+#define MockReader_H
+
+#include "odb_api/IteratorProxy.h"
+
+namespace odb {
+namespace tool {
+
+template <typename T>
+class MockReader
+{
+public:
+	typedef T iterator_class;
+	typedef odb::IteratorProxy<T, MockReader, const double> iterator;
+
+	iterator begin() { return iterator(new T); }
+	const iterator end() { return iterator(0); }
+};
+
+#include "MockReader.cc"
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/ODA2RequestTool.cc b/odb_api/src/odb_api/tools/ODA2RequestTool.cc
new file mode 100755
index 0000000..5452671
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ODA2RequestTool.cc
@@ -0,0 +1,235 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/config/Resource.h"
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/StringTools.h"
+#include "eckit/parser/Tokenizer.h"
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/GribCodes.h"
+#include "odb_api/Select.h"
+#include "odb_api/tools/ODA2RequestTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+char * static_argv[] = { const_cast<char *>("oda2request") };
+
+ODA2RequestTool::ODA2RequestTool(int argc, char **argv)
+: Tool(argc, argv)
+{
+	registerOptionWithArgument("-c");
+	readConfig();
+}
+
+ODA2RequestTool::ODA2RequestTool()
+: Tool(1, static_argv)
+{
+	registerOptionWithArgument("-c");
+	readConfig();
+}
+
+ODA2RequestTool::~ODA2RequestTool() {}
+
+void ODA2RequestTool::help(std::ostream &o) { o << "Creates MARS ARCHIVE request for a given file"; }
+
+void ODA2RequestTool::usage(const std::string& name, std::ostream &o)
+{
+	o << name << " [-c configFile] [-q] <input-file.odb> [<output-file>]";
+}
+
+void ODA2RequestTool::run()
+{
+	eckit::PathName inputFile;
+	string outputFile;
+
+	switch (parameters().size())
+	{
+		case 3:
+			outputFile = parameters(2);
+		case 2:
+			inputFile = parameters(1);
+			break;
+		default:
+			Log::error() << "Usage: ";
+			usage(parameters(0), Log::error());
+			Log::error() << std::endl;
+			return;// 1;
+			break;
+	}
+
+	readConfig();
+
+	string request = generateMarsRequest(inputFile, optionIsSet("-q"));
+
+	if (outputFile.size() == 0)
+		std::cout << request << std::endl;
+	else
+	{
+		ofstream out(outputFile.c_str());
+		out << request << std::endl;
+		out.close();
+	}
+	
+	return;
+}
+
+PathName ODA2RequestTool::config()
+{
+	string ODB_API_HOME = Resource<std::string>("$ODB_API_HOME", "/usr/local/lib/metaps/lib/odalib/current");
+	return optionArgument("-c", ODB_API_HOME + "//etc//ODA2RequestTool.cfg");
+}
+
+void ODA2RequestTool::readConfig() { readConfig(config()); }
+
+void ODA2RequestTool::readConfig(const PathName& fileName)
+{
+	Log::debug() << "ODA2RequestTool::readConfig: reading file '" << fileName << "'" << std::endl;
+	columnName2requestKey_.clear();
+
+	string s = readFile(fileName);
+	
+	Log::debug() << "ODA2RequestTool::readConfig: parsing '" << s << "'" << std::endl;
+
+	parseConfig(s);
+}
+
+void ODA2RequestTool::parseConfig(const std::string& s)
+{
+	Log::debug() << "ODA2RequestTool::parseConfig: '" << s << "'" << std::endl;
+
+    vector<std::string> lines;
+    Tokenizer("\n")(s, lines);
+
+    Tokenizer tokenizer(": \t");
+    for (size_t i = 0; i < lines.size(); ++i)
+	{
+		vector<std::string> words;
+		tokenizer(lines[i], words);
+
+		if (words.size() == 0)
+			continue;
+
+		ASSERT("Each line of config file should be like: 'MARS_KEYWORD : oda_column_name'" && words.size() == 2);
+		columnName2requestKey_[words[1]] = words[0];
+	}
+}
+
+inline string int_as_double2string(double v)
+{
+	stringstream s;
+	s.precision(0);
+	s << fixed << v;
+	return s.str();
+}
+
+string ODA2RequestTool::gatherStatsFast(const PathName& inputFile)
+{
+	FastODA2Request<ODA2RequestClientTraits> o;
+	o.parseConfig(readFile(config()));
+	o.scanFile(inputFile);
+	return o.genRequest();
+}
+
+void ODA2RequestTool::gatherStats(const PathName& inputFile)
+{
+	size_t n = columnName2requestKey_.size();
+	values_ = vector<Values>(n);
+
+	string columnList;
+	for (std::map<string, string>::iterator it = columnName2requestKey_.begin();
+		 it != columnName2requestKey_.end();
+		 ++it)
+	{
+		if (it != columnName2requestKey_.begin())
+			columnList += ", ";
+		columnList += it->first;
+	}
+	
+	const string select = std::string("select ") + columnList + " from \"" + inputFile + "\";";
+	Log::info() << "Executing '" << select << "'" << std::endl;
+
+	Translator<double, string> double2string;
+	odb::Select oda(select, inputFile);
+	odb::Select::iterator end = oda.end();
+	for (odb::Select::iterator row = oda.begin(); row != end; ++row) 
+		for (size_t i = 0; i < n; ++i)
+		{
+			odb::ColumnType type = row->columns()[i]->type();
+			Value v = type == odb::STRING ? (*row).string(i)
+					: type == odb::INTEGER ? int_as_double2string((*row)[i])
+					: double2string((*row)[i]);
+			values_[i].insert(v);
+		}
+}
+
+string ODA2RequestTool::generateMarsRequest(const PathName& inputFile, bool fast)
+{
+	stringstream request;
+
+	if (fast)
+		request << gatherStatsFast(inputFile);
+	else
+	{
+		gatherStats(inputFile);
+
+		size_t i = 0;
+		std::map<string, string>::iterator end = columnName2requestKey_.end();
+		for (std::map<string, string>::iterator it = columnName2requestKey_.begin(); it != end; ++it)
+		{
+			if (request.str().size()) request << ",\n";
+
+			const std::string& key = it->second;
+			const string k = StringTools::upper(key);
+
+			string valuesList;	
+			Values& vs = values_[i++];
+			for (Values::iterator vi = vs.begin(); vi != vs.end(); ++vi)
+			{
+				string v = *vi;
+				Log::debug() << "ODA2RequestTool::genRequest: v = '" << v  << "', key = " << key << std::endl;
+				if (k == "TIME")
+					v = StringTool::patchTimeForMars(v);
+				else
+				if (k == "CLASS" || k == "TYPE" || k == "STREAM")
+				{
+					Log::debug() << "ODA2RequestTool::genRequest: checking if '" << v << "' is numeric" << std::endl;
+					if (StringTool::check(v, isdigit))
+					{
+						v = StringTools::trim(v);
+						Log::debug() << "ODA2RequestTool::genRequest: replacing " << v << " with ";
+						v = GribCodes::alphanumeric(StringTools::lower(key), v);
+						Log::debug() << v << std::endl;
+					}
+					v = StringTools::upper(v);
+				}
+
+				if (vi != vs.begin())
+					valuesList += "/";
+
+				valuesList += v;
+			}
+			request << key << " = " << valuesList;
+		}
+	}
+
+	stringstream str;
+	str << "ODB," << std::endl;
+	str << request.str();
+	return str.str();
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ODA2RequestTool.h b/odb_api/src/odb_api/tools/ODA2RequestTool.h
new file mode 100755
index 0000000..6fe36b1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ODA2RequestTool.h
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODA2RequestTool_H
+#define ODA2RequestTool_H
+
+#include "odb_api/tools/Tool.h"
+
+namespace odb {
+namespace tool {
+
+class ODA2RequestTool : public Tool {
+	typedef std::string Value;
+	typedef std::set<Value> Values;
+
+public:
+	ODA2RequestTool();
+	ODA2RequestTool(int argc, char **argv);
+	~ODA2RequestTool();
+
+	static void help(std::ostream &o);
+	static void usage(const std::string& name, std::ostream &o);
+
+	virtual void run();
+
+	void readConfig();
+	void readConfig(const eckit::PathName&);
+	void parseConfig(const std::string&);
+
+    std::string generateMarsRequest(const eckit::PathName& inputFile, bool fast = false);
+
+protected:
+	std::vector<Values>& values() { return values_; }
+
+	void gatherStats(const eckit::PathName& inputFile);
+	std::string gatherStatsFast(const eckit::PathName& inputFile);
+
+	eckit::PathName config();
+
+private:
+	std::map<std::string, std::string> columnName2requestKey_;
+	std::vector<Values> values_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/ODAHeaderTool.cc b/odb_api/src/odb_api/tools/ODAHeaderTool.cc
new file mode 100644
index 0000000..50ae33f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ODAHeaderTool.cc
@@ -0,0 +1,194 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "ODAHeaderTool.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+typedef odb::MetaDataReader<odb::MetaDataReaderIterator> MDReader;
+
+class MDPrinter {
+public:
+	virtual void print(std::ostream&, MDReader::iterator &) = 0;
+	virtual void printSummary(std::ostream&) {};
+};
+
+class VerbosePrinter : public MDPrinter {
+public:
+	VerbosePrinter() : headerCount_() {}
+	void print(std::ostream& o, MDReader::iterator &r)
+	{
+        o << std::endl << "Header " << ++headerCount_ << ". "
+			<< "Begin offset: " << (**r).blockStartOffset() << ", end offset: " << (**r).blockEndOffset()
+			<< ", number of rows in block: " << r->columns().rowsNumber() 
+			<< ", byteOrder: " << (((**r).byteOrder() == 1) ? "same" : "other")
+            << std::endl
+			<< r->columns();
+	}
+private:
+	unsigned long headerCount_;
+};
+
+class OffsetsPrinter : public MDPrinter {
+public:
+	OffsetsPrinter() {}
+	void print(std::ostream& o, MDReader::iterator &r)
+	{
+		Offset offset ((**r).blockStartOffset());
+		Length length ((**r).blockEndOffset() - (**r).blockStartOffset());
+		o << offset << " " << length << " " << r->columns().rowsNumber() << " " << r->columns().size() << std::endl;
+	}
+private:
+	unsigned long headerCount_;
+};
+
+class DDLPrinter : public MDPrinter {
+public:
+
+    DDLPrinter(const std::string& path, const std::string& tableName)
+    : path_(path), tableName_(tableName) {}
+
+	void print(std::ostream& o, MDReader::iterator &r)
+	{
+        if (md_.empty() || md_.back() != r->columns())
+        {
+			md_.push_back(r->columns());
+            return;
+        }
+	}
+
+	void printSummary(std::ostream& o) 
+    {
+        for (size_t i(0); i < md_.size(); ++i)
+            printTable(o, md_[i], tableName_, path_);
+    }
+
+    static std::string typeName(const odb::Column& c)
+    {
+        switch (c.type())
+        {
+            case STRING:   return "STRING";
+            case INTEGER:  return "INTEGER";
+            case BITFIELD: return "INTEGER";
+            case REAL:     return "REAL";
+            case DOUBLE:   return "DOUBLE";
+            default:
+                throw new Exception("unknown type");
+        }
+    }
+
+    static std::pair<std::string,std::string> typeDefinitionAndName(const std::string& tableName, const odb::Column& c)
+    {
+        std::stringstream definition;
+        std::string type_name (typeName(c));
+
+        if (c.type() == BITFIELD)
+        {
+            const odb::BitfieldDef& bd (c.bitfieldDef());
+            const std::vector<std::string>& fieldNames (bd.first);
+            const std::vector<int>& sizes (bd.second);
+
+            type_name = stripAtTable(tableName, c.name()) + "_at_" + tableName + "_t";
+
+            definition << "CREATE TYPE " << type_name << " AS (";
+            for (size_t i(0); i < sizes.size(); ++i)
+                definition << fieldNames[i] << " bit" << sizes[i] 
+                           << ((i+1 < sizes.size()) ? ", " : "");
+            definition << ");\n";
+        }
+
+        return make_pair(definition.str(), type_name);
+    }
+
+
+    static std::string stripAtTable(const std::string& tableName, const std::string& columnName)
+    {
+        std::string suffix ( std::string("@") + tableName ); 
+
+        if (columnName.size() >= suffix.size()
+            && columnName.compare(columnName.size() - suffix.size(), suffix.size(), suffix) == 0)
+            return columnName.substr(0, columnName.size() - suffix.size());
+
+        return columnName; 
+    }
+
+    static void printTable(std::ostream& s, const odb::MetaData& md, const std::string& tableName, const std::string& path)
+    {
+        std::stringstream create_type, create_table;
+
+        create_table << "CREATE TABLE " << tableName << " AS (\n";
+        for (size_t i (0); i < md.size(); ++i)
+        {
+            std::pair<std::string,std::string> p (typeDefinitionAndName(tableName, *md[i]));
+            const std::string& def (p.first), 
+                               type (p.second);
+
+            create_type << def;
+            create_table << "  " << stripAtTable(tableName, md[i]->name()) << " " << type << ",\n";
+        }
+        create_table << ") ON '" << path << "';\n";
+
+        s << create_type.str();
+        s << create_table.str();
+    }
+
+private:
+	std::vector<odb::MetaData> md_;
+    const std::string path_;
+    const std::string tableName_;
+};
+
+HeaderTool::HeaderTool (int argc, char *argv[]) : Tool(argc, argv) {}
+
+void HeaderTool::run()
+{
+    registerOptionWithArgument("-table");
+	if (parameters().size() < 2)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+	const std::string db (parameters(1));
+    std::ostream& o (std::cout);
+
+	VerbosePrinter verbosePrinter;
+	OffsetsPrinter offsetsPrinter;
+	DDLPrinter ddlPrinter (db, optionArgument("-table", std::string("foo")));
+
+	MDPrinter& printer(* 
+        (optionIsSet("-offsets") ? static_cast<MDPrinter*>(&offsetsPrinter) : 
+         optionIsSet("-ddl")     ? static_cast<MDPrinter*>(&ddlPrinter) :
+                                   static_cast<MDPrinter*>(&verbosePrinter)));
+
+	MDReader oda(db);
+	MDReader::iterator r(oda.begin());
+	MDReader::iterator end(oda.end());
+
+	odb::MetaData metaData(r->columns());
+	for(; r != end; ++r)
+	{
+		ASSERT (r->isNewDataset());
+		printer.print(o, r);
+		metaData = r->columns();
+	}
+    printer.printSummary(o);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ODAHeaderTool.h b/odb_api/src/odb_api/tools/ODAHeaderTool.h
new file mode 100644
index 0000000..997a1b7
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ODAHeaderTool.h
@@ -0,0 +1,45 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+#ifndef HeaderTool_H
+#define HeaderTool_H
+
+#include "Tool.h"
+
+namespace odb {
+namespace tool {
+
+class HeaderTool : public Tool {
+public:
+	HeaderTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{
+		o << "Shows header(s) and metadata(s) of file";
+	}
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " [-offsets] [-ddl] [-table <table-name-in-the-generated-ddl>] <file-name>";
+	}
+
+private:
+// No copy allowed
+    HeaderTool(const HeaderTool&);
+    HeaderTool& operator=(const HeaderTool&);
+
+	std::string readFile(const std::string &fileName);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/PrintHandler.cc b/odb_api/src/odb_api/tools/PrintHandler.cc
new file mode 100644
index 0000000..25161a9
--- /dev/null
+++ b/odb_api/src/odb_api/tools/PrintHandler.cc
@@ -0,0 +1,41 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "PrintHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/ExecutionContext.h"
+#include "ecml/Environment.h"
+#include "ecml/Interpreter.h"
+
+using namespace std;
+using namespace eckit;
+
+PrintHandler::PrintHandler(const string& name, const string& end)
+: RequestHandler(name),
+  end_(end)
+{}
+
+Values PrintHandler::handle(ExecutionContext& context)
+{
+    Values r (Cell::clone(context.environment().lookupNoThrow("values")));
+    if (!r )
+        r = new Cell("_list", "", 0, 0);
+    else
+        for (Request e(r); e; e = e->rest())
+            if (e->value())
+                cout << context.interpreter().eval(e->value(), context) << " ";
+            else
+                cout << "NULL" << " ";
+
+    cout << end_;
+    return r;
+}
+
diff --git a/odb_api/src/odb_api/tools/PrintHandler.h b/odb_api/src/odb_api/tools/PrintHandler.h
new file mode 100644
index 0000000..27327f0
--- /dev/null
+++ b/odb_api/src/odb_api/tools/PrintHandler.h
@@ -0,0 +1,28 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef odb_api_PrintHandler_H
+#define odb_api_PrintHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/RequestHandler.h"
+
+class PrintHandler : public ecml::RequestHandler {
+public:
+    PrintHandler(const std::string&, const std::string&);
+    virtual eckit::Values handle(ecml::ExecutionContext&);
+private:
+    const std::string end_;
+};
+
+#endif
diff --git a/odb_api/src/odb_api/tools/SQLTool.cc b/odb_api/src/odb_api/tools/SQLTool.cc
new file mode 100755
index 0000000..1f5ec7c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SQLTool.cc
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/Length.h"
+#include "eckit/io/PartFileHandle.h"
+#include "eckit/io/FileDescHandle.h"
+#include "eckit/parser/StringTools.h"
+
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/odb_api.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/tools/SQLTool.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb::sql;
+
+namespace odb {
+namespace tool {
+
+SQLTool::SQLTool(int argc,char **argv)
+: Tool(argc,argv),
+  sqlOutputConfig_(),
+  inputFile_(),
+  offset_(),
+  length_()
+{
+	registerOptionWithArgument("-o");
+	registerOptionWithArgument("-i");
+	registerOptionWithArgument("-I");
+	registerOptionWithArgument("-delimiter");
+	registerOptionWithArgument("-f"); // output format 
+	registerOptionWithArgument("-offset"); 
+	registerOptionWithArgument("-length");
+
+	sqlOutputConfig_.doNotWriteColumnNames(optionIsSet("-T"));
+	sqlOutputConfig_.doNotWriteNULL(optionIsSet("-N"));
+	sqlOutputConfig_.fieldDelimiter(optionArgument("-delimiter", std::string("\t")));
+
+    if ((inputFile_ = optionArgument("-i", std::string(""))) == "-")
+		inputFile_ = "/dev/stdin";
+
+    sqlOutputConfig_.outputFile(optionArgument("-o", std::string("")));
+    if (sqlOutputConfig_.outputFile() == "-")
+		sqlOutputConfig_.outputFile("/dev/stdout");
+
+    sqlOutputConfig_.outputFormat(optionArgument("-f", std::string("default")));
+    sqlOutputConfig_.displayBitfieldsBinary(optionIsSet("--bin") || optionIsSet("--binary"));
+    sqlOutputConfig_.displayBitfieldsHexadecimal(optionIsSet("--hex") || optionIsSet("--hexadecimal"));
+    sqlOutputConfig_.disableAlignmentOfColumns(optionIsSet("--no_alignment"));
+
+    sqlOutputConfig_.fullPrecision(optionIsSet("--full_precision"));
+
+	offset_ = optionArgument("-offset", (long) 0); // FIXME@ optionArgument should accept unsigned long etc
+	length_ = optionArgument("-length", (long) 0);
+}
+
+SQLTool::~SQLTool() {}
+
+void SQLTool::run()
+{
+    if (parameters().size() < 2)
+    {
+        Log::error() << "Usage: ";
+        usage(parameters(0), Log::error());
+        Log::error() << std::endl;
+        return;// 1;
+    }
+    std::vector<std::string> params(parameters());
+    params.erase(params.begin());
+
+    std::string sql(StringTool::isSelectStatement(params[0])
+                ? StringTools::join(" ",  params) + ";"
+                // FIXME:
+                : StringTool::readFile(params[0] == "-" ? "/dev/tty" : params[0]) + ";");
+    std::auto_ptr<std::ofstream> foutPtr(optionIsSet("-o")
+                                ? new std::ofstream(optionArgument("-o", std::string("")).c_str())
+                                : 0);
+    std::ostream& out(foutPtr.get() ? *foutPtr : std::cout);
+    SQLInteractiveSession session(out);
+    session.selectFactory().config(sqlOutputConfig_);
+    SQLOutputConfig config (session.selectFactory().config());
+    PathName inputFile(inputFile_);
+    SQLParser parser;
+    runSQL(sql, inputFile, session, parser, config, offset_, length_);
+}
+
+void SQLTool::execute(const string& sql)
+{
+    execute(sql, cout);
+}
+
+void SQLTool::execute(const string& sql, ostream& out)
+{
+    SQLInteractiveSession session(out);
+    SQLParser parser;
+    SQLOutputConfig config(session.selectFactory().config());
+    runSQL(sql, "", session, parser, config);
+}
+
+void SQLTool::runSQL(const string& sql, const PathName& inputFile, SQLSession& session, SQLParser& parser, const SQLOutputConfig& config)
+{
+    if (inputFile.path().size() == eckit::Length(0)) {
+        parser.parseString(session, sql, static_cast<DataHandle*>(0), config);
+    } else if (inputFile == "/dev/stdin" || inputFile == "stdin") {
+        Log::info() << "Reading from standard input" << std::endl;
+        FileDescHandle fh(0);
+        fh.openForRead();
+        parser.parseString(session, sql, &fh, config);
+    } else {
+        FileHandle fh(inputFile);
+        fh.openForRead();
+        parser.parseString(session, sql, &fh, config);
+    }
+}
+
+void SQLTool::runSQL(const string& sql, const PathName& inputFile, SQLSession& session, SQLParser& parser, const SQLOutputConfig& config, const Offset& offset, const Length& length)
+{
+    if (offset == Offset(0) && length == Length(0))
+    {
+        runSQL(sql, inputFile, session, parser, config);
+        return;
+    }
+
+    if (inputFile.path().size() == eckit::Length(0))
+        parser.parseString(session, sql, static_cast<DataHandle*>(0), config);
+    else
+    {
+        Log::info() << "Selecting " << length << " bytes from offset " << offset << " of " << inputFile << std::endl;
+        PartFileHandle fh(inputFile, offset, length); 
+        fh.openForRead();
+        parser.parseString(session, sql, &fh, config);
+    } 
+}
+
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/SQLTool.h b/odb_api/src/odb_api/tools/SQLTool.h
new file mode 100755
index 0000000..92a8f56
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SQLTool.h
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef _SQLTool_H
+#define _SQLTool_H
+
+#include "odb_api/tools/Tool.h"
+#include "odb_api/SQLOutputConfig.h"
+
+namespace odb {
+
+namespace sql { class SQLSession; class SQLParser; class SQLOutputConfig; }
+
+namespace tool {
+
+class SQLTool : public Tool {
+
+public:
+	SQLTool(int argc, char **argv);
+	~SQLTool();
+	virtual void run();
+    static void execute(const std::string&);
+    static void execute(const std::string&, std::ostream&);
+
+	static void help(std::ostream &o) { o << "Executes SQL statement"; }
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " <select-statement> | <script-filename>" << std::endl;
+        o << "             [-T]                  Disables printing of column names" << std::endl;
+        o << "             [-offset <offset>]    Start processing file at a given offset" << std::endl;
+        o << "             [-length <length>]    Process only given bytes of data" << std::endl;
+        o << "             [-N]                  Do not write NULLs, but proper missing data values" << std::endl;
+        o << "             [-i <inputfile>]      ODB input file" << std::endl;
+        o << "             [-o <outputfile>]     ODB output file" << std::endl;
+        o << "             [-f default|wide|odb] ODB output format (default is ascii; odb is binary ODB, wide is ascii with"
+												<< " bitfields definitions in header)" << std::endl;
+        o << "             [-delimiter <delim>]  Changes the default values' delimiter (TAB by default)" << std::endl; 
+        o << "                                   delim can be any character or string" << std::endl;
+        o << "             [--binary|--bin]      Print bitfields in binary notation" << std::endl;
+        o << "             [--hexadecimal|--hex] Print bitfields in hexadecimal notation" << std::endl;
+        o << "             [--no_alignment]      Do not align columns" << std::endl;
+        o << "             [--full_precision]    Print with full precision" << std::endl;
+	}
+
+private:
+	static void runSQL(const std::string&,
+                const eckit::PathName&,
+                odb::sql::SQLSession&,
+                odb::sql::SQLParser&,
+                const odb::sql::SQLOutputConfig&);
+
+	static void runSQL(const std::string&,
+                const eckit::PathName&,
+                odb::sql::SQLSession&,
+                odb::sql::SQLParser&,
+                const odb::sql::SQLOutputConfig&,
+                const eckit::Offset& offset,
+                const eckit::Length& length);
+
+    sql::SQLOutputConfig sqlOutputConfig_;
+
+	std::string inputFile_;           // -i
+	eckit::Offset offset_;       // -offset
+	eckit::Length length_;       // -length
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/SetTool.cc b/odb_api/src/odb_api/tools/SetTool.cc
new file mode 100644
index 0000000..0c04159
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SetTool.cc
@@ -0,0 +1,110 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include <strings.h>
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "eckit/parser/Tokenizer.h"
+
+#include "odb_api/ConstantSetter.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+
+#include "SetTool.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+SetTool::SetTool (int argc, char *parameters[]) : Tool(argc, parameters) { }
+
+void SetTool::run()
+{
+	if (parameters().size() != 4)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;
+	}
+
+	std::vector<std::string> columns;
+	std::vector<double> values;
+
+	PathName inFile = parameters(2);
+	PathName outFile = parameters(3);
+
+	odb::Reader in(inFile);
+	odb::Writer<> out(outFile);
+
+	odb::Writer<>::iterator writer(out.begin());
+
+	odb::Reader::iterator sourceIt = in.begin();
+	const odb::Reader::iterator sourceEnd = in.end();
+
+	parseUpdateList(parameters(1), columns, values);
+
+	typedef odb::ConstantSetter<odb::Reader::iterator> Setter;
+	Setter setter(sourceIt, sourceEnd, columns, values);
+	Setter::iterator begin = setter.begin();
+	const Setter::iterator end = setter.end();
+	writer->pass1(begin, end);
+}
+
+void SetTool::parseUpdateList(std::string s, std::vector<std::string>& columns, std::vector<double>& values)
+{
+    Tokenizer splitAssignments(",");
+    std::vector<std::string> assignments;
+    splitAssignments(s, assignments);
+	
+    Tokenizer splitEq("=");
+
+	for (size_t i = 0; i < assignments.size(); ++i)
+	{
+		std::vector<std::string> assignment;
+		splitEq(assignments[i], assignment);
+		ASSERT(assignment.size() == 2);
+
+		std::string colName = assignment[0];
+		std::string value = assignment[1];
+		
+		Log::info() << "SetTool::parseUpdateList: " << colName << "='" << value << "'" << std::endl;
+
+		double v = 0;
+
+		if (value.find("0x") != 0)
+			v = translate(value); 
+		else
+		{
+			value = value.substr(2);
+			ASSERT("Format of the hexadecimal value is not correct" && (value.size() % 2) == 0);
+			ASSERT("Hexadecimal literal is too long" && (value.size() / 2) <= sizeof(double));
+		
+			bzero(&v, sizeof(double));
+			for (size_t i = 0; i < value.size() / 2; ++i)
+			{
+				std::string byteInHex = value.substr(i * 2, 2);
+				char *p = 0;
+				unsigned char x;
+				reinterpret_cast<unsigned char*>(&v)[i] = x = static_cast<unsigned char>(strtoul(byteInHex.c_str(), &p, 16));
+				Log::debug() << "SetTool::parseUpdateList: '" << byteInHex << "' => " << x << std::endl;
+			}
+		}
+
+		columns.push_back(colName);
+		values.push_back(v);
+	}
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/SetTool.h b/odb_api/src/odb_api/tools/SetTool.h
new file mode 100644
index 0000000..149cbff
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SetTool.h
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SetTool_H
+#define SetTool_H
+
+#include "Tool.h"
+
+namespace odb {
+namespace tool {
+
+class SetTool : public Tool {
+public:
+	SetTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{ o << "Creates a new file setting columns to given values"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{ o << name << " <update-list> <input.odb> <output.odb>"; }
+
+private:
+// No copy allowed
+
+    SetTool(const SetTool&);
+    SetTool& operator=(const SetTool&);
+
+	void parseUpdateList(std::string s, std::vector<std::string>& columns, std::vector<double>& values);
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
+
diff --git a/odb_api/src/odb_api/tools/SplitTool.cc b/odb_api/src/odb_api/tools/SplitTool.cc
new file mode 100644
index 0000000..f0cf5c9
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SplitTool.cc
@@ -0,0 +1,157 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/PartFileHandle.h"
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Select.h"
+#include "odb_api/Stack.h"
+#include "odb_api/TemplateParameters.h"
+#include "SplitTool.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace tool {
+
+typedef odb::MetaDataReader<odb::MetaDataReaderIterator> MDReader;
+
+SplitTool::SplitTool (int argc, char *argv[])
+: Tool(argc, argv),
+  sort_(false),
+  maxOpenFiles_(200)
+{
+	registerOptionWithArgument("-maxopenfiles");
+}
+
+void SplitTool::run()
+{
+	if (parameters().size() != 3)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << endl;
+		return;
+	}
+
+	if (optionIsSet("-sort")) sort_ = true;
+   
+	maxOpenFiles_ = optionArgument("-maxopenfiles", maxOpenFiles_);
+	Log::debug() << "SplitTool: maxOpenFiles_ = " << maxOpenFiles_ << endl;
+
+	PathName inFile (parameters(1));
+	string outFileTemplate (parameters(2));
+
+	if (sort_)
+		presortAndSplit(inFile, outFileTemplate);
+	else
+		split(inFile, outFileTemplate, maxOpenFiles_, !optionIsSet("-no_verification"));
+}
+
+/**
+ * @param maxExpandedSize maximum size of the data in chunks after decoding
+*/
+vector<pair<Offset,Length> > SplitTool::getChunks(const PathName& inFile, size_t maxExpandedSize)
+{
+    ostream &L(Log::debug());
+	L << "SplitTool::getChunks: " << endl;
+
+    vector<pair<Offset,Length> > r;
+
+    MDReader mdr(inFile);
+    MDReader::iterator it(mdr.begin()), end(mdr.end());
+
+	Offset currentOffset(0);
+	Length currentLength(0);
+	size_t currentSize (0);
+
+    for(; it != end; ++it)
+    {   
+        Offset offset((**it).blockStartOffset());
+        Length length((**it).blockEndOffset() - offset);
+		size_t numberOfRows (it->columns().rowsNumber());
+		size_t numberOfColumns (it->columns().size());
+
+		L << "SplitTool::getChunks: " << offset << " " << length << endl;
+
+		size_t size (numberOfRows * numberOfColumns * sizeof(double));
+		if (currentSize + size > maxExpandedSize)
+		{
+			L << "SplitTool::getChunks: collect " << currentOffset << " " << currentLength << endl;
+            r.push_back(make_pair(currentOffset, currentLength));
+			currentOffset = offset;
+			currentLength = length;
+		} else {
+			currentLength += length;
+			currentSize += numberOfRows * numberOfColumns * sizeof(double);
+		}
+    } 
+	if (r.size() == 0 || r.back().first != currentOffset)
+        r.push_back(make_pair(currentOffset, currentLength));
+	return r;
+}
+
+std::string SplitTool::genOrderBySelect(const std::string& inFile, const std::string& outFileTemplate)
+{
+    MDReader mdr(inFile);
+    MDReader::iterator it(mdr.begin());
+    TemplateParameters templateParameters;
+    TemplateParameters::parse(outFileTemplate, templateParameters, it->columns());
+    std::stringstream ss;
+	ss << "select * order by ";
+	for (size_t i = 0; i < templateParameters.size(); ++i)
+	{
+		if (i) ss << ",";
+		ss << templateParameters[i]->name;
+	}
+	std::string sql (ss.str());
+	Log::info() << "SplitTool::genOrderBySelect: sql: '" << sql << "'" << endl;
+	return sql;
+}
+
+void SplitTool::presortAndSplit(const PathName& inFile, const std::string& outFileTemplate)
+{
+	odb::DispatchingWriter out(outFileTemplate, 1); 
+	odb::DispatchingWriter::iterator outIt (out.begin());
+
+	string sql(genOrderBySelect(inFile, outFileTemplate));
+	
+    vector<std::pair<Offset,Length> > chunks(getChunks(inFile));
+    for(size_t i=0; i < chunks.size(); ++i)
+    {   
+		PartFileHandle h(inFile, chunks[i].first, chunks[i].second);
+		h.openForRead();
+		odb::Select in(sql, h);
+		outIt->pass1(in.begin(), in.end());
+    } 
+}
+
+void SplitTool::split(const PathName& inFile, const std::string& outFileTemplate, size_t maxOpenFiles, bool verify)
+{
+	odb::Reader in(inFile);
+	odb::DispatchingWriter out(outFileTemplate, maxOpenFiles);
+
+	odb::DispatchingWriter::iterator outIt (out.begin());
+	outIt->pass1(in.begin(), in.end());
+
+	odb::Reader input(inFile);
+	odb::Reader::iterator begin(input.begin());
+	odb::Reader::iterator end(input.end());
+	outIt->close();
+    if (verify) (**outIt).verify(begin, end);
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/SplitTool.h b/odb_api/src/odb_api/tools/SplitTool.h
new file mode 100644
index 0000000..d0a0fcc
--- /dev/null
+++ b/odb_api/src/odb_api/tools/SplitTool.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef SplitTool_H
+#define SplitTool_H
+
+#include "eckit/io/Length.h"
+#include "eckit/io/Offset.h"
+#include "Tool.h"
+
+namespace odb {
+namespace tool {
+
+class SplitTool : public Tool {
+public:
+	SplitTool (int argc, char *argv[]); 
+
+	void run(); 
+
+	static void help(std::ostream &o)
+	{
+		o << "Splits file according to given template";
+	}
+
+	static void usage(const std::string& name, std::ostream &o)
+	{
+		o << name << " [-no_verification] [-maxopenfiles <N>] <input.odb> <output_template.odb>";
+	}
+
+	static void split(const eckit::PathName&, const std::string&, size_t, bool verify=true);
+	static void presortAndSplit(const eckit::PathName&, const std::string&);
+
+    static std::vector<std::pair<eckit::Offset,eckit::Length> > getChunks(const eckit::PathName&, size_t maxExpandedSize = 100*1024*1024);
+private:
+// No copy allowed
+    SplitTool(const SplitTool&);
+    SplitTool& operator=(const SplitTool&);
+
+	static std::string genOrderBySelect(const std::string&, const std::string&);
+
+	long maxOpenFiles_;
+	bool sort_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif 
diff --git a/odb_api/src/odb_api/tools/TestAAAImportODB.cc b/odb_api/src/odb_api/tools/TestAAAImportODB.cc
new file mode 100644
index 0000000..5e872c0
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAAAImportODB.cc
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/config/Resource.h"
+#include "eckit/log/CodeLocation.h"
+#include "eckit/log/Log.h"
+
+#include "odb_api/StringTool.h"
+#include "TestCase.h"
+#include "ToolFactory.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	string e(Resource<std::string>("$ODB_API_TEST_DATA_PATH", string(""))); 
+	if (e.size())
+		Log::info() << "ODB_API_TEST_DATA_PATH=" << e << std::endl;
+	string testDataPath = e.size() ? e : "../../../odb_api/src/migrator";
+
+	string cmd;
+	if (getenv("ODB_ROOT"))
+	{
+		cmd = std::string("rm -rf 2000010106 && gzip -d <") + testDataPath + "/2000010106.old.ECMA.tar.gz|tar xf - && ODB_COMPILER_FLAGS=`pwd`/2000010106/ECMA/ECMA.flags odb_migrator 2000010106/ECMA . 2000010106.odb";
+	}
+	else
+	{
+		Log::warning() << "UnitTest: ODB_ROOT not set, skipping testing of odb_migrator" << std::endl;
+
+		cmd = std::string("rm -rf 2000010106 && gzip -d <") + testDataPath + "/2000010106.odb.gz >2000010106.odb";
+	}
+    odb::StringTool::shell(cmd.c_str(), Here());
+}
+
+static void setUp(){}
+
+static void tearDown(){}
+
+
+SIMPLE_TEST(AAAImportODB)
diff --git a/odb_api/src/odb_api/tools/TestAAAImportODBDispatching.cc b/odb_api/src/odb_api/tools/TestAAAImportODBDispatching.cc
new file mode 100644
index 0000000..fa6ee07
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAAAImportODBDispatching.cc
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Log.h"
+#include "eckit/log/CodeLocation.h"
+
+#include "odb_api/StringTool.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	if (getenv("ODB_ROOT"))
+        odb::StringTool::shell("MALLOC_CHECK_=2 ODB_COMPILER_FLAGS=`pwd`/2000010106/ECMA/ECMA.flags odb_migrator 2000010106/ECMA . 2000010106.{obstype}.{sensor}.odb", Here());
+	else {
+		Log::warning() << "UnitTest: ODB_ROOT not set, skipping testing of odb_migrator" << std::endl;
+        odb::StringTool::shell("odb split 2000010106.odb 2000010106.{obstype}.{sensor}.odb", Here());
+	}
+}
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(AAAImportODBDispatching)
diff --git a/odb_api/src/odb_api/tools/TestAggregateFunctions.cc b/odb_api/src/odb_api/tools/TestAggregateFunctions.cc
new file mode 100644
index 0000000..cfbea5f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAggregateFunctions.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	string sql = 
+"SELECT "
+"	count(*),"
+"	count(lat),"
+"	sum(blacklist.fg_depar at body) as sumfg_depar,"
+"	sum((blacklist.fg_depar at body) * (blacklist.fg_depar at body)) as s2umfg_depar,"
+"	min(blacklist.fg_depar at body) as minfg_depar,"
+"	max(blacklist.fg_depar at body) as maxfg_depar, "
+"	sum(biascorr at body) as sumbiascorr,"
+"	sum((biascorr at body) * (biascorr at body)) as s2umbiascorr,"
+"	min(biascorr at body) as minbiascorr,"
+"	max(biascorr at body) as maxbiascorr,"
+"	sum(blacklist.fg_depar at body + biascorr at body) as sumfgdp_unc,"
+"	sum((blacklist.fg_depar at body + biascorr at body) * (blacklist.fg_depar at body + biascorr at body)) as s2umfgdp_unc,"
+"	min(blacklist.fg_depar at body + biascorr at body) as minfgdp_unc,"
+"	max(blacklist.fg_depar at body + biascorr at body) as maxfgdp_unc,\n"
+
+"--obstype at hdr as obstype, varno at body as varno,  status at body as status, lldegrees(lat at hdr)<=-20 as latbin0, lldegrees(lat at hdr)<=20 AND lldegrees(lat at hdr)>-20 as latbin1, lldegrees(lat at hdr)>20 as latbin2 \n"
+
+"FROM \"2000010106.odb\" "
+
+"WHERE (biascorr at body is not NULL and biascorr at body <> 0)"
+"	AND  not((obstype at hdr == 10 and obschar.codetype at hdr == 250))"
+"	AND (obstype at hdr in (1,4,8,9) or (obstype at hdr == 7 and (obschar.codetype at hdr == 215 or obschar.codetype at hdr == 206)));"
+;
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	//Log::info() << "it->columns().size() => " << it->columns().size() << std::endl;
+	ASSERT(it->columns().size() == 14);
+	ASSERT((*it)[0] == 91119); // COUNT(*) == 91119
+	ASSERT((*it)[1] == 91119); // COUNT(lat) == 91119
+}
+
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(AggregateFunctions)
diff --git a/odb_api/src/odb_api/tools/TestAggregateFunctions.sql b/odb_api/src/odb_api/tools/TestAggregateFunctions.sql
new file mode 100644
index 0000000..a0522e5
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAggregateFunctions.sql
@@ -0,0 +1,37 @@
+-- © Copyright 1996-2012 ECMWF.
+-- 
+-- This software is licensed under the terms of the Apache Licence Version 2.0
+-- which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+-- In applying this licence, ECMWF does not waive the privileges and immunities 
+-- granted to it by virtue of its status as an intergovernmental organisation nor
+-- does it submit to any jurisdiction.
+
+SELECT
+	count(*),
+	count(lat),
+	sum(blacklist.fg_depar at body) as sumfg_depar,
+	sum((blacklist.fg_depar at body) * (blacklist.fg_depar at body)) as s2umfg_depar,
+	min(blacklist.fg_depar at body) as minfg_depar,
+	max(blacklist.fg_depar at body) as maxfg_depar, 
+	sum(biascorr at body) as sumbiascorr,
+	sum((biascorr at body) * (biascorr at body)) as s2umbiascorr,
+	min(biascorr at body) as minbiascorr,
+	max(biascorr at body) as maxbiascorr,
+	sum(blacklist.fg_depar at body + biascorr at body) as sumfgdp_unc,
+	sum((blacklist.fg_depar at body + biascorr at body) * (blacklist.fg_depar at body + biascorr at body)) as s2umfgdp_unc,
+	min(blacklist.fg_depar at body + biascorr at body) as minfgdp_unc,
+	max(blacklist.fg_depar at body + biascorr at body) as maxfgdp_unc,
+
+--obstype at hdr as obstype, varno at body as varno,  status at body as status, lldegrees(lat at hdr)<=-20 as latbin0, lldegrees(lat at hdr)<=20 AND lldegrees(lat at hdr)>-20 as latbin1, lldegrees(lat at hdr)>20 as latbin2 
+
+FROM "2000010106/ECMA.odb" 
+
+WHERE (biascorr at body is not NULL and biascorr at body <> 0)
+	AND  not((obstype at hdr == 10 and obschar.codetype at hdr == 250))
+	AND (obstype at hdr in (1,4,8,9) or (obstype at hdr == 7 and (obschar.codetype at hdr == 215 or obschar.codetype at hdr == 206)));
+
+JUST_ONE_TEST(test, setUp, tearDown)
+void test(){}
+void setUp(){}
+void tearDown(){}
+JUST_ONE_TEST(test, setUp, tearDown)
diff --git a/odb_api/src/odb_api/tools/TestAggregateFunctions2.cc b/odb_api/src/odb_api/tools/TestAggregateFunctions2.cc
new file mode 100644
index 0000000..30f8f1b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAggregateFunctions2.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestAggregateFunctions2.h
+///
+/// @author Piotr Kuchta, ECMWF, September 2010
+
+#include "eckit/io/StdFileHandle.h"
+
+#include "odb_api/Select.h"
+#include "odb_api/Writer.h"
+
+#include "ImportTool.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	string sql = "select count(*) from \"TestAggregateFunctions2.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(it->columns().size() == 1);
+	ASSERT((*it)[0] == 10); 
+
+	odb::Select sel(sql);
+	odb::Select::iterator it2 = sel.begin();
+	odb::Select::iterator end2 = sel.end();
+
+	FILE *fout = fopen("TestAggregateFunctions2.odb", "w");
+	StdFileHandle fhout(fout);
+	odb::Writer<> writer(fhout);
+	odb::Writer<>::iterator outit = writer.begin();
+
+	//outit->pass1(it2, end2);
+	size_t i = 0;
+	for (; it2 != end2; ++it2)
+	{
+		++i;
+		ASSERT( (*it2)[0] == 10);
+	}
+	ASSERT( i == 1);
+}
+
+static void setUp()
+{
+	stringstream s;
+	s << "a:REAL" << std::endl;
+	for (size_t i = 1; i <= 10; ++i)
+		s << i << std::endl;
+    odb::tool::ImportTool::importText(s.str().c_str(), "TestAggregateFunctions2.odb");
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(AggregateFunctions2)
diff --git a/odb_api/src/odb_api/tools/TestAggregateFunctions3.cc b/odb_api/src/odb_api/tools/TestAggregateFunctions3.cc
new file mode 100644
index 0000000..878c597
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAggregateFunctions3.cc
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestAggregateFunctions3.h
+///
+/// @author Piotr Kuchta, ECMWF, September 2010
+
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+
+#include "ImportTool.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	string sql = "select sum(a) from \"TestAggregateFunctions3.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select sel(sql);
+	odb::Select::iterator it2 = sel.begin();
+	odb::Select::iterator end2 = sel.end();
+
+	ASSERT( (*it2)[0] == 55);
+}
+
+static void setUp()
+{
+	stringstream s;
+	s << "a:REAL" << std::endl;
+	for (size_t i = 1; i <= 10; ++i)
+		s << i << std::endl;
+    odb::tool::ImportTool::importText(s.str().c_str(), "TestAggregateFunctions3.odb");
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(AggregateFunctions3)
diff --git a/odb_api/src/odb_api/tools/TestAtTableInTheOutput.cc b/odb_api/src/odb_api/tools/TestAtTableInTheOutput.cc
new file mode 100644
index 0000000..d73f3d1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestAtTableInTheOutput.cc
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestAtTableInTheOutput.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/Comparator.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void setUp()
+{
+	odb::Writer<> f("TestAtTableInTheOutput_A.odb");
+	odb::Writer<>::iterator it = f.begin();
+
+    it->setNumberOfColumns(4);
+    it->setColumn(0, "lat at hdr", odb::REAL);
+	it->setColumn(1, "lon at hdr", odb::REAL);
+	it->setColumn(2, "obsvalue", odb::REAL);
+
+    odb::BitfieldDef bfDef;
+	bfDef.first.push_back("x");
+	bfDef.second.push_back(1);
+	bfDef.first.push_back("y");
+	bfDef.second.push_back(2);
+
+	it->setBitfieldColumn(3, "bf", odb::BITFIELD, bfDef);
+
+	it->writeHeader();
+
+	for (size_t i = 1; i <= 2; i++)
+	{
+		(*it)[0] = i; // col 0
+		(*it)[1] = i; // col 1
+		(*it)[2] = i; // col 2
+		++it;
+	}
+}
+
+static void selectIntoSecondFile()
+{
+	const string fileName = "TestAtTableInTheOutput_A.odb";
+	string sql = "select lat,lon,obsvalue,bf into \"TestAtTableInTheOutput_B.odb\"";
+	sql += " from \"" + fileName + "\" ;";
+
+	odb::Select f(sql); //, fileName);
+	odb::Select::iterator it = f.begin();
+
+	//string c0 = it.columns()[0]->name();
+	//string c1 = it.columns()[1]->name();
+	//string c2 = it.columns()[2]->name();
+
+	++it; // this is needed to push the second row to the INTO file 
+
+	//Log::info()  << "c0=" << c0 << ", c1=" << c1 << ", c2=" << c2 << std::endl;
+	///ASSERT("");
+}
+
+static void compareFiles()
+{
+	odb::Reader oda1("TestAtTableInTheOutput_A.odb");
+	odb::Reader oda2("TestAtTableInTheOutput_B.odb");
+
+	odb::Reader::iterator it1(oda1.begin());
+	odb::Reader::iterator end1(oda1.end());
+	odb::Reader::iterator it2(oda2.begin());
+	odb::Reader::iterator end2(oda2.end());
+	
+	odb::Comparator().compare(it1, end1, it2, end2, "TestAtTableInTheOutput_A.odb", "TestAtTableInTheOutput_B.odb");
+}
+
+
+static void test()
+{
+    selectIntoSecondFile();
+    compareFiles();
+}
+
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(AtTableInTheOutput)
diff --git a/odb_api/src/odb_api/tools/TestBitfields.cc b/odb_api/src/odb_api/tools/TestBitfields.cc
new file mode 100644
index 0000000..8a8f990
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestBitfields.cc
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Log.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest problem fixed with p4 change 23687
+///
+static void test()
+{
+	string statusFields =
+"status.active at body,status.passive at body,status.rejected at body,status.blacklisted at body,status.monthly at body,status.constant at body,status.experimental at body,status.whitelist at body";
+
+	statusFields = "status.*@body";
+	const string SELECT = std::string("select status at body, ") + statusFields + " from \"2000010106.odb\";";
+
+	Log::info() << "Executing '" << SELECT << "'" << std::endl;
+
+	odb::Select oda(SELECT);
+	long int i=0;
+
+	odb::Select::iterator it = oda.begin();
+
+	Log::debug() << "test: it->columns().size() == " << it->columns().size() << std::endl;
+
+	ASSERT(it->columns().size() == 9);
+
+	for ( ; it != oda.end() && i < 5000; ++it, ++i) 
+	{
+		unsigned int sum =
+			  int ((*it)[1])
+			| int ((*it)[2]) << 1
+			| int ((*it)[3]) << 2
+			| int ((*it)[4]) << 3
+			| int ((*it)[5]) << 4
+			| int ((*it)[6]) << 5
+			| int ((*it)[7]) << 6
+			| int ((*it)[8]) << 7;
+		//Log::info() << i << ": " << (*it)[0] << " " << sum << std::endl;
+		ASSERT((*it)[0] == sum);
+	}
+}
+
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(Bitfields)
diff --git a/odb_api/src/odb_api/tools/TestCase.cc b/odb_api/src/odb_api/tools/TestCase.cc
new file mode 100644
index 0000000..058dc07
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCase.cc
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestCase.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <iostream>
+#include <vector>
+#include <map>
+using namespace std;
+
+#include "Tool.h"
+#include "TestCase.h"
+#include "ToolFactory.h"
+
+namespace odb {
+namespace tool {
+namespace test {
+
+TestCase::TestCase(int argc, char **argv)
+: Tool(argc, argv)
+{}
+
+void TestCase::run()
+{
+	setUp();
+	test();
+	tearDown();
+}
+
+TestCase::~TestCase() {}
+
+void TestCase::setUp() {}
+void TestCase::test() {}
+void TestCase::tearDown() {}
+
+} // namespace test 
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/TestCase.h b/odb_api/src/odb_api/tools/TestCase.h
new file mode 100644
index 0000000..3fed71f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCase.h
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestCase.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef TestCase_H
+#define TestCase_H
+
+#include "Tool.h"
+#include "ToolFactory.h"
+
+namespace odb {
+namespace tool {
+namespace test {
+
+class TestCase : public Tool {
+public:
+
+	static void help(std::ostream &o) { o << "No help available for this command yet"; }
+	static void usage(const std::string& name, std::ostream &o) { o << name << ": Not implemented yet"; }
+	virtual void run();
+
+	virtual void setUp();
+	virtual void test();
+	virtual void tearDown();
+
+	virtual ~TestCase();
+
+protected:
+	TestCase(int argc, char **argv);
+};
+
+typedef std::vector<TestCase*> TestCases;
+
+#define TESTCASE(F) \
+struct Test_##F : public TestCase { Test_##F(int argc, char **argv) : TestCase(argc, argv) {} void test() { F(); } }; \
+ToolFactory<Test_##F> test_##F(std::string("Test_") + #F); 
+
+#define TEST_FIXTURE(F, T) \
+struct Test_##F##_##T : public F, public odb::tool::test::TestCase \
+{ \
+    Test_##F##_##T(int argc, char **argv) : F(), odb::tool::test::TestCase(argc, argv) {} \
+    void test(); \
+}; \
+odb::tool::ToolFactory<Test_##F##_##T> test_##F##_##T(std::string("Test_") + #F + std::string("_") + #T); \
+void Test_##F##_##T::test()
+
+#define TEST(T) \
+struct Test_##T : public odb::tool::test::TestCase \
+{ \
+    Test_##T(int argc, char **argv) : odb::tool::test::TestCase(argc, argv) {} \
+    void test(); \
+}; \
+odb::tool::ToolFactory<Test_##T> test_##T(std::string("Test_") + #T); \
+void Test_##T::test()
+
+#define SIMPLE_TEST(name) \
+struct Test_##name : public odb::tool::test::TestCase \
+{ \
+    Test_##name(int argc, char **argv) : odb::tool::test::TestCase(argc, argv) {} \
+    void setUp() { ::setUp(); } \
+    void tearDown() { ::tearDown(); } \
+    void test() { ::test(); } \
+}; \
+odb::tool::ToolFactory<Test_##name> test_##name(std::string("Test_") + #name);
+
+#define CHECK(expected) ASSERT(expected)
+#define CHECK_EQUAL(expected, actual) ASSERT((expected) == (actual))
+
+template <typename Expected, typename Actual>
+bool CheckArrayEqual(const Expected& expected, const Actual& actual, const int count)
+{
+    bool equal = true;
+    for (int i = 0; i < count; ++i)
+        equal &= (expected[i] == actual[i]);
+    return equal;
+}
+
+#define CHECK_ARRAY_EQUAL(expected, actual, count) \
+ASSERT(odb::tool::test::CheckArrayEqual(expected, actual, count))
+
+} // namespace test 
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/TestCatFiles.cc b/odb_api/src/odb_api/tools/TestCatFiles.cc
new file mode 100644
index 0000000..69fed50
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCatFiles.cc
@@ -0,0 +1,173 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void setUp()
+{
+    {
+        odb::Writer<> oda("file1.odb");
+        odb::Writer<>::iterator row = oda.begin();
+
+        row->setNumberOfColumns(3);
+
+        row->setColumn(0, "x", odb::REAL);
+        row->setColumn(1, "y", odb::REAL);
+        row->setColumn(2, "z", odb::REAL);
+
+        row->writeHeader();
+
+        for (size_t i = 1; i <= 2; i++)
+        {
+            (*row)[0] = i; // col 0
+            (*row)[1] = i; // col 1
+            (*row)[2] = i; // col 2
+            ++row;
+        }
+
+        odb::Writer<> oda2("file2.odb");
+        odb::Writer<>::iterator row2 = oda2.begin();
+        row2->setNumberOfColumns(3);
+        row2->setColumn(0, "x", odb::REAL);
+        row2->setColumn(1, "y", odb::REAL);
+        row2->setColumn(2, "v", odb::REAL);
+
+        row2->writeHeader();
+
+        for (size_t i = 1; i <= 2; i++)
+        {
+            (*row2)[0] = i * 10; // col 0
+            (*row2)[1] = i * 100; // col 1
+            (*row2)[2] = i * 1000; // col 2
+            ++row2;
+        }
+
+        odb::Writer<> oda3("file3.odb");
+        odb::Writer<>::iterator row3 = oda3.begin();
+        row3->setNumberOfColumns(4);
+
+        row3->setColumn(0, "x", odb::REAL);
+        row3->setColumn(1, "v", odb::REAL);
+        row3->setColumn(2, "y", odb::REAL);
+        row3->setColumn(3, "z", odb::REAL);
+        row3->writeHeader();
+
+        for (size_t i = 1; i <= 2; i++)
+        {
+            (*row3)[0] = i * 10; // col 0
+            (*row3)[1] = i * 1000; // col 1
+            (*row3)[2] = i * 100; // col 2
+            (*row3)[3] = 13;     // col 3
+            ++row3;
+        }
+        // dtors fire here
+    }
+
+    int catStatus = system("cat file1.odb file2.odb file3.odb >concatenated.odb");
+    ASSERT(WEXITSTATUS(catStatus) == 0);
+
+}
+
+
+static void test()
+{
+    const string sql = "select X,Y from \"concatenated.odb\";";
+    const string fileName = "concatenated.odb";
+    odb::Select oda(sql, fileName);
+    Log::info() << "Iterating " << sql << std::endl;
+
+    //for (Reader::iterator it = oda.begin();
+    //	it != oda.end();
+    //	++it)
+    //{}
+
+    odb::Select::iterator it = oda.begin();
+    int j = 1;
+
+    Log::info() << "j = " << j << std::endl;
+    const double * data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j);
+    ASSERT(data[1] == j);
+    ++j;
+
+    ++it;
+    Log::info() << "j = " << j << std::endl;
+    data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j);
+    ASSERT(data[1] == j);
+    ++j;
+
+    j = 1; // data from file2.oda
+
+    ++it;
+    Log::info() << "j = " << j << std::endl;
+    data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j * 10);
+    ASSERT(data[1] == j * 100);
+    ++j;
+
+    ++it;
+    Log::info() << "j = " << j << std::endl;
+    data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j * 10);
+    ASSERT(data[1] == j * 100);
+
+
+    j = 1; // data from file3.oda
+
+    ++it;
+    Log::info() << "j = " << j << std::endl;
+    data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j * 10);
+    ASSERT(data[1] == j * 100);
+    ++j;
+
+    ++it;
+    Log::info() << "j = " << j << std::endl;
+    data = it->data();
+    ASSERT(data);
+    Log::info() << "data[0] = " << data[0] << std::endl;
+    Log::info() << "data[1] = " << data[1] << std::endl;
+    ASSERT(data[0] == j * 10);
+    ASSERT(data[1] == j * 100);
+}
+
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(CatFiles)
diff --git a/odb_api/src/odb_api/tools/TestCodec.cc b/odb_api/src/odb_api/tools/TestCodec.cc
new file mode 100644
index 0000000..aaad5be
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCodec.cc
@@ -0,0 +1,90 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <strings.h>
+
+#include "eckit/io/DataHandle.h"
+#include "odb_api/DataStream.h"
+#include "odb_api/Codec.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+class MockDataHandle : public DataHandle
+{
+public:
+    MockDataHandle() : offset_(0) { bzero(buf_, sizeof(buf_)); }
+
+    virtual void print(std::ostream& s) const { }
+    virtual Length openForRead() { offset_ = 0; return sizeof(buf_); }
+    virtual void openForWrite(const Length&) { offset_ = 0; }
+    virtual void openForAppend(const Length&) {}
+
+    virtual long read(void *p, long n)
+    {
+        memcpy(p, buf_ + offset_, n);
+        offset_ += n;
+        return n;
+    }
+    virtual long write(const void* p, long n) { return n; }
+    virtual void close() {}
+    virtual void rewind() {}
+    virtual Length estimate() { return 0; }
+    virtual Offset position() { return 0; }
+
+    unsigned char *buffer() { return buf_; }
+
+private:
+    unsigned char buf_[1024];
+    size_t offset_;
+};
+
+
+odb::codec::Codec *c = 0;
+
+static void test()
+{
+    Log::info() << fixed;
+
+    string codecNames[] = {"short_real", "long_real"};
+    for (size_t i = 0; i < sizeof(codecNames) / sizeof(string); i++)
+    {
+        Log::info() << "UnitTest codec " << codecNames[i] << std::endl;
+
+        MockDataHandle dh;
+        c = odb::codec::Codec::findCodec<odb::DataStream<odb::SameByteOrder, DataHandle> >(codecNames[i], false);
+
+        unsigned char *s = dh.buffer();
+
+        double v = odb::MDI::realMDI();
+        Log::info() << "Encode: " << v << std::endl;
+
+        s = c->encode(s, v);
+
+        c->dataHandle(&dh);
+        double d = c->decode();
+        Log::info() << "Decoded: " << d << std::endl;
+
+        ASSERT(v == d);
+    }
+}
+
+static void tearDown() { delete c; }
+
+static void setUp(){}
+
+SIMPLE_TEST(Codec)
diff --git a/odb_api/src/odb_api/tools/TestCodecOptimization.cc b/odb_api/src/odb_api/tools/TestCodecOptimization.cc
new file mode 100644
index 0000000..8f35f60
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCodecOptimization.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestCodecOptimization.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/exception/Exceptions.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+const size_t numberOfColumns = 1;
+
+
+static void setUp()
+{
+
+    odb::Writer<> oda("TestCodecOptimization.odb");
+    odb::Writer<>::iterator row = oda.begin();
+
+    row->setNumberOfColumns(numberOfColumns);
+
+    row->setColumn(0, "x", odb::REAL);
+    //row->setColumn(1, "y", odb::REAL);
+    //row->setColumn(2, "z", odb::REAL);
+
+    row->writeHeader();
+
+    //for (size_t i = 1; i <= 2; i++)
+    //{
+    size_t i = 1;
+    (*row)[0] = i; // col 0
+    //(*row)[1] = i; // col 1
+    //(*row)[2] = i; // col 2
+    ++row;
+    //}
+    row->close();
+
+}
+
+static void test()
+{
+
+    odb::Reader reader("TestCodecOptimization.odb");
+    odb::Reader::iterator it = reader.begin();
+    const odb::MetaData& md = it->columns();
+
+    Log::info() << "testSelectIterator: md = " << md << std::endl;
+    ASSERT(md.size() == numberOfColumns);
+
+    string codecName = md[0]->coder().name();
+
+    Log::info() << "testSelectIterator: codecName = " << codecName << std::endl;
+
+    ASSERT("All columns should be constant" && codecName == "constant");
+    return;
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(CodecOptimization)
diff --git a/odb_api/src/odb_api/tools/TestCommandLineParsing.cc b/odb_api/src/odb_api/tools/TestCommandLineParsing.cc
new file mode 100644
index 0000000..1490e8c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestCommandLineParsing.cc
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, July 2009
+
+#include "eckit/exception/Exceptions.h"
+
+#include "Tool.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+struct TestTool : public odb::tool::Tool {
+
+    TestTool(int argc, char **argv) : odb::tool::Tool(argc, argv)
+	{
+		registerOptionWithArgument("-foo");
+		registerOptionWithArgument("-intOpt");
+	}
+
+	static void help(std::ostream &o) { o << "No help available for this command yet." << std::endl; }
+
+	void run() {
+		Log::info() << "test: run" << std::endl;
+
+		ASSERT(optionArgument("-foo", std::string("NONE")) == "bar");
+		ASSERT(optionArgument("-intOpt", 0) == 69);
+
+		ASSERT(optionIsSet("-blah"));
+		ASSERT(optionIsSet("-blahblah"));
+		ASSERT(! optionIsSet("-blahblahblah"));
+		ASSERT(optionIsSet("-lastOption"));
+
+		ASSERT(parameters().size() == 3);
+		ASSERT(parameters()[0] == "p1");
+		ASSERT(parameters()[1] == "p2");
+		ASSERT(parameters()[2] == "p3");
+		
+	}
+};
+
+static void test()
+{
+	const char *args[] = {"-foo", "bar", "-intOpt", "69", "-blah", "-blahblah", "p1", "p2", "-lastOption", "p3", 0};
+
+    TestTool testTool(sizeof(args) / sizeof(char *) - 1, const_cast<char **>(args));
+	testTool.run();
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(CommandLineParsing)
diff --git a/odb_api/src/odb_api/tools/TestConstCodec.cc b/odb_api/src/odb_api/tools/TestConstCodec.cc
new file mode 100644
index 0000000..8a758b6
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestConstCodec.cc
@@ -0,0 +1,128 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestConstCodec.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/log/Log.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/Codec.h"
+#include "odb_api/Column.h"
+#include "odb_api/DataStream.h"
+#include "odb_api/HashTable.h"
+#include "odb_api/Header.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Reader.h"
+#include "odb_api/ReaderIterator.h"
+#include "odb_api/SQLAST.h"
+#include "odb_api/SQLBitfield.h"
+#include "odb_api/SchemaAnalyzer.h"
+#include "odb_api/SelectIterator.h"
+#include "odb_api/Writer.h"
+#include "odb_api/odb_api.h"
+#include "MockReader.h"
+#include "TestCase.h"
+#include "Tool.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+const char *pies = "pies\0\0\0\0";
+
+class MockReaderIterator 
+{
+public:
+	MockReaderIterator()
+	: columns_(1),
+	  nRows_(2),
+      n_(nRows_),
+      data_(reinterpret_cast<double *>(const_cast<char *>(pies))),
+      refCount_(0),
+      noMore_(false)
+	{
+		odb::Column* col = columns_[0] = new odb::Column(columns_);
+		ASSERT(col);
+
+		col->name("foobar"); 
+		col->type<DataStream<SameByteOrder, DataHandle> >(odb::STRING, false);
+		col->hasMissing(false);
+		//col->missingValue(0);
+		odb::codec::HashTable *ht = new odb::codec::HashTable;
+		ht->store("pies");
+		odb::codec::CodecChars<odb::SameByteOrder> *codec = new odb::codec::CodecChars<odb::SameByteOrder>;
+		codec->hashTable(ht);
+		col->coder(codec);
+	}
+
+	odb::MetaData& columns() { return columns_; }
+
+	bool isNewDataset() { return false; } // { return n_ == nRows_; }
+	double* data() { return data_; }
+	
+	MockReaderIterator& operator++() { next(0); return *this; }
+	bool operator!=(const MockReaderIterator& o) { return nRows_ > 0; }
+	const MockReaderIterator& end() { return *reinterpret_cast<MockReaderIterator*>(0); }
+
+	bool next(ecml::ExecutionContext*) { bool r = nRows_-- > 0; noMore_ = !r; return r; }
+
+private:
+	odb::MetaData columns_;
+	int nRows_;
+	int n_;
+	double *data_;
+public:
+	int refCount_;
+	bool noMore_;
+    ecml::ExecutionContext* context_;
+};
+
+
+static void setUp()
+{
+	Timer t("Writing TestConstCodec.odb");
+	odb::Writer<> oda("TestConstCodec.odb");
+
+	typedef tool::MockReader<MockReaderIterator> M;
+
+	M mockReader;
+	odb::Writer<>::iterator outit = oda.begin();
+
+	M::iterator b = mockReader.begin();
+	const M::iterator e = mockReader.end();
+		
+	outit->pass1(b, e);
+}
+
+static void test()
+{
+	odb::Reader oda("TestConstCodec.odb");
+	odb::Reader::iterator it = oda.begin();
+	odb::Reader::iterator end = oda.end();
+
+	Log::info() << it->columns() << std::endl;
+	
+	for ( ; it != end; ++it)
+	{
+		//Log::debug() << "test: '" << it.string(0) << "' (" << (*it)[0] << ")" << std::endl;
+		ASSERT((*it)[0] == * ((double *) pies));
+	}
+
+	Log::debug() << "test: codec name is '" << it->columns()[0]->coder().name() << "'" << std::endl;
+	ASSERT(it->columns()[0]->coder().name() == "constant_string");
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(ConstCodec)
diff --git a/odb_api/src/odb_api/tools/TestConstIntegerCodec.cc b/odb_api/src/odb_api/tools/TestConstIntegerCodec.cc
new file mode 100644
index 0000000..095f8ea
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestConstIntegerCodec.cc
@@ -0,0 +1,96 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "MockReader.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+const long the_const_value = 20090624;
+
+class MockReaderIterator2 
+{
+public:
+	MockReaderIterator2() : columns_(1), nRows_(2), n_(nRows_), data_(the_const_value), refCount_(0), noMore_(false)
+	{
+		odb::Column* col = columns_[0] = new odb::Column(columns_);
+		ASSERT(col);
+
+		col->name("date"); 
+        col->type<odb::DataStream<odb::SameByteOrder, DataHandle> >(odb::INTEGER, false);
+		col->hasMissing(false);
+		odb::codec::CodecInt32<odb::SameByteOrder> *codec = new odb::codec::CodecInt32<odb::SameByteOrder>;
+		col->coder(codec);
+	}
+
+	odb::MetaData& columns() { return columns_; }
+
+	bool isNewDataset() { return false; } // { return n_ == nRows_; }
+	double* data() { return &data_; }
+
+	bool next(ecml::ExecutionContext*) { return !(noMore_ = nRows_-- <= 0); }
+
+private:
+	odb::MetaData columns_;
+	int nRows_;
+	int n_;
+	double data_;
+
+public:
+	int refCount_;
+	bool noMore_;
+    ecml::ExecutionContext* context_;
+};
+
+static void setUp()
+{
+	Timer t("Writing test_integer_const.odb");
+	odb::Writer<> oda("test_integer_const.odb");
+
+	typedef tool::MockReader<MockReaderIterator2> M;
+	M reader;
+
+	odb::Writer<>::iterator outit = oda.begin();
+
+	M::iterator b = reader.begin();
+	const M::iterator e = reader.end();
+	outit->pass1(b, e);
+}
+
+static void test()
+{
+	odb::Reader oda("test_integer_const.odb");
+	odb::Reader::iterator it = oda.begin();
+	odb::Reader::iterator end = oda.end();
+
+	Log::info() << it->columns() << std::endl;
+	
+	for ( ; it != end; ++it)
+		ASSERT((*it)[0] == the_const_value);
+
+	Log::debug() << "test: codec name is '" << it->columns()[0]->coder().name() << "'" << std::endl;
+	ASSERT(it->columns()[0]->coder().name() == "constant");
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(ConstIntegerCodec)
diff --git a/odb_api/src/odb_api/tools/TestDataJoin.cc b/odb_api/src/odb_api/tools/TestDataJoin.cc
new file mode 100644
index 0000000..5dad67b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataJoin.cc
@@ -0,0 +1,188 @@
+/// @file   TestDataJoin.cc
+/// @author Tomas Kral
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataJoin.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+
+struct Tables
+{
+    Tables()
+      : left("left", columns("left")),
+        right("right", columns("right"))
+    {}
+
+    static DataColumns columns(const string& name);
+
+    DataTable left;
+    DataTable right;
+};
+
+DataColumns Tables::columns(const string& name)
+{
+    DataColumns columns;
+
+    if (name == "left")
+        columns.add("id at left", "INTEGER");
+    else if (name == "right")
+        columns.add("id at right", "INTEGER");
+    else if (name == "result")
+    {
+        columns.add("id at left", "INTEGER");
+        columns.add("id at right", "INTEGER");
+    }
+
+    return columns;
+}
+
+struct LinkedTables : public Tables
+{
+    DataJoin join;
+    DataTable result;
+
+    LinkedTables()
+      : Tables(),
+        join(left, right, "id at left", "id at right"),
+        result("expected", columns("result"))
+    {
+        left  << 1, 2, 3, 4, 5;
+
+        right << 1,
+                 2, 2,
+                 3, 3, 3,
+                 4, 4, 4, 4,
+                 5, 5, 5, 5, 5;
+
+        result << 1, 1,
+                  2, 2,
+                  2, 2,
+                  3, 3, 3,
+                  3, 3, 3,
+                  4, 4, 4, 4,
+                  4, 4, 4, 4,
+                  5, 5, 5, 5, 5,
+                  5, 5, 5, 5, 5;
+    }
+};
+
+TEST_FIXTURE(LinkedTables, DataJoinHasExpectedNumberOfColumns)
+{
+    unsigned expected = left.columns().size() + right.columns().size();
+    unsigned actual = join.columns().size();
+
+    CHECK_EQUAL(expected, actual);
+}
+
+TEST_FIXTURE(LinkedTables, DataJoinHasExpectedColumns)
+{
+    CHECK((left.columns() + right.columns()) == join.columns());
+}
+
+TEST_FIXTURE(LinkedTables, DataJoinReturnsExpectedNumberOfResults)
+{
+    unsigned count = 0;
+
+    for (DataJoin::iterator it = join.begin();
+            it != join.end(); ++it, ++count);
+
+    CHECK_EQUAL(right.size(), count);
+}
+
+TEST_FIXTURE(LinkedTables, DataJoinReturnsExpectedValues)
+{
+    DataJoin::iterator actual = join.begin();
+    DataTable::const_iterator expected = result.begin();
+
+    for (const unsigned size = join.columns().size();
+            actual != join.end(); ++actual, ++expected)
+    {
+        CHECK_ARRAY_EQUAL(expected->data(), actual->data(), size);
+    }
+}
+
+TEST_FIXTURE(LinkedTables, DataJoinCanBeUsedWithStlAlgorithms)
+{
+    DataTable copied("copied", join.columns());
+
+    copy(join.begin(), join.end(), back_inserter(copied));
+
+    DataTable::const_iterator actual = copied.begin();
+    DataTable::const_iterator expected = result.begin();
+
+    for (const unsigned size = copied.columns().size();
+            actual != copied.end(); ++actual, ++expected)
+    {
+        CHECK_ARRAY_EQUAL(expected->data(), actual->data(), size);
+    }
+}
+
+struct RelatedTables : public Tables
+{
+    RelatedTables() : Tables()
+    {
+        left << 0, 1, 2, 3, 4;
+        right << 1, 2, 2, 3, 3, 3, 5;
+    }
+};
+
+struct InnerJoin : public RelatedTables
+{
+    DataJoin join;
+    DataTable expected;
+
+    InnerJoin()
+      : RelatedTables(),
+        join(left, right, "id at left", "id at right", DataJoin::INNER),
+        expected("result", join.columns())
+    {
+        expected << 1, 1,
+                    2, 2,
+                    2, 2,
+                    3, 3,
+                    3, 3,
+                    3, 3;
+    }
+
+};
+
+TEST_FIXTURE(InnerJoin, InnerJoinHasExpectedNumberOfColumns)
+{
+    unsigned expected = left.columns().size() + right.columns().size();
+    unsigned actual = join.columns().size();
+
+    CHECK_EQUAL(expected, actual);
+}
+
+TEST_FIXTURE(InnerJoin, InnerJoinHasExpectedColumns)
+{
+    CHECK((left.columns() + right.columns()) == join.columns());
+}
+
+TEST_FIXTURE(InnerJoin, InnerJoinReturnsExpectedNumberOfResults)
+{
+    unsigned count = 0;
+
+    for (DataJoin::iterator it = join.begin();
+            it != join.end(); ++it, ++count);
+
+    CHECK_EQUAL(expected.size(), count);
+}
+
+TEST_FIXTURE(InnerJoin, InnerJoinReturnsExpectedValues)
+{
+    DataTable::const_iterator expect = expected.begin();
+    DataJoin::iterator actual = join.begin();
+
+    for (const unsigned size = expected.columns().size();
+            actual != join.end(); ++expect, ++actual)
+    {
+        CHECK_ARRAY_EQUAL(expect->data(), actual->data(), size);
+    }
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataLink.cc b/odb_api/src/odb_api/tools/TestDataLink.cc
new file mode 100644
index 0000000..31458a8
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataLink.cc
@@ -0,0 +1,209 @@
+/// @file   TestDataLink.cc
+/// @author Tomas Kral
+
+#include <string>
+#include <vector>
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataLink.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+
+struct FilledTables
+{
+    FilledTables();
+   ~FilledTables();
+
+    static DataColumns columns(const string& name);
+    static DataTableProperties properties();
+    
+    DataTable parent;
+    DataTable child;
+    vector<DataTable*> result;
+};
+
+FilledTables::FilledTables()
+  : parent("parent", columns("parent"), properties()),
+    child("child", columns("child"), properties()),
+    result(2)
+{
+    parent << 0,
+              1;
+
+    child << 0, 0.0,
+             0, 1.0,
+             0, 2.0,
+             0, 3.0,
+             0, 4.0,
+             1, 5.0,
+             1, 6.0,
+             1, 7.0,
+             1, 8.0,
+             1, 9.0;
+
+    for (unsigned i = 0; i < result.size(); ++i)
+        result[i] = new DataTable("result", columns("child"));
+
+    (*result[0]) << 0, 0.0,
+                    0, 1.0,
+                    0, 2.0,
+                    0, 3.0,
+                    0, 4.0;
+
+    (*result[1]) << 1, 5.0,
+                    1, 6.0,
+                    1, 7.0,
+                    1, 8.0,
+                    1, 9.0;
+}
+
+FilledTables::~FilledTables()
+{
+    for (unsigned i = 0; i < result.size(); ++i)
+        delete result[i];
+}
+
+DataColumns FilledTables::columns(const string& name)
+{
+    DataColumns columns;
+
+    if (name == "parent")
+    {
+        columns.add("key", "INTEGER");
+    }
+    else if (name == "child")
+    {
+        columns.add("key", "INTEGER");
+        columns.add("ordinal", "DOUBLE");
+    }
+    
+    return columns;
+}
+
+DataTableProperties FilledTables::properties()
+{
+    DataTableProperties properties;
+    properties.blockSizeInNumberOfRows(5);
+    return properties;
+}
+
+struct FilledLink : public FilledTables
+{
+    FilledLink()
+      : FilledTables(),
+        link(parent, child, "key", "key") {}
+
+    DataLink link;
+};
+
+TEST_FIXTURE(FilledLink, DataLinkReturnsExpectedParentTable)
+{
+    CHECK_EQUAL(&parent, &link.parent());
+}
+
+TEST_FIXTURE(FilledLink, DataLinkReturnsExpectedChildTable)
+{
+    CHECK_EQUAL(&child, &link.child());
+}
+
+TEST_FIXTURE(FilledLink, DataLinkHasExpectedSize)
+{
+    CHECK_EQUAL(parent.size(), link.size());
+}
+
+TEST_FIXTURE(FilledLink, DataLinkIteratorReturnsExpectedNumberOfParentRows)
+{
+    unsigned count = 0;
+
+    for (DataLink::const_iterator range = link.begin();
+            range != link.end(); ++range, ++count);
+
+    CHECK_EQUAL(parent.size(), count);
+}
+
+TEST_FIXTURE(FilledLink, DataLinkIteratorReturnsExpectedNumberOfChildRows)
+{
+    unsigned i = 0;
+
+    for (DataLink::const_iterator range = link.begin(),
+            end = link.end(); range != end; ++range, ++i)
+    {
+        const unsigned expected = result[i]->size();
+        unsigned actual = 0;
+
+        for (DataTable::const_iterator it = range->begin();
+                it != range->end(); ++it, ++actual);
+
+        CHECK_EQUAL(expected, actual);
+    }
+}
+
+TEST_FIXTURE(FilledLink, DataLinkIteratorReturnsExpectedRows)
+{
+    unsigned i = 0;
+
+    for (DataLink::const_iterator range = link.begin(),
+            end = link.end(); range != end; ++range, ++i)
+    {
+        const unsigned size = child.columns().size();
+
+        for (DataTable::const_iterator expected = result[i]->begin(),
+                actual = range->begin(); actual != range->end();
+                ++expected, ++actual)
+        {
+            CHECK_ARRAY_EQUAL(expected->data(), actual->data(), size);
+        }
+    }
+}
+
+TEST_FIXTURE(FilledLink, InsertRowAtTheBeginningOfTheFirstRange)
+{
+    DataRow row(2);
+
+    row[0] =  0;
+    row[1] = -1;
+
+    DataLink::iterator range = link.begin();
+    DataTable::iterator position = range->begin();
+
+    size_t oldSize = range->size();
+    link.insert(range, position, row);
+    CHECK_EQUAL(oldSize + 1, range->size());
+
+    int ordinal = -1;
+    for (DataTable::iterator it = range->begin();
+            it != range->end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<int>(1));
+    }
+}
+
+TEST_FIXTURE(FilledLink, InsertRowAtTheEndOfTheLastRange)
+{
+    DataLink::iterator range = ++link.begin();
+    DataTable::iterator first = range->begin();
+    DataTable::iterator last = range->end();
+
+    DataRow row(2);
+
+    row[0] =  (*first)[0];
+    row[1] =  (*(last-1))[1] + 1;
+
+    size_t oldSize = range->size();
+    link.insert(range, last, row);
+    CHECK_EQUAL(oldSize + 1, range->size());
+
+    int ordinal = (*range->begin())[1];
+    for (DataTable::iterator it = range->begin();
+            it != range->end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<int>(1));
+    }
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataLoader.cc b/odb_api/src/odb_api/tools/TestDataLoader.cc
new file mode 100644
index 0000000..a2011b6
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataLoader.cc
@@ -0,0 +1,226 @@
+/// @file   TestDataLoader.cc
+/// @author Tomas Kral
+
+#include <string>
+
+#include "eckit/filesystem/TmpFile.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataLoader.h"
+#include "odb_api/DataSaver.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataTableMappings.h"
+#include "odb_api/DataSet.h"
+
+using namespace std;
+using namespace odb;
+using namespace eckit;
+
+namespace {
+
+typedef odb::DataStream<odb::SameByteOrder, odb::PrettyFastInMemoryDataHandle> DataStream;
+
+struct Fixture
+{
+    Fixture();
+
+    TmpFile input;
+    TmpFile output;
+};
+
+const int PARENT_ROWS_COUNT = 5; 
+const int CHILD_ROWS_COUNT = 10; 
+const int TOTAL_ROWS_COUNT = PARENT_ROWS_COUNT * CHILD_ROWS_COUNT;
+
+Fixture::Fixture()
+  : input(),
+    output()
+{
+    odb::Writer<> writer(input);
+    odb::Writer<>::iterator it = writer.begin();
+
+    MetaData md (it->columns());
+    md.addColumn /*<DataStream> */("parent_id at parent", "INTEGER");
+    md.addColumn /*<DataStream> */("child.offset at parent", "INTEGER");
+    md.addColumn /*<DataStream> */("child.len at parent", "INTEGER");
+    md.addColumn /*<DataStream> */("child_id at child", "INTEGER");
+    it->columns(md);
+    it->writeHeader();
+
+    for (int ordinal = 0; ordinal < TOTAL_ROWS_COUNT; ++ordinal, ++it)
+    {
+        const int offset = ordinal / CHILD_ROWS_COUNT * CHILD_ROWS_COUNT;
+
+        it->data()[0] = offset / 10;
+        it->data()[1] = offset;
+        it->data()[2] = CHILD_ROWS_COUNT;
+        it->data()[3] = ordinal;
+    }
+}
+
+TEST_FIXTURE(Fixture, LoadDataSetFromSqlQueries)
+{
+    DataLoader loader(input);
+    DataSet dataset("test");
+
+    loader.select("SELECT DISTINCT parent_id;");
+    loader.fill(dataset, "parent");
+    loader.select("SELECT child_id;");
+    loader.fill(dataset, "child");
+
+    DataTable* parent = *dataset.tables().find("parent");
+    DataTable* child = *dataset.tables().find("child");
+
+    CHECK(parent);
+    CHECK(child);
+
+    CHECK_EQUAL(PARENT_ROWS_COUNT, (int)parent->size());
+    CHECK_EQUAL(TOTAL_ROWS_COUNT, (int)child->size());
+
+    int ordinal = 0;
+    for (DataTable::const_iterator it = parent->begin();
+            it != parent->end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<int>(0));
+    }
+
+    ordinal = 0;
+    for (DataTable::const_iterator it = child->begin();
+            it != child->end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<int>(0));
+    }
+}
+
+TEST_FIXTURE(Fixture, DataLoaderWithTableMappings)
+{
+    DataSet dataset; 
+
+    DataTableMappings mappings;
+    mappings.add("parent", "header");
+    mappings.add("child", "body");
+
+    DataLoader loader(input);
+    loader.mappings(mappings);
+    loader.fill(dataset);
+
+    CHECK(dataset.tables().count("header"));
+    CHECK(dataset.tables().count("body"));
+}
+
+struct FilledDataSet : public Fixture
+{
+    DataSet dataset;
+
+    FilledDataSet() : dataset()
+    {
+        DataLoader loader(input);
+        loader.fill(dataset);
+    }
+};
+
+TEST_FIXTURE(FilledDataSet, DataLoaderReturnsExpectedNumberOfTables)
+{
+    CHECK_EQUAL(2u, dataset.tables().size());
+}
+
+TEST_FIXTURE(FilledDataSet, DataLoaderReturnsExpectedTables)
+{
+    CHECK(dataset.tables().count("parent"));
+    CHECK(dataset.tables().count("child"));
+}
+
+struct ParentTable : public FilledDataSet
+{
+    const DataTable& parent;
+
+    ParentTable()
+      : FilledDataSet(),
+        parent(*dataset.tables()["parent"])
+    {}
+};
+
+TEST_FIXTURE(ParentTable, DataLoaderReturnsExpectedNumberOfParentTableColumns)
+{
+    CHECK_EQUAL(1u, parent.columns().size());
+}
+
+TEST_FIXTURE(ParentTable, DataLoaderReturnsExpectedNumberOfParentTableRows)
+{
+    int count = 0;
+
+    for (DataTable::const_iterator row = parent.begin();
+            row != parent.end(); ++row, ++count);
+
+    CHECK_EQUAL(PARENT_ROWS_COUNT, count);
+}
+
+TEST_FIXTURE(ParentTable, DataLoaderReturnsExpectedParentTableRows)
+{
+    int ordinal = 0;
+
+    for (DataTable::const_iterator row = parent.begin();
+            row != parent.end(); ++row, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, row->get<int>(0));
+    }
+}
+
+struct ChildTable : public FilledDataSet
+{
+    const DataTable& child;
+
+    ChildTable()
+      : FilledDataSet(),
+        child(*dataset.tables()["child"])
+    {}
+};
+
+TEST_FIXTURE(ChildTable, DataLoaderReturnsExpectedNumberOfChildTableColumns)
+{
+    CHECK_EQUAL(1u, child.columns().size());
+}
+
+TEST_FIXTURE(ChildTable, DataLoaderReturnsExpectedNumberOfChildTableRows)
+{
+    int count = 0;
+
+    for (DataTable::const_iterator row = child.begin();
+            row != child.end(); ++row, ++count);
+
+    CHECK_EQUAL(TOTAL_ROWS_COUNT, count);
+}
+
+TEST_FIXTURE(ChildTable, DataLoaderReturnsExpectedChildTableRows)
+{
+    int ordinal = 0;
+
+    for (DataTable::const_iterator row = child.begin();
+            row != child.end(); ++row, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, row->get<int>(0));
+    }
+}
+
+TEST_FIXTURE(FilledDataSet, DataSaverOutputContainsExpectedRows)
+{
+    DataSaver saver(output);
+    saver.save(dataset, "parent");
+    saver.close();
+
+    Reader expected(input);
+    Reader actual(output);
+
+    Reader::iterator e = expected.begin();
+    Reader::iterator a = actual.begin();
+
+    for (size_t size = e->columns().size();
+            e != expected.end(); ++e, ++a)
+    {
+        CHECK_ARRAY_EQUAL(e->data(), a->data(), size);
+    }
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataPage.cc b/odb_api/src/odb_api/tools/TestDataPage.cc
new file mode 100644
index 0000000..1de1cc9
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataPage.cc
@@ -0,0 +1,409 @@
+/// @file   TestDataPage.cc
+/// @author Tomas Kral
+
+#include <string>
+#include <iostream>
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataPage.h"
+
+using namespace std;
+using namespace odb;
+using namespace odb::internal;
+
+namespace {
+
+struct EmptyPage
+{
+    static DataColumns columns();
+    static DataTableProperties properties();
+    static size_t capacity() { return 10; }
+
+    EmptyPage()
+      : table("table", columns(), properties()),
+        page(table) {}
+
+    EmptyPage(const DataTableProperties& properties)
+      : table("table", columns(), properties),
+        page(table) {}
+
+    DataTable table;
+    DataPage page;
+};
+
+DataColumns EmptyPage::columns()
+{
+    DataColumns columns;
+    columns.add("ordinal", "INTEGER");
+    return columns;
+}
+
+DataTableProperties EmptyPage::properties()
+{
+    DataTableProperties properties;
+    properties.blockSizeInNumberOfRows(10);
+    return properties;
+}
+
+struct HalfEmptyPage : public EmptyPage
+{
+    HalfEmptyPage();
+};
+
+HalfEmptyPage::HalfEmptyPage()
+  : EmptyPage()
+{
+    DataRow row(1);
+
+    for (size_t i = 0; i < capacity() / 2; i++)
+    {
+        row[0] = i;
+        page.push_back(row);
+    }
+}
+
+struct FullPage : public EmptyPage
+{
+    FullPage();
+};
+
+FullPage::FullPage()
+  : EmptyPage()
+{
+    DataRow row(1);
+
+    for (size_t i = 0; i < capacity(); i++)
+    {
+        row[0] = i;
+        page.push_back(row);
+    }
+}
+
+TEST_FIXTURE(EmptyPage, HasTheCorrectCapacity)
+{
+    CHECK_EQUAL(capacity(), page.capacity());
+}
+
+TEST_FIXTURE(EmptyPage, EmptyPageIsEmpty)
+{
+    CHECK(page.empty());
+}
+
+TEST_FIXTURE(FullPage, FullPageIsNotEmpty)
+{
+    CHECK(!page.empty());
+}
+
+TEST_FIXTURE(EmptyPage, EmptyPageIsNotFull)
+{
+    CHECK(!page.full());
+}
+
+TEST_FIXTURE(FullPage, FullPageIsFull)
+{
+    CHECK(page.full());
+}
+
+TEST_FIXTURE(EmptyPage, EmptyPageHasZeroSize)
+{
+    CHECK_EQUAL(0u, page.size());
+}
+
+TEST_FIXTURE(FullPage, FullPageHasSizeOfItsCapacity)
+{
+    CHECK_EQUAL(page.capacity(), page.size());
+}
+
+TEST_FIXTURE(EmptyPage, EmptyPageCanBeCleared)
+{
+    page.clear();
+    CHECK_EQUAL(0u, page.size());
+}
+
+TEST_FIXTURE(FullPage, FullPageCanBeCleared)
+{
+    page.clear();
+    CHECK_EQUAL(0u, page.size());
+}
+
+TEST_FIXTURE(EmptyPage, IteratorReturnsExpectedNumberOfRows)
+{
+    const unsigned expected = 0;
+    unsigned actual = 0;
+
+    for (DataRowProxy* row = page.begin();
+            row != page.end(); ++row, ++actual);
+
+    CHECK_EQUAL(expected, actual);
+}
+
+TEST_FIXTURE(FullPage, IteratorReturnsExpectedNumberOfRows)
+{
+    const unsigned expected = page.size();
+    unsigned actual = 0;
+
+    for (DataRowProxy* row = page.begin();
+            row != page.end(); ++row, ++actual);
+
+    CHECK_EQUAL(expected, actual);
+}
+
+TEST_FIXTURE(FullPage, IteratorReturnsExpectedRows)
+{
+    int ordinal = 0;
+
+    for (DataRowProxy* row = page.begin();
+            row != page.end(); ++row, ++ordinal)
+    {
+        DataRow& r = static_cast<DataRow&>(*row);
+        CHECK_EQUAL(ordinal, r.get<int>(0));
+    }
+}
+
+TEST_FIXTURE(EmptyPage, PushingBackRowsToAnEmptyPage)
+{
+    DataRow row(1);
+
+    for (unsigned i = 0; i < capacity(); i++)
+    {
+        row[0] = i;
+        CHECK_EQUAL(true, page.push_back(row));
+    }
+
+    for (unsigned i = 0; i < capacity(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(i, row.get<unsigned>(0));
+        CHECK_EQUAL(false, row.modified());
+    }
+}
+
+TEST_FIXTURE(FullPage, PushingBackRowsToTheFullPage)
+{
+    DataRow row(1);
+
+    CHECK_EQUAL(false, page.push_back(row));
+    CHECK_EQUAL(capacity(), page.size());
+}
+
+TEST_FIXTURE(EmptyPage, DecreadingSizeOfEmptyPageToZero)
+{
+    const unsigned n = 0;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+}
+
+TEST_FIXTURE(FullPage, DecreadingSizeOfFullPageByHalf)
+{
+    const unsigned n = page.size() / 2;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+}
+
+TEST_FIXTURE(FullPage, DecreadingSizeOfFullPageToZero)
+{
+    const unsigned n = 0;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+}
+
+TEST_FIXTURE(EmptyPage, IncreadingSizeOfEmptyPageToHalfTheCapacity)
+{
+    const unsigned n = page.capacity() / 2;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+
+    for (unsigned i = 0; i < page.size(); i++)
+    {
+        const DataRow& row = page[i];
+        CHECK(!row.initialized());
+        CHECK(row.used());
+    }
+}
+
+TEST_FIXTURE(FullPage, IncreadingSizeOfFullPageBeyondItsCapacity)
+{
+    const unsigned n = page.capacity() + 1;
+    const unsigned size = page.size();
+
+    CHECK_EQUAL(size, page.resize(n));
+    CHECK_EQUAL(size, page.size());
+}
+
+TEST_FIXTURE(FullPage, DecreasingSizeOfFullPageToHalfTheCapacity)
+{
+    const unsigned n = page.capacity() / 2;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+
+    for (unsigned i = 0; i < page.size(); i++)
+    {
+        const DataRow& row = page[i];
+        CHECK_EQUAL(false, row.initialized());
+        CHECK_EQUAL(true, row.used());
+    }
+}
+
+struct EmptyPageWithFillMark : public EmptyPage
+{
+    static DataTableProperties properties();
+    EmptyPageWithFillMark() : EmptyPage(properties()) {}
+};
+
+DataTableProperties EmptyPageWithFillMark::properties()
+{
+    DataTableProperties properties = EmptyPage::properties();
+    size_t n = properties.blockSizeInNumberOfRows();
+    properties.blockFillMarkInNumberOfRows(n / 2);
+    return properties;
+}
+
+TEST_FIXTURE(EmptyPageWithFillMark, ResizeEmptyPageWithFillMark)
+{
+    size_t n = page.capacity();
+    size_t m = table.properties().blockFillMarkInNumberOfRows();
+
+    CHECK_EQUAL(m, page.resize(n));
+    CHECK_EQUAL(m, page.size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK(!row.initialized());
+        CHECK(row.used());
+    }
+}
+
+TEST_FIXTURE(EmptyPageWithFillMark, PushBackRowsToEmptyPageWithFillMark)
+{
+    size_t n = page.capacity();
+    size_t m = table.properties().blockFillMarkInNumberOfRows();
+
+    DataRow row(1);
+
+    for (size_t i = 0; i < m; i++)
+    {
+        row.set(0, i);
+
+        CHECK(page.push_back(row));
+        CHECK(!page.back().initialized());
+        CHECK(page.back().used());
+        CHECK_EQUAL(double(i), page.back()[0]);
+    }
+
+    for (size_t i = m; i < n; i++)
+    {
+        row.set(0, i);
+        CHECK(!page.push_back(row));
+        CHECK_EQUAL(double(m-1), page.back()[0]);
+    }
+}
+
+TEST_FIXTURE(EmptyPage, CanIncreasePageSizeAndInitializeValues)
+{
+    size_t n = page.capacity() / 2;
+
+    CHECK_EQUAL(n, page.resize(n, true));
+    CHECK_EQUAL(n, page.size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(2147483647, row.get<int>(0));
+        CHECK(row.initialized());
+        CHECK(row.used());
+    }
+}
+
+TEST_FIXTURE(EmptyPage, CannotIncreasePageSizeBeyondItsCapacity)
+{
+    size_t c = page.capacity();
+    size_t n = c + 1;
+
+    CHECK_EQUAL(c, page.resize(n));
+    CHECK_EQUAL(c, page.size());
+}
+
+TEST_FIXTURE(FullPage, CanDecreasePageSize)
+{
+    size_t n = page.capacity() / 2;
+
+    CHECK_EQUAL(n, page.resize(n));
+    CHECK_EQUAL(n, page.size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(i, row.get<size_t>(0));
+    }
+}
+
+TEST_FIXTURE(HalfEmptyPage, InsertRowAtTheBeginning)
+{
+    DataRow row(1);
+    row[0] = -1;
+    size_t n = page.size();
+
+    CHECK(page.insert(page.begin(), row));
+    CHECK_EQUAL(n + 1, page.size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(int(i) - 1, row.get<int>(0));
+    }
+}
+
+TEST_FIXTURE(HalfEmptyPage, InsertRowAtTheEnd)
+{
+    DataRow row(1);
+    row[0] = page.size();
+    size_t n = page.size();
+
+    CHECK_EQUAL(true, page.insert(page.end(), row));
+    CHECK_EQUAL(n + 1, page.size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(i, row.get<size_t>(0));
+    }
+}
+
+TEST_FIXTURE(FullPage, CanNotInsertRowToAFullPage)
+{
+    DataRow row(1);
+    CHECK_EQUAL(false, page.insert(page.begin(), row));
+}
+
+TEST_FIXTURE(FullPage, SplitFullPage)
+{
+    size_t newSize = page.size() / 2;
+    DataPage* newPage = page.split();
+
+    CHECK_EQUAL(newSize, page.size());
+    CHECK_EQUAL(newSize, newPage->size());
+
+    for (size_t i = 0; i < page.size(); i++)
+    {
+        DataRow& row = page[i];
+        CHECK_EQUAL(i, row.get<size_t>(0));
+    }
+
+    for (size_t i = 0; i < newPage->size(); i++)
+    {
+        DataRow& row = (*newPage)[i];
+        CHECK_EQUAL(i + newSize, row.get<size_t>(0));
+    }
+
+    delete newPage;
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataRow.cc b/odb_api/src/odb_api/tools/TestDataRow.cc
new file mode 100644
index 0000000..748b581
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataRow.cc
@@ -0,0 +1,164 @@
+/// @file   TestDataRow.cc
+/// @author Tomas Kral
+
+#include <cstdlib>
+#include <string>
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataRow.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataColumns.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+
+TEST(UnInitializedDataRowHasExpectedSize)
+{
+    DataRow row(5u);
+    CHECK_EQUAL(5u, row.size());
+}
+
+TEST(UnInitializedDataRowHasExpectedFlags)
+{
+    DataRow row(5u);
+    CHECK(row.standalone());
+    CHECK(!row.initialized());
+    CHECK(!row.modified());
+}
+
+TEST(InitializedDataRowHasExpectedSize)
+{
+    DataRow row(5, 1.0);
+    CHECK_EQUAL(5u, row.size());
+}
+
+TEST(InitializedDataRowHasExpectedFlags)
+{
+    DataRow row(5, 1.0);
+    CHECK(row.standalone());
+    CHECK(row.initialized());
+    CHECK(!row.modified());
+}
+
+TEST(InitializedDataRowHasExpectedValues)
+{
+    DataRow row(5, 1.0);
+    for (unsigned i = 0; i < row.size(); i++)
+        CHECK_EQUAL(1.0, row[i]);
+}
+
+TEST(DataRowInitializedFromColumnsHasExpectedSize)
+{
+    DataColumns columns;
+    columns.add("one", "INTEGER");
+    columns.add("two", "INTEGER");
+
+    DataRow row(columns);
+
+    CHECK_EQUAL(columns.size(), row.size());
+}
+
+TEST(DataRowInitializedFromColumnsHasExpectedFlags)
+{
+    DataColumns columns;
+    columns.add("one", "INTEGER");
+    columns.add("two", "INTEGER");
+
+    DataRow row(columns);
+
+    CHECK(row.standalone());
+    CHECK(!row.initialized());
+    CHECK(!row.modified());
+}
+
+TEST(DataRowInitializedFromColumnsHasExpectedValues)
+{
+    DataColumns columns;
+    columns.add("one", "INTEGER");
+    columns.add("two", "INTEGER");
+
+    DataRow row(columns, true);
+
+    for (unsigned i = 0; i < row.size(); i++)
+        CHECK_EQUAL(columns[i].defaultValue(), row[i]);
+}
+
+struct Ordinals
+{
+    DataRow ordinals;
+
+    Ordinals() : ordinals(5)
+    {
+        for (unsigned i = 0; i < ordinals.size(); i++)
+            ordinals[i] = i;
+    }
+};
+
+TEST_FIXTURE(Ordinals, DataRowCopyHasExpectedSize)
+{
+    DataRow row(ordinals);
+    CHECK_EQUAL(ordinals.size(), row.size());
+}
+
+TEST_FIXTURE(Ordinals, DataRowCopyHasExpectedFlags)
+{
+    DataRow row(ordinals);
+    CHECK(row.standalone());
+    CHECK(row.initialized());
+    CHECK(!row.modified());
+}
+
+TEST_FIXTURE(Ordinals, DataRowCopyHasExpectedValues)
+{
+    DataRow row(ordinals);
+    for (unsigned i = 0; i < row.size(); i++)
+        CHECK_EQUAL(ordinals[i], row[i]);
+}
+
+TEST_FIXTURE(Ordinals, CopiedDataRowHasExpectedFlags)
+{
+    DataRow row(ordinals.size(), 0);
+    copy(ordinals.begin(), ordinals.end(), row.begin());
+
+    CHECK(row.standalone());
+    CHECK(row.initialized());
+    CHECK(row.modified());
+}
+
+TEST_FIXTURE(Ordinals, CopiedDataRowHasExpectedValues)
+{
+    DataRow row(ordinals.size(), 0);
+    copy(ordinals.begin(), ordinals.end(), row.begin());
+
+    for (unsigned i = 0; i < row.size(); i++)
+        CHECK_EQUAL(ordinals[i], row[i]);
+}
+
+TEST(DataRowCanSetIntegers)
+{
+    DataRow row(1);
+    row.set<int>(0, 3.14);
+    CHECK(row.modified());
+    CHECK_EQUAL(3, row[0]);
+}
+
+TEST(DataRowCanSetDoubles)
+{
+    DataRow row(1);
+    row.set<double>(0, 3.14);
+    CHECK(row.modified());
+    CHECK_EQUAL(3.14, row[0]);
+}
+
+TEST(DataRowCanSetStrings)
+{
+    DataRow row(1);
+    row.set<string>(0, "ABCD");
+    CHECK(row.modified());
+    CHECK_ARRAY_EQUAL("ABCD    ", reinterpret_cast<char*>(&row[0]), sizeof(double));
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataSelect.cc b/odb_api/src/odb_api/tools/TestDataSelect.cc
new file mode 100644
index 0000000..28a8d38
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataSelect.cc
@@ -0,0 +1,207 @@
+/// @file   TestDataSelect.cc
+/// @author Tomas Kral
+
+#include <string>
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataSelect.h"
+#include "odb_api/DataSelectIterator.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+    /*
+
+struct FilledDataSet
+{
+    DataSet dataset;
+    DataTable* table;
+    FilledDataSet();
+};
+
+FilledDataSet::FilledDataSet()
+  : dataset("TestDataSelect")
+{
+    DataColumns columns;
+    columns.add("digit", "INTEGER");
+    columns.add("name", "STRING");
+    columns.add("flag", "BITFIELD");
+
+    table = new DataTable("ordinals", columns);
+
+    *table << 0, "zero",  1 << 0,
+              1, "one",   1 << 1,
+              2, "two",   1 << 2,
+              3, "three", 1 << 3,
+              4, "four",  1 << 4,
+              5, "five",  1 << 5;
+
+    dataset.tables().insert(table);
+}
+
+struct SelectAll : public FilledDataSet
+{
+    DataSelect select;
+
+    SelectAll()
+     : FilledDataSet(),
+       select("SELECT * FROM ordinals;", dataset)
+    {}
+};
+
+struct SelectWhere : public FilledDataSet
+{
+    DataSelect select;
+
+    SelectWhere()
+     : FilledDataSet(),
+       select("SELECT digit, flag FROM ordinals WHERE digit IN (0, 2, 4);", dataset)
+    {}
+};
+
+struct SelectAggregate : public FilledDataSet
+{
+    DataSelect select;
+
+    SelectAggregate()
+     : FilledDataSet(),
+       select("SELECT SUM(digit) FROM ordinals;", dataset)
+    {}
+};
+
+struct SelectFirst : public FilledDataSet
+{
+    DataSelect select;
+
+    SelectFirst()
+     : FilledDataSet(),
+       select("SELECT digit FROM ordinals;", dataset)
+    {}
+};
+
+TEST_FIXTURE(SelectAll, ReturnsExpectedNumberOfColumns)
+{
+    CHECK_EQUAL(table->columns().size(), select.columns().size());
+}
+
+
+TEST_FIXTURE(SelectWhere, ReturnsExpectedNumberOfColumns)
+{
+    CHECK_EQUAL(2u, select.columns().size());
+}
+
+TEST_FIXTURE(SelectAggregate, ReturnsExpectedNumberOfColumns)
+{
+    CHECK_EQUAL(1u, select.columns().size());
+}
+
+
+TEST_FIXTURE(SelectAll, ReturnsExpectedColumns)
+{
+    const DataColumns& expected = table->columns();
+    const DataColumns& actual = select.columns();
+
+    for (unsigned i = 0; i < actual.size(); i++)
+        CHECK(expected[i] == actual[i]);
+}
+
+
+TEST_FIXTURE(SelectWhere, ReturnsExpectedColumns)
+{
+    const DataColumns& expected = table->columns();
+    const DataColumns& actual = select.columns();
+
+    CHECK(expected["digit"] == actual[0]);
+    CHECK(expected["flag"] == actual[1]);
+}
+
+
+TEST_FIXTURE(SelectAggregate, ReturnsExpectedColumns)
+{
+    const DataColumn& column = select.columns()[0];
+    CHECK_EQUAL("sum(digit)", column.name());
+}
+
+
+TEST_FIXTURE(SelectAll, ReturnsExpectedNumberOfResults)
+{
+    unsigned count = 0;
+
+    for (DataSelect::iterator it = select.begin();
+            it != select.end(); ++it, ++count);
+
+    CHECK_EQUAL(table->size(), count);
+}
+
+TEST_FIXTURE(SelectWhere, ReturnsExpectedNumberOfResults)
+{
+    unsigned count = 0;
+
+    for (DataSelect::iterator it = select.begin();
+            it != select.end(); ++it, ++count);
+
+    CHECK_EQUAL(3u, count);
+}
+
+
+TEST_FIXTURE(SelectAggregate, ReturnsExpectedNumberOfResults)
+{
+    unsigned count = 0;
+
+    for (DataSelect::iterator it = select.begin();
+            it != select.end(); ++it, ++count);
+
+    CHECK_EQUAL(1u, count);
+}
+
+
+TEST_FIXTURE(SelectAll, ReturnsExpectedResults)
+{
+    DataSelect::iterator actual = select.begin();
+    DataTable::iterator expected = table->begin();
+
+    for (; actual != select.end(); ++actual, ++expected)
+        for (unsigned i = 0; i < select.columns().size(); i++)
+            CHECK_EQUAL((*expected)[i], (*actual)[i]);
+}
+
+TEST_FIXTURE(SelectWhere, ReturnsExpectedResults)
+{
+    DataSelect::iterator actual = select.begin();
+    DataTable::iterator expected = table->begin();
+
+    for (; actual != select.end(); ++actual, ++expected)
+    {
+        if (expected->get<int>(0) % 2) ++expected;
+
+        CHECK_EQUAL((*expected)[0], (*actual)[0]);
+        CHECK_EQUAL((*expected)[2], (*actual)[1]);
+    }
+}
+
+TEST_FIXTURE(SelectAggregate, ReturnsExpectedResults)
+{
+    DataSelect::iterator actual = select.begin();
+    CHECK_EQUAL(15, (*actual)[0]);
+}
+
+TEST_FIXTURE(SelectAll, CanUseCopyToAppendResults)
+{
+    DataTable results("results", select.columns());
+
+    copy(select.begin(), select.end(), back_inserter(results));
+
+    DataTable::iterator expected = table->begin();
+    DataTable::iterator actual = results.begin();
+
+    for (; actual != results.end(); ++actual, ++expected)
+        for (unsigned i = 0; i < select.columns().size(); i++)
+            CHECK_EQUAL((*expected)[i], (*actual)[i]);
+}
+*/
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataSet.cc b/odb_api/src/odb_api/tools/TestDataSet.cc
new file mode 100644
index 0000000..d6a38e7
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataSet.cc
@@ -0,0 +1,41 @@
+/// @file   TestDataSet.cc
+/// @author Tomas Kral
+
+#include "eckit/exception/Exceptions.h"
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataSet.h"
+#include "odb_api/DataTable.h"
+#include "odb_api/DataLink.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+
+struct Fixture
+{
+    Fixture();
+    DataSet dataset;
+};
+
+Fixture::Fixture()
+  : dataset("dataset")
+{}
+
+TEST_FIXTURE(Fixture, InsertingTablesIncreasesSize)
+{
+    DataColumns columns;
+
+    DataTable* parent = new DataTable("parent", columns);
+    DataTable* child = new DataTable("child", columns);
+
+    dataset.tables().insert(parent);
+    CHECK_EQUAL(1u, dataset.tables().size());
+
+    dataset.tables().insert(child);
+    CHECK_EQUAL(2u, dataset.tables().size());
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDataTable.cc b/odb_api/src/odb_api/tools/TestDataTable.cc
new file mode 100644
index 0000000..2b6347a
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDataTable.cc
@@ -0,0 +1,307 @@
+/// @file   TestDataTable.cc
+/// @author Tomas Kral
+
+#include <string>
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/DataTable.h"
+
+using namespace std;
+using namespace odb;
+
+namespace {
+
+struct EmptyTable
+{
+    static DataColumns columns();
+    static DataTableProperties properties();
+    static size_t capacity() { return 10; }
+    EmptyTable() : table("table", columns(), properties()) {}
+    DataTable table;
+};
+
+DataColumns EmptyTable::columns()
+{
+    DataColumns columns;
+    columns.add("column", "INTEGER");
+    return columns;
+}
+
+DataTableProperties EmptyTable::properties()
+{
+    DataTableProperties properties;
+    properties.blockSizeInNumberOfRows(10);
+    return properties;
+}
+
+struct FilledTable : public EmptyTable
+{
+    static size_t size() { return 1.5 * capacity(); }
+    FilledTable();
+};
+
+FilledTable::FilledTable()
+  : EmptyTable()
+{
+    table.resize(size());
+
+    for (size_t i = 0; i < table.size(); i++)
+    {
+        DataRow& row = table[i];
+        row.set(0, i);
+    }
+}
+
+TEST_FIXTURE(EmptyTable, HasZeroSize)
+{
+    CHECK_EQUAL(0u, table.size());
+}
+
+TEST_FIXTURE(FilledTable, HasExpectedNonZeroSize)
+{
+    CHECK_EQUAL(FilledTable::size(), table.size());
+}
+
+TEST_FIXTURE(EmptyTable, IsEmpty)
+{
+    CHECK(table.empty());
+}
+
+TEST_FIXTURE(FilledTable, IsNotEmpty)
+{
+    CHECK(!table.empty());
+}
+
+TEST_FIXTURE(EmptyTable, HasExpectedCapacity)
+{
+    CHECK_EQUAL(capacity(), table.capacity());
+}
+
+TEST_FIXTURE(FilledTable, HasExpectedCapacity)
+{
+    CHECK_EQUAL(2 * capacity(), table.capacity());
+}
+
+TEST_FIXTURE(EmptyTable, CapacityCanBeIncreased)
+{
+    size_t n = 2 * capacity();
+    table.reserve(n);
+    CHECK_EQUAL(n, table.capacity());
+}
+
+TEST_FIXTURE(FilledTable, CapacityCanBeIncreased)
+{
+    size_t n = 3 * capacity();
+    table.reserve(n);
+    CHECK_EQUAL(n, table.capacity());
+}
+
+
+TEST_FIXTURE(EmptyTable, SizeCanBeIncreased)
+{
+    size_t n = 2 * capacity();
+    table.resize(n, true);
+    CHECK_EQUAL(n, table.size());
+
+    for (size_t i = 0; i < table.size(); i++)
+    {
+        DataRow& row = table[i];
+        CHECK_EQUAL(2147483647, row.get<int>(0));
+    }
+}
+
+TEST_FIXTURE(FilledTable, SizeCanBeDecreased)
+{
+    size_t n = capacity() / 2;
+    table.resize(n);
+    CHECK_EQUAL(n, table.size());
+
+    for (size_t i = 0; i < table.size(); i++)
+    {
+        DataRow& row = table[i];
+        CHECK_EQUAL(i, row.get<size_t>(0));
+    }
+}
+
+TEST_FIXTURE(FilledTable, SizeCanBeIncreased)
+{
+    size_t n = 3 * capacity();
+    table.resize(n, true);
+    CHECK_EQUAL(n, table.size());
+
+    size_t i = 0;
+    for (; i < FilledTable::size(); i++)
+    {
+        CHECK_EQUAL(i, table[i].get<size_t>(0));
+    }
+
+    for (; i < table.size(); i++)
+    {
+        CHECK_EQUAL(2147483647, table[i].get<int>(0));
+    }
+}
+
+TEST_FIXTURE(EmptyTable, CanBeCleared)
+{
+    table.clear();
+    CHECK_EQUAL(0u, table.size());
+}
+
+TEST_FIXTURE(FilledTable, CanBeCleared)
+{
+    table.clear();
+    CHECK_EQUAL(0u, table.size());
+}
+
+TEST_FIXTURE(FilledTable, CanIterateForeward)
+{
+    size_t count = 0;
+
+    for (DataTable::const_iterator it = table.begin();
+            it != table.end(); ++it, ++count)
+    {
+        CHECK_EQUAL(count, it->get<size_t>(0));
+    }
+    
+    CHECK_EQUAL(table.size(), count);
+}
+
+TEST_FIXTURE(FilledTable, CanIterateBackward)
+{
+    size_t count = table.size() - 1;
+
+    for (DataTable::const_iterator it = table.end() - 1;
+            it != table.begin(); --it, --count)
+    {
+        CHECK_EQUAL(count, it->get<size_t>(0));
+    }
+    
+    CHECK_EQUAL(0u, count);
+}
+
+TEST_FIXTURE(FilledTable, AdvanceIteratorByZero)
+{
+    size_t n = 0;
+    DataTable::iterator it = table.begin() + n;
+    CHECK_EQUAL(n, it->get<size_t>(0));
+}
+
+TEST_FIXTURE(FilledTable, AdvanceIteratorForewardByOne)
+{
+    size_t n = 1;
+    DataTable::iterator it = table.begin() + n;
+    CHECK_EQUAL(n, it->get<size_t>(0));
+}
+
+TEST_FIXTURE(FilledTable, AdvanceIteratorByHalf)
+{
+    size_t n = table.size() / 2;
+    DataTable::iterator it = table.begin() + n;
+    CHECK_EQUAL(n, it->get<size_t>(0));
+}
+
+TEST_FIXTURE(FilledTable, AdvanceIteratorToTheEnd)
+{
+    size_t n = table.size();
+    DataTable::iterator it = table.begin() + n;
+    CHECK(it == table.end());
+}
+
+TEST_FIXTURE(FilledTable, AdvanceIteratorBackward)
+{
+    size_t n = table.size();
+    DataTable::iterator it = table.end() - n;
+    CHECK_EQUAL(0u, it->get<size_t>(0));
+}
+
+TEST_FIXTURE(FilledTable, DistanceBetweenTheSameIteratorsIsZero)
+{
+    CHECK_EQUAL(0, table.begin() - table.begin());
+}
+
+TEST_FIXTURE(FilledTable, DistanceBetweenBeginAndEndIteratorIsAsExpected)
+{
+    CHECK_EQUAL((ptrdiff_t)table.size(), table.end() - table.begin());
+    CHECK_EQUAL(-(ptrdiff_t)table.size(), table.begin() - table.end());
+}
+
+TEST_FIXTURE(FilledTable, InsertRowAtTheBeginning)
+{
+    size_t n = table.size();
+    DataRow row(1, -1);
+    DataTable::iterator begin = table.begin();
+    table.insert(begin, row);
+
+    CHECK_EQUAL(n + 1, table.size());
+
+    int ordinal = -1;
+    for (DataTable::const_iterator it = table.begin();
+            it != table.end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<int>(0));
+    }
+}
+
+TEST_FIXTURE(FilledTable, InsertRowAtTheEnd)
+{
+    size_t n = table.size();
+    DataRow row(1, n);
+    DataTable::iterator end = table.end();
+    table.insert(end, row);
+
+    CHECK_EQUAL(n + 1, table.size());
+
+    size_t ordinal = 0;
+    for (DataTable::const_iterator it = table.begin();
+            it != table.end(); ++it, ++ordinal)
+    {
+        CHECK_EQUAL(ordinal, it->get<size_t>(0));
+    }
+}
+
+TEST_FIXTURE(FilledTable, GetRowAtTheBack)
+{
+    int expected = table.size() - 1;
+    int actual = table.back().get<int>(0);
+    CHECK_EQUAL(expected, actual);
+}
+
+struct ThreeColumnTable
+{
+    static DataColumns columns();
+    static size_t capacity() { return 10; }
+    ThreeColumnTable() : table("table", columns()) {}
+    DataTable table;
+};
+
+DataColumns ThreeColumnTable::columns()
+{
+    DataColumns columns;
+    columns.add("ordinal", "INTEGER");
+    columns.add("value", "DOUBLE");
+    columns.add("name", "STRING");
+    return columns;
+}
+
+TEST_FIXTURE(ThreeColumnTable, LineByLineInitialization)
+{
+    table << 1, 1.0, string("one");
+    table << 2, 2.0, string("two");
+
+    CHECK_EQUAL(2u, table.size());
+    CHECK_EQUAL(1.0, table[0].get<double>(1));
+    CHECK_EQUAL(2.0, table[1].get<double>(1));
+}
+
+TEST_FIXTURE(ThreeColumnTable, MultiLineInitialization)
+{
+    table << 1, 1.0, string("one"),
+             2, 2.0, string("two");
+
+    CHECK_EQUAL(2u, table.size());
+    CHECK_EQUAL(1.0, table[0].get<double>(1));
+    CHECK_EQUAL(2.0, table[1].get<double>(1));
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestDecoding.cc b/odb_api/src/odb_api/tools/TestDecoding.cc
new file mode 100644
index 0000000..3992d62
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDecoding.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, June 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Reader.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest DispatchingWriter
+///
+static void test()
+{
+	const string fileName = "2000010106.odb";
+
+	Timer t(std::string("test: reading file '") + fileName + "'");
+
+	odb::Reader f(fileName);
+	odb::Reader::iterator it = f.begin();
+	odb::Reader::iterator end = f.end();
+
+	unsigned long long n = 0;	
+	for (; it != end; ++it)
+		++n;
+
+	Log::info() << "test decoded " << n << " lines." << std::endl;
+}
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(Decoding)
diff --git a/odb_api/src/odb_api/tools/TestDispatchingWriter.cc b/odb_api/src/odb_api/tools/TestDispatchingWriter.cc
new file mode 100644
index 0000000..cc1c07f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDispatchingWriter.cc
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, June 2009
+
+#include "odb_api/DispatchingWriter.h"
+#include "odb_api/Reader.h"
+#include "odb_api/StringTool.h"
+
+#include "TestCase.h"
+#include "CountTool.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest DispatchingWriter
+///
+static void test()
+{
+	const string fileName = "2000010106.odb";
+
+	odb::Reader oda(fileName);
+	odb::Reader::iterator it = oda.begin();
+	const odb::Reader::iterator end = oda.end();
+	ASSERT(it->columns().size() > 0);
+
+	odb::DispatchingWriter writer("disp.{obstype}.{sensor}.odb");
+	odb::DispatchingWriter::iterator wi = writer.begin();
+	unsigned long long n1 = wi->pass1(it, end);
+	wi->close();
+	
+	unsigned long long sum = 0;
+	vector<PathName> files = (**wi).outputFiles();
+	for (size_t i = 0; i < files.size(); ++i)
+	{
+        unsigned long long n = odb::tool::CountTool::rowCount(files[i]);
+		Log::info() << i << ". " << files[i] << ": " << n << std::endl;
+		sum += n;
+	}
+
+	ASSERT(n1 == sum);
+    odb::StringTool::shell("ls -l disp.*.*.odb", Here());
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(DispatchingWriter)
diff --git a/odb_api/src/odb_api/tools/TestDistinct.cc b/odb_api/src/odb_api/tools/TestDistinct.cc
new file mode 100644
index 0000000..d8d056b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestDistinct.cc
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, September 2010
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/Select.h"
+#include "odb_api/tools/ImportTool.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+///
+static void test()
+{
+    std::string sql = "select distinct a from \"TestDistinct_a1to10twice.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select sel(sql);
+	odb::Select::iterator it2 = sel.begin();
+	odb::Select::iterator end2 = sel.end();
+
+	int i = 0;
+	for (; it2 != end2; ++it2)
+		ASSERT((*it2)[0] == ++i);
+
+	ASSERT((*it2)[0] == 10);
+}
+
+static void setUp()
+{
+	stringstream s;
+	s << "a:REAL" << std::endl;
+	for (size_t i = 1; i <= 10; ++i) s << i << std::endl;
+	for (size_t i = 1; i <= 10; ++i) s << i << std::endl;
+    odb::tool::ImportTool::importText(s.str().c_str(), "TestDistinct_a1to10twice.odb");
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(Distinct)
diff --git a/odb_api/src/odb_api/tools/TestFastODA2Request.cc b/odb_api/src/odb_api/tools/TestFastODA2Request.cc
new file mode 100644
index 0000000..43857b6
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFastODA2Request.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+#include "odb_api/FastODA2Request.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+const char * cfg = "let, DATE = 'andate at desc', TIME = 'antime at desc', TYPE = type, EXPVER = expver";
+
+static void test()
+{
+    odb::FastODA2Request<odb::ODA2RequestClientTraits> o;
+	o.parseConfig(cfg);
+	o.scanFile("2000010106.2.0.odb");
+
+	string r = o.genRequest();
+	unsigned long long n = o.rowsNumber();
+
+	Log::info() << "test: request is:" << std::endl << r << std::endl;
+	Log::info() << "test: file has " << n << " rows(s)." << std::endl;
+
+	ASSERT(o.getValues("DATE").size() == 1);
+	ASSERT(*o.getValues("DATE").begin() == "20000101");
+
+	ASSERT(o.getValues("TIME").size() == 1);
+	ASSERT(*o.getValues("TIME").begin() == "60000");
+
+	ASSERT(o.getValues("TYPE").size() == 1);
+	ASSERT(*o.getValues("TYPE").begin() == "MISSING");
+
+	map<string, double> m = o.getUniqueValues();
+	ASSERT(m["TYPE"] == odb::MDI::realMDI());
+	ASSERT(m["DATE"] == 20000101);
+	ASSERT(m["TIME"] == 60000);
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(FastODA2Request)
diff --git a/odb_api/src/odb_api/tools/TestFastODA2Request2.cc b/odb_api/src/odb_api/tools/TestFastODA2Request2.cc
new file mode 100644
index 0000000..986ecf9
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFastODA2Request2.cc
@@ -0,0 +1,130 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/ODAHandle.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static Length size1;
+static Length size2;
+
+Length createFile(const std::string& fileName, unsigned int andate, unsigned int antime, unsigned int reportype)
+{
+	{
+		odb::Writer<> oda(fileName);
+		odb::Writer<>::iterator row = oda.begin();
+		row->setNumberOfColumns(3);
+		row->setColumn(0, "antime", odb::INTEGER);
+		row->setColumn(1, "andate", odb::INTEGER);
+		row->setColumn(2, "reportype", odb::INTEGER);
+		row->writeHeader();
+		for (size_t i = 0; i < 1; ++i, ++row)
+		{
+			(*row)[0] = andate; 
+			(*row)[1] = antime;
+			(*row)[2] = reportype;
+			++row;
+		}
+	}
+	PathName p(fileName);
+	return p.size();
+} 
+
+static void setUp()
+{
+
+    size1 = createFile("TestFastODA2Request21.odb", 20110823, 0, 21);
+    size2 = createFile("TestFastODA2Request22.odb", 20110823, 0, 22);
+
+    const char *cmd =
+    "cat "
+    "TestFastODA2Request21.odb TestFastODA2Request21.odb TestFastODA2Request21.odb "
+    "TestFastODA2Request22.odb TestFastODA2Request22.odb "
+    " >TestFastODA2Request2BIG.odb"
+    ;
+
+    int catStatus = system(cmd);
+    ASSERT(WEXITSTATUS(catStatus) == 0);
+
+    catStatus = system("cat TestFastODA2Request2BIG.odb TestFastODA2Request2BIG.odb >TestFastODA2Request2BAD.odb");
+    ASSERT(WEXITSTATUS(catStatus) == 0);
+}
+static void test()
+{
+	const char * configFile = "/tmp/p4/mars/server/dev/oda/mars/marsKeywordToODBColumn";
+	const char * config = "let, DATE = andate, TIME = antime, REPORTYPE = reportype" ;
+
+	FastODA2Request<ODA2RequestServerTraits> o;
+	//o.parseConfig(StringTool::readFile(cfgFile));
+	o.parseConfig(config);
+
+	OffsetList offsets;
+	LengthList lengths;
+	vector<ODAHandle*> handles;
+
+	o.scanFile("TestFastODA2Request2BIG.odb", offsets, lengths, handles);
+
+	for (size_t i = 0; i < handles.size(); ++i)
+		Log::info() << "test: handles[" << i << "]=" << *handles[i] << std::endl;
+
+	ASSERT(handles.size() == 2);
+	ASSERT(0 == handles[0]->start());
+    ASSERT(size1 * 3 == handles[0]->end());
+    ASSERT(size1 * 3 == handles[1]->start());
+    ASSERT(size1 * 3 + size2 * 2 == handles[1]->end());
+
+	string r = o.genRequest();
+	Log::info() << "test: o.genRequest() => " << std::endl << r << std::endl;
+
+	unsigned long long n = o.rowsNumber();
+	Log::info() << "test: rowsNumber == " << n <<  std::endl;
+	ASSERT(n == 2 * (2 + 3));
+
+	
+	OffsetList offsets2;
+	LengthList lengths2;
+	vector<ODAHandle*> handles2;
+
+	FastODA2Request<ODA2RequestClientTraits> o2;
+	o2.parseConfig(config);
+	bool rc = o2.scanFile("TestFastODA2Request2BAD.odb", offsets2, lengths2, handles2);
+	ASSERT(rc == false);
+
+	OffsetList offsets3;
+	LengthList lengths3;
+	vector<ODAHandle*> handles3;
+
+	FastODA2Request<ODA2RequestServerTraits> o3;
+	o3.parseConfig(config);
+	
+	bool exceptionThrown = false;
+	try { o3.scanFile("TestFastODA2Request2BAD.odb", offsets3, lengths3, handles3); }
+	catch (UserError e)
+	{
+		exceptionThrown = true;
+	}
+	ASSERT(exceptionThrown);
+}
+
+
+static void tearDown() { }
+
+
+SIMPLE_TEST(FastODA2Request2)
diff --git a/odb_api/src/odb_api/tools/TestFastODA2Request3.cc b/odb_api/src/odb_api/tools/TestFastODA2Request3.cc
new file mode 100644
index 0000000..0274566
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFastODA2Request3.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+#include "eckit/config/Resource.h"
+#include "eckit/types/Types.h"
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/ODAHandle.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+//	const char * configFile = "/tmp/p4/mars/server/dev/oda/mars/marsKeywordToODBColumn";
+	const char * config = 
+	"DATE: andate\n"
+	"TIME: antime\n"
+	"REPORTYPE: reportype\n"
+	"CLASS: class\n"
+	"TYPE: type\n"
+	"STREAM: stream\n"
+	"OBSGROUP: groupid\n"
+	"EXPVER: expver\n"
+	;
+
+	FastODA2Request<ODA2RequestClientTraits> o;
+	//o.parseConfig(StringTool::readFile(cfgFile));
+	o.parseConfig(config);
+
+	OffsetList offsets;
+	LengthList lengths;
+	vector<ODAHandle*> handles;
+
+	//PathName pathName("mondb_conv.17.16001.odb.fn6x");
+	ASSERT(getenv("ODB_API_TEST_DATA_PATH") && "ODB_API_TEST_DATA_PATH must be set");
+	string e = Resource<std::string>("$ODB_API_TEST_DATA_PATH", string("../../../odb_api/src/odb"));
+	PathName pathName(e + "/mondb.1.12.odb");
+	bool rc = o.scanFile(pathName, offsets, lengths, handles);
+	ASSERT(rc == true);
+
+	for (size_t i = 0; i < handles.size(); ++i)
+		Log::info() << "test: handles[" << i << "]=" << *handles[i] << std::endl;
+
+	string r = o.genRequest();
+	Log::info() << "test: o.genRequest() => " << std::endl << r << std::endl;
+
+	unsigned long long n = o.rowsNumber();
+	Log::info() << "test: rowsNumber == " << n <<  std::endl;
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(FastODA2Request3)
diff --git a/odb_api/src/odb_api/tools/TestFunctionCircle.cc b/odb_api/src/odb_api/tools/TestFunctionCircle.cc
new file mode 100644
index 0000000..e2ff203
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionCircle.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =   7e-6;
+
+#include <cmath>
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test Circle function");
+	odb::Writer<> oda("test_circle.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "lat", odb::REAL);
+	row->setColumn(1, "lon", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 45.0;
+	(*row)[1] = 10.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_circle.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select circle(lat,46.0, lon,11.0,1.0), circle(lat,46.0, lon,11.0,1.5) from \"test_circle.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(fabs((*it)[0] - 0.0e0) < EPS); // 
+	ASSERT(fabs((*it)[1] - 1.0) < EPS); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionCircle)
diff --git a/odb_api/src/odb_api/tools/TestFunctionDateAndTime.cc b/odb_api/src/odb_api/tools/TestFunctionDateAndTime.cc
new file mode 100644
index 0000000..3410c1d
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionDateAndTime.cc
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =     1e-6;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test DateAndTime function");
+	odb::Writer<> oda("test_date_and_time.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "date", odb::INTEGER);
+	row->setColumn(1, "time", odb::INTEGER);
+	
+	row->writeHeader();
+
+	(*row)[0] = 20090706.0;
+	(*row)[1] = 210109.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_date_and_time.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select julian(date,time), year(date),month(date),day(date),hour(time),minute(time),second(time), timestamp(date,time) from \"test_date_and_time.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(fabs((*it)[0] - 2455019.) < EPS); // 
+	ASSERT((*it)[1] == 2009); // 
+	ASSERT((*it)[2] == 7); // 
+	ASSERT((*it)[3] == 6); // 
+	ASSERT((*it)[4] == 21); // 
+	ASSERT((*it)[5] == 1); // 
+	ASSERT((*it)[6] == 9); // 
+	ASSERT((*it)[7] == 20090706210109ll); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionDateAndTime)
diff --git a/odb_api/src/odb_api/tools/TestFunctionDistance.cc b/odb_api/src/odb_api/tools/TestFunctionDistance.cc
new file mode 100644
index 0000000..5b0ce00
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionDistance.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =     7e-6;
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test various functions to compute the distance");
+	odb::Writer<> oda("test_distance.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+    row->setNumberOfColumns(2);
+
+	row->setColumn(0, "lat", odb::REAL);
+	row->setColumn(1, "lon", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 45.0;
+	(*row)[1] = 0.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_distance.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select rad(45.0,0.0,1.0,lat,lon), rad(10.0,0.0,0.0,lat,lon),distance(46.0,0.0,lat,lon),km(46.0,0.0,lat,lon),dist(100.,46.0,1.0,lat,lon), dist(40.0,5.0,1000.0,lat,lon) from \"test_distance.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+    cout << "rad(lat,lon,1.0,45.0,0.0) = " << (*it)[0] << std::endl;
+    cout << "rad(lat,lon,0.0,10.0,0.0) = " << (*it)[1] << std::endl;
+    cout << "distance(lat,lon,46.0,0.0) = " << (*it)[2] << std::endl;
+    cout << "km(lat,lon,46.0,0.0) = " << (*it)[3] << std::endl;
+    cout << "dist(lat,lon,100.,46.0,0.0) = " << (*it)[4] << std::endl;
+    cout << "dist(lat,lon,120.,46.0,0.0) = " << (*it)[5] << std::endl;
+	ASSERT((*it)[0] == 1); // 
+	ASSERT((*it)[1] == 0); // 
+	ASSERT(fabs((*it)[2] - 11112)<EPS); // 
+	ASSERT(fabs((*it)[3] - 111.12e0)<EPS); // 
+	ASSERT((*it)[4] == 0); // 
+	ASSERT((*it)[5] == 1); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionDistance)
diff --git a/odb_api/src/odb_api/tools/TestFunctionDotp.cc b/odb_api/src/odb_api/tools/TestFunctionDotp.cc
new file mode 100644
index 0000000..19b6090
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionDotp.cc
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =     4e-5;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp()
+{
+	Timer t("Test Dotp function");
+	odb::Writer<> oda("test_dotp.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "x", odb::REAL);
+	row->setColumn(1, "y", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 3.0;
+	(*row)[1] = 2.0;
+    ++row;
+	(*row)[0] = 7.5;
+	(*row)[1] = 112.0;
+    ++row;
+	(*row)[0] = 93.7;
+	(*row)[1] = 12.3;
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_dotp.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select dotp(x,y) from \"test_dotp.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(fabs((*it)[0] - 1998.51e0)<EPS); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionDotp)
diff --git a/odb_api/src/odb_api/tools/TestFunctionEqBox.cc b/odb_api/src/odb_api/tools/TestFunctionEqBox.cc
new file mode 100644
index 0000000..31441ab
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionEqBox.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =   7e-6;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp()
+{
+	Timer t("Test eq_boxlat and eq_boxlon functions");
+	odb::Writer<> oda("test_eq_box.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "lat", odb::REAL);
+	row->setColumn(1, "lon", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 45.0;
+	(*row)[1] = 5.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_eq_box.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select eq_boxlat(lat,lon,10.5), eq_boxlon(lat,lon,10.5) from \"test_eq_box.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(fabs((*it)[0] - 47.134066345052e0)<EPS); // 
+	ASSERT((*it)[1] == 0); // 
+
+}
+
+
+SIMPLE_TEST(FunctionEqBox)
diff --git a/odb_api/src/odb_api/tools/TestFunctionNorm.cc b/odb_api/src/odb_api/tools/TestFunctionNorm.cc
new file mode 100644
index 0000000..556938c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionNorm.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS = 4e-5;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test Norm function");
+	odb::Writer<> oda("test_norm.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "x", odb::REAL);
+	row->setColumn(1, "y", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 3.0;
+	(*row)[1] = 16.0;
+    ++row;
+	(*row)[0] = 4.0;
+	(*row)[1] = 12.0;
+    ++row;
+	(*row)[0] = 2.0;
+	(*row)[1] = 24.0;
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_norm.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select norm(x,y) from \"test_norm.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT(fabs((*it)[0] - 12e0)<EPS); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionNorm)
diff --git a/odb_api/src/odb_api/tools/TestFunctionRggBox.cc b/odb_api/src/odb_api/tools/TestFunctionRggBox.cc
new file mode 100644
index 0000000..619d866
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionRggBox.cc
@@ -0,0 +1,73 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =   7e-6;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test rgg_boxlat and rgg_boxlon functions");
+	odb::Writer<> oda("test_rgg_box.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "lat", odb::REAL);
+	row->setColumn(1, "lon", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 45.0;
+	(*row)[1] = 5.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_rgg_box.odb").unlink();
+}
+
+static void test()
+{
+	ASSERT(getenv("ODB_RTABLE_PATH") && "environment variable must be set for rgg_boxlat and rgg_boxlon to work properly");
+    const string sql = "select rgg_boxlat(lat,lon,31), rgg_boxlon(lat,lon,31) from \"test_rgg_box.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	Log::info() << "UnitTest: '" << sql << "' => [" << (*it)[0] << ", " << (*it)[1] << "]" << std::endl;
+
+	ASSERT(fabs((*it)[0] - 47.069642059688e0)<EPS); // 
+	ASSERT(fabs((*it)[1] - 6)<EPS); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionRggBox)
diff --git a/odb_api/src/odb_api/tools/TestFunctionTdiff.cc b/odb_api/src/odb_api/tools/TestFunctionTdiff.cc
new file mode 100644
index 0000000..fd6cee5
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionTdiff.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+//const double EPS =     7e-6
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp()
+{
+	Timer t("Test tdiff function");
+	odb::Writer<> oda("test_tdiff.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "date", odb::INTEGER);
+	row->setColumn(1, "time", odb::INTEGER);
+	
+	row->writeHeader();
+
+	(*row)[0] = 20090706.0;
+	(*row)[1] = 210109.0;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_tdiff.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select tdiff(date,time,20090707.0,0.0) from \"test_tdiff.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT((*it)[0] == -10731); // 
+
+}
+
+
+
+SIMPLE_TEST(FunctionTdiff)
diff --git a/odb_api/src/odb_api/tools/TestFunctionThin.cc b/odb_api/src/odb_api/tools/TestFunctionThin.cc
new file mode 100644
index 0000000..db466c0
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionThin.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS =   7e-6;
+
+#include <cmath>
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test thin function");
+	odb::Writer<> oda("test_thin.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(1);
+
+	row->setColumn(0, "lat", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 45.0;
+	(*++row)[0] = 45.0;
+	(*++row)[0] = 45.0;
+	(*++row)[0] = 45.0;
+	(*++row)[0] = 45.0;
+	(*++row)[0] = 45.0;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_thin.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select thin(2.0,lat) from \"test_thin.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+    int i=0;
+	odb::Select oda(sql);
+    for(odb::Select::iterator it = oda.begin(); it != oda.end(); ++it) {
+      if (i % 2 == 1) 
+	    ASSERT(fabs((*it)[0] - 0.0e0) < EPS); // 
+      else
+	    ASSERT(fabs((*it)[0] - 1.0) < EPS); // 
+      ++i;
+    }
+
+}
+
+
+SIMPLE_TEST(FunctionThin)
diff --git a/odb_api/src/odb_api/tools/TestFunctionTypeConversion.cc b/odb_api/src/odb_api/tools/TestFunctionTypeConversion.cc
new file mode 100644
index 0000000..11f22e1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionTypeConversion.cc
@@ -0,0 +1,68 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	Timer t("Test TypeConversion function");
+	odb::Writer<> oda("test_type_conversion.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(1);
+
+	row->setColumn(0, "obsvalue", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 247.53;
+
+    ++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_type_conversion.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select ceil(obsvalue),floor(obsvalue), trunc(obsvalue),int(obsvalue),nint(obsvalue) from \"test_type_conversion.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT((*it)[0] == 248); // 
+	ASSERT((*it)[1] == 247); // 
+	ASSERT((*it)[2] == 247); // 
+	ASSERT((*it)[3] == 247); // 
+	ASSERT((*it)[4] == 248); // 
+
+}
+
+
+
+
+SIMPLE_TEST(FunctionTypeConversion)
diff --git a/odb_api/src/odb_api/tools/TestFunctionsForAngleConversion.cc b/odb_api/src/odb_api/tools/TestFunctionsForAngleConversion.cc
new file mode 100644
index 0000000..1d3faa9
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionsForAngleConversion.cc
@@ -0,0 +1,102 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+const double EPS = 7e-6;
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+#include "odb_api/piconst.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp()
+{
+	Timer t("Test various functions to convert angles (radians to degrees, etc.)");
+	odb::Writer<> oda("test_angleconv.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "radian_col", odb::REAL);
+	row->setColumn(1, "degrees_col", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = piconst::pi;
+	(*row)[1] = 180.0e0;
+	++row;
+
+	(*row)[0] = 0.0e0;
+	(*row)[1] = 0.0e0;
+	++row;
+
+	(*row)[0] = piconst::pi/4.0e0;
+	(*row)[1] = 45.0e0;
+	++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_angleconv.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select degrees(radian_col),radians(degrees_col), rad2deg(radian_col), deg2rad(degrees_col), radians(degrees(radian_col)), degrees(radians(degrees_col)) from \"test_angleconv.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+// because stored as single real precision; we loose some accuracy
+	ASSERT(fabs((*it)[0] - 180) < EPS); // 
+	ASSERT(fabs((*it)[1] - piconst::pi) < EPS); //
+	ASSERT(fabs((*it)[2] - 180.0) < EPS); // 
+	ASSERT(fabs((*it)[3] - piconst::pi) < EPS);    // 
+
+	ASSERT(fabs((*it)[4] - piconst::pi) < EPS); // 
+	ASSERT(fabs((*it)[5] - 180.0e0) < EPS);    //
+
+    ++it;
+    ASSERT(fabs((*it)[0]) < EPS); // 
+    ASSERT(fabs((*it)[1]) < EPS); //
+    ASSERT(fabs((*it)[2]) < EPS); // 
+    ASSERT(fabs((*it)[3]) < EPS);    // 
+
+    ASSERT(fabs((*it)[4]) < EPS); // 
+    ASSERT(fabs((*it)[5]) < EPS);    //
+
+    ++it;
+    ASSERT(fabs((*it)[0] - 45) < EPS); // 
+    ASSERT(fabs((*it)[1] - piconst::pi/4.0) < EPS); //
+    ASSERT(fabs((*it)[2] - 45.0) < EPS); // 
+    ASSERT(fabs((*it)[3] - piconst::pi/4.0) < EPS);    // 
+
+    ASSERT(fabs((*it)[4] - piconst::pi/4.0) < EPS); // 
+    ASSERT(fabs((*it)[5] - 45.0e0) < EPS);    //
+
+
+}
+
+
+
+SIMPLE_TEST(FunctionsForAngleConversion)
diff --git a/odb_api/src/odb_api/tools/TestFunctionsForTemperatureConversion.cc b/odb_api/src/odb_api/tools/TestFunctionsForTemperatureConversion.cc
new file mode 100644
index 0000000..b022b7c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestFunctionsForTemperatureConversion.cc
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.cc
+///
+/// @author ECMWF, July 2010
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp()
+{
+	Timer t("Test various functions to convert temperatures");
+	odb::Writer<> oda("test_tempconv.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(3);
+
+	row->setColumn(0, "kelvin_col", odb::REAL);
+	row->setColumn(1, "celsius_col", odb::REAL);
+	row->setColumn(2, "fahrenheit_col", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = 273.15;
+	(*row)[1] = 0.0;
+	(*row)[2] = 32;
+	++row;
+}
+
+static void tearDown() 
+{ 
+	PathName("test_tempconv.odb").unlink();
+}
+
+static void test()
+{
+    const string sql = "select celsius(kelvin_col), fahrenheit(kelvin_col), c2k(celsius_col),c2f(celsius_col),f2c(fahrenheit_col), f2k(fahrenheit_col), k2f(kelvin_col) from \"test_tempconv.odb\";";
+
+	Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+	odb::Select oda(sql);
+	odb::Select::iterator it = oda.begin();
+
+	ASSERT((*it)[0] == 0.0); // celsius(273.15) = 0.0
+	ASSERT((*it)[1] == 32); // farhenheit(273.15) = 31.73
+	ASSERT((*it)[2] == 273.15); // c2k
+	ASSERT((*it)[3] == 32);    // c2f
+
+	ASSERT((*it)[4] == 0); // f2c
+	ASSERT((*it)[5] == 273.15);    // f2k
+	ASSERT((*it)[6] == 32);    // k2f
+
+}
+
+
+
+SIMPLE_TEST(FunctionsForTemperatureConversion)
diff --git a/odb_api/src/odb_api/tools/TestInMemoryDataHandle.cc b/odb_api/src/odb_api/tools/TestInMemoryDataHandle.cc
new file mode 100644
index 0000000..37d3fea
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestInMemoryDataHandle.cc
@@ -0,0 +1,51 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <strings.h>
+
+#include "odb_api/InMemoryDataHandle.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+
+
+static void test()
+{
+	char data[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};
+	char data2[] = {1,2,3,4,5,6,7,8,9,10,11,12,13};
+	bzero(data2, sizeof(data2));
+
+    odb::InMemoryDataHandle h;
+    h.openForWrite(0);
+
+	h.write(data, sizeof(data));
+	Length len = h.openForRead();
+	Log::info() << "Len = " << len << std::endl;
+	ASSERT(len == Length(sizeof(data)));
+	h.read(data2, sizeof(data2));
+
+	for (size_t i = 0; i < sizeof(data); i++)
+	{
+		Log::info() << "data[i]=" << (int) data[i] << ", data2[i]=" << (int) data2[i] << std::endl;
+		ASSERT(data[i] == data2[i]);
+	}
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(InMemoryDataHandle)
diff --git a/odb_api/src/odb_api/tools/TestInt16_MissingCodec.cc b/odb_api/src/odb_api/tools/TestInt16_MissingCodec.cc
new file mode 100644
index 0000000..39c7321
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestInt16_MissingCodec.cc
@@ -0,0 +1,134 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2010
+
+#include "eckit/log/Timer.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "MockReader.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+class MockReaderIterator3 
+{
+public:
+	MockReaderIterator3()
+    : noMore_(false), refCount_(0), columns_(1), nRows_(0), min_(23), data_(0), context_(0) 
+	{
+		odb::Column* col = columns_[0] = new odb::Column(columns_);
+		ASSERT(col);
+
+		col->name("column_name"); 
+		col->type<DataStream<SameByteOrder, DataHandle> >(odb::INTEGER, false);
+		col->hasMissing(true);
+		next(context_);
+	}
+
+	odb::MetaData& columns() { return columns_; }
+
+	bool isNewDataset() { return false; } 
+	double* data() { return &data_; }
+	
+	//MockReaderIterator3& operator++() { next(); return *this; }
+
+	const MockReaderIterator3& end() { return *reinterpret_cast<MockReaderIterator3*>(0); }
+
+	bool next(ecml::ExecutionContext*)
+	{
+		if (noMore_) return noMore_;
+		switch (nRows_++)
+		{
+			case 0:
+				data_ = min_ + 0;
+				break;
+			case 1:
+				data_ = min_ + (0xffff - 1) / 2;
+				break;
+			case 2:
+				data_ = min_ + (0xffff - 1);
+				break;
+			case 3:
+				data_ = columns_[0]->coder().missingValue();
+				break;
+			default:
+				return !(noMore_ = true);
+				break;
+		}
+		return true;
+	}
+	
+	bool noMore_;
+	int refCount_;
+    ecml::ExecutionContext* context_;
+
+private:
+	odb::MetaData columns_;
+	unsigned int nRows_;
+	double min_;
+	double data_;
+};
+
+static void setUp()
+{
+	Timer t("Writing test_int16_missing.odb");
+	odb::Writer<> oda("test_int16_missing.odb");
+
+	typedef tool::MockReader<MockReaderIterator3> M;
+	M reader;
+	M::iterator b = reader.begin();
+	const M::iterator e = reader.end();
+
+	odb::Writer<>::iterator outit = oda.begin();
+	outit->pass1(b, e);
+}
+
+static void test()
+{
+	odb::Reader oda("test_int16_missing.odb");
+	odb::Reader::iterator it = oda.begin();
+	odb::Reader::iterator end = oda.end();
+
+	typedef tool::MockReader<MockReaderIterator3> M;
+	M reader;
+	M::iterator originalIt = reader.begin();
+	const M::iterator originalItEnd = reader.end();
+
+	Log::info() << it->columns() << std::endl;
+	
+	for ( ; it != end; ++it, ++originalIt)
+	{
+		Log::info() << "it[0] = " << (*it)[0] << ", originalIt.data()[0]=" << (*originalIt)[0] << std::endl;
+		ASSERT((*it)[0] == (*originalIt)[0]);
+	}
+
+	odb::codec::Codec& coder ( it->columns()[0]->coder() );
+
+	string name = coder.name();
+
+	Log::debug() << "test: codec name is '" << name << "'" << std::endl;
+
+	ASSERT(name == "int16_missing");
+
+	Log::debug() << "test: OK" << std::endl;
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(Int16_MissingCodec)
diff --git a/odb_api/src/odb_api/tools/TestIntegerValues.cc b/odb_api/src/odb_api/tools/TestIntegerValues.cc
new file mode 100644
index 0000000..0652a36
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestIntegerValues.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestIntegerValues.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/Reader.h"
+
+#include "ImportTool.h"
+
+#include "TestCase.h"
+
+
+
+
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void setUp()
+{
+	const char* data =
+	"date:REAL\n"
+	"20101130\n"
+	"20101201\n"
+	"20101202\n";
+    odb::tool::ImportTool::importText(data, "TestIntegerValues.odb");
+}
+
+
+static void test()
+{
+	//Log::info() << fixed;
+ //////////////////////////////           ODB from MARS             //////////////////////////////    
+
+    const std::string fileNameOdb="TestIntegerValues.odb";
+
+	odb::Reader odb(fileNameOdb);
+	odb::Reader::iterator it = odb.begin();
+
+	for (unsigned int i=0; i < it->columns().size(); ++i) {
+		std::cout << "Name = " << it->columns()[i]->name() << " " ;
+	}
+
+	std::cout << std::endl;
+	int nrows=0;
+	for(; it != odb.end(); ++it)
+	{
+		++nrows;
+		for (size_t i=0; i < it->columns().size(); ++i)
+		{
+			//float nr = ((*it)[i]); /// <- WRONG!
+			double nr = ((*it)[i]);
+			switch(it->columns()[i]->type())
+			{
+			case odb::INTEGER:
+			case odb::BITFIELD:
+				std::cout <<  static_cast<int>(nr) << " ";
+				//cout <<  "* should be: " << it->integer(i) << " ";
+				break;
+			case odb::REAL:
+				std::cout <<  nr << " ";
+				break;
+			case odb::IGNORE:
+			default:
+				ASSERT("Unknown type" && false);
+				break;
+			}
+ 		}
+ 		std::cout << std::endl;
+	}
+}
+
+static void tearDown() { }
+
+
+SIMPLE_TEST(IntegerValues)
diff --git a/odb_api/src/odb_api/tools/TestMetaData.cc b/odb_api/src/odb_api/tools/TestMetaData.cc
new file mode 100644
index 0000000..f4f3f2f
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestMetaData.cc
@@ -0,0 +1,56 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/DataStream.h"
+#include "odb_api/MetaData.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	typedef DataStream<SameByteOrder, DataHandle> DS;
+
+	MetaData md1(0);
+	md1.addColumn /*<DS>*/("A", "REAL");//, false, 0.);
+	md1.addColumn /*<DS>*/("B", "INTEGER");//, false, 0.);
+
+	Log::info() << "md1: " << std::endl << md1 << std::endl;
+
+	MetaData md2(0);
+	md2.addColumn /*<DS> */("C", "STRING");//, false, 0.);
+
+	Log::info() << "md2:" << std::endl << md2 << std::endl;
+
+	MetaData sum = md1 + md2;
+	Log::info() << "md1 + md2: " << std::endl <<  sum << std::endl;
+
+	ASSERT(sum.size() == md1.size() + md2.size());
+	ASSERT(sum == md1 + md2);
+
+	MetaData sum2 = md1;
+	sum2 += md2;
+
+	ASSERT(sum2.size() == md1.size() + md2.size());
+	ASSERT(sum == sum2);
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(MetaData)
diff --git a/odb_api/src/odb_api/tools/TestMetaDataReader.cc b/odb_api/src/odb_api/tools/TestMetaDataReader.cc
new file mode 100644
index 0000000..759c578
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestMetaDataReader.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestMetaDataReader.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+#include "odb_api/MetaData.h"
+
+#include "ImportTool.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void setUp()
+{
+    odb::tool::ImportTool::importText("x:REAL\n1\n2\n3\n", "TestMetaDataReader.odb");
+}
+
+
+static void test()
+{
+	const char *fileName = "TestMetaDataReader.odb";
+
+	MetaData wholeFileMD(MetaData::scanFile(fileName));
+
+	ASSERT(wholeFileMD[0]->min() == 1 && wholeFileMD[0]->max() == 3);
+
+	Log::info() << "test wholeFileMD==" << wholeFileMD << std::endl;
+}
+
+static void tearDown() { }
+
+
+SIMPLE_TEST(MetaDataReader)
diff --git a/odb_api/src/odb_api/tools/TestMetaDataReader.ksh b/odb_api/src/odb_api/tools/TestMetaDataReader.ksh
new file mode 100755
index 0000000..fb372b7
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestMetaDataReader.ksh
@@ -0,0 +1,46 @@
+set -e
+
+function log
+{
+	if [ x$MARS_SMS_LABEL != x ];
+	then
+		smslabel $MARS_SMS_LABEL "$*";
+	else
+		echo ++++++++++++++++++++++++++++++++++++++++++++
+		echo + $*
+		echo ++++++++++++++++++++++++++++++++++++++++++++
+	fi 
+}
+
+#set -v
+set -x
+
+log +++ Test: odb import
+
+cat >TestMetaDataReader1.csv <<@@
+x:INTEGER
+1
+@@
+./odb import TestMetaDataReader1.csv TestMetaDataReader1.odb
+
+cat >TestMetaDataReader2.csv <<@@
+x:INTEGER
+2
+@@
+./odb import TestMetaDataReader2.csv TestMetaDataReader2.odb
+
+cat >TestMetaDataReader3.csv <<@@
+x:INTEGER
+3
+@@
+./odb import TestMetaDataReader3.csv TestMetaDataReader3.odb
+cat TestMetaDataReader1.odb TestMetaDataReader2.odb TestMetaDataReader3.odb >TestMetaDataReader.odb
+
+./odb sql select \* from \"TestMetaDataReader.odb\"
+
+log +++ All tests completed OK
+JUST_ONE_TEST(test, setUp, tearDown)
+void test(){}
+void setUp(){}
+void tearDown(){}
+JUST_ONE_TEST(test, setUp, tearDown)
diff --git a/odb_api/src/odb_api/tools/TestMinMax.cc b/odb_api/src/odb_api/tools/TestMinMax.cc
new file mode 100644
index 0000000..e178dbc
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestMinMax.cc
@@ -0,0 +1,86 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+#ifndef INT32_MAX
+# define INT32_MAX		(2147483647)
+#endif
+
+#ifndef INT32_MIN
+# define INT32_MIN		(-INT32_MAX-1)
+#endif
+
+const string SELECT  = "select * from \"test.odb\";";
+
+
+static void setUp()
+{
+	Timer t("Writing testminmax.odb");
+	odb::Writer<> oda("testminmax.odb");
+
+	odb::Writer<>::iterator row = oda.begin();
+	row->setNumberOfColumns(2);
+
+	row->setColumn(0, "intcol", odb::INTEGER);
+	row->setColumn(1, "realcol", odb::REAL);
+	
+	row->writeHeader();
+
+	(*row)[0] = INT32_MIN;
+	(*row)[1] = 1;
+	++row;
+
+	(*row)[0] = INT32_MAX;
+	(*row)[1] = 1;
+	++row;
+	
+	(*row)[0] = INT32_MAX + INT32_MIN;
+	(*row)[1] = 1;
+	++row;
+}
+
+static void tearDown() { }
+
+static void test()
+{
+	odb::Reader oda("testminmax.odb");
+	odb::Reader::iterator it = oda.begin();
+
+	//ASSERT(it->integer(0) == INT32_MIN);
+	ASSERT((*it)[0] == INT32_MIN);
+	++it;
+
+	//ASSERT(it->integer(0) == INT32_MAX);
+	ASSERT((*it)[0] == INT32_MAX);
+	++it;
+
+	//ASSERT(it->integer(0) == INT32_MAX + INT32_MIN);
+	ASSERT((*it)[0] == INT32_MAX + INT32_MIN);
+	++it;
+
+	//ASSERT(! (it != oda.end()));
+}
+
+
+
+SIMPLE_TEST(MinMax)
diff --git a/odb_api/src/odb_api/tools/TestMissingValue.cc b/odb_api/src/odb_api/tools/TestMissingValue.cc
new file mode 100644
index 0000000..5503293
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestMissingValue.cc
@@ -0,0 +1,145 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestMissingValue.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/Comparator.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Tracer.h"
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+static void setUp()
+{
+	Tracer t(Log::debug(), "setUp");
+
+	odb::Writer<> f("TestMissingValue.odb");
+	odb::Writer<>::iterator it = f.begin();
+
+    it->setNumberOfColumns(2);
+
+	it->setColumn(0, "lat at hdr", odb::REAL);
+	it->missingValue(0, 1);
+
+	BitfieldDef bfDef;
+	bfDef.first.push_back("x");
+	bfDef.second.push_back(1);
+	bfDef.first.push_back("y");
+	bfDef.second.push_back(2);
+
+	it->setBitfieldColumn(1, "bf", odb::BITFIELD, bfDef);
+
+	it->writeHeader();
+
+	for (size_t i = 0; i <= 2; i++)
+	{
+		(*it)[0] = i;
+		(*it)[1] = i;
+		++it;
+	}
+}
+
+static void selectIntoSecondFile()
+{
+    Tracer t(Log::debug(), "selectIntoSecondFile");
+
+    const string fileName = "TestMissingValue.odb";
+    string sql = "select lat,bf into \"TestMissingValue.odb\"";
+    sql += " from \"" + fileName + "\" ;";
+
+    odb::Select f(sql); //, fileName);
+    odb::Select::iterator it = f.begin();
+
+    ++it; // this is needed to push the second row to the INTO file
+    ++it; // this is needed to push the third row to the INTO file
+}
+
+
+static void test()
+{
+	selectIntoSecondFile();
+
+	odb::Comparator().compare("TestMissingValue.odb", "TestMissingValue.odb");
+
+	{
+		odb::Reader f("TestMissingValue.odb");
+		odb::Reader::iterator fbegin(f.begin());
+		odb::Reader::iterator fend(f.end());
+
+		odb::Select s("select * from \"TestMissingValue.odb\";");
+		odb::Select::iterator sbegin(s.begin());
+		odb::Select::iterator send(s.end());
+
+		odb::Comparator().compare(fbegin, fend, sbegin, send, "TestMissingValue.odb", "SELECT TestMissingValue.odb");
+	}
+
+	{
+		odb::Reader f("TestMissingValue.odb");
+		odb::Reader::iterator it = f.begin();
+		odb::Reader::iterator end = f.end();
+
+        Column& column = *it->columns()[0];
+		codec::Codec& codec = column.coder();
+
+		Log::info() << "test: codec: " << codec << std::endl;	
+
+		ASSERT(codec.hasMissing());
+		ASSERT(codec.missingValue() == 1);
+
+
+		for (; it != end; ++it)
+		{
+			ASSERT( (*it).missingValue(0) == 1 );
+
+			if ( (*it)[0] == 1 )
+				ASSERT( (*it).isMissing(0) );
+			else
+				ASSERT( ! (*it).isMissing(0) );
+		}
+	}
+
+	{
+		// Check the isMissing and missingValue API of SelectIterator
+		odb::Select s("select * from \"TestMissingValue.odb\";"); //, fileName);
+		odb::Select::iterator i = s.begin();
+		odb::Select::iterator e = s.end();
+		for (; i != e; ++i)
+		{
+			ASSERT( (*i).missingValue(0) == 1 );
+			ASSERT( (*i).missingValue(1) == 0 );
+
+			if ( (*i)[0] == 1 )
+				ASSERT( (*i).isMissing(0) );
+			else
+				ASSERT( ! (*i).isMissing(0) );
+
+			// For Bitfields missing value by default equals 0
+			if ( (*i)[1] == 0 )
+				ASSERT( (*i).isMissing(1) );
+			else
+				ASSERT( ! (*i).isMissing(1) );
+		}
+	}
+
+}
+
+
+static void tearDown() { }
+
+
+
+SIMPLE_TEST(MissingValue)
diff --git a/odb_api/src/odb_api/tools/TestODBModule.cc b/odb_api/src/odb_api/tools/TestODBModule.cc
new file mode 100644
index 0000000..d5a5ff6
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestODBModule.cc
@@ -0,0 +1,53 @@
+/// @file   TestODBModule.cc
+/// @author Piotr Kuchta
+
+#include "odb_api/tools/TestCase.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/ODBModule.h"
+
+#include "eckit/log/Log.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "ecml/core/ExecutionContext.h"
+
+using namespace std;
+using namespace odb;
+using namespace eckit;
+
+namespace {
+
+struct ODBModule
+{
+    ODBModule()
+    : odbModule(),
+      context()
+    {
+        context.import(odbModule);
+    }
+
+    odb::ODBModule odbModule;
+    ecml::ExecutionContext context;
+};
+
+
+TEST_FIXTURE(ODBModule, SQLSourceDoesNotExistThrows)
+{
+    bool exceptionThrown (false);
+    try {
+        context.execute(
+        "sql,"
+        " source = non_existing_file.odb,"
+        " sql = 'select * where varno = 1',"
+        " target = output.odb"
+        );
+
+    } catch (eckit::Exception& e) {
+        exceptionThrown = true;
+        string msg (e.what());
+        Log::info() << "WHAT: " << msg << endl;
+        CHECK(msg.find("non_existing_file.odb") != string::npos);
+    }
+    CHECK(exceptionThrown);
+}
+
+} // namespace
diff --git a/odb_api/src/odb_api/tools/TestOdaCAPI.cc b/odb_api/src/odb_api/tools/TestOdaCAPI.cc
new file mode 100755
index 0000000..64575f8
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestOdaCAPI.cc
@@ -0,0 +1,261 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/eckit.h"
+#include "eckit/exception/Exceptions.h"
+#include "eckit/log/Log.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/ColumnType.h"
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+extern "C" {
+#include "odb_api/odbcapi.h"
+}
+
+#include "TestOdaCAPI.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+namespace odb {
+namespace tool {
+namespace test {
+
+int test_odacapi_setup_in_C(int argc, char* argv[])
+{
+    const char *filename = "test.odb";
+    int err = 0;
+
+    oda_writer* writer = odb_writer_create("", &err);
+    ASSERT(writer);
+
+    oda_write_iterator* wi = odb_create_write_iterator(writer, filename, &err);
+    ASSERT(wi);
+
+    ASSERT(0 == odb_write_iterator_set_no_of_columns(wi, 2));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 0, odb::INTEGER, "ifoo"));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 1, odb::REAL, "nbar"));
+
+    ASSERT(0 == odb_write_iterator_write_header(wi));
+
+    double data[2];
+    for (int i = 1; i <= 10; i++)
+    {
+        data[0] = i;
+        data[1] = i;
+
+        ASSERT(0 == odb_write_iterator_set_next_row(wi, data, 2));
+    }
+
+    ASSERT(0 == odb_write_iterator_destroy(wi));
+    ASSERT(0 == odb_writer_destroy(writer));
+    return 0;
+}
+
+int test_odacapi_setup(int argc, char* argv[])
+{
+    Timer t("Writing test.odb");
+    odb::Writer<> oda("test.odb");
+
+    odb::Writer<>::iterator writer = oda.begin();
+    writer->setNumberOfColumns(2);
+
+    writer->setColumn(0, "ifoo", odb::INTEGER);
+    writer->setColumn(1, "nbar", odb::REAL);
+
+    writer->writeHeader();
+
+    for (int i = 1; i <= 10; i++)
+    {
+        writer->data()[0] = i; // col 0
+        writer->data()[1] = i; // col 1
+        ++writer;
+    }
+    //writer->close();
+    return 0;
+}
+
+int test_odacapi1(int argc, char* argv[])
+{
+    std::cout << "UnitTest odacapi..." << std::endl;
+
+    int err;
+
+    std::cout << "Calling oda_create..." << std::endl;
+
+    oda_ptr oh = odb_read_create("", &err);
+
+    oda_read_iterator* it = odb_create_read_iterator(oh, "test.odb", &err);
+    ASSERT(0 == err);
+    ASSERT(0 != it);
+
+    int nCols;
+    ASSERT(0 == odb_read_iterator_get_no_of_columns(it, &nCols));
+    ASSERT(nCols == 2);
+
+    int type0;
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 0, &type0));
+    ASSERT(type0 == 1 /*INTEGER*/);
+
+    int type1;
+    ASSERT(0 == odb_read_iterator_get_column_type(it, 1, &type1));
+    ASSERT(type1 == 2 /*REAL*/);
+
+    char *name0;
+    int name0size;
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 0, &name0, &name0size));
+
+    char *name1;
+    int name1size;
+    ASSERT(0 == odb_read_iterator_get_column_name(it, 1, &name1, &name1size));
+
+    double buffer[2];
+    double* data = buffer;
+    int newDataset = 0;
+    int nRows = 0;
+    while (0 == odb_read_iterator_get_next_row(it, 2, data, &newDataset))
+    {
+        ++nRows;
+        int v0 = int(data[0]);
+
+        std::cout << "Read row " << nRows << std::endl;
+
+        ASSERT(v0 == nRows);
+    }
+
+    ASSERT(0 == odb_read_iterator_destroy(it));
+    ASSERT(0 == odb_read_destroy(oh));
+    std::cout << "OK" << std::endl;
+    return 0;
+}
+
+int test_odacapi2(int argc, char* argv[])
+{
+    std::cout << "UnitTest odacapi 2..." << std::endl;
+
+    int err;
+
+    std::cout << "Calling odb_start_with_args..." << std::endl;
+    odb_start_with_args(argc, argv);
+
+    std::cout << "Calling odb_create..." << std::endl;
+
+    oda_ptr oh = odb_select_create("", &err);
+
+    Log::info() << "Log::info initialised properly." << std::endl;
+
+    oda_select_iterator* it = odb_create_select_iterator(oh, "select * from \"test.odb\";", &err);
+    ASSERT(0 == err);
+    ASSERT(0 != it);
+
+    int nCols;
+    ASSERT(0 == odb_select_iterator_get_no_of_columns(it, &nCols));
+    ASSERT(nCols == 2);
+
+    int type0;
+    ASSERT(0 == odb_select_iterator_get_column_type(it, 0, &type0));
+    ASSERT(type0 == 1 /*INTEGER*/);
+
+    int type1;
+    ASSERT(0 == odb_select_iterator_get_column_type(it, 1, &type1));
+    ASSERT(type1 == 2 /*REAL*/);
+
+    char *name0;
+    int name0size;
+    ASSERT(0 == odb_select_iterator_get_column_name(it, 0, &name0, &name0size));
+
+    char *name1;
+    int name1size;
+    ASSERT(0 == odb_select_iterator_get_column_name(it, 1, &name1, &name1size));
+
+    double buffer[2];
+    double* data = buffer;
+    int newDataset = 0;
+    int nRows = 0;
+    while (0 == odb_select_iterator_get_next_row(it, 2, data, &newDataset))
+    {
+        ++nRows;
+        int v0 = int(data[0]);
+
+        std::cout << "Read row " << nRows << std::endl;
+
+        ASSERT(v0 == nRows);
+    }
+
+    ASSERT(0 == odb_select_iterator_destroy(it));
+    ASSERT(0 == odb_read_destroy(oh));
+    std::cout << "OK" << std::endl;
+    return 0;
+}
+
+int test_odacapi3(int argc, char* argv[])
+{
+    std::cout << "UnitTest ODB C API append to file functionality..." << std::endl;
+
+    const char *filename = "test.odb";
+    int err = 0;
+
+    double n = odb_count(filename);
+    std::cout << "test_odacapi3: number of rows = " << n << std::endl;
+    ASSERT(n == 10);
+
+    oda_writer* writer = odb_writer_create("", &err);
+    ASSERT(writer);
+
+    oda_write_iterator* wi = odb_create_append_iterator(writer, filename, &err);
+    ASSERT(wi);
+    ASSERT(0 == err);
+    ASSERT(0 != wi);
+
+    ASSERT(0 == odb_write_iterator_set_no_of_columns(wi, 2));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 0, odb::INTEGER, "ifoo"));
+    ASSERT(0 == odb_write_iterator_set_column(wi, 1, odb::REAL, "nbar"));
+
+    ASSERT(0 == odb_write_iterator_write_header(wi));
+
+    double data[2];
+    for (int i = 1; i <= 10; i++)
+    {
+        data[0] = i;
+        data[1] = i;
+
+        ASSERT(0 == odb_write_iterator_set_next_row(wi, data, 2));
+    }
+
+    ASSERT(0 == odb_write_iterator_destroy(wi));
+    ASSERT(0 == odb_writer_destroy(writer));
+
+
+    n = odb_count(filename);
+    std::cout << "test_odacapi3: number of rows = " << n << std::endl;
+    ASSERT(n == 20);
+
+    return 0;
+}
+
+int test_odacapi(int argc, char* argv[])
+{
+    std::cout << "Calling odb_init..." << std::endl;
+    odb_start_with_args(argc, argv);
+    Log::info() << "Log::info initialised properly." << std::endl;
+
+    //return test_odacapi_setup()
+    return test_odacapi_setup_in_C(argc, argv)
+            || test_odacapi1(argc, argv)
+            || test_odacapi2(argc, argv)
+            || test_odacapi3(argc, argv);
+}
+
+} // namespace test 
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/TestOdaCAPI.h b/odb_api/src/odb_api/tools/TestOdaCAPI.h
new file mode 100755
index 0000000..98b9932
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestOdaCAPI.h
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef TEST_ODA_C_API_H
+#define TEST_ODA_C_API_H
+
+namespace odb {
+namespace tool {
+namespace test {
+
+int test_odacapi(int argc, char *argv[]);
+
+int test_odacapi_setup_in_C(int argc, char *argv[]);
+int test_odacapi_setup(int argc, char *argv[]);
+int test_odacapi1(int argc, char *argv[]);
+int test_odacapi2(int argc, char *argv[]);
+int test_odacapi3(int argc, char *argv[]);
+
+} // namespace test 
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/tools/TestOrderBy.cc b/odb_api/src/odb_api/tools/TestOrderBy.cc
new file mode 100644
index 0000000..be75967
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestOrderBy.cc
@@ -0,0 +1,135 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, September 2010
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/parser/StringTools.h"
+#include "odb_api/Select.h"
+
+#include "ImportTool.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+///
+static void test()
+{
+	{
+		string sql = "select distinct a from \"TestOrderBy_a1to10twice.odb\" order by a;";
+		Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+		odb::Select sel(sql);
+		odb::Select::iterator it = sel.begin();
+		odb::Select::iterator end = sel.end();
+
+		Log::info()  << "test: entering the loop" << std::endl;
+		int i = 0;
+		for (; it != end; ++it)
+		{
+			int v = (*it)[0];
+			Log::info()  << "test:" <<  v  << std::endl;
+			ASSERT(v == ++i);
+		}
+		Log::info()  << "test: i = " <<  i  << std::endl;
+		ASSERT(i == 10);
+	}
+
+	{
+		string sql = "select a from \"TestOrderBy_a1to10twice.odb\" order by a;";
+		Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+		odb::Select sel(sql);
+		odb::Select::iterator it = sel.begin();
+		odb::Select::iterator end = sel.end();
+
+		Log::info()  << "test: entering the loop" << std::endl;
+		int i = 0, j = 0;
+		for (; it != end; ++it, ++j)
+		{
+			int v = (*it)[0];
+			Log::info()  << "test:" <<  v  << std::endl;
+			ASSERT(i <= v);
+			i = v;
+		}
+		Log::info()  << "test: i=" <<  i  << ", j=" << j << std::endl;
+		ASSERT(i == 10);
+		ASSERT(j == 20);
+	}
+
+	{
+		string sql = "select distinct a from \"TestOrderBy_a1to10twice.odb\" order by a desc;";
+		Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+		odb::Select sel(sql);
+		odb::Select::iterator it = sel.begin();
+		odb::Select::iterator end = sel.end();
+
+		Log::info()  << "test: entering the loop" << std::endl;
+		int i = 10, j = 0;
+		for (; it != end; ++it, ++j)
+		{
+			int v = (*it)[0];
+			Log::info()  << "test:" <<  v  << std::endl;
+			ASSERT(i-- == v);
+		}
+		Log::info()  << "test: i = " <<  i  << std::endl;
+		ASSERT(i == 0);
+		ASSERT(j == 10);
+	}
+
+	{
+		const char *in =
+		"a:REAL,b:REAL,c:STRING\n"
+		"1,10,'one'\n"
+		"1,20,'two'\n"
+		"2,30,'three'\n"
+		"2,40,'four'\n";
+        odb::tool::ImportTool::importText(in, "TestOrderBy.odb");
+
+		string sql = "select distinct a,b,c from \"TestOrderBy.odb\" order by a desc, b asc;";
+		Log::info() << "Executing: '" << sql << "'" << std::endl;
+
+		odb::Select sel(sql);
+		odb::Select::iterator it = sel.begin();
+		odb::Select::iterator end = sel.end();
+
+		Log::info()  << "test: entering the loop" << std::endl;
+        int i = 0, v1 = 0 , v2 = 0;
+		string s;
+		for (; it != end; ++it, ++i)
+		{
+			v1 = (*it)[0];
+			v2 = (*it)[1];
+			s = (*it).string(2);
+			Log::info() << "test:" <<  v1  << ", " << v2 << ", '" << s << "'" << std::endl;
+		}
+		Log::info()  << "test: i = " <<  i  << std::endl;
+		ASSERT(i == 4);
+		ASSERT(v1 == 1 && v2 == 20 && StringTools::trim(s) == "two");
+	}
+}
+
+
+static void setUp()
+{
+	stringstream s;
+	s << "a:REAL" << std::endl;
+	for (size_t i = 1; i <= 10; ++i) s << i << std::endl;
+	for (size_t i = 1; i <= 10; ++i) s << i << std::endl;
+    odb::tool::ImportTool::importText(s.str().c_str(), "TestOrderBy_a1to10twice.odb");
+}
+static void tearDown(){}
+
+SIMPLE_TEST(OrderBy)
diff --git a/odb_api/src/odb_api/tools/TestRunner.cc b/odb_api/src/odb_api/tools/TestRunner.cc
new file mode 100644
index 0000000..de819bf
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestRunner.cc
@@ -0,0 +1,202 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestRunner.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <sstream>
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Timer.h"
+#include "eckit/parser/StringTools.h"
+
+#include "odb_api/CommandLineParser.h"
+#include "Tool.h"
+#include "TestCase.h"
+#include "ToolFactory.h"
+#include "TestRunner.h"
+
+using namespace eckit;
+using namespace std;
+
+namespace odb {
+namespace tool {
+namespace test {
+
+TestRunner::TestRunner (CommandLineParser& clp)
+: clp_(clp),
+  mars_sms_label_(false),
+  label_()
+{
+	if (getenv("MARS_SMS_LABEL"))
+	{
+		mars_sms_label_ = true;
+		label_ = getenv("MARS_SMS_LABEL");
+	}
+}
+
+TestRunner::~TestRunner () {}
+
+void TestRunner::run()
+{
+	ASSERT(getenv("ODB_API_TEST_DATA_PATH") && "ODB_API_TEST_DATA_PATH must be set");
+
+	stringstream totalRunningTime;
+	auto_ptr<Timer> allTestsTimer(new Timer("Total", totalRunningTime));
+	auto_ptr<TestCases> tests(0);
+	
+	failed_.clear();
+
+	if (clp_.parameters().size() == 1)
+	{
+		tests.reset(AbstractToolFactory::testCases());
+        Log::info() << "clp_.parameters()" << clp_.parameters() << endl;
+		runTests(*tests);
+	}
+	else
+	{
+		// TODO: keep the config in the ODB_API_TEST_DATA_PATH
+		//readConfig("../../../odb_api/src/odb/TestRunnerApplication.cfg");
+		readConfig("/tmp/Dropbox/work/odb_api/src/odb/TestRunnerApplication.cfg");
+		readConfig("/tmp/Dropbox/work/odb_api/src/tools/TestRunnerApplication.cfg");
+		tests.reset(new TestCases());
+		for (size_t i = 1; i < clp_.parameters().size(); ++i)
+		{
+			string suiteName = clp_.parameters()[i];
+			ASSERT("Suite does not exist" && suites_.find(suiteName) != suites_.end());
+			vector<string>& suite = suites_[suiteName];
+			auto_ptr<vector<TestCase*> > tsts(AbstractToolFactory::testCases(suite));
+
+			runTests(*tsts);
+			tests->insert(tests->end(), tsts->begin(), tsts->end());
+		}
+	}
+
+	allTestsTimer.reset();
+
+	ofstream xmlf("testresults.xml");
+	xmlf << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+	xmlf << "<testsuite name=\"unittests\" time=\"" << StringTools::split(" ", totalRunningTime.str())[1] << "\">" << endl;
+	xmlf << xml_.str();
+	xmlf << "</testsuite>" << endl;
+
+	size_t nTests = tests->size();
+	for (size_t i = 0; i < nTests; ++i)
+		delete (*tests)[i];
+
+	if (failed_.size() == 0) {
+		Log::info() << endl << "+- Phew, made it! All " << nTests << " tests passed successfully. " << endl << endl;
+		Log::info() << runningTimes_.str() << endl;;
+		Log::info() << totalRunningTime.str() << endl;
+	}
+	else
+	{
+		Log::error() << endl << "+- Summary: " << failed_.size() << " test(s) failed." << endl;
+		for (vector<FailedTest>::iterator it = failed_.begin(); it != failed_.end(); ++it) {
+			const string& name = it->first;
+			const string& what = it->second;
+			Log::error() << "\t" << name << ": " << endl << what;
+		}
+		Log::error() << endl;
+
+		stringstream ss;
+		ss << " " << failed_.size() << " test(s) failed";
+		throw eckit::SeriousBug(ss.str());
+	}
+}
+
+void TestRunner::runTests(const TestCases& tests)
+{
+	for (TestCases::const_iterator it = tests.begin(); it != tests.end(); ++it)
+	{
+		bool exceptionThrown = false;
+		string what;
+		TestCase *tst = *it;
+		const string& name = tst->name();
+
+		Log::info() << "+- Running " << name << " ..." << endl;
+		smslabel(name);
+
+		stringstream runningTime;
+		auto_ptr<Timer> timer(new Timer(name, runningTime));
+		try {
+			tst->setUp();
+			tst->test();
+		} catch (std::exception &e) {
+			Log::warning() << "+- FAILED" << endl;
+			exceptionThrown = true;
+			what += string(e.what()) + '\n';
+		} catch (...) {
+			Log::warning() << "+- FAILED: unknown exception!" << endl;
+			exceptionThrown = true;
+			what += string("Uknown exception") + '\n';
+		}
+		try {
+			tst->tearDown();
+		} catch (std::exception &e) {
+			Log::warning() << "+- Exception thrown from tearDown." << endl;
+			exceptionThrown = true;
+			what += string("[In tearDown:]") + string(e.what()) + '\n';
+		} catch (...) {
+			Log::warning() << "+- FAILED: unknown exception!" << endl;
+			exceptionThrown = true;
+			what += string("Uknown exception") + '\n';
+		}
+
+		if (exceptionThrown) {
+			failed_.push_back(make_pair(name, what));
+			xml_ << "<testcase classname=\"test\" name=\"" << name << "\">" << endl;
+			xml_ << "	<failure type=\"exception\"><![CDATA[" << what << "]]></failure>" << endl;
+			xml_ << "</testcase>" << endl;
+		}
+		else {
+			timer.reset();
+			runningTimes_ << runningTime.str();
+			Log::info() << "+- Passed." << endl << endl;
+		 	xml_ << "<testcase classname=\"test\" name=\"" << name 
+				<< "\" time=\"" << StringTools::split(" ", runningTime.str())[1] << "\"/>" << endl;
+		}
+	}
+}
+
+void TestRunner::readConfig(const PathName fileName)
+{
+	Log::debug() << "TestRunner::readConfig: reading file '" << fileName << "'" << endl;
+	suites_.clear();
+
+    vector<string> lines = StringTool::readLines(fileName);
+    for (size_t i = 0; i < lines.size(); ++i)
+	{
+		vector<string> words = StringTools::split(":", lines[i]);
+		if (words.size() == 0)
+			continue;
+		ASSERT("Each line of config file should be like: '<suite_name> : TestPattern1 TestPattern2 ...'" && words.size() == 2);
+
+		suites_[words[0]] = StringTools::split(" \t", words[1]);
+		Log::debug() << "TestRunner::readConfig(\"" << fileName << "\"): "
+			<< words[0] << ": "
+			<< suites_[words[0]].size() << " entries." << endl;
+	}
+}
+
+void TestRunner::smslabel(const string &s)
+{
+	if (! mars_sms_label_)
+		return;
+	string cmd = "smslabel ";
+	cmd += label_ + " " + s;
+    system(cmd.c_str());
+}
+
+} // namespace test
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/TestRunner.h b/odb_api/src/odb_api/tools/TestRunner.h
new file mode 100644
index 0000000..4dedf58
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestRunner.h
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestRunner.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef TestRunner_H
+#define TestRunner_H
+
+#include "odb_api/CommandLineParser.h"
+#include "eckit/filesystem/PathName.h"
+
+namespace odb {
+namespace tool {
+namespace test {
+
+class TestRunner {
+public:
+	TestRunner (CommandLineParser&);
+	virtual ~TestRunner ();
+
+	size_t numberOfFailed() { return failed_.size(); }
+
+	void run();
+
+private:
+	typedef std::pair<std::string, std::string> FailedTest;
+	typedef std::map<std::string, std::vector<std::string> > Suites;
+
+	void readConfig(const eckit::PathName fileName);
+	void runTests(const TestCases &tests);
+
+	void smslabel(const std::string &);
+
+	CommandLineParser clp_;
+
+	Suites suites_;
+	std::vector<FailedTest> failed_;
+	std::stringstream runningTimes_;
+	std::stringstream xml_;
+
+	bool mars_sms_label_;
+	std::string label_;
+};
+
+} // namespace test
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/tools/TestRunnerApplication.cc b/odb_api/src/odb_api/tools/TestRunnerApplication.cc
new file mode 100644
index 0000000..4c434db
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestRunnerApplication.cc
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestRunnerApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include <sstream>
+
+#include "Tool.h"
+#include "TestCase.h"
+#include "ToolFactory.h"
+#include "TestRunnerApplication.h"
+#include "TestRunner.h"
+
+namespace odb {
+namespace tool {
+namespace test {
+
+TestRunnerApplication::TestRunnerApplication (int argc, char **argv)
+: ODBApplication(argc, argv)
+{}
+
+TestRunnerApplication::~TestRunnerApplication () {}
+
+void TestRunnerApplication::run()
+{
+	TestRunner testRunner(commandLineParser());
+	testRunner.run();
+}
+
+} // namespace test
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/TestRunnerApplication.cfg b/odb_api/src/odb_api/tools/TestRunnerApplication.cfg
new file mode 100644
index 0000000..a416008
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestRunnerApplication.cfg
@@ -0,0 +1,13 @@
+importodb: TestAAAImportODB TestAAAImportODBDispatching
+
+disp:      TestODADispatchingWriter
+
+sql:       TestAggregateFunctions TestSelectIterator TestSelectStarAt TestStar TestBitfields TestODASelectDataHandle TestDistinct TestOrderBy
+
+basic:     Test.*Codec.* TestInMemoryDataHandle TestCommandLineParsing TestMinMax TestOdaCAPI TestCatFiles TestTextSelect TestTextSelect2 .*selectAggregatedAndNonAggregated.* Test_vector_syntax Test_bitfieldsLength Test_stringInWhere Test_vector_syntax2 Test_filterInPlace Test_filterInPlace2 Test_rownumber1
+
+codecs:    Test.*Codec.*
+
+functions: Test.*Function.*
+
+include:   Test_include Test_include
diff --git a/odb_api/src/odb_api/tools/TestRunnerApplication.h b/odb_api/src/odb_api/tools/TestRunnerApplication.h
new file mode 100644
index 0000000..301c51e
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestRunnerApplication.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestRunnerApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef TestRunnerApplication_H
+#define TestRunnerApplication_H
+
+#include "odb_api/ODBApplication.h"
+
+namespace odb {
+namespace tool {
+namespace test {
+
+class TestRunnerApplication : public ODBApplication {
+public:
+	TestRunnerApplication (int argc, char **argv);
+	virtual ~TestRunnerApplication ();
+
+	void run();
+};
+
+} // namespace test
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/tools/TestSQLFunctionsInfo.cc b/odb_api/src/odb_api/tools/TestSQLFunctionsInfo.cc
new file mode 100644
index 0000000..4678a8b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSQLFunctionsInfo.cc
@@ -0,0 +1,44 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/FunctionFactory.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void test()
+{
+	typedef odb::sql::expression::function::FunctionFactory::FunctionInfo FI;
+
+	FI& functionsInfo = odb::sql::expression::function::FunctionFactory::instance().functionsInfo();
+
+	Log::info() << "FunctionFactory::functionsInfo().size() == " << functionsInfo.size() << std::endl;
+	for (FI::iterator i = functionsInfo.begin(); i != functionsInfo.end(); ++i)
+	{
+		Log::info() << i->first.first << "/" << i->first.second;
+		if (i + 1 != functionsInfo.end())
+			Log::info() << ", ";
+	}
+	Log::info() << std::endl;
+}
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(SQLFunctionsInfo)
diff --git a/odb_api/src/odb_api/tools/TestSelectDataHandle.cc b/odb_api/src/odb_api/tools/TestSelectDataHandle.cc
new file mode 100644
index 0000000..aad54c8
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectDataHandle.cc
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2010
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest syntax 'select lat, lon' (no file name)
+///
+static void test()
+{
+	string sql = "select * where obstype = 7;";
+	//string sql = "select * where obstype = 7;";
+	//string sql = "select obstype from \"input.oda\";";
+
+	const string fileName = "2000010106.odb";
+	FileHandle fh(fileName);
+	fh.openForRead();
+	odb::Select oda(sql, fh);
+	
+	Log::info() << "test: Execute '" << sql << "'" << std::endl;
+	long n = 0;
+	{
+		Timer t("test: selecting rows using SQL" );
+
+		odb::Select::iterator it = oda.begin();
+		odb::Select::iterator end = oda.end();
+
+		for( ; it != end; ++it)
+			++n;
+	}
+	Log::info() << "test: selected " << n << " rows." << std::endl;
+	ASSERT(n == 3134386); 
+	fh.close();
+}
+
+static void setUp()
+{
+#if 0
+	string s = "Data to be saved";
+	
+	TemporaryFile tmp;
+	ofstream os(tmp.c_str());
+	os << s;
+	os.close();
+	if(!os) throw WriteError(tmp);
+
+	string cmd = "ls -l ";
+	cmd += tmp;
+	system(cmd.c_str());
+
+	cmd = "cat ";
+	cmd += tmp;
+	system(cmd.c_str());
+#endif
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(SelectDataHandle)
diff --git a/odb_api/src/odb_api/tools/TestSelectIterator.cc b/odb_api/src/odb_api/tools/TestSelectIterator.cc
new file mode 100644
index 0000000..d0b56db
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectIterator.cc
@@ -0,0 +1,219 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+const string SELECT  = "select * from \"test.odb\";";
+
+unsigned char REF_DATA[] = 
+{
+	0x0,0x0,0x0,0x60,0x9b,0x5e,0x41,0x40,  // 34.7391
+	0x0,0x0,0x0,0x20,0xcc,0xf,0x11,0xc0,   // -4.26543
+	0x0,0x0,0x0,0x60,0x66,0x46,0x6b,0x40,  // 218.2
+	0x0,0x0,0x0,0x0,0x77,0xc8,0x3b,0x40,   // 27.7831
+	0x0,0x0,0x0,0x80,0xed,0xb,0xef,0xbf,   // -0.970206
+	0x0,0x0,0x0,0xc0,0xcc,0xcc,0x6b,0x40,  // 222.4
+	0x0,0x0,0x0,0x80,0x6,0xff,0x48,0x40,   // 49.9924
+	0x0,0x0,0x0,0x80,0x81,0xec,0xeb,0x3f,  // 0.87262
+	0x0,0x0,0x0,0x60,0x66,0x26,0x6b,0x40,  // 217.2
+	0x0,0x0,0x0,0x0,0xc7,0x18,0x45,0x40,   // 42.1936
+	0x0,0x0,0x0,0xc0,0x56,0x91,0xe7,0x3f,  // 0.736492
+	0x0,0x0,0x0,0xc0,0xcc,0xec,0x6a,0x40,  // 215.4
+	0x0,0x0,0x0,0x0,0xc7,0x18,0x45,0x40,   // 42.1936
+	0x0,0x0,0x0,0xc0,0x56,0x91,0xe7,0xbf,  // -0.736492
+	0x0,0x0,0x0,0xc0,0xcc,0xec,0x6a,0x40,  // 215.4
+	0x0,0x0,0x0,0xa0,0xa5,0x7c,0x45,0x40,  // 42.9738
+	0x0,0x0,0x0,0x40,0xc7,0x2,0xf8,0xbf,   // -1.50068
+	0x0,0x0,0x0,0x60,0x66,0xe6,0x6a,0x40,  // 215.2
+	0x0,0x0,0x0,0x60,0xf9,0xcb,0x45,0x40,  // 43.5935
+	0x0,0x0,0x0,0x80,0x9,0x63,0x8,0xc0,    // -3.04836
+	0x0,0x0,0x0,0x60,0x66,0xe6,0x6a,0x40,  // 215.2
+	0x0,0x0,0x0,0x40,0xa3,0x22,0x36,0x40   // 22.1353
+};
+///
+/// UnitTest problem fixed with p4 change 23687
+///
+static void testBug01()
+{
+
+#if defined(AIX) || defined(_HPUX_SOURCE)
+	// Swap the data on this big-endian box.
+	for (int i = 0; i < sizeof(REF_DATA) / 8; i++)
+	{
+		for (int j = 0; j < 4; j++)
+			swap(REF_DATA[i * 8 + j], REF_DATA[(i + 1) * 8 - 1 - j]);
+		//cout << *(reinterpret_cast<double *>(&REF_DATA[i * 8])) << std::endl;
+	}
+#endif
+
+	const double *OBSVALUE = reinterpret_cast<const double*>(REF_DATA);
+
+	const string SELECT = "select obsvalue from \"2000010106.odb\";";
+
+	odb::Select oda(SELECT);
+	size_t i = 0;
+	for (odb::Select::iterator it = oda.begin();
+		it != oda.end() && i < (sizeof(REF_DATA) / sizeof(double));
+		++it, ++i) 
+	{
+		Log::info() << "testBug01: it[" << i << "]=" << (*it)[0] << ", should be " << OBSVALUE[i] << std::endl;
+		ASSERT( (*it)[0] == OBSVALUE[i] );
+	}
+}
+
+
+
+
+template<class T> struct Count: public unary_function<T, void>
+{
+	Count() : counter(0) {}
+	void operator() (T& row)
+	{
+		++counter;
+
+		//long long i = row.integer(0);
+		long long i = row[0];
+
+		cerr << "Count::operator() counter=" << counter << ", i=" << i << std::endl; 
+		ASSERT(i == counter);
+		//cout << "col[0] (" << row.columns()[0]->name() << "): " << i << ", " ; 
+
+		double n = row[1];
+		n = n;
+		//cout << "col[1] (" << row.columns()[1]->name() << "): " << n << " "  << std::endl;
+	}
+	long counter;
+};
+
+
+static void setUp()
+{
+	Log::debug() << "setUp" << std::endl;
+
+	Timer t("Writing test.odb");
+	odb::Writer<> oda("test.odb");
+
+	odb::Writer<>::iterator writer = oda.begin();
+	writer->setNumberOfColumns(3);
+
+	(**writer).setColumn(0, "ifoo", odb::INTEGER);
+	(**writer).setColumn(1, "nbar", odb::REAL);
+	(**writer).setColumn(2, "string", odb::STRING);
+	
+	(**writer).writeHeader();
+
+	for (size_t i = 1; i <= 10; i++)
+	{
+		writer->data()[0] = i; // col 0
+		writer->data()[1] = i; // col 1
+		++writer;
+	}
+}
+
+static void tearDown() { }
+
+static void testReaderIteratorForEach()
+{
+	Log::debug() << "testReaderIteratorForEach" << std::endl;
+
+	odb::Reader oda("test.odb");
+	Timer t("for_each oda.reader Reading test.odb");
+	Count<odb::Reader::row> counter1;
+	counter1 = for_each(oda.begin(), oda.end(), counter1);
+
+	//Log::info() << "for_each ReaderIterator: Read " << counter1.counter << " row(s)." << std::endl;
+
+	ASSERT(counter1.counter == 10);
+}
+
+static void testReaderIteratorLoop()
+{
+	Log::debug() << "testReaderIteratorLoop" << std::endl;
+
+	odb::Reader oda("test.odb");
+	int j = 0;
+	for (odb::Reader::iterator it = oda.begin();
+		it != oda.end();
+		++it)
+	{
+		j++;
+
+		const double * data = it->data();
+		ASSERT(data);
+		
+		//int i = it->integer(0);
+		int i = (*it)[0];
+		double d = (*it)[1];
+
+		//cout << i << "	" << d << std::endl;
+
+		ASSERT( i == j );
+		d = d;
+	}
+	ASSERT(j == 10);
+}
+
+static void testSelectIteratorLoop()
+{
+	odb::Select oda(SELECT, "test.odb");
+	int i=0;
+	for (odb::Select::iterator it = oda.begin();
+		it != oda.end();
+		++it) 
+	{
+		++i;
+		//long v0 = it->integer(0);
+		//double v1 = it->data(1);
+		//cout << "v0 = " << v0 << ", v1 = " << v1 << std::endl;
+
+		//ASSERT(it->integer(0) == i);
+		ASSERT((*it)[0] == i);
+		ASSERT((*it)[1] == i);
+	}
+	ASSERT(i == 10);
+}
+
+static void testSelectIteratorForEach()
+{
+	odb::Select oda(SELECT, "test.odb");
+	Count<odb::Select::row> counter;
+	counter = for_each(oda.begin(), oda.end(), counter);
+	
+	ASSERT(counter.counter == 10);
+}
+
+
+
+static void test()
+{
+    testReaderIteratorForEach();
+    testReaderIteratorLoop();
+    testSelectIteratorForEach();
+    testSelectIteratorLoop();
+    testBug01();
+}
+
+
+
+SIMPLE_TEST(SelectIterator)
diff --git a/odb_api/src/odb_api/tools/TestSelectIterator2.cc b/odb_api/src/odb_api/tools/TestSelectIterator2.cc
new file mode 100644
index 0000000..039b780
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectIterator2.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+const string testFile = "TestSelectIterator2.odb";
+
+const double VALUE[] = { 1, 2, 3 };
+
+static void setUp()
+{
+	Log::debug() << "setUp" << std::endl;
+
+	Timer t("Writing " + testFile );
+	odb::Writer<> oda(testFile);
+
+	odb::Writer<>::iterator writer = oda.begin();
+    writer->setNumberOfColumns(1);
+	(**writer).setColumn(0, "value", odb::INTEGER);
+	(**writer).writeHeader();
+
+	for (size_t i = 0; i < sizeof(VALUE) / sizeof(double); ++i)
+	{
+		(*writer)[0] = VALUE[i]; // col 0
+		++writer;
+	}
+}
+
+///
+/// UnitTest problem fixed with p4 change 23687
+///
+static void test()
+{
+	const string SELECT = "select * from \"" + testFile + "\";";
+
+	odb::Select oda(SELECT);
+	size_t i=0;
+	for (odb::Select::iterator it = oda.begin();
+		it != oda.end() && i < sizeof(VALUE) / sizeof(double);
+		++it, ++i) 
+	{
+		Log::info() << "testBug01: it[" << i << "]=" << (*it)[0] << ", should be " << VALUE[i] << std::endl;
+		ASSERT((*it)[0] == VALUE[i]);
+	}
+}
+
+static void tearDown() { }
+
+
+SIMPLE_TEST(SelectIterator2)
diff --git a/odb_api/src/odb_api/tools/TestSelectIterator3.cc b/odb_api/src/odb_api/tools/TestSelectIterator3.cc
new file mode 100644
index 0000000..62fbeca
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectIterator3.cc
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/Select.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+const string testFile = "TestSelectIterator3.odb";
+
+const double VALUE[] = { 1, 2, 3 };
+
+static void setUp()
+{
+	Log::debug() << "setUp" << std::endl;
+
+	Timer t("Writing " + testFile );
+	odb::Writer<> oda(testFile);
+
+	odb::Writer<>::iterator writer = oda.begin();
+    writer->setNumberOfColumns(1);
+	(**writer).setColumn(0, "value", odb::INTEGER);
+	(**writer).writeHeader();
+
+	for (size_t i = 0; i < sizeof(VALUE) / sizeof(double); ++i)
+	{
+		(*writer)[0] = VALUE[i]; // col 0
+		++writer;
+	}
+}
+
+///
+/// UnitTest problem fixed with p4 change 23687
+///
+static void test()
+{
+	const string SELECT = "select * from \"" + testFile + "\";";
+
+	odb::Select oda(SELECT);
+	size_t i=0;
+	for (odb::Select::iterator it = oda.begin();
+		it != oda.end() && i < sizeof(VALUE) / sizeof(double);
+		++it, ++i) 
+	{
+		Log::info() << "testBug01: it[" << i << "]=" << (*it)[0] << ", should be " << VALUE[i] << std::endl;
+		ASSERT((*it)[0] == VALUE[i]);
+	}
+}
+
+static void tearDown() { }
+
+
+
+SIMPLE_TEST(SelectIterator3)
diff --git a/odb_api/src/odb_api/tools/TestSelectStarAt.cc b/odb_api/src/odb_api/tools/TestSelectStarAt.cc
new file mode 100644
index 0000000..a0945fd
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectStarAt.cc
@@ -0,0 +1,58 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest expansion of '*@hdr' into a list of columns of the hdr ODB table.
+///
+static void test()
+{
+    // TODO: make sure a 'select ... into ... from ...', e.g.:
+    //
+    //	const string SELECT = "select *@hdr into \"out.odb\" from \"2000010106.odb\";";
+    //  is not returning a result set (iterator). Or perhaps it is returning an empty result set.
+
+    const string SELECT = "select *@hdr from \"2000010106.odb\";";
+
+    odb::Select oda(SELECT);
+
+    Log::info() << "Executing: '" << SELECT << "'" << std::endl;
+    odb::Select::iterator it = oda.begin();
+
+    Log::info() << "it->columns().size() => " << it->columns().size() << std::endl;
+    ASSERT(it->columns().size() == 27);
+
+#if 0
+    unsigned long long i = 0;
+    for ( ; it != oda.end(); ++it)
+        ++i;
+
+    Log::info() << "i == " << i << std::endl;
+    ASSERT(i == 3321753);
+#endif
+}
+
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(SelectStarAt)
diff --git a/odb_api/src/odb_api/tools/TestSelectTwoFiles.cc b/odb_api/src/odb_api/tools/TestSelectTwoFiles.cc
new file mode 100644
index 0000000..52bdcf2
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSelectTwoFiles.cc
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/Select.h"
+
+#include "odb_api/Tracer.h"
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void setUp()
+{
+	Tracer t(Log::debug(), "setUp");
+	{
+		odb::Writer<> f("TestSelectTwoFiles1.odb");
+		odb::Writer<>::iterator it = f.begin();
+        it->setNumberOfColumns(1);
+		it->setColumn(0, "a", odb::REAL);
+		it->writeHeader();
+		(*it)[0] = 1;
+		++it;
+	}
+	{
+		odb::Writer<> f("TestSelectTwoFiles2.odb");
+		odb::Writer<>::iterator it = f.begin();
+        it->setNumberOfColumns(1);
+		it->setColumn(0, "b", odb::REAL);
+		it->writeHeader();
+		(*it)[0] = 2;
+		++it;
+	}
+}
+
+static void test()
+{
+	Tracer t(Log::debug(), "test");
+
+    odb::Select s("select * from \"TestSelectTwoFiles1.odb\", \"TestSelectTwoFiles2.odb\";");
+	odb::Select::iterator it = s.begin();
+	odb::Select::iterator end = s.end();
+
+    ASSERT(it->columns().size() == 2);
+
+	unsigned long i = 0;
+	for (; it != end; ++it)
+	{
+		Log::debug() << "test:    " << (*it)[0] << "    " << (*it)[0] << std::endl;
+
+		ASSERT( ((*it)[0] == 1) && ((*it)[1] == 2) );
+		++i;
+	}
+
+	ASSERT(i == 1);
+}
+
+static void tearDown() { }
+
+SIMPLE_TEST(SelectTwoFiles)
diff --git a/odb_api/src/odb_api/tools/TestSetvbuffer.cc b/odb_api/src/odb_api/tools/TestSetvbuffer.cc
new file mode 100644
index 0000000..f906b7e
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestSetvbuffer.cc
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file TestSetvbuffer.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/log/Timer.h"
+#include "odb_api/ODBAPISettings.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+
+static void setUp() { }
+
+
+static void createFile(size_t numberOfColumns, long long numberOfRows, size_t setvbufferSize)
+{
+
+	ODBAPISettings::instance().setvbufferSize(setvbufferSize);
+
+	odb::Writer<> oda("TestSetvbuffer.odb");
+	odb::Writer<>::iterator row = oda.begin();
+
+    row->setNumberOfColumns(numberOfColumns);
+
+	for (size_t i = 0; i < numberOfColumns; ++i)
+	{
+		stringstream name;
+		name << "Column" << i;
+		row->setColumn(i, name.str().c_str(), odb::REAL);
+	}
+	row->writeHeader();
+
+	for (long long i = 1; i <= numberOfRows; ++i, ++row)
+		for (size_t c = 0; c < numberOfColumns; ++c)
+			(*row)[c] = c;
+}
+
+static void tearDown()
+{
+	int catStatus = system("ls -l TestSetvbuffer.odb");
+	ASSERT(WEXITSTATUS(catStatus) == 0);
+}
+
+
+static void test()
+{
+    size_t cols = 400;
+    long long rows = 1000;
+    size_t buffSize = 8 * 1024 * 1024;
+
+    for (size_t i = 0; i < 10; ++i)
+    {
+        stringstream s;
+        s << "setUp(): createFile(" << cols << ", " << rows << ", " << buffSize << ")" << std::endl;
+        Timer t(s.str());
+        createFile(cols, rows, buffSize);
+    }
+}
+
+
+
+SIMPLE_TEST(Setvbuffer)
diff --git a/odb_api/src/odb_api/tools/TestStar.cc b/odb_api/src/odb_api/tools/TestStar.cc
new file mode 100644
index 0000000..c3c57a1
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestStar.cc
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, May 2009
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+/// UnitTest syntax: select *@odb_table from "file.oda";
+///
+static void test()
+{
+    const std::string SELECT = "select *@hdr from \"2000010106.odb\";";
+
+	odb::Select oda(SELECT);
+
+	odb::Select::iterator it = oda.begin();
+	ASSERT("hdr has 27 columns excluding @LINKs." && it->columns().size() == 27);
+}
+
+
+
+static void setUp(){}
+static void tearDown(){}
+
+SIMPLE_TEST(Star)
diff --git a/odb_api/src/odb_api/tools/TestTEMPLATE.cc b/odb_api/src/odb_api/tools/TestTEMPLATE.cc
new file mode 100644
index 0000000..e43c88c
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTEMPLATE.cc
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Jan 2011
+
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+
+using namespace std;
+using namespace eckit;
+//using namespace odb;
+
+
+
+static void test()
+{
+	//eckit::Log::info() << "I'm just a template, I don't test anything, really." << std::endl;
+}
+
+static void tearDown() { }
+
+
+static void setUp(){}
+
+SIMPLE_TEST(TEMPLATE)
diff --git a/odb_api/src/odb_api/tools/TestTextSelect.cc b/odb_api/src/odb_api/tools/TestTextSelect.cc
new file mode 100644
index 0000000..6c00d76
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTextSelect.cc
@@ -0,0 +1,83 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+// @author Piotr Kuchta, ECMWF, Oct 2010
+
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+    ofstream o("UnitTest.txt");
+    o << "a:REAL" << std::endl;
+    for (size_t i = 1; i <= 10; ++i)
+        o << i << std::endl;
+}
+
+
+
+static void selectStarOneColumn()
+{
+    string sql = "select * where a > 4;";
+    const string fileName = "UnitTest.txt";
+    ifstream fs(fileName.c_str());
+
+    odb::Select oda(sql, fs, ",");
+
+    Log::info() << "selectStarOneColumn: Execute '" << sql << "'" << std::endl;
+    odb::Select::iterator it = oda.begin();
+    odb::Select::iterator end = oda.end();
+
+    double v = 5;
+    unsigned long n = 0;
+    for( ; it != end; ++it, ++n)
+        ASSERT(v++ == (*it)[0]);
+
+    ASSERT(n == 6);
+}
+
+static void selectSumOneColumn()
+{
+    string sql = "select sum(a);";
+    const string fileName = "UnitTest.txt";
+    ifstream fs(fileName.c_str());
+
+    odb::Select oda(sql, fs, ",");
+
+    Log::info() << "selectSumOneColumn: Execute '" << sql << "'" << std::endl;
+    odb::Select::iterator it = oda.begin();
+    odb::Select::iterator end = oda.end();
+
+    ++it;
+    ASSERT(! (it != end));
+    ASSERT((*it)[0] == 55);
+}
+
+
+/// UnitTest syntax 'select lat, lon' (no file name)
+///
+static void test()
+{
+    selectStarOneColumn();
+    selectSumOneColumn();
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(TextSelect)
diff --git a/odb_api/src/odb_api/tools/TestTextSelect.txt b/odb_api/src/odb_api/tools/TestTextSelect.txt
new file mode 100644
index 0000000..f4c4a39
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTextSelect.txt
@@ -0,0 +1,16 @@
+a:REAL
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+JUST_ONE_TEST(test, setUp, tearDown)
+void test(){}
+void setUp(){}
+void tearDown(){}
+JUST_ONE_TEST(test, setUp, tearDown)
diff --git a/odb_api/src/odb_api/tools/TestTextSelect2.cc b/odb_api/src/odb_api/tools/TestTextSelect2.cc
new file mode 100644
index 0000000..7e5b9ce
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTextSelect2.cc
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+// @author Piotr Kuchta, ECMWF, Oct 2010
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+
+static void setUp()
+{
+	ofstream f("UnitTest.txt");
+	f << 
+	"a:REAL,b:REAL\n"
+	"1,1\n"
+	"2,2\n"
+	"3,3\n"
+	"4,4\n"
+	"5,5\n"
+	"6,6\n"
+	"7,7\n"
+	"8,8\n"
+	"9,9\n"
+	"10,10\n";
+}
+
+
+
+static void selectStarOneColumn()
+{
+	string sql = "select * where a > 4;";
+	const string fileName = "UnitTest.txt";
+	ifstream fs(fileName.c_str());
+	
+	odb::Select oda(sql, fs, ",");
+	
+	Log::info() << "selectStarOneColumn: Execute '" << sql << "'" << std::endl;
+	odb::Select::iterator it = oda.begin();
+	odb::Select::iterator end = oda.end();
+
+	ASSERT((*it).columns().size() == 2);
+
+	double v = 5;
+	unsigned long n = 0;
+	for( ; it != end; ++it, ++n)
+	{
+		ASSERT((*it).columns().size() == 2);
+		ASSERT(v++ == (*it)[0]);
+	}
+
+	ASSERT(n == 6);
+}
+
+static void selectSumOneColumn()
+{
+	string sql = "select sum(a), sum(b);";
+	const string fileName = "UnitTest.txt";
+	ifstream fs(fileName.c_str());
+	
+	odb::Select oda(sql, fs, ",");
+	
+	Log::info() << "selectSumOneColumn: Execute '" << sql << "'" << std::endl;
+	odb::Select::iterator it = oda.begin();
+	odb::Select::iterator end = oda.end();
+
+	ASSERT((*it).columns().size() == 2);
+
+	++it;
+	ASSERT(! (it != end));
+	ASSERT((*it)[0] == 55);
+	ASSERT((*it)[1] == 55);
+}
+
+
+/// UnitTest syntax 'select lat, lon' (no file name)
+///
+static void test()
+{
+    selectStarOneColumn();
+    selectSumOneColumn();
+}
+
+
+static void tearDown(){}
+
+SIMPLE_TEST(TextSelect2)
diff --git a/odb_api/src/odb_api/tools/TestTextSelect2.txt b/odb_api/src/odb_api/tools/TestTextSelect2.txt
new file mode 100644
index 0000000..18676e5
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTextSelect2.txt
@@ -0,0 +1,16 @@
+a:REAL,b:REAL
+1,1
+2,2
+3,3
+4,4
+5,5
+6,6
+7,7
+8,8
+9,9
+10,10
+JUST_ONE_TEST(test, setUp, tearDown)
+void test(){}
+void setUp(){}
+void tearDown(){}
+JUST_ONE_TEST(test, setUp, tearDown)
diff --git a/odb_api/src/odb_api/tools/TestTextSelect3.cc b/odb_api/src/odb_api/tools/TestTextSelect3.cc
new file mode 100644
index 0000000..46ae55b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestTextSelect3.cc
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+// @author Piotr Kuchta, ECMWF, Oct 2010
+
+#include "odb_api/MetaData.h"
+#include "odb_api/Select.h"
+
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+// TODO: (test not finished yet)
+
+
+static void setUp() { }
+
+
+static void selectStarOneColumn()
+{
+	string sql = "select *";
+	const string fileName = "2000010106.4.0.oda.csv";//"UnitTest.txt";
+	ifstream fs(fileName.c_str());
+    //::system("ls -lrt 2000010106.4.0.oda.csv");
+	
+	odb::Select oda(sql, fs, ",");
+	
+	Log::info() << "selectStarOneColumn: Execute '" << sql << "'" << std::endl;
+	odb::Select::iterator it = oda.begin();
+	odb::Select::iterator end = oda.end();
+
+	Log::info() << "selectStarOneColumn: columns().size():  " << it->columns().size() << std::endl;
+
+	unsigned long n = 0;
+	for( ; it != end; ++it, ++n)
+	{
+	}
+
+	Log::info() << "selectStarOneColumn: number of rows:  " << n << std::endl;
+}
+
+static void selectSumOneColumn()
+{
+/*
+	string sql = "select sum(a), sum(b)";
+	const string fileName = "UnitTest.txt";
+	ifstream fs(fileName.c_str());
+	
+	odb::Select oda(sql, fs);
+	
+	Log::info() << "selectSumOneColumn: Execute '" << sql << "'" << std::endl;
+	odb::Select::iterator it = oda.begin();
+	odb::Select::iterator end = oda.end();
+
+	ASSERT((*it).columns().size() == 2);
+
+	++it;
+	ASSERT(! (it != end));
+	ASSERT((*it)[0] == 55);
+	ASSERT((*it)[1] == 55);
+*/
+}
+
+
+/// UnitTest syntax 'select lat, lon' (no file name)
+///
+static void test()
+{
+    selectStarOneColumn();
+    selectSumOneColumn();
+}
+
+
+static void tearDown(){}
+
+//SIMPLE_TEST(TextSelect3)
diff --git a/odb_api/src/odb_api/tools/TestWriteCatFiles.cc b/odb_api/src/odb_api/tools/TestWriteCatFiles.cc
new file mode 100644
index 0000000..3659560
--- /dev/null
+++ b/odb_api/src/odb_api/tools/TestWriteCatFiles.cc
@@ -0,0 +1,107 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file UnitTest.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "odb_api/Comparator.h"
+#include "odb_api/Reader.h"
+
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+
+static void test()
+{
+	Reader in("concatenated.odb");
+	Reader::iterator it = in.begin();
+	Reader::iterator end = in.end();
+
+	Writer<> out("copy_of_concatenated.odb");
+	Writer<>::iterator o = out.begin();
+	o->pass1(it, end);
+
+	Comparator().compare("concatenated.odb", "copy_of_concatenated.odb");
+}
+
+static void setUp()
+{
+ {
+	odb::Writer<> oda("file1.odb");
+	odb::Writer<>::iterator row = oda.begin();
+
+	row->setNumberOfColumns(3);
+
+	row->setColumn(0, "x", odb::REAL);
+	row->setColumn(1, "y", odb::REAL);
+	row->setColumn(2, "z", odb::REAL);
+	
+	row->writeHeader();
+
+	for (size_t i = 1; i <= 2; i++)
+	{
+		(*row)[0] = i; // col 0
+		(*row)[1] = i; // col 1
+		(*row)[2] = i; // col 2
+		++row;
+	}
+
+	odb::Writer<> oda2("file2.odb");
+	odb::Writer<>::iterator row2 = oda2.begin();
+	row2->setNumberOfColumns(3);
+	row2->setColumn(0, "x", odb::REAL);
+	row2->setColumn(1, "y", odb::REAL);
+	row2->setColumn(2, "v", odb::REAL);
+	
+	row2->writeHeader();
+
+	for (size_t i = 1; i <= 2; i++)
+	{
+		(*row2)[0] = i * 10; // col 0
+		(*row2)[1] = i * 100; // col 1
+		(*row2)[2] = i * 1000; // col 2
+		++row2;
+	}
+
+	odb::Writer<> oda3("file3.odb");
+	odb::Writer<>::iterator row3 = oda3.begin();
+	row3->setNumberOfColumns(4);
+
+	row3->setColumn(0, "x", odb::REAL);
+	row3->setColumn(1, "v", odb::REAL);
+	row3->setColumn(2, "y", odb::REAL);
+	row3->setColumn(3, "z", odb::REAL);
+	row3->writeHeader();
+
+	for (size_t i = 1; i <= 2; i++)
+	{
+		(*row3)[0] = i * 10; // col 0
+		(*row3)[1] = i * 1000; // col 1
+		(*row3)[2] = i * 100; // col 2
+		(*row3)[3] = 13;     // col 3
+		++row3;
+	}
+	// dtors fire here
+ }
+
+	int catStatus = system("cat file1.odb file2.odb file3.odb >concatenated.odb");
+	ASSERT(WEXITSTATUS(catStatus) == 0);
+
+}
+
+static void tearDown() { }
+
+
+
+SIMPLE_TEST(WriteCatFiles)
diff --git a/odb_api/src/odb_api/tools/Tool.cc b/odb_api/src/odb_api/tools/Tool.cc
new file mode 100644
index 0000000..7dc12d4
--- /dev/null
+++ b/odb_api/src/odb_api/tools/Tool.cc
@@ -0,0 +1,69 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file Tool.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "ECMLTool.h"
+#include "CompactTool.h"
+#include "CompareTool.h"
+#include "CountTool.h"
+#include "IndexTool.h"
+#include "FixedSizeRowTool.h"
+#include "ImportTool.h"
+#include "LSTool.h"
+#include "MDSetTool.h"
+#include "MergeTool.h"
+#include "ODA2RequestTool.h"
+#include "ODAHeaderTool.h"
+#include "SQLTool.h"
+#include "SetTool.h"
+#include "SplitTool.h"
+#include "Tool.h"
+#include "ToolFactory.h"
+#include "XYVTool.h"
+
+namespace odb {
+namespace tool {
+
+Tool::~Tool() {}
+
+Tool::Tool(int argc, char **argv)
+: CommandLineParser(argc, argv)
+{}
+
+Tool::Tool(const CommandLineParser& clp)
+: CommandLineParser(clp)
+{}
+
+void Tool::registerTools()
+{
+	static ToolFactory<ECMLTool> ecml("ecml");
+	static ToolFactory<CompactTool> compact("compact");
+	static ToolFactory<CompareTool> compare("compare");
+	static ToolFactory<CountTool> countTool("count");
+	static ToolFactory<IndexTool> indexTool("index");
+	static ToolFactory<FixedSizeRowTool> fixedSizeRow("fixrowsize");
+	static ToolFactory<ImportTool> import("import");
+	static ToolFactory<LSTool> lsTool("ls");
+	static ToolFactory<MDSetTool> mdset("mdset");
+	static ToolFactory<MergeTool> mergeTool("merge");
+	static ToolFactory<ODA2RequestTool> oda2requestTool("oda2request");
+	static ToolFactory<HeaderTool> odaHeader("header");
+	static ToolFactory<SQLTool> sqlTool("sql");
+	static ToolFactory<SetTool> set("set");
+	static ToolFactory<SplitTool> split("split");
+	static ToolFactory<XYVTool> xyvTool("xyv");	
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/Tool.h b/odb_api/src/odb_api/tools/Tool.h
new file mode 100644
index 0000000..fdbd422
--- /dev/null
+++ b/odb_api/src/odb_api/tools/Tool.h
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file Tool.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef Tool_H
+#define Tool_H
+
+#include "odb_api/CommandLineParser.h"
+#include "odb_api/StringTool.h"
+
+namespace eckit { class PathName; }
+class Application;
+
+namespace odb {
+namespace tool {
+
+class Tool : public StringTool, public CommandLineParser {
+public:
+
+	virtual void run() = 0;
+
+	virtual ~Tool();
+
+	std::string name() { return name_; }
+	void name(const std::string& s) { name_ = s; }
+
+	static void registerTools();
+
+protected:
+    
+	Tool(int argc, char **argv);
+	Tool(const CommandLineParser&);
+
+private:
+	std::string name_;
+};
+
+
+template <typename T> struct ExperimentalTool { enum { experimental = false }; };
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/ToolFactory.cc b/odb_api/src/odb_api/tools/ToolFactory.cc
new file mode 100644
index 0000000..ac7ae80
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ToolFactory.cc
@@ -0,0 +1,171 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// \file ToolFactory.cc
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+///
+
+//#include <iostream>
+//#include <string>
+//#include <map>
+//#include <vector>
+
+//#include "eckit/utils/Regex.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "Tool.h"
+#include "TestCase.h"
+#include "ToolFactory.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+std::map<std::string, AbstractToolFactory *> *AbstractToolFactory::toolFactories = 0;
+
+class MatchAll : public std::vector<std::string> {
+public:
+	MatchAll() { push_back(".*"); }
+};
+
+const std::vector<std::string> AbstractToolFactory::matchAll = MatchAll();
+
+std::vector<test::TestCase*>* AbstractToolFactory::testCases(const std::vector<std::string> &patterns)
+{
+	ASSERT(toolFactories != 0);
+
+	std::vector<test::TestCase*> *v = new std::vector<test::TestCase*>();
+
+	for (std::map<std::string,AbstractToolFactory*>::iterator it = toolFactories->begin();
+		it != toolFactories->end();
+		it++)
+	{
+		std::string testName = it->first;
+		if (testName.find("Test") == std::string::npos 
+			|| !Tool::matchAny(patterns, testName))
+			continue;
+
+		AbstractToolFactory *factory = it->second;
+		char *argv[] = {const_cast<char*>("test"), 0};
+		Tool *tool = factory->create(1, argv);
+		ASSERT(tool);
+
+		test::TestCase *testCase = dynamic_cast<test::TestCase *>(tool);
+		if (testCase != 0)
+		{
+			testCase->name(testName);
+			v->push_back(testCase);
+		}
+		else
+		{
+			Log::warning() << "AbstractToolFactory::testCases: " << testName
+				<< " is not a TestCase. Skipping" << std::endl;
+			delete tool;
+		}
+	}
+	return v;
+}
+
+AbstractToolFactory& AbstractToolFactory::findTool(const std::string &name)
+{
+	ASSERT(toolFactories);
+
+	std::map<std::string,AbstractToolFactory*>::const_iterator it = toolFactories->find(name);
+
+	ASSERT("Unknown tool" && it != toolFactories->end());
+
+	return *it->second;
+}
+
+void AbstractToolFactory::printToolHelp(const std::string& name, std::ostream &s)
+{
+	findTool(name).help(s);
+	s << std::endl;
+}
+
+void AbstractToolFactory::printToolUsage(const std::string& name, std::ostream &s)
+{
+	findTool(name).usage(name, s);
+	s << std::endl;
+}
+
+void AbstractToolFactory::printToolsHelp(std::ostream &s)
+{
+	ASSERT(toolFactories);
+
+	for (std::map<std::string,AbstractToolFactory*>::iterator it = toolFactories->begin();
+		it != toolFactories->end();
+		it++)
+	{
+		std::string toolName = it->first;
+		AbstractToolFactory *toolFactory = it->second;
+		
+		if (toolName.find("Test") == std::string::npos && !toolFactory->experimental())
+		{
+				s << toolName << ":\t";
+				toolFactory->help(s);
+				s << std::endl << "Usage:" << std::endl << "\t";
+				toolFactory->usage(toolName, s);
+				s << std::endl << std::endl;
+		}
+	}
+}
+
+void AbstractToolFactory::listTools(std::ostream& s)
+{
+	ASSERT(toolFactories);
+
+	for (std::map<std::string,AbstractToolFactory*>::iterator it = toolFactories->begin();
+		it != toolFactories->end();
+		it++)
+	{
+		std::string toolName = it->first;
+		AbstractToolFactory *toolFactory = it->second;
+		
+		if (toolName.find("Test") == std::string::npos && !toolFactory->experimental())
+		{
+				s << "	" << toolName << "	";
+				toolFactory->help(s);
+				s << std::endl;
+		}
+	}
+}
+
+Tool* AbstractToolFactory::createTool(const std::string& name, int argc, char **argv)
+{
+	AbstractToolFactory *factory = (*toolFactories)[name];
+	if (factory == 0)
+		return 0;
+
+	return factory->create(argc, argv);
+};
+
+AbstractToolFactory::AbstractToolFactory(const std::string& name)
+{
+	if (toolFactories == 0)
+		toolFactories = new std::map<std::string, AbstractToolFactory *>();
+
+	(*toolFactories)[name] = this;
+}
+
+AbstractToolFactory::~AbstractToolFactory()
+{
+	if (toolFactories)
+	{
+		delete toolFactories;
+		toolFactories = 0;
+	}
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ToolFactory.h b/odb_api/src/odb_api/tools/ToolFactory.h
new file mode 100644
index 0000000..aed77c2
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ToolFactory.h
@@ -0,0 +1,65 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+#ifndef ToolFactory_H
+#define ToolFactory_H
+
+//#include "TestCase.h"
+
+namespace odb {
+namespace tool {
+
+    namespace test { class TestCase; }
+    class Tool;
+
+class AbstractToolFactory {
+public:
+	static Tool* createTool(const std::string& name, int argc, char **argv);
+
+	static void printToolHelp(const std::string&, std::ostream &);
+	static void printToolUsage(const std::string& name, std::ostream &);
+	static void printToolsHelp(std::ostream &);
+    static std::vector<odb::tool::test::TestCase*>* testCases(const std::vector<std::string>& = matchAll);
+
+	static void listTools(std::ostream&);
+
+	virtual Tool* create(int argc, char **argv) = 0;
+
+	virtual void help(std::ostream &) = 0;
+	virtual void usage(const std::string&, std::ostream &) = 0;
+	virtual bool experimental() = 0;
+	
+protected:
+	AbstractToolFactory(const std::string& name); 
+	virtual ~AbstractToolFactory ();
+
+private:
+	static AbstractToolFactory& findTool(const std::string &name);
+	static std::map<std::string, AbstractToolFactory *> *toolFactories;
+	static const std::vector<std::string> matchAll;
+};
+
+
+template <class T>
+class ToolFactory : public AbstractToolFactory {
+public:
+	ToolFactory (const std::string& name) : AbstractToolFactory(name) {}
+
+	Tool* create(int argc, char **argv) { return new T(argc, argv); }
+
+	void help(std::ostream &o) { T::help(o); }
+	void usage(const std::string &name, std::ostream &o) { T::usage(name, o); }
+	bool experimental() { return odb::tool::ExperimentalTool<T>::experimental; }
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/ToolRunnerApplication.cc b/odb_api/src/odb_api/tools/ToolRunnerApplication.cc
new file mode 100755
index 0000000..537dc18
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ToolRunnerApplication.cc
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ToolRunnerApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/filesystem/PathName.h"
+
+#include "odb_api/tools/Tool.h"
+#include "odb_api/tools/ToolFactory.h"
+#include "odb_api/tools/ToolRunnerApplication.h"
+
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+ToolRunnerApplication::ToolRunnerApplication (int argc, char **argv, bool createCommandLineTool, bool deleteTool)
+: ODBApplication(argc, argv),
+  tool_(!createCommandLineTool ? 0 : AbstractToolFactory::createTool(PathName(argv[1]).baseName(), argc - 1, argv + 1)),
+  deleteTool_(deleteTool)
+{}
+
+ToolRunnerApplication::ToolRunnerApplication (odb::tool::Tool &tool, int argc, char **argv)
+: ODBApplication(argc, argv),
+  tool_(&tool),
+  deleteTool_(false)
+{}
+
+ToolRunnerApplication::~ToolRunnerApplication ()
+{
+	if (deleteTool_) delete tool_;
+}
+
+void ToolRunnerApplication::tool(odb::tool::Tool *tool)
+{
+	tool_ = tool;
+}
+
+void ToolRunnerApplication::run()
+{
+	if (tool_ == 0)
+	{
+        std::cerr << name() << ": Unknown command '" << argv(1) << "'" << std::endl;
+		return;
+	}
+
+	tool_->run();
+
+	if (deleteTool_)
+	{
+		delete tool_;
+		tool_ = 0;
+	}
+}
+
+
+int ToolRunnerApplication::printHelp(std::ostream &out)
+{
+	if (tool_ == 0)
+	{
+        std::cerr << name() << ": Unknown command '" << argv(1) << "'" << std::endl;
+		return 1;
+	}
+	//tool_->help(out);
+	return 0;
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/ToolRunnerApplication.h b/odb_api/src/odb_api/tools/ToolRunnerApplication.h
new file mode 100755
index 0000000..a9d93fe
--- /dev/null
+++ b/odb_api/src/odb_api/tools/ToolRunnerApplication.h
@@ -0,0 +1,43 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file ToolRunnerApplication.h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#ifndef ToolRunnerApplication_H
+#define ToolRunnerApplication_H
+
+#include "odb_api/ODBApplication.h"
+
+namespace odb {
+namespace tool {
+
+class Tool;
+
+class ToolRunnerApplication : public ODBApplication {
+public:
+	ToolRunnerApplication (int argc, char **argv, bool createCommandLineTool = true, bool deleteTool=true);
+	ToolRunnerApplication (odb::tool::Tool &tool, int argc, char **argv);
+	~ToolRunnerApplication ();
+
+	void tool(odb::tool::Tool *);
+
+	void run();
+	int printHelp(std::ostream &out);
+private:
+	odb::tool::Tool* tool_;
+	bool deleteTool_;
+};
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
diff --git a/odb_api/src/odb_api/tools/UnitTests.cc b/odb_api/src/odb_api/tools/UnitTests.cc
new file mode 100644
index 0000000..df4f1df
--- /dev/null
+++ b/odb_api/src/odb_api/tools/UnitTests.cc
@@ -0,0 +1,1126 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ *
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
+ * In applying this licence, ECMWF does not waive the privileges and immunities
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+/// \file .h
+///
+/// @author Piotr Kuchta, ECMWF, Feb 2009
+
+#include "eckit/io/FileHandle.h"
+#include "eckit/io/BufferedHandle.h"
+#include "eckit/log/Timer.h"
+#include "eckit/exception/Exceptions.h"
+
+#include "odb_api/Comparator.h"
+#include "odb_api/DateTime.h"
+#include "odb_api/Decoder.h"
+//#include "odb_api/MapReduce.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/Reader.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLSelectFactory.h"
+#include "odb_api/Select.h"
+#include "odb_api/TextReader.h"
+#include "odb_api/TextReaderIterator.h"
+#include "odb_api/Writer.h"
+#include "TestCase.h"
+#include "odb_api/tools/CountTool.h"
+#include "odb_api/tools/ImportTool.h"
+#include "odb_api/tools/SplitTool.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/odbql.h"
+#include "ecml/data/DataHandleFactory.h"
+#include "odb_api/DispatchingWriter.h"
+
+extern "C" {
+#include "odb_api/odbcapi.h"
+}
+
+using namespace std;
+using namespace eckit;
+using namespace odb;
+using namespace odb::sql;
+
+
+typedef long long llong;
+
+static void foobar()
+{
+    Reader in("concatenated.odb");
+    Reader::iterator it = in.begin();
+    Reader::iterator end = in.end();
+
+    Writer<> out("copy_of_concatenated.odb");
+    Writer<>::iterator o = out.begin();
+    o->pass1(it, end);
+
+    Comparator().compare("concatenated.odb", "copy_of_concatenated.odb");
+}
+//TESTCASE(foobar);
+
+static void createDataForMixedAggregated()
+{
+    // See UnitTest.sql as well
+    const char *data =
+            "x:INTEGER,y:INTEGER,v:DOUBLE\n"
+            "1,1,0.3\n"
+            "1,1,0.2\n"
+            "2,2,0.4\n"
+            "2,2,0.1\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "selectAggregatedAndNonAggregated.odb");
+}
+
+TEST(selectAggregatedAndNonAggregated)
+{
+    createDataForMixedAggregated();
+
+    odb::Select oda("select x,min(v),max(v) from \"selectAggregatedAndNonAggregated.odb\";");
+    odb::Select::iterator it = oda.begin();
+
+    double r0 = (*it)[0], r1 = (*it)[1], r2 = (*it)[2];
+
+    Log::info() << "selectAggregatedAndNonAggregated: " << r0 << ", " << r1 << ", " << r2 << std::endl;
+
+    ASSERT(Comparator::same(r0, 1));
+    ASSERT(Comparator::same(r1, 0.2));
+    ASSERT(Comparator::same(r2, 0.3));
+
+    ++it;
+
+    r0 = (*it)[0];
+    r1 = (*it)[1];
+    r2 = (*it)[2];
+
+    Log::info() << "selectAggregatedAndNonAggregated: " << r0 << ", " << r1 << ", " << r2 << std::endl;
+
+    //ASSERT((*it)[0] == 2 && (*it)[1] == 0.1);
+    ASSERT(r0 == 2);
+    ASSERT(r1 == 0.1);
+    ASSERT(r2 == 0.4);
+    ++it;
+    ASSERT( ! (it != oda.end() ));
+}
+
+
+static void createDataForMixedAggregated2()
+{
+    Writer<> out("selectAggregatedAndNonAggregated2.odb");
+    Writer<>::iterator o = out.begin();
+    MetaData md(o->columns());
+
+    typedef DataStream<SameByteOrder, DataHandle> DS;
+    md.addColumn /* <DS> */("x", "INTEGER");//, true, .0);
+    md.addColumn /* <DS> */("y", "INTEGER");//, true, .0);
+    md.addColumn /* <DS> */("v", "DOUBLE");//, true, .0);
+    o->columns(md);
+    o->writeHeader();
+
+    for (size_t row = 0; row < 1000; ++row)
+        for (size_t x = 0; x < 10; (*o)[0] = ++x)
+            for (size_t y = 0; y < 10; (*o)[1] = ++y)
+                for (double v = 0; v < 10; (*o)[2] = ++v)
+                    ++o;
+}
+
+TEST(selectAggregatedAndNonAggregated2)
+{
+    createDataForMixedAggregated2();
+    odb::Select oda("select x,min(v),y,max(v) from \"selectAggregatedAndNonAggregated2.odb\";");
+    odb::Select::iterator it = oda.begin();
+    unsigned long counter = 0;
+    for ( ; it != oda.end(); ++it, ++counter)
+    {
+        //double r0 = (*it)[0], r1 = (*it)[1], r2 = (*it)[2], r3 = (*it)[3];
+        //Log::info() << "selectAggregatedAndNonAggregated2: " << r0 << ", " << r1 << ", " << r2 << ", " << r3 << std::endl;
+    }
+    Log::info() << "selectAggregatedAndNonAggregated2: counter= " << counter << std::endl;
+    ASSERT(counter == 110);
+}
+
+static void createDataForMixedAggregated3()
+{
+    // See UnitTest.sql as well
+    const char *data =
+            "x:STRING,y:INTEGER,v:DOUBLE\n"
+            "'A',1,0.3\n"
+            "'A',1,0.2\n"
+            "'B',2,0.4\n"
+            "'B',2,0.1\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "selectAggregatedAndNonAggregated3.odb");
+}
+
+TEST(selectAggregatedAndNonAggregated3)
+{
+    createDataForMixedAggregated3();
+
+    odb::Select oda("select x,count(*) from \"selectAggregatedAndNonAggregated3.odb\";");
+    odb::Select::iterator it = oda.begin();
+    unsigned long counter = 0;
+    for ( ; it != oda.end(); ++it, ++counter)
+    {
+        double r0 = (*it)[0], r1 = (*it)[1];
+        Log::info() << "selectAggregatedAndNonAggregated3: " << r0 << ", " << r1 << std::endl;
+    }
+    Log::info() << "selectAggregatedAndNonAggregated3: counter= " << counter << std::endl;
+    ASSERT(counter == 2);
+}
+
+
+static void createDataForMixedAggregatedNULL()
+{
+    // See UnitTest.sql as well
+    const char *data =
+            "x:REAL,y:INTEGER,v:DOUBLE\n"
+            "100,1,0.3\n"
+            "100,1,0.2\n"
+            "101,2,0.4\n"
+            "101,2,0.1\n"
+            "NULL,1,0.1\n"
+            "NULL,2,0.2\n"
+            "NULL,3,0.3\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "selectAggregatedAndNonAggregatedNULL.odb");
+}
+
+TEST(selectAggregatedAndNonAggregatedNULL)
+{
+    createDataForMixedAggregatedNULL();
+
+    odb::Select oda("select x,count(*) from \"selectAggregatedAndNonAggregatedNULL.odb\";");
+    odb::Select::iterator it = oda.begin();
+    unsigned long counter = 0;
+    for ( ; it != oda.end(); ++it, ++counter)
+    {
+        double r0 = (*it)[0], r1 = (*it)[1];
+        Log::info() << "selectAggregatedAndNonAggregatedNULL: " << r0 << ", " << r1 << std::endl;
+    }
+    Log::info() << "selectAggregatedAndNonAggregatedNULL: counter= " << counter << std::endl;
+    ASSERT(counter == 3);
+}
+
+
+/////////////////////////////////////////
+// Regular expressions on the select list
+
+static void createDataForRegex1()
+{
+    // See UnitTest.sql as well
+    const char *data =
+            "aa:INTEGER,ab:INTEGER,ba:INTEGER,bb:INTEGER\n"
+            "1,2,3,4\n"
+            "10,20,30,40\n"
+            "11,22,33,44\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "regex1.odb");
+}
+
+static void regex1()
+{
+    //createDataForRegex1();
+    odb::Select oda("select \"/a.*/\" from \"regex1.odb\";");
+    odb::Select::iterator it = oda.begin();
+
+    Log::info() << "regex1: " << it->columns() << std::endl;
+
+    ASSERT(it->columns().size() == 2);
+}
+//TESTCASE(regex1);
+
+TEST(vector_syntax)
+{
+
+    const char *data =
+            "a:INTEGER,b:INTEGER\n"
+            "1,1\n"
+            "2,2\n"
+            "3,3\n"
+            "4,4\n"
+            "5,5\n"
+            "6,6\n"
+            "7,7\n"
+            "8,8\n"
+            "9,9\n"
+            "10,10\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "vector_syntax.odb");
+
+    const char *sql =
+            "set $X = [1,2,3,4,5];"
+            "select * from \"vector_syntax.odb\" where a in $X;"
+            ;
+
+    odb::Select oda(sql);
+    odb::Select::iterator it = oda.begin();
+    odb::Select::iterator end = oda.end();
+    unsigned long counter = 0;
+    for ( ; it != oda.end(); ++it, ++counter)
+        ;
+    ASSERT(counter == 5);
+}
+
+
+TEST(bitfieldsLength)
+{
+    Log::info() << "sizeof(Decoder::W)" << sizeof(Decoder::W) << std::endl;
+    Log::info() << "sizeof(double)" << sizeof(double) << std::endl;
+
+    //>>> int('0b11100110011',2)
+    //1843
+    //>>> len('0b11100110011')
+    //13
+    //>>>
+    //stringstream s;
+    //Decoder::printBinary(s, 1843);
+    //string r = s.str();
+    //Log::info() << "r: " << r << std::endl;
+
+    ASSERT(Decoder::printBinary(1843).size() == 11);
+    ASSERT(Decoder::printBinary(1843) == "11100110011");
+
+    ASSERT(Decoder::printBinary(0).size() == 1);
+    ASSERT(Decoder::printBinary(0) == "0");
+}
+
+/// ODB-85
+TEST(bitfieldsPrintHexadecimal) 
+{
+    ASSERT(Decoder::printHexadecimal(1843) == std::string("733"));
+
+    //eckit::Log::info() << Decoder::printHexadecimal(15)  << std::endl;
+    ASSERT(Decoder::printHexadecimal(10) == std::string("a"));
+    ASSERT(Decoder::printHexadecimal(11) == std::string("b"));
+    ASSERT(Decoder::printHexadecimal(15) == std::string("f"));
+    ASSERT(Decoder::printHexadecimal(255) == std::string("ff"));
+}
+
+static void create_stringInWhere_file()
+{
+    const char *data =
+            "a:STRING,b:INTEGER\n"
+            "'aaa',1\n"
+            "'aaaa',2\n"
+            "'bbb',2\n"
+            "'bbbc',2\n"
+            ;
+    odb::tool::ImportTool::importText(data, "stringInWhere.odb");
+}
+
+TEST(stringInWhere)
+{
+    create_stringInWhere_file();
+    odb::Select oda("select * from 'stringInWhere.odb' where a = 'aaa';");
+
+    unsigned long counter = 0;
+    for (odb::Select::iterator it = oda.begin(); it != oda.end(); ++it, ++counter)
+        ;
+    ASSERT(counter == 1);
+}
+
+TEST(vector_syntax2)
+{
+    const char* sql = "set $y = 100; set $x = [$y, 'a', 'b', [1, 2]];";
+    odb::sql::SQLInteractiveSession session;
+    odb::sql::SQLParser p;
+    //p.parseString(sql, static_cast<DataHandle*>(0), odb::sql::SQLSelectFactory::instance().config());
+
+}
+
+TEST(blocksSizes)
+{
+    size_t numberOfBlocks = 0;
+    off_t* offsets = 0;
+    size_t* sizes = 0;
+
+    int r = get_blocks_offsets("TestFastODA2Request2BIG.odb", &numberOfBlocks, &offsets, &sizes);
+    ASSERT(r == 0);
+
+    Log::info() << "num of blocks: " << numberOfBlocks << std::endl;
+    for (size_t i = 0; i < numberOfBlocks; ++i)
+    {
+        Log::info() << "UnitTest: #" << i << ": offset: " << offsets[i] << ", sizes: " << sizes[i] << std::endl;
+    }
+    Log::info() << "blocksSizes: numberOfBlocks=" << numberOfBlocks << std::endl;
+    ASSERT(numberOfBlocks == 5);
+
+    release_blocks_offsets(&offsets);
+    release_blocks_sizes(&sizes);
+}
+
+
+TEST(rownumber1)
+{
+	const char *inputData = "a:INTEGER,b:INTEGER\n" "1,1\n" "2,2\n" "3,3\n" "4,4\n" "5,5\n" "6,6\n" "7,7\n" "8,8\n" "9,9\n" "10,10\n";
+
+    string path("Test_rownumber1.odb");
+	odb::tool::ImportTool::importText(inputData, path);
+    string query("SELECT rownumber() from \"" + path + "\";");
+
+    odb::Select select(query);
+    odb::Select::iterator it = select.begin();
+    odb::Select::iterator end = select.end();
+
+    llong i = 0;
+    for (; it != end; ++it)
+    {
+        ASSERT((*it)[0] == ++i);
+    }
+    ASSERT(i == 10);
+}
+
+TEST(sqlOutputFormatting)
+{
+    /*
+    // See UnitTest.sql as well
+    const char *data =
+            "x:REAL,y:INTEGER,v:DOUBLE\n"
+            "100,1,0.3\n"
+            "100,1,0.2\n"
+            "101,2,0.4\n"
+            "101,2,0.1\n"
+            "NULL,1,0.1\n"
+            "NULL,2,0.2\n"
+            "NULL,3,0.3\n"
+            ;
+
+    const char* testFile("sqlOutputFormatting.odb");
+
+    odb::tool::ImportTool::importText(data, testFile);
+
+    bool doNotWriteColumnNames(false); // -T
+    bool doNotWriteNULL(false);        // -N
+    string delimiter(" ");           // -delimiter
+    string inputFile(testFile);           // -i
+    string outputFile;          // -o
+    string outputFormat;        // default is ascii
+
+    odb::sql::SQLSelectFactory::instance()
+            .config(odb::sql::SQLOutputConfig(doNotWriteColumnNames, doNotWriteNULL, delimiter, outputFile, outputFormat));
+
+    ostream& out = cout;
+    odb::sql::SQLInteractiveSession session(out);
+    odb::sql::SQLParser p;
+
+    FileHandle fh(inputFile);
+    fh.openForRead();
+    //p.parseString(StringTool::readFile(fileName), &fh, odb::sql::SQLSelectFactory::instance().config());
+    p.parseString("select x,y,v;", &fh, odb::sql::SQLSelectFactory::instance().config());
+    */
+
+}
+
+double julian(double d, double t)
+{
+    int indate = (int) d;
+    int intime = (int) t;
+    int year_target = indate/10000;
+    int month_target = (indate%10000)/100;
+    int day_target = indate%100;
+    int hour_target = intime/10000;
+    int min_target = (intime%10000)/100;
+    int sec_target = intime%100;
+
+    utils::DateTime d1(year_target, month_target, day_target,
+                       hour_target, min_target, sec_target);
+
+    return d1.dateToJulian();
+}
+
+TEST(dateTime)
+{
+    int j1 = julian(20120714, 120000);
+    int j2 = julian(20120714, 0);
+    //ASSERT(j1 > j2);
+}
+
+static void createDataForWindSpeedWindDirection()
+{
+    const char *data =
+            "u:REAL,v:REAL\n"
+            "11.7,-5.8\n"
+            "0.0,0.0\n"
+            "0,5.4\n"
+            "5.4,0.0\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "uv.odb");
+}
+
+TEST(windSpeedWindDirection)
+{
+    createDataForWindSpeedWindDirection();
+    string path("uv.odb");
+    string query("SELECT ff(u,v), dd(u,v), speed(u,v),dir(u,v), sqrt(u*u+v*v), fmod(atan2(-u,-v)+360.,360.) from \"" + path + "\";");
+
+    odb::Select select(query);
+    odb::Select::iterator it = select.begin();
+    odb::Select::iterator end = select.end();
+
+    llong i = 0;
+    for (; it != end; ++it)
+    {
+        Log::info() << " ff = " << (*it)[0] << " speed sqrt= " << (*it)[4] << std::endl;
+        Log::info() << " dd = " << (*it)[1] << " direction atan= " << (*it)[5] << std::endl;
+        ASSERT((*it)[0] == (*it)[4]);
+        ASSERT((*it)[1] == (*it)[5]);
+    }
+}
+
+//TEST(odbcapi)
+//{
+//	odb::tool::test::test_odacapi_setup_in_C(0,0);
+//	odb::tool::test::test_odacapi3(0,0);
+//}
+
+TEST(HashTable_clone)
+{
+    using namespace odb::codec;
+    odb::codec::HashTable *h = new odb::codec::HashTable;
+    h->store("BUFRDATA");
+    h->store("OTHER_ST");
+
+    odb::codec::HashTable *c = h->clone();
+
+    //ASSERT(*h == *c);
+    delete h;
+    delete c;
+}
+
+static void SplitTool_chunks()
+{
+    const char * fn = "selectAggregatedAndNonAggregated.odb";
+    unsigned long long n = odb::tool::CountTool::fastRowCount(fn);
+    vector<pair<Offset,Length> > chunks = odb::tool::SplitTool::getChunks(fn);
+
+    Log::info() << "chunks.size():" << chunks.size() << std::endl;
+    ASSERT(chunks.size() == 1 && chunks[0].first == Offset(0) && chunks[0].second == Length(357));
+}
+//TESTCASE(SplitTool_chunks);
+
+
+static void FilePool1()
+{
+    //FilePool<SimpleFilePoolTraits> pool(1);
+}
+//TESTCASE(FilePool1);
+
+static void copyVectorToArray()
+{
+    const size_t size = 1024;
+    const size_t n = 1000000;
+
+    vector<double> v(size);
+    double a[size];
+
+    {
+        Timer timer("std::copy");
+        for (size_t i = 0; i < n; ++i)
+            std::copy(v.begin(), v.end(), a);
+    }
+
+    {
+        Timer timer("for loop");
+        for (size_t i = 0; i < n; ++i)
+            for (size_t j = 0; j < size; ++j)
+                a[j] = v[j];
+    }
+
+}
+//TESTCASE(copyVectorToArray);
+
+
+static void count(void *counter, const double* data, size_t n) { ++*((llong*)counter); }
+static void *create_counter()
+{
+    llong* r = new llong;
+    *r = 0;
+    return r;
+}
+static void destroy_counter(void *counter) { delete (llong*) counter; }
+static void *reduce_counter(void *left, void *right)
+{
+    llong *result = new llong;
+    *result = (*(llong *) left) + (*(llong *) right);
+    return result;
+}
+
+class TemporaryPathName : public PathName {
+public:
+    TemporaryPathName(const string &fn) : PathName (fn) {}
+    ~TemporaryPathName() { unlink(); }
+};
+
+typedef TemporaryPathName ScratchFile;
+
+TEST(hash_operator_on_select_list)
+{
+    const char *data =
+            "x:INTEGER,y:INTEGER\n"
+            "1,1\n"
+            "2,2\n"
+            "3,3\n"
+            "4,4\n"
+            "5,5\n"
+            "6,6\n"
+            "7,7\n"
+            "8,8\n"
+            "9,9\n"
+            "10,10\n"
+            ;
+
+    ScratchFile f("hash_operator_on_select_list.odb");
+    odb::tool::ImportTool::importText(data, f);
+
+    string sql("select x,x#-1,x#1 from \"" + f + "\";");
+    odb::Select select(sql);
+    odb::Select::iterator it = select.begin();
+    odb::Select::iterator end = select.end();
+    for (; it != end; ++it)
+    {
+        Log::info() << it << std::endl;
+    }
+
+}
+
+/// Shift or hash (#) operator doesn't work in the WHERE clause.
+/// This test doesn't test anything yet.
+TEST(hash_operator_in_where)
+{
+    const char *data =
+            "x:INTEGER,y:INTEGER\n"
+            "1,1\n"
+            "2,2\n"
+            "3,3\n"
+            "4,4\n"
+            "5,5\n"
+            "6,6\n"
+            "7,7\n"
+            "8,8\n"
+            "9,9\n"
+            "10,10\n"
+            ;
+
+    ScratchFile f("hash_operator_in_where.odb");
+    odb::tool::ImportTool::importText(data, f);
+
+    string sql("select x,x#-1,x#1 from \"" + f + "\" where x=2 and x#1=3;");
+    odb::Select select(sql);
+    odb::Select::iterator it = select.begin();
+    odb::Select::iterator end = select.end();
+    for (; it != end; ++it)
+    {
+        Log::info() << it << std::endl;
+    }
+}
+
+TEST(bitfields_hash_operator)
+{
+    PathName f("2000010106.4.0.odb");
+    //odb::Select select("select lat,lat#1 from \"" + f + "\"");
+    odb::Select select("select anflag at body,anflag.final at body,anflag.*@body from \"" + f + "\";");
+    odb::Select::iterator it = select.begin();
+    odb::Select::iterator end = select.end();
+    for (; it != end; ++it)
+    {
+        Log::info() << it << std::endl;
+    }
+
+}
+
+TEST(select_constant_value)
+{
+    const char *sql =
+            "set $foo = 27;"
+            "select $foo;"
+            ;
+
+    odb::Select o(sql);
+
+    odb::Select::iterator it = o.begin();
+    odb::Select::iterator end = o.end();
+    unsigned long counter = 0;
+    for ( ; it != o.end(); ++it, ++counter)
+    {
+        Log::info() << it << std::endl;
+        CHECK_EQUAL(it->data(0), 27);
+    }
+    CHECK_EQUAL(counter, 1);
+}
+
+TEST(include)
+{
+    ofstream f("stuff.sql");
+    f
+            //<< "select * from \"file1.odb\";" << endl
+            << "set $foo = 10;" << endl
+            << "set $bar = 20;" << std::endl;
+    f.close();
+
+    const char *sql =
+            "#include \"stuff.sql\"\n"
+            "set $baz = $bar;"
+            "select $foo * $bar;"
+            ;
+
+    unsigned long counter = 0;
+    odb::Select o(sql);
+    for (odb::Select::iterator it(o.begin()), end(o.end());
+         it != o.end();
+         ++it, ++counter)
+    {
+        Log::info() << it << std::endl;
+    }
+}
+
+TEST(log_error)
+{
+    Log::error() << "Just a logger test" << std::endl;
+    // TODO: test Log::error writes to stderr
+    // TODO: test Log::error has a prefirx with timestamp and other things
+}
+
+/*
+TEST(create_table_using_variable)
+{
+
+    const char *sql =
+    "SET $c = { 2 : \"foo\" };\n"
+    "SET $s = 2;\n"
+    "CREATE TABLE t AS (c[$s]);"
+    ;
+
+    unsigned long counter = 0;
+    odb::Select o(sql);
+    for (odb::Select::iterator it(o.begin()), end(o.end());
+        it != o.end();
+        ++it, ++counter)
+    {
+        Log::info() << it << std::endl;
+    }
+}
+*/
+
+typedef MetaDataReader<MetaDataReaderIterator> MDR;
+
+TEST(meta_data_reader_checks_if_file_truncated)
+{
+    ASSERT(0 == system("dd if=disp.7.1.odb of=disp.7.1.odb.truncated bs=1914000 count=1"));
+    MDR mdr("disp.7.1.odb.truncated");
+    try {
+        for(MDR::iterator it(mdr.begin()), end(mdr.end()); it != end; ++it)
+            ;
+        ASSERT(0 && "Scanning of truncated file did not fail");
+    } catch (eckit::ShortFile ex) {
+        Log::info() << "Scanning of truncated file disp.7.1.odb.truncated failed as expected." << std::endl;
+    }
+}
+
+TEST(meta_data_reader_fails_scanning_corrupted_file)
+{
+    ASSERT(0 == system("cat disp.7.1.odb disp.7.1.odb.truncated >corrupted.odb"));
+    MDR mdr("corrupted.odb");
+    try {
+        for(MDR::iterator it(mdr.begin()), end(mdr.end()); it != end; ++it)
+            ;
+        ASSERT(0 && "Scanning of corrupted.odb did not fail");
+    } catch (eckit::ShortFile ex) {
+        Log::info() << "Scanning of corrupted.odb failed as expected." << std::endl;
+    }
+}
+
+TEST(operator_ge)
+{
+    const char *data =
+            "a:INTEGER,b:INTEGER\n"
+            "1,1\n"
+            "2,2\n"
+            "3,3\n"
+            "4,4\n"
+            "5,5\n"
+            "6,6\n"
+            "7,7\n"
+            "8,8\n"
+            "9,9\n"
+            "10,10\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "1to10.odb");
+
+    odb::Select odb("select a,b from \"1to10.odb\" where a >= 3;");
+    unsigned long counter = 0;
+    for (odb::Select::iterator it = odb.begin(), end = odb.end();
+         it != end;
+         ++it, ++counter)
+        ;
+    ASSERT(counter == 8);
+}
+
+static void create_1to10()
+{
+    const char *data =
+            "a:INTEGER,b:INTEGER\n"
+            "1,1\n"
+            "2,2\n"
+            "3,3\n"
+            "4,4\n"
+            "5,5\n"
+            "6,6\n"
+            "7,7\n"
+            "8,8\n"
+            "9,9\n"
+            "10,10\n"
+            ;
+
+    odb::tool::ImportTool::importText(data, "1to10.odb");
+}
+
+/* FIXME
+TEST(Select_isNewDataset)
+{
+    create_1to10();
+    size_t blocks (0);
+
+    odb::Select odb("select * from \"1to10.odb\";");
+    for (odb::Select::iterator it = odb.begin(), end = odb.end();
+        it != end;
+        ++it)
+        if (it->isNewDataset())
+            ++blocks;
+    ASSERT(blocks == 1);
+}
+*/
+
+template<typename T> 
+static void test_isNewDataset()
+{
+    create_1to10();
+    size_t blocks (0);
+
+    blocks = 0;
+    T odb("1to10.odb");
+    for (typename T::iterator it = odb.begin(), end = odb.end();
+         it != end;
+         ++it)
+        if (it->isNewDataset())
+            ++blocks;
+    ASSERT(blocks == 1);
+}
+
+/*
+TEST(Gabor)
+{
+    const char * cfg =
+    "CLASS: class\n"
+    "DATE: andate\n"
+    "TIME: antime\n"
+    "TYPE: type\n"
+    "OBSGROUP: groupid\n"
+    "REPORTYPE: reportype\n"
+    "STREAM: stream\n"
+    "EXPVER: expver\n"
+    ;
+    const string fileName("/tmp/gabor/massaged.odb");
+
+    odb::FastODA2Request<odb::ODA2RequestClientTraits> o2r;
+    o2r.parseConfig(cfg);
+
+    eckit::OffsetList offsets;
+    eckit::LengthList lengths;
+    vector<ODAHandle*> handles;
+    bool rc = o2r.scanFile(fileName, offsets, lengths, handles);
+    for (size_t i = 0; i < handles.size(); ++i)
+        delete handles[i];
+    handles.clear();
+    ASSERT(rc);
+
+    ASSERT(lengths.size());
+    ASSERT(lengths.size() == offsets.size());
+    for(size_t i = 1; i < offsets.size(); i++)
+        ASSERT(offsets[i] > offsets[i-1]);
+    size_t last = offsets.size()-1;
+    ASSERT(PathName(fileName).size() == offsets[last] + lengths[last]);
+
+    unsigned long long cnt = o2r.rowsNumber();
+
+    string filesRequest = "ARCHIVE,\n";
+    filesRequest += o2r.genRequest();
+}
+// FIXME
+TEST(Reader_isNewDataset) { test_isNewDataset<Reader>(); }
+// FIXME
+TEST(MetaDataReader_isNewDataset) { test_isNewDataset<odb::MetaDataReader<MetaDataReaderIterator> >(); }
+
+TEST(create_temporary_table)
+{
+    const char* sql = "CREATE "
+                      " TEMPORARY "
+                      " TABLE foo AS (col1 pk9real, col2 pk9real,) INHERITS (bar,baz);";
+
+    cout << "Trying to execute: '" << sql << "'" << std::endl;
+
+    odb::Select o(sql);
+    odb::tool::SQLTool::execute(sql);
+
+}
+*/
+
+TEST(TextReaderIterator_parseBitfields_32bits_limit)
+{
+    string bitfieldDefinition ( "en4_level_flag at hdr:bitfield[TempLevelReject:1;SaltLevelReject:1;LevelVertStability:1;IncreasingDepthCheck:1;NotUsed1:1;NotUsed2:1;NotUsed3:1;NotUsed4:1;NotUsed5:1;TempLevelStatList:1;TempLevelArgoQC:1;TempLevelOutOfRangeSetToMDI:1;TempLevelEN3List:1;TempLevelVertCheck:1;TempLevelNoBckgrnd:1;TempLevelBays:1;TempLevelBaysBud:1;TempLevelBaysBudReinstate:1;TempLevelWaterfallCheck:1;NotUsed6:1;NotUsed7:1;SaltLevelStatList:1;SaltLevelArgoQC:1;SaltLevelOutOfRang [...]
+    TextReader reader("dummy_path",",");
+    TextReaderIterator it(reader);
+    try {
+        odb::BitfieldDef def (it.parseBitfields(bitfieldDefinition));
+        ASSERT("TextReaderIterator::parseBitfields should throw UserError");
+    }
+    catch (UserError e) {
+        // That was expected.
+    }
+}
+
+
+TEST(TextReaderIterator_parseBitfields)
+{
+    string bitfieldDefinition ( "en4_level_flag at hdr:bitfield[TempLevelReject:1;SaltLevelReject:1;LevelVertStability:1;IncreasingDepthCheck:1;NotUsed1:1;NotUsed2:1;NotUsed3:1;NotUsed4:1;NotUsed5:1;TempLevelStatList:1;TempLevelArgoQC:1;TempLevelOutOfRangeSetToMDI:1;TempLevelEN3List:1;TempLevelVertCheck:1;TempLevelNoBckgrnd:1;TempLevelBays:1;TempLevelBaysBud:1;TempLevelBaysBudReinstate:1;TempLevelWaterfallCheck:1;NotUsed6:1;NotUsed7:1;SaltLevelStatList:1;SaltLevelArgoQC:1;SaltLevelOutOfRang [...]
+    TextReader reader("dummy_path",",");
+    TextReaderIterator it(reader);
+    odb::BitfieldDef def (it.parseBitfields(bitfieldDefinition));
+    FieldNames names(def.first);
+    Sizes sizes(def.second);
+
+    Log::info() << "TextReaderIterator_parseBitfields: sizeof names:" << names.size() << std::endl;
+    Log::info() << "TextReaderIterator_parseBitfields: sizeof sizes:" << sizes.size() << std::endl;
+    ASSERT(names.size() == 31);
+    ASSERT(sizes.size() == 31);
+
+    Log::info() << "TextReaderIterator_parseBitfields: FieldNames: " << names << std::endl;
+    for (size_t i = 0; i < sizes.size(); ++i)
+        Log::info() << "TextReaderIterator_parseBitfields: size: " << i << " " << sizes[i] << std::endl;
+}
+
+
+TEST(JULIAN_SECONDS)
+{
+    ASSERT(1 == (*odb::Select("select julian_seconds(19750311,0) < julian_seconds(20140210,0) from dual;").begin())[0]);
+}
+
+TEST(CREATE_TABLE_and_SELECT_INTO)
+{
+	const char *inputData = 
+	"a:INTEGER,b:INTEGER\n"
+	"1,1\n"
+	"2,2\n"
+	"3,3\n"
+	"4,4\n"
+	"5,5\n"
+	"6,6\n"
+	"7,7\n"
+	"8,8\n"
+	"9,9\n"
+	"10,10\n"
+	;
+
+	odb::tool::ImportTool::importText(inputData, "CREATE_TABLE_and_SELECT_INTO.odb");
+    const char* sql =
+    "CREATE TYPE mybitfield AS ( "
+    "codetype bit9,"
+    "instype bit10,"
+    "retrtype bit6,"
+    "geoarea bit6,"
+    ");"
+
+    "CREATE TABLE \"foo.odb\" AS ( "
+    "lat real,"
+    "lon real,"
+    "status mybitfield,"
+    ");"
+
+    "SELECT a,b,a*b INTO \"foo.odb\" FROM \"CREATE_TABLE_and_SELECT_INTO.odb\";"
+    ;
+
+    {
+        odb::Select o(sql);
+        odb::Select::iterator it = o.begin();
+        unsigned long counter = 0;
+        for ( ; it != o.end(); ++it, ++counter)
+            ;
+        Log::info() << "CREATE_TABLE_and_SELECT_INTO: counter=" << counter << endl;
+    }
+    system("ls -l foo.odb; ");
+    system((ODBAPISettings::instance().fileInHome("~/bin/odb") + " header foo.odb").c_str());
+}
+
+/*
+TEST(SELECT_ALL)
+{
+    ostream& L(eckit::Log::info());
+    odb::tool::ImportTool::importText("a:INTEGER,b:INTEGER\n1,2\n", "select_all_1.odb");
+    odb::tool::ImportTool::importText("a:INTEGER,b:INTEGER,c:INTEGER\n1,2,3\n", "select_all_2.odb");
+    system("cat select_all_1.odb select_all_2.odb >select_all.odb");
+
+    L << "--- Test_SELECT_ALL: open select_all.odb" << endl;
+    odb::Select o("SELECT ALL * FROM \"select_all.odb\";");
+    odb::Select::iterator it (o.begin()), end (o.end());
+    L << "--- Test_SELECT_ALL: row #0" << endl;
+    ++it;
+    ASSERT(it->columns().size() == 2);
+    L << "--- Test_SELECT_ALL: row #1" << endl;
+    ++it;
+    ASSERT(it->columns().size() == 3);
+}
+*/
+
+// ODB-106
+TEST(SELECT_WHERE_0)
+{
+    odb::tool::ImportTool::importText("a:INTEGER,b:INTEGER\n1,2\n3,4\n", "select_where_0.odb");
+    odb::Select o("SELECT * FROM \"select_where_0.odb\" WHERE 0;");
+    odb::Select::iterator it (o.begin()), end (o.end());
+    ++it;
+}
+
+TEST(QuestionMarkHandlingWhenSplittingByStringColumn_ODB235)
+{
+    const char *inFile ("ODB_235.odb");
+    const char *data (
+            "a:INTEGER,b:INTEGER,expver:STRING\n"
+            "1,1,'?'\n"
+            "2,2,'?'\n"
+            "3,3,'?'\n"
+            );
+    const char* outFileTemplate ("ODB_235_{a}_{expver}.odb");
+
+    odb::tool::ImportTool::importText(data, inFile);
+
+	odb::Reader in(inFile);
+	odb::DispatchingWriter out(outFileTemplate, /*maxOpenFiles*/ 3);
+
+	odb::DispatchingWriter::iterator outIt (out.begin());
+	outIt->pass1(in.begin(), in.end());
+
+    ASSERT(PathName("ODB_235_1_?.odb").exists());
+    ASSERT(PathName("ODB_235_2_?.odb").exists());
+    ASSERT(PathName("ODB_235_3_?.odb").exists());
+
+    PathName("ODB_235_1_?.odb").unlink();
+    PathName("ODB_235_2_?.odb").unlink();
+    PathName("ODB_235_3_?.odb").unlink();
+    PathName("ODB_235.odb").unlink();
+}
+
+TEST(LegacyAPIExecuteSelectTwice)
+{
+    const std::string fn("legacy_execute_select_twice.odb");
+    odb::tool::ImportTool::importText("a:INTEGER,b:INTEGER\n1,2\n3,4\n", fn);
+    odb::Select o(std::string("SELECT * FROM \"") + fn + "\";");
+
+    int i (0), j (0);
+
+    for (odb::Select::iterator it (o.begin()), end (o.end()); it != end; ++it)
+        ++i;
+
+    ASSERT(i == 2);
+
+    for (odb::Select::iterator it (o.begin()), end (o.end()); it != end; ++it)
+        ++j;
+
+    ASSERT(j == 2);
+}
+
+TEST(LegacyAPITraverseReaderTwice)
+{
+    const std::string fn("legacy_traverse_reader_twice.odb");
+    odb::tool::ImportTool::importText("a:INTEGER,b:INTEGER\n1,2\n3,4\n", fn);
+    odb::Reader o(fn);
+
+    int i (0), j (0);
+
+    for (odb::Reader::iterator it (o.begin()), end (o.end()); it != end; ++it)
+        ++i;
+
+    ASSERT(i == 2);
+
+    for (odb::Reader::iterator it (o.begin()), end (o.end()); it != end; ++it)
+        ++j;
+
+    ASSERT(j == 2);
+}
+
+#define checkRC(return_code, message, db) { \
+    if (return_code != ODBQL_OK) { \
+        std::stringstream ss; \
+        ss << message << ": " << odbql_errmsg(db); \
+        odbql_close(db); \
+        throw SeriousBug(ss.str()); \
+    } \
+}
+
+TEST(ODB269)
+{
+    odbql *db;
+    odbql_stmt *stmt;
+    int i;
+
+    int rc = odbql_open("", &db);
+
+    checkRC(rc, "Cannot open database", db);
+    
+    rc = odbql_prepare_v2(db, "CREATE TABLE foo AS ( x INTEGER, y DOUBLE, v STRING) ON 'test_odb269.odb';", -1, &stmt, 0);
+    checkRC(rc, "Failed to prepare DDL statements", db);
+
+    rc = odbql_prepare_v2(db, "INSERT INTO foo (x,y,v) VALUES (?,?,?);", -1, &stmt, 0);
+    checkRC(rc, "Failed to prepare INSERT statement", db);
+
+    for (i = 0; i < 3; ++i) 
+        odbql_bind_null(stmt, i);
+    rc = odbql_step(stmt);
+    rc = odbql_finalize(stmt);
+    checkRC(rc, "odbql_finalize failed", db);
+
+    for (int i(0); i < 2000; ++i)
+    {
+        cout << i << " " << endl;
+        rc = odbql_prepare_v2(db, "SELECT count(*) FROM 'test_odb269.odb';", -1, &stmt, 0);
+        checkRC(rc, "Failed to prepare statement", db);
+        while((rc = odbql_step(stmt)) != ODBQL_DONE) 
+        {
+            ASSERT(odbql_column_count(stmt) == 1);
+            odbql_value* v (odbql_column_value(stmt, 0));
+            ASSERT(odbql_value_int(v) == 1);
+        }
+        rc = odbql_finalize(stmt);
+    }
+
+    rc = odbql_close(db);
+    checkRC(rc, "odbql_close failed", db);
+}
+
+//void buildMultiHandle(eckit::MultiHandle&, const std::vector<std::string>&);
+//void buildMultiHandle(eckit::MultiHandle&, const std::string&);
+TEST(HttpHandle)
+{
+    eckit::DataHandle* in (ecml::DataHandleFactory::openForRead("http://localhost/conv.odb"));
+    //eckit::DataHandle* out (DataHandleFactory::openForWrite(const std::string&, const eckit::Length& = eckit::Length(0)));
+
+    odb::Select o("select *;", *in);
+
+    for (odb::Select::iterator it (o.begin()); it != o.end(); ++it)
+    {
+        Log::info() << "." << std::endl;
+    }
+}
+
diff --git a/odb_api/src/odb_api/tools/VariableLookupHandler.cc b/odb_api/src/odb_api/tools/VariableLookupHandler.cc
new file mode 100644
index 0000000..20200c8
--- /dev/null
+++ b/odb_api/src/odb_api/tools/VariableLookupHandler.cc
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "VariableLookupHandler.h"
+
+#include "ecml/parser/Request.h"
+#include "ecml/ExecutionContext.h"
+#include "ecml/Environment.h"
+#include "ecml/Interpreter.h"
+
+using namespace std;
+using namespace eckit;
+
+VariableLookupHandler::VariableLookupHandler(const string& name) : RequestHandler(name) {}
+
+Values VariableLookupHandler::handle(ExecutionContext& context)
+{
+    vector<string> vars (getValueAsList(context, "of"));
+    ASSERT("'value,of' expects one variable name currently" && vars.size() == 1);
+
+    string var (vars[0]);
+    Values r (context.interpreter().eval(context.environment().lookup(var), context));
+
+    Log::debug() << "value,of=" << var << " => " << r << endl;
+
+    return r;
+}
+
diff --git a/odb_api/src/odb_api/tools/VariableLookupHandler.h b/odb_api/src/odb_api/tools/VariableLookupHandler.h
new file mode 100644
index 0000000..e5cb8d2
--- /dev/null
+++ b/odb_api/src/odb_api/tools/VariableLookupHandler.h
@@ -0,0 +1,26 @@
+/*
+ * (C) Copyright 1996-2013 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+/// @author Piotr Kuchta, May 2015
+
+#ifndef VariableLookupHandler_H
+#define VariableLookupHandler_H
+
+#include <sstream>
+
+#include "ecml/parser/Request.h"
+#include "ecml/RequestHandler.h"
+
+class VariableLookupHandler : public ecml::RequestHandler {
+public:
+    VariableLookupHandler(const std::string&);
+    virtual eckit::Values handle(ecml::ExecutionContext&);
+};
+
+#endif
diff --git a/odb_api/src/odb_api/tools/XYVTool.cc b/odb_api/src/odb_api/tools/XYVTool.cc
new file mode 100755
index 0000000..86626d6
--- /dev/null
+++ b/odb_api/src/odb_api/tools/XYVTool.cc
@@ -0,0 +1,64 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/filesystem/PathName.h"
+#include "eckit/log/Log.h"
+#include "odb_api/Select.h"
+#include "XYVTool.h"
+
+using namespace std;
+using namespace eckit;
+
+namespace odb {
+namespace tool {
+
+XYVTool::XYVTool(int argc,char **argv): Tool(argc, argv) {}
+
+XYVTool::~XYVTool() {}
+
+void XYVTool::run()
+{
+	if (parameters().size() != 4)
+	{
+		Log::error() << "Usage: ";
+		usage(parameters(0), Log::error());
+		Log::error() << std::endl;
+		return;// 1;
+	}
+
+	PathName inputFile = parameters(1);
+	std::string valueColumn = parameters(2);
+	PathName outputFile = parameters(3);
+
+    std::ofstream out;
+	out.open(outputFile.asString().c_str());
+
+	out << "#GEO" << std::endl << std::endl;
+	out << "#FORMAT XYV" << std::endl << std::endl;
+	out << "PARAMETER = 12004" << std::endl << std::endl; 
+	out << "x/long	y/lat	value" << std::endl;
+	out << "#DATA" << std::endl << std::endl;
+
+	std::string select = std::string("select lat, lon, ") + valueColumn + " from \"" + inputFile + "\";";
+	Log::info() << select << std::endl;
+
+	odb::Select oda(select);
+	for (odb::Select::iterator it = oda.begin();
+		it != oda.end();
+		++it) 
+	{
+		out << (*it)[0] << "\t" << (*it)[1] << "\t" << (*it)[2] << std::endl;
+	}
+	out.close();
+}
+
+} // namespace tool 
+} // namespace odb 
+
diff --git a/odb_api/src/odb_api/tools/XYVTool.h b/odb_api/src/odb_api/tools/XYVTool.h
new file mode 100755
index 0000000..f72a25b
--- /dev/null
+++ b/odb_api/src/odb_api/tools/XYVTool.h
@@ -0,0 +1,40 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef XYVTool_H
+#define XYVTool_H
+
+#include "Tool.h"
+
+namespace odb {
+namespace tool {
+
+class XYVTool : public Tool {
+
+public:
+	XYVTool(int argc, char **argv);
+	~XYVTool();
+
+	static void help(std::ostream &o)
+	{ o << "Creates XYV representation of file for displaying in a graphics program"; }
+
+	static void usage(const std::string& name, std::ostream &o)
+	{ o << name << " <input-file.odb> <value-column> <output-file.odb>"; }
+
+	virtual void run();
+};
+
+template <> struct ExperimentalTool<XYVTool> { enum { experimental = true }; };
+
+} // namespace tool 
+} // namespace odb 
+
+#endif
+
diff --git a/odb_api/src/odb_api/tools/odb.cc b/odb_api/src/odb_api/tools/odb.cc
new file mode 100755
index 0000000..5010e9a
--- /dev/null
+++ b/odb_api/src/odb_api/tools/odb.cc
@@ -0,0 +1,170 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#include "eckit/io/FileHandle.h"
+
+#include "odb_api/FunctionFactory.h"
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/odbcapi.h"
+
+#include "TestOdaCAPI.h"
+#include "Tool.h"
+#include "ToolFactory.h"
+#include "ToolRunnerApplication.h"
+#include "TestRunnerApplication.h"
+
+using namespace std;
+using namespace eckit;
+
+using namespace odb::tool;
+
+int executeCommand(int argc, char *argv[]);
+int gdb(int argc, char *argv[]);
+int valgrind(int argc, char *argv[]);
+int sqlhelp(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+	try { return executeCommand(argc, argv);
+	} catch (std::exception& e) {
+		cerr << argv[0] << ": " << e.what() << std::endl;
+		return 1;
+	} catch (...) {
+		cerr << argv[0] << ": unknown exception" << std::endl;
+		return 1;
+    }
+}
+
+int executeCommand(int argc, char *argv[])
+{
+	odb::tool::Tool::registerTools();
+    register_extra_data_handle_factories();
+	if (argc < 2)
+	{
+        odb_start_with_args(argc, argv);
+		cerr << "Usage:" << endl
+			<< "        " << argv[0] << " <command> [<command's-parameters>]" << endl 
+			<< "        " << argv[0] << " help <command>" << std::endl << endl
+			<< "Available commands:" << std::endl;
+
+		AbstractToolFactory::listTools(cout);
+		return 1;
+	}
+
+	const string firstArg(argv[1]);
+
+    if (firstArg == "g")  { odb_start_with_args(argc, argv); return gdb(argc, argv); }
+    if (firstArg == "vg") { odb_start_with_args(argc, argv); return valgrind(argc, argv); }
+    
+	if (firstArg == "testodbcapi") return odb::tool::test::test_odacapi(argc, argv);
+	if (firstArg == "test")
+	{
+		if (argc == 2) //no args => test all
+		{
+			std::cout << "Testing C API" << std::endl;
+
+			string testCapi = std::string(argv[0]) + " testodbcapi";
+			cerr << "Executing '" << testCapi << "'" << std::endl;
+			int rc = system(testCapi.c_str());
+			if (rc) return rc;
+		}
+
+		std::cout << std::endl << "Running tests." << std::endl;
+
+		odb::tool::test::TestRunnerApplication testRunner(argc - 1, argv + 1);
+		testRunner.start();
+		// It never really gets here.
+		return 0;
+	}
+
+	if (firstArg == "help")
+	{
+        odb_start_with_args(argc, argv);
+		if (argc == 2)
+			AbstractToolFactory::printToolsHelp(cout);
+		else
+		{
+			AbstractToolFactory::printToolHelp(argv[2], cout);
+			std::cout << std::endl << "Usage:" << std::endl << std::endl << "\t";
+			AbstractToolFactory::printToolUsage(argv[2], cout);
+		}
+		return 0;
+	}
+
+	if (firstArg == "sqlhelp") return sqlhelp(argc, argv);
+
+	if (firstArg == "-V" || firstArg == "-v" || firstArg == "--version")
+	{
+		std::cout << "ODBAPI Version: " << odb::ODBAPIVersion::version() << std::endl;
+		std::cout << "File format version: " << odb::ODBAPIVersion::formatVersionMajor() << "." << odb::ODBAPIVersion::formatVersionMinor() <<  std::endl;
+		return 0;
+	}
+
+	ToolRunnerApplication runner(argc, argv);
+	return runner.start();
+}
+
+int gdb(int argc, char *argv[])
+{
+	string cmd = argv[0];
+	string args;
+	string gdbScript = argv[1];
+	for (int i = 2; i < argc; i++)
+		args += std::string(" ") + argv[i];
+
+	PathName scriptFile (std::string(".gdb_") + std::string(argc < 3 ? "odb" : argv[2]));
+    
+    if (! scriptFile.exists())
+	{
+		string s = std::string("file ") + cmd + "\nbreak main\nrun " + args + "\ncatch throw\n";
+		FileHandle f(scriptFile);
+		f.openForWrite(1024);
+		f.write(s.c_str(), s.size());
+		f.close();
+	}
+	string vi = std::string("vi ") + scriptFile;
+	string gdb = std::string("gdb -x ") + scriptFile;
+	std::cout << "Executing '" << vi << "'" << std::endl;
+    system(vi.c_str());
+	std::cout << "Executing '" << gdb << "'" << std::endl;
+	return system(gdb.c_str());
+}
+
+// valgrind --log-file=v.log --show-reachable=yes --leak-check=full ./oda test 
+int valgrind(int argc, char *argv[])
+{
+	string cmd = argv[0];
+	string args;
+	for (int i = 2; i < argc; i++)
+		args += std::string(" ") + argv[i];
+
+
+	string logFile = std::string("vg.") + argv[2] + ".log";
+	string vg = std::string("valgrind --log-file=") + logFile + " --show-reachable=yes --leak-check=full "
+         + " --db-attach=yes " // --suppressions=eckit.supp "
+		 + cmd + " " + args;
+	std::cout << "Executing '" << vg << "'" << std::endl;
+	return system(vg.c_str());
+}
+
+int sqlhelp(int argc, char *argv[])
+{
+	typedef odb::sql::expression::function::FunctionFactory::FunctionInfo FI;
+	FI& fi = odb::sql::expression::function::FunctionFactory::instance().functionsInfo();
+	for (FI::iterator i = fi.begin(); i != fi.end(); ++i)
+	{
+		if (i->first.first == "FunctionFactory")
+			continue;
+
+		std::cout << i->first.first << "/" << i->first.second << " " << i->second << std::endl;
+	}
+	
+	return 0;
+}
diff --git a/odb_api/src/odb_api/tools/odb_api_tools_c.cc b/odb_api/src/odb_api/tools/odb_api_tools_c.cc
new file mode 100644
index 0000000..55035b5
--- /dev/null
+++ b/odb_api/src/odb_api/tools/odb_api_tools_c.cc
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+///
+/// \file odb_api_tools_c.cc
+///
+/// @author Piotr Kuchta, September 2015
+
+#include "odb_api/odb_api.h"
+
+#include "odb_api/FastODA2Request.h"
+#include "odb_api/MetaData.h"
+#include "odb_api/MetaDataReader.h"
+#include "odb_api/MetaDataReaderIterator.h"
+#include "odb_api/ODAHandle.h"
+#include "odb_api/ODBAPISettings.h"
+#include "odb_api/ODBAPIVersion.h"
+#include "odb_api/odbcapi.h"
+#include "odb_api/Select.h"
+#include "odb_api/Reader.h"
+#include "odb_api/Writer.h"
+
+using namespace eckit;
+using namespace odb;
+
+extern "C" {
+
+int import_text(const char* text, const char* output_file)
+{
+    try {
+        odb::tool::ImportTool::importText(text, output_file);
+        return 0;
+    } catch (Exception e) {
+        return 1;
+    }
+}
+
+} // extern "C" 
+
diff --git a/odb_api/src/odb_api/tools/odb_api_tools_c.h b/odb_api/src/odb_api/tools/odb_api_tools_c.h
new file mode 100644
index 0000000..501dde7
--- /dev/null
+++ b/odb_api/src/odb_api/tools/odb_api_tools_c.h
@@ -0,0 +1,35 @@
+/*
+ * (C) Copyright 1996-2012 ECMWF.
+ * 
+ * This software is licensed under the terms of the Apache Licence Version 2.0
+ * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+ * In applying this licence, ECMWF does not waive the privileges and immunities 
+ * granted to it by virtue of its status as an intergovernmental organisation nor
+ * does it submit to any jurisdiction.
+ */
+
+#ifndef ODB_API_TOOLS_C_H
+
+/**
+ * \file odb_api_tools_c.h
+ *
+ * @author Piotr Kuchta, September 2015
+ *
+ */
+
+#include <stddef.h>
+#include <sys/types.h>
+// For off_t on Cray:
+#include <unistd.h>
+
+#if defined(__cplusplus) || defined(c_plusplus) || defined(SWIGPYTHON)
+extern "C" {
+#endif
+
+int import_text(const char* text, const char* output_file);
+
+#if defined(__cplusplus) || defined(c_plusplus) || defined(SWIGPYTHON)
+}
+#endif
+#endif
+
diff --git a/odb_api/src/odb_api/tools/test.ksh b/odb_api/src/odb_api/tools/test.ksh
new file mode 100755
index 0000000..1755dcc
--- /dev/null
+++ b/odb_api/src/odb_api/tools/test.ksh
@@ -0,0 +1,109 @@
+set -e
+
+function log
+{
+	if [ x$MARS_SMS_LABEL != x ];
+	then
+		smslabel $MARS_SMS_LABEL "$*";
+	else
+		echo ++++++++++++++++++++++++++++++++++++++++++++
+		echo + $*
+		echo ++++++++++++++++++++++++++++++++++++++++++++
+	fi 
+}
+
+#set -v
+set -x
+
+log +++ Test: odb set
+
+export INPUT=disp.4.0.odb
+export OUTPUT=disp.4.0.modified.odb
+
+
+log Check the input data is there and has the expected values
+ls -l $INPUT
+./odb header $INPUT | grep expver | grep 0018
+./odb header $INPUT | grep andate | grep 20000101
+./odb sql select expver,andate -i $INPUT | grep 0018 | grep 20000101 >/dev/null
+
+log Create new file with changed values of expver and andate
+./odb set "expver at desc='0019    ',andate=20100101" $INPUT $OUTPUT
+
+log Check new files have the correct values
+ls -l $OUTPUT
+./odb ls -s expver,andate $OUTPUT | head -n 1
+
+./odb sql select expver from \"$OUTPUT\" |uniq #|grep 0019 | head -n 1
+./odb sql select andate from \"$OUTPUT\" |uniq #|grep 20100101 | head -n 1
+
+./odb header $OUTPUT | grep expver | grep 0019
+./odb header $OUTPUT | grep andate | grep 20100101
+
+log +++ odb set Completed OK
+
+
+log +++ Test: odb import
+
+cat >test_odb_text_input.csv <<@@
+stream:STRING,expver:INTEGER,value:REAL,report_status at hdr:BITFIELD[active:1;passive:1;rejected:1;blacklisted:1]
+'001',1,0.1,0000
+'001',2,0.2,0001
+'001',3,0.3,5
+'001',4,0.4,15
+@@
+./odb import test_odb_text_input.csv test_import.odb
+./odb sql select \* from \"test_import.odb\" >test_odb_text_input.out
+#diff test_odb_text_input.csv test_odb_text_input.out 
+
+
+#grep "VARCHAR(30)\|INT" ../obsdb/obsdb.ddl|xargs|sed 's/ VARCHAR(30)/:STRING/g'|sed 's/ INT/:INTEGER/g'|sed 's/ //g'|sed 's/,/\&/g' >rtt.csv
+#python ../obsdb/convertRTT.py >>rtt.csv
+#./odb import -d \& rtt.csv rtt.odb
+#./odb sql select \* from \"rtt.csv\"
+
+log +++ odb import Completed OK
+
+log +++ Test: SQL Functions
+#./odb test functions
+log +++ Completed. SQL Functions OK
+
+log +++ Test: Input file command line option: -i. Also, test printing of column names by default
+./odb sql select \* -i test_import.odb |grep stream |grep expver|grep value
+log +++ odb Completed OK
+
+
+log +++ Test: Input file command line option: -i. Also, test option -T: do not print column names
+[ `./odb sql select \* -i test_import.odb -T|grep stream|wc|awk '{print $1}'` == 0 ]
+log +++ odb Completed OK
+
+./odb sql select time,lat,lon,obsvalue  order by time,lat,lon -i 2000010106.1.0.odb -f odb -o order_by_out.odb
+./odb header order_by_out.odb
+
+log +++ Test SELECT with mixed aggregated and non-aggregated functions
+
+cat >test_group_by.csv <<@@
+stream:STRING,expver:INTEGER,value:REAL
+'001',1,0.1
+'001',2,0.2
+NULL,4,0.4
+NULL,5,0.5
+@@
+
+./odb import test_group_by.csv test_group_by.odb
+./odb sql select stream,min\(expver\) from \"test_group_by.odb\"
+
+log +++ Test portability of the split tool
+
+rm -rf split.*.*.odb
+./odb split $ODB_API_TEST_DATA_PATH/split_crash_on_andate_and_antime.odb split.{andate}.{antime}.odb
+ls -l split.*.*.odb
+
+log +++ odb Completed OK
+
+#log +++ Test import tool
+#og +++ odb Completed OK
+
+log +++ All tests completed OK
+
+
diff --git a/odb_api/src/odb_api/tools/xxx b/odb_api/src/odb_api/tools/xxx
new file mode 100755
index 0000000..6b4a4b5
--- /dev/null
+++ b/odb_api/src/odb_api/tools/xxx
@@ -0,0 +1,6 @@
+FILES=$(grep -l SIMPLE_TEST *cc)
+for f in $FILES; do
+    test_name=$(echo $f|sed s/.cc//g|sed s/^Test//g)
+    echo $f $test_name
+    perl -p -i -e s/SIMPLE_TEST/SIMPLE_TEST\($test_name\)/g $f
+done
diff --git a/odb_api/src/odb_api_config.h.in b/odb_api/src/odb_api_config.h.in
new file mode 100644
index 0000000..eeb2706
--- /dev/null
+++ b/odb_api/src/odb_api_config.h.in
@@ -0,0 +1,16 @@
+#ifndef odb_api_config_h
+#define odb_api_config_h
+
+#include "odb_api_ecbuild_config.h" // generated by ecbuild_generate_config_headers()
+
+#define ODB_API_VERSION_STR "@ODB_API_VERSION_STR@"
+#define ODB_API_VERSION     "@ODB_API_VERSION@"
+
+#define ODB_API_MAJOR_VERSION  @ODB_API_MAJOR_VERSION@
+#define ODB_API_MINOR_VERSION  @ODB_API_MINOR_VERSION@
+#define ODB_API_PATCH_VERSION  @ODB_API_PATCH_VERSION@
+
+#define ODB_API_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
+#define ODB_API_BINARY_DIR     "@CMAKE_BINARY_DIR@"
+
+#endif // odb_api_config_h
diff --git a/odb_api/src/python/CMakeLists.txt b/odb_api/src/python/CMakeLists.txt
new file mode 100644
index 0000000..e13a20f
--- /dev/null
+++ b/odb_api/src/python/CMakeLists.txt
@@ -0,0 +1,46 @@
+###############################################################################
+# swig python interface
+
+if( HAVE_PYTHON AND SWIG_FOUND )
+  join( ECKIT_INCLUDE_DIRS "', '" _ECKIT_INCLUDES )
+  configure_file( setup.py.in setup.py )
+  file( COPY odb test_python_odb_api.py legacy_test_python_odb_api.py legacy_odb_api_python_examples.py DESTINATION . )
+
+  set( _odbapi_swig "odb/_pyodbapi${CMAKE_SHARED_LIBRARY_SUFFIX}" )
+  # Build the extension module for use in build tree with RPATH pointing to the build tree
+  add_custom_command( OUTPUT ${_odbapi_swig}
+                      COMMAND ${PYTHON_EXECUTABLE} setup.py build_ext --inplace --force --rpath ${CMAKE_BINARY_DIR}/lib
+                      DEPENDS odb/pyodbapi.i setup.py.in Odb )
+  add_custom_target(odb_api_build_swig_wrapper ALL DEPENDS ${_odbapi_swig})
+
+  # Build the extension module for use in install tree with RPATH pointing to install tree
+  install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} setup.py build_ext --rpath ${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
+  # Call distutils for installation
+  install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} setup.py install --root \$ENV{DESTDIR}/ --prefix ${CMAKE_INSTALL_PREFIX} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
+
+  list( APPEND python_odb_api_data_files  2000010106.odb ATOVS.trimmed.odb )
+  ecbuild_get_test_multidata( TARGET python_odb_api_get_test_data
+                              NAMES ${python_odb_api_data_files}
+                              NOCHECK )
+
+  set( test_environment_python
+         ODB_API_SCHEMA_PATH=${CMAKE_CURRENT_BINARY_DIR}/../../tests/cma.hh)
+
+  ecbuild_add_test ( TARGET       test_python_odb_api.py
+                     TYPE         PYTHON
+                     ENVIRONMENT  ${test_environment_python} 
+                     COMMAND      ${CMAKE_CURRENT_BINARY_DIR}/test_python_odb_api.py
+                     CONDITION    HAVE_PYTHON
+                     LABELS       odb_api odb_api_python
+                     #TEST_DEPENDS python_odb_api_get_test_data
+                   )
+
+  ecbuild_add_test ( TARGET       legacy_test_python_odb_api.py
+                     TYPE         PYTHON
+                     COMMAND      ${CMAKE_CURRENT_BINARY_DIR}/legacy_test_python_odb_api.py
+                     CONDITION    HAVE_PYTHON
+                     LABELS       odb_api odb_api_python
+                     TEST_DEPENDS python_odb_api_get_test_data
+                   )
+
+endif()
diff --git a/odb_api/src/python/legacy_odb_api_python_examples.py b/odb_api/src/python/legacy_odb_api_python_examples.py
new file mode 100644
index 0000000..5368dae
--- /dev/null
+++ b/odb_api/src/python/legacy_odb_api_python_examples.py
@@ -0,0 +1,57 @@
+import odb
+
+# See also https://software.ecmwf.int/wiki/display/ODB/Python+interface
+
+test_file = '2000010106.odb'
+
+def read_file(file_name = test_file):
+    """ Read contents of a file without using SQL."""
+
+    # Open a file and read rows in a loop.
+    for row in odb.open(file_name):
+        # Row object can be indexed with column names (strings).
+        assert row['andate'] == 20000101
+        # OK, one row is enough for the testing purposes.
+        break
+
+def sql_select(file_name = test_file):
+    """ SQL filtering / calculations. """
+
+    s = '''select varno,count(*),min(obsvalue),max(obsvalue) 
+           from "%s"
+           order by 2 desc''' % file_name
+    for row in odb.sql(s):
+        print ', '.join(map(str, row[:]))
+
+def indexing_result_set_row(file_name = test_file):
+    """ Various ways of indexing row object."""
+
+    for row in odb.sql('select * from "%s"' % file_name):
+        # Row can be indexed with a tuple containing column names
+        expver, analysis_date, analysis_time = row['expver', 'andate', 'antime']
+        print 'expver: "%s",  analysis date: %d analysis time: %d' % (expver, analysis_date, analysis_time)
+        break
+
+    for row in odb.sql('select lat,lon,varno,obsvalue from "%s"' % file_name):
+        # Row can be indexed with integers representing position of the column in the result set,
+        # starting from 0
+        print row[0], row[1], row[2], row[3]
+        # We can also index with a tuple containing several integers;
+        # the result will be a tuple of values.
+        print row[0,1,2,3]
+        break
+
+def reading_metadata_of_result_set(file_name = test_file):
+    for row in odb.sql('select * from "%s"' % file_name):
+        columns = [c.name() for c in row.columns()]
+        break
+    assert columns[:5] == ['expver at desc', 'andate at desc', 'antime at desc', 'seqno at hdr', 'obstype at hdr']
+    
+
+def main():
+    read_file()
+    sql_select()
+    indexing_result_set_row()
+    reading_metadata_of_result_set()
+
+if __name__ == '__main__': main()
diff --git a/odb_api/src/python/legacy_test_python_odb_api.py b/odb_api/src/python/legacy_test_python_odb_api.py
new file mode 100755
index 0000000..8acd5be
--- /dev/null
+++ b/odb_api/src/python/legacy_test_python_odb_api.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python 
+
+import os
+print '*** ', __file__, 'CWD:', os.getcwd()
+
+import odb
+import unittest 
+import legacy_odb_api_python_examples as examples
+
+def open_non_existing_file(file_name = 'a_non_existin_file.odb', f = lambda fn: odb.open(fn)):
+    didThrow = False
+    try:
+        for row in f(file_name):
+            pass
+    except (IOError, Exception) as e:
+        didThrow = True
+        #print "I/O error({0}): {1}, e.args: {2}".format(e.errno, e.strerror, e.args)
+    assert didThrow and "Opening of a non existing file was supposed to fail with IOError, see ODB-93" 
+
+def read_non_existing_file(): 
+    return open_non_existing_file()
+
+def select_non_existing_file(): 
+    return open_non_existing_file(f = lambda fn: odb.sql('select * from "%s"' % fn))
+
+class TestODBAPI(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def test_open_non_existing_file(self):
+        with self.assertRaises(IOError) as ex:
+            for row in odb.sql('select * from "non_existing_file.odb";'):
+                pass
+
+    def test_syntax_error_throws_exception(self):
+        with self.assertRaises(SyntaxError) as ex:
+            for row in odb.sql('select * from '):
+                pass
+
+    def test_odb_module_appends_semicolon_if_needed(self):
+        """ Check that semicolon at the end of SQL statement is not obligatory (ODB-114)
+            Expect IOError, and not SyntaxError.
+        """
+        with self.assertRaises(IOError) as ex:
+            for row in odb.sql('select * from "non_existing_file.odb"'):
+                pass
+
+    def test_examples_read_file(self): examples.read_file();
+    def test_examples_read_file(self): examples.read_file();
+    def test_examples_sql_select(self): examples.sql_select();
+    def test_indexing_result_set_row(self): examples.indexing_result_set_row()
+    def test_reading_metadata_of_result_set(self): examples.reading_metadata_of_result_set()
+    def test_reading_non_existing_file(self): read_non_existing_file()
+    def test_selecting_non_existing_file(self): select_non_existing_file()
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/odb_api/src/python/odb/__init__.py b/odb_api/src/python/odb/__init__.py
new file mode 100644
index 0000000..4a2f224
--- /dev/null
+++ b/odb_api/src/python/odb/__init__.py
@@ -0,0 +1,2 @@
+from odb import *
+
diff --git a/odb_api/src/python/odb/odb.py b/odb_api/src/python/odb/odb.py
new file mode 100644
index 0000000..ef2c599
--- /dev/null
+++ b/odb_api/src/python/odb/odb.py
@@ -0,0 +1,70 @@
+# (C) Copyright 1996-2012 ECMWF.
+# 
+# This software is licensed under the terms of the Apache Licence Version 2.0
+# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
+# In applying this licence, ECMWF does not waive the privileges and immunities 
+# granted to it by virtue of its status as an intergovernmental organisation nor
+# does it submit to any jurisdiction.
+
+""" 
+Python interface for ODB API
+Piotr Kuchta - ECMWF Nov 2011
+
+The new, PEP-0249 conformant API is in module odbql, see its source for 
+documentation and examples. Starting point is function connect.
+
+Functions sql and open are a legacy, unsupported API, with limited functionality (read only). 
+
+"""
+
+from odbql import *
+
+# Disable SWIG based interface for now
+'''
+try:
+    import pyodbapi
+
+    def addSemicolon(s):
+        """ Adds semicolon to the end of SQL statement if it's missing.
+        """
+        if not s.strip().endswith(';'):
+            s += ';'
+        return s;
+
+    class Select:
+        def __init__(self, sql):
+            if not isinstance(sql, str):
+                raise TypeError('sql must be a string object')
+            self.sql = sql
+            
+    def sql(statement):
+        """
+        Executes an SQL statement. Returns an iterator over rows of the statement's result set.
+        This is a legacy API. It is recommended to use the new, PEP-0249 conformant API, see
+        function connect (module odbql.py)
+        """
+        if not isinstance(statement, str):
+            raise TypeError('statement must be a string object')
+        statement = addSemicolon(statement)
+        return pyodbapi.Select(statement)
+
+    def open(fileName):
+        """
+        Opens an ODB API file. Returns an iterator over rows in the file.
+        This is a legacy API. It is recommended to use the new, PEP-0249 conformant API, see
+        function connect (module odbql.py)
+        """
+        if not isinstance(fileName, str):
+            raise TypeError('fileName must be a string object')
+        return pyodbapi.Reader(fileName)
+
+except ImportError:
+    # In case we cannot import SWIG generated wrappers, use new ctypes based implementation.
+    # Note, the functions sql and open are the old, legacy API. It is recommended to
+    # use the new PEP-0249 conformant API, see function connect, imported here from module odbql. 
+    # See source of odbql.py for dccumentation and examples.
+    sql = new_sql
+    open = new_open
+'''
+sql = new_sql
+open = new_open
diff --git a/odb_api/src/python/odb/odbql.py b/odb_api/src/python/odb/odbql.py
new file mode 100644
index 0000000..c1c8b6c
--- /dev/null
+++ b/odb_api/src/python/odb/odbql.py
@@ -0,0 +1,422 @@
+"""
+Python Database API (PEP 249) implementation for ODB API
+
+ at author Piotr Kuchta, ECMWF, August 2016
+
+This module is a Python wrapper for ODB API, ECMWF library for encoding,
+decoding and processing of observational data.
+
+ODB API includes a streaming SQL engine and a MARS language syntax
+based embedded scripting language, ECML. ECML verbs can be called using
+this module as stored procedures via Cursor.callproc method.
+
+
+Examples:
+
+    # Select data from a file
+
+    >>> import odbql
+    >>> con = odbql.connect('')
+    >>> c = con.cursor()
+    >>> c.execute('''CREATE TABLE conv on 'conv.odb';''')
+    >>> c.execute('select * from conv;')
+    >>> c.fetchone()
+        [262, '    0001', 1, 1025, 20160902, 120000, 16001, 3, 13, 11, 4287629, 0, 3, 17, 1, 14, None, '   89324', 20160902, 140000, 1, 0, 0, -80.0, -119.4000015258789, 1.0, 1534.8011474609375, 1530.0, None, 0, 1, 78120.0, 110, 2, 15004.1748046875, None, 1, 4096, 1, 3145728, 0, -200.0, -200.0, 106.85060119628906, 40.43746566772461, 0.44642725586891174, 0.0, 0.0, 53.77234649658203, 53.77234649658203, 19.578968048095703, 28.869945526123047, 80.12548828125]
+    >>> print [d[0] for d in c.description]
+        ['type', 'expver', 'class', 'stream', 'andate', 'antime', 'reportype', 'mxup_traj at desc', 'numtsl at desc', 'timeslot at timeslot_index', 'seqno at hdr', 'bufrtype at hdr', 'subtype at hdr', 'groupid at hdr', 'obstype at hdr', 'codetype at hdr', 'sensor at hdr', 'statid at hdr', 'date at hdr', 'time at hdr', 'report_status at hdr', 'report_event1 at hdr', 'report_rdbflag at hdr', 'lat at hdr', 'lon at hdr', 'lsm at modsurf', 'orography at modsurf', 'stalt at hdr', 'sonde_type at conv', 'station_type at conv', 'entryno at body', 'obsvalue at body', 'va [...]
+
+
+
+
+"""
+
+import os
+from ctypes import *
+import types
+from sys import platform
+
+apilevel = '2.0'
+threadsafety = 1 # https://www.python.org/dev/peps/pep-0249/#threadsafety
+paramstyle = 'qmark' # https://www.python.org/dev/peps/pep-0249/#paramstyle
+
+def connect(file_name):
+    """
+    Returns a Connection object.
+
+    Parameter: file_name  file to be open or empty string
+
+    Examples:
+
+        >>> conn1 = connect("conv.odb")
+
+        >>> conn2 = connect("mars://RETRIEVE,DATABASE=marsod,CLASS=OD,TYPE=MFB,STREAM=OPER,EXPVER=0001,DATE=20160830,TIME=1200,REPORTYPE=16001")
+
+    See also:
+
+        https://www.python.org/dev/peps/pep-0249/#connect
+
+    """
+    return Connection(file_name)
+
+def lib_extension():
+    if 'linux' in platform: return '.so'
+    if 'darwin' in platform: return '.dylib'
+    if 'win' in platform: return '.DLL'
+    raise Exception("Don't know lib extension for platform " + platform)
+
+def __find_lib(paths, lib='libOdb', extension = lib_extension()):
+    file_name = lib + extension
+    for p in paths:
+        path = p.split(os.sep)[:-1]
+        for i in range(len(path)):
+            pth = os.sep.join(path + ['..'] * i + ['lib', file_name])
+            try:
+                r = CDLL(pth)
+                #print '__find_libOdb: FOUND', pth
+                return r
+            except OSError:
+                #print '__find_libOdb: not found: ', pth
+                pass
+    raise Exception("Can't find " + file_name)
+
+libodb = __find_lib([__file__, '/tmp/build/bundle/debug/bin'])
+
+# odbql prototypes
+odbql_open = libodb.odbql_open
+odbql_prepare_v2 = libodb.odbql_prepare_v2
+odbql_step = libodb.odbql_step
+odbql_column_name = libodb.odbql_column_name
+odbql_column_name.restype = c_char_p
+odbql_column_count = libodb.odbql_column_count
+odbql_finalize = libodb.odbql_finalize
+odbql_close = libodb.odbql_close
+odbql_bind_null = libodb.odbql_bind_null
+odbql_bind_int = libodb.odbql_bind_int
+odbql_bind_double = libodb.odbql_bind_double
+odbql_bind_text = libodb.odbql_bind_text
+odbql_bind_int = libodb.odbql_bind_int
+odbql_column_value = libodb.odbql_column_value
+odbql_column_text = libodb.odbql_column_text
+odbql_column_text.restype = c_char_p  # TODO: should be unsigned: const unsigned char *odbql_column_text(odbql_stmt* stmt, int iCol)
+odbql_column_type = libodb.odbql_column_type
+odbql_value_double = libodb.odbql_value_double
+odbql_value_double.restype = c_double
+odbql_value_int = libodb.odbql_value_int
+odbql_errmsg = libodb.odbql_errmsg
+odbql_errmsg.restype = c_char_p
+
+# odbql constants
+ODBQL_OK               = 0
+ODBQL_ROW              = 100
+ODBQL_DONE             = 101
+ODBQL_METADATA_CHANGED = 102
+
+ODBQL_STATIC           = c_voidp(0)
+
+ODBQL_INTEGER          = 1
+ODBQL_FLOAT            = 2
+ODBQL_TEXT             = 3
+ODBQL_BLOB             = 4
+ODBQL_NULL             = 5
+ODBQL_BITFIELD         = 6
+
+def type_name(i):
+    return [None, 'INTEGER', 'REAL', 'TEXT', 'BLOB', 'NULL', 'BITFIELD'][i]
+
+class Connection:
+    def __init__(self, file_name):
+        self.file_name = file_name
+        self.db = c_voidp()
+        rc = odbql_open(self.file_name, byref(self.db))
+        # TODO
+        #self.assertEqual(rc, ODBQL_OK)
+
+    def close(self): pass
+    def commit(self): pass
+    # Not supported:
+    #def rollback(self): pass
+
+    def cursor(self):
+        return Cursor(self.file_name, self)
+
+
+class fetchall_generator(object):
+    def __init__(self, cursor):
+        self.cursor = cursor
+    def __iter__(self): return self
+    def __next__(self): return self.next()
+    def next(self):
+        v = self.cursor.fetchone()
+        if not v:
+            raise StopIteration()
+        return v
+
+class Cursor:
+
+    def __init__(self, file_name, connection):
+        self.file_name = file_name
+        self.stmt = c_voidp(0)
+        self.number_of_columns = None
+        ## https://www.python.org/dev/peps/pep-0249/#id28
+        self.connection = connection
+
+        ## https://www.python.org/dev/peps/pep-0249/#description
+        #
+        #  This read-only attribute is a sequence of 7-item sequences.
+        #
+        # Each of these sequences contains information describing one result column:
+        #   name, type_code, display_size, internal_size, precision, scale, null_ok
+        self.description = []
+
+    def __column_info(self, name, typ):
+        return (name,
+                typ,   # type_code ## TODO
+                None,  # display_size
+                None,  # internal_size
+                None,  # precision
+                None,  # scale
+                True   # null_ok
+                )
+
+    def close(self):
+        if self.stmt is not None:
+            rc = odbql_finalize(self.stmt)
+            assert rc == ODBQL_OK
+            self.stmt = None
+
+    def execute(self, operation, parameters = None):
+        self.close()
+        self.stmt, tail, db = c_voidp(0), c_char_p(0), self.connection.db
+
+        operation = self.__add_semicolon_if_needed(operation)
+        rc = odbql_prepare_v2(db, operation, -1, byref(self.stmt), byref(tail))
+        if rc <> ODBQL_OK:
+            err_msg = odbql_errmsg(db).strip()
+            #print 'execute: odbql_prepare_v2 failed with error message: "%s"' % err_msg
+            if err_msg == "syntax error":
+                raise SyntaxError()
+
+            if err_msg.startswith('Cannot open ') and err_msg.endswith('(No such file or directory)'):
+                #'Cannot open non_existing.odb  (No such file or directory)'
+                raise IOError("No such file or directory: '" + err_msg.split()[2] + "'")
+
+            raise Exception('execute: prepare failed')
+
+        self.__populate_meta_data()
+
+    def __populate_meta_data(self):
+        self.number_of_columns = odbql_column_count(self.stmt)
+        self.names = [odbql_column_name(self.stmt, i) for i in range(self.number_of_columns)]
+        self.types = [odbql_column_type(self.stmt, i) for i in range(self.number_of_columns)]
+        self.description = map (self.__column_info, self.names, self.types)
+
+    def fetchall(self):
+        return fetchall_generator(self)
+
+    def __iter__(self): return self
+
+    def next(self):
+        r = self.fetchone()
+        if r:
+            return r
+        raise StopIteration()
+
+    def fetchone(self):
+        if not self.stmt:
+            raise Exception('fetchone: you must call execute first')
+
+        rc = odbql_step(self.stmt)
+
+        if rc == ODBQL_METADATA_CHANGED or not self.description:
+            self.__populate_meta_data()
+
+        if not rc in (ODBQL_ROW, ODBQL_METADATA_CHANGED):
+            return None
+
+        r = [self.value(column) for column in range(self.number_of_columns)]
+        return r
+
+    def value(self, column):
+        v = odbql_column_value(self.stmt, column)
+        if not v: return None
+        else:
+            t = self.types[column]
+            if t == ODBQL_FLOAT: return odbql_value_double(v)
+            if t == ODBQL_INTEGER: return odbql_value_int(v)
+            if t == ODBQL_TEXT: return odbql_column_text(self.stmt, column)
+            if t == ODBQL_NULL: return None
+            if t == ODBQL_BITFIELD: return odbql_value_int(v)
+            #ODBQL_BLOB     = 4
+            return odbql_column_text(self.stmt, column)
+
+    def executemany(self, operation, parameters):
+        """
+        """
+
+        self.close()
+        self.stmt, tail, db = c_voidp(0), c_char_p(0), self.connection.db
+
+        operation = self.__add_semicolon_if_needed(operation)
+        rc = odbql_prepare_v2(db, operation, -1, byref(self.stmt), byref(tail))
+        #self.assertEqual(rc, ODBQL_OK)
+
+        for ps in parameters:
+            self.__bind(ps)
+            rc = odbql_step(self.stmt)
+            #self.assertEqual(rc, ODBQL_ROW)
+
+        #rc = odbql_finalize(self.stmt)
+        #self.assertEqual(rc, ODBQL_OK)
+
+    def callproc(self, procname, *parameters, **keyword_parameters):
+        """
+        Execute ECML verb
+        """
+        db, self.stmt, tail = c_voidp(), c_voidp(), c_char_p()
+        rc = odbql_open(self.file_name, byref(db))
+
+        operation = '{ ' + self.__marsify(procname, keyword_parameters) + ' }; '
+
+        rc = odbql_prepare_v2(db, operation, -1, byref(self.stmt), byref(tail))
+        rc = odbql_step(self.stmt)
+        #rc = odbql_finalize(self.stmt)
+        return ODBQL_OK
+
+
+    def __marsify(self, procname, keyword_parameters):
+
+        def marslist(l):
+            if type(l) in (types.GeneratorType, types.ListType, types.TupleType):
+                return '/'.join([str(x) for x in l])
+            else:
+                return str(l)
+
+        r = procname + "".join ( [ ',' + k + '=' + marslist(v) for k,v in keyword_parameters.iteritems()] )
+        return r
+
+
+    def __bind(self, parameters):
+        for i in range(len(parameters)):
+
+            p = parameters[i]
+
+            if p is None:
+                rc = odbql_bind_null(self.stmt, i)
+                #self.assertEqual(rc, ODBQL_OK)
+
+            if type(p) == str:
+                rc = odbql_bind_text(self.stmt, i, p, len(p), ODBQL_STATIC)
+                #self.assertEqual(rc, ODBQL_OK)
+            elif type(p) == int:
+                rc = odbql_bind_int(self.stmt, i, p)
+                #self.assertEqual(rc, ODBQL_OK)
+            elif type(p) == float:
+                rc = odbql_bind_double(self.stmt, i, c_double(p))
+                #self.assertEqual(rc, ODBQL_OK)
+            else:
+                raise "Don't know how to bind parameter " + str(p) + ' of type ' + str(type(p))
+
+    def __add_semicolon_if_needed(self, s):
+        if s.strip().endswith(';'):
+            return s
+        else:
+            return s + ';'
+
+
+class __new_sql_generator:
+    def __init__(self, cursor): self.cursor = cursor
+    def __iter__(self): return self
+    def __next__(self): return self.next()
+    def next(self):
+        v = self.cursor.fetchone()
+        if not v:
+            raise StopIteration()
+        return v
+
+## Support for legacy functions open and sql
+
+class new_sql_row(object):
+
+    def __init__(self, cursor):
+        self.cursor = cursor
+        self.name_to_index = None
+        self.names = None
+
+    def __getitem__(self, *indices):
+        if len(indices) == 1:
+            if type(indices[0]) == tuple:
+                return tuple(self.__getitem__(i) for i in indices[0])
+            else:
+                return self.__get_one_item__(indices[0])
+
+        return [self.__get_one_item__(i) for i in indices]
+
+    def columns(self):
+        class column_info:
+            def __init__(self, name, typ):
+                self.__name = name
+                self.__typ = typ
+
+            def name(self): return self.__name
+            def type(self): return self.__typ
+
+        return [column_info(c[0],c[1]) for c in self.cursor.description]
+
+
+    def __get_one_item__(self, index):
+        if type(index) == int: return self.cursor.value(index)
+        if type(index) == str: return self.__value_by_name(index)
+        if type(index) == tuple: return tuple(self.__get_one_item__(i) for i in index)
+        if index == slice(None,None,None):
+            return [self.cursor.value(i) for i in range(len(self.cursor.description))]
+        if type(index) == slice:
+            return [self.__get_one_item__(i) for i in [t[0] for t in enumerate(self.cursor.description)][index.start : index.stop : index.step]]
+
+        raise TypeError('__get_one_item__: index == ' + str(index))
+
+    def __value_by_name(self, index):
+        if self.name_to_index is None:
+            self.name_to_index = dict([(self.cursor.description[i][0], i) for i in range(len(self.cursor.description))])
+            self.names = [self.cursor.description[i][0] for i in range(len(self.cursor.description))]
+        try:
+            return self.cursor.value(self.name_to_index[index])
+        except KeyError:
+            simillar_columns = [n for n in self.names if n.startswith(index)]
+
+            if len(simillar_columns) == 1 and simillar_columns[0].startswith(index + '@'):
+                self.name_to_index[index] = self.name_to_index[simillar_columns[0]]
+                return self.cursor.value(self.name_to_index[index])
+
+            msg = simillar_columns and \
+                  'could be: ' + ','.join(simillar_columns) \
+                  or 'should be one of: ' + ','.join(self.names)
+
+            raise KeyError(str(index) + ', ' + msg)
+
+
+class new_sql_generator(object):
+    def __init__(self, cursor):
+        self.cursor = cursor
+    def __iter__(self): return self
+    def __next__(self): return self.next()
+    def next(self):
+        if not self.cursor.stmt:
+            raise Exception('execute must be called first')
+
+        rc = odbql_step(self.cursor.stmt)
+        if not rc in (ODBQL_ROW, ODBQL_METADATA_CHANGED):
+            raise StopIteration()
+        return new_sql_row(self.cursor)
+
+def new_sql(s):
+    conn = connect("")
+    c = conn.cursor()
+    c.execute(s)
+    return new_sql_generator(c)
+
+def new_open(fn):
+    s = '''select all * from "{}";'''.format(fn)
+    conn = connect("")
+    c = conn.cursor()
+    c.execute(s)
+    return new_sql_generator(c)
diff --git a/odb_api/src/python/odb/pyodbapi.i b/odb_api/src/python/odb/pyodbapi.i
new file mode 100644
index 0000000..614f520
--- /dev/null
+++ b/odb_api/src/python/odb/pyodbapi.i
@@ -0,0 +1,119 @@
+%module pyodbapi
+%warnfilter(503) operator<<;
+%warnfilter(389) operator[];
+%warnfilter(383) operator++;
+%warnfilter(362) operator=;
+%warnfilter(508) operator->;
+%rename(_print) print;
+
+#include <cstddef>
+%include "std_string.i"
+%include "std_vector.i"
+%{
+#define SWIG_FILE_WITH_INIT
+#include "odb_api/pyodbapi.h"
+
+%}
+
+
+%exception {
+	using namespace ::odb;
+	using namespace ::odb::sql;
+    try {
+        $action
+    } catch (const ODBStopIteration& e) {
+		PyErr_SetString(PyExc_StopIteration, "no more data");
+		return NULL;
+    } catch (const ODBIndexError& e) {
+		PyErr_SetString(PyExc_IndexError, "column index out of range");
+		return NULL;
+	} catch (const eckit::FileError& e) {
+		PyErr_SetString(PyExc_IOError, e.what());
+		return NULL;
+	} catch (const ::odb::sql::SyntaxError& e) {
+		PyErr_SetString(PyExc_SyntaxError, e.what());
+		return NULL;
+	}  catch (const eckit::Exception& e) {
+		PyErr_SetString(PyExc_RuntimeError, e.what());
+		return NULL;
+	}
+	
+}
+
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <math.h>
+#include <stdint.h>
+
+using namespace std;
+
+%include "odb_api/ODBAPIVersion.h"
+%include "odb_api/ODBAPISettings.h"
+
+#include "eckit/filesystem/PathName.h"
+#include "odb_api/MemoryBlock.h"
+#include "eckit/io/DataHandle.h"
+#include "eckit/filesystem/FileHandle.h"
+#include "eckit/utils/Timer.h"
+#include "eckit/config/Resource.h"
+
+using namespace eckit;
+
+%include "odb_api/Select.h"
+%include "odb_api/SQLType.h"
+
+%include "odb_api/ColumnType.h"
+%include "odb_api/Types.h"
+%include "odb_api/SQLBitfield.h"
+#include "odb_api/StringTool.h"
+#include "odb_api/DataStream.h"
+%include "odb_api/HashTable.h"
+%include "odb_api/Codec.h"
+%include "odb_api/Column.h"
+#include "odb_api/HashTable.h"
+#include "odb_api/SQLIteratorSession.h"
+
+using namespace odb;
+
+%template(MetaDataBase) std::vector<Column*>;
+%include "odb_api/MetaData.h"
+
+#include "odb_api/Header.h"
+
+%include "exception.i"
+
+%include "odb_api/IteratorProxy.h"
+%template(ReaderIteratorProxy) odb::IteratorProxy<odb::ReaderIterator,odb::Reader,const double>;
+%template(ReaderIteratorRow) odb::Row_<odb::ReaderIterator,odb::Reader,const double,odb::IteratorProxy<odb::ReaderIterator,odb::Reader,const double> >;
+
+%template(SelectIteratorProxy) odb::IteratorProxy<odb::SelectIterator,odb::Select,const double>;
+%template(SelectIteratorRow) odb::Row_<odb::SelectIterator,odb::Select,const double,odb::IteratorProxy<odb::SelectIterator,odb::Select,const double> >;
+
+#include "odb_api/TemplateParameters.h"
+%include "odb_api/Reader.h"
+%include "odb_api/TextReader.h"
+%include "odb_api/Select.h"
+%include "odb_api/Writer.h"
+%include "odb_api/WriterBufferingIterator.h"
+%include "odb_api/WriterDispatchingIterator.h"
+%include "odb_api/DispatchingWriter.h"
+%include "odb_api/DispatchingWriter.h"
+%include "odb_api/ReaderIterator.h"
+%include "odb_api/TextReaderIterator.h"
+%include "odb_api/SelectIterator.h"
+#include "odb_api/FixedSizeWriterIterator.h"
+
+#include "odb_api/SQLType.h"
+#include "odb_api/SQLInteractiveSession.h"
+#include "odb_api/SQLIteratorSession.h"
+#include "odb_api/SQLTable.h"
+#include "odb_api/SQLSelect.h"
+#include "odb_api/SQLParser.h"
+#include "odb_api/SQLExpression.h"
+
+#include "odb_api/odbcapi.h"
+%init %{
+	void python_api_start();
+	python_api_start();
+%}
diff --git a/odb_api/src/python/odb269.py b/odb_api/src/python/odb269.py
new file mode 100644
index 0000000..b7d19bc
--- /dev/null
+++ b/odb_api/src/python/odb269.py
@@ -0,0 +1,23 @@
+import odb
+
+conn = odb.connect('')
+c = conn.cursor()
+c.execute('''
+    CREATE TABLE foo AS
+    ( x INTEGER, y DOUBLE, v STRING)
+    ON 'test_odb269.odb';
+    ''')
+c.executemany('INSERT INTO foo (x,y,v,status) VALUES (?,?,?);',
+               [[1,0.1, '  one   '],
+                [2,0.2, '  two   '],
+                [3,0.3, '  three '],
+                [4,0.4, '  four  ']])
+
+conn.commit()
+
+for i in range(100000):
+    print str(i) + '.'
+    c.execute('SELECT count(*) from foo')
+    for row in c.fetchall():
+        print row[0]
+    c.close()
diff --git a/odb_api/src/python/odbless.py b/odb_api/src/python/odbless.py
new file mode 100644
index 0000000..04e5271
--- /dev/null
+++ b/odb_api/src/python/odbless.py
@@ -0,0 +1,41 @@
+import sys
+import odb
+
+def quote(s): return '"%s"' % s
+
+statsFunctions = "count,min,max,avg,rms".split(',')
+
+def genStatSQL(columnNames, fs = statsFunctions):
+	def stats(column):
+		return ",".join([f + '(%(column)s)' for f in fs]) % locals()
+	select_list = ",".join([stats(c) for c in columnNames])
+	return 'select %s' % select_list
+
+def chunks(l, n):
+	for i in xrange(0, len(l), n):
+		yield l[i:i+n]
+
+def getColumns(dataFile):
+	f = odb.open(dataFile)
+	for r in f:
+		return [(c.name(), c.type()) for c in r.columns()]
+
+def printStats(dataFile, fs = statsFunctions):
+	columns = getColumns(dataFile)
+	maxColumn = max([len(c[0]) for c in columns])
+	columnFormat = '%-' + str(maxColumn) + 's'
+	valueFormat = '%20s'
+	sql = genStatSQL([c[0] for c in columns], fs) + ' from ' + quote(dataFile)
+	for r in odb.sql(sql):
+		print columnFormat % 'column', "".join([valueFormat % v for v in fs])
+		values = [str(v) for v in r[:]]
+		for (c, vs) in zip(columns, chunks(values, len(fs))):
+			#if c[1] in [3,4]: vs = ['NA' for v in vs]
+			print columnFormat % c[0], "".join([valueFormat % v for v in vs])
+
+if __name__ == '__main__':
+	if len(sys.argv) == 1:
+		print 'Usage:\n\t', sys.argv[0], ' <fileName>+'
+	else:
+		for fn in sys.argv[1:]:
+			printStats(fn)
diff --git a/odb_api/src/python/psql.py b/odb_api/src/python/psql.py
new file mode 100644
index 0000000..ccd8022
--- /dev/null
+++ b/odb_api/src/python/psql.py
@@ -0,0 +1,59 @@
+import os, subprocess, multiprocessing
+
+exe = "/tmp/p4/source/main/build/Debug/bin/odb"
+exe = "/marsdev/data/p4/odb_api_dev/development/build/Production/bin/odb"
+exe = "/vol/marsdev/data/p4/linux/x86_64/sles11/odb_api_dev/main/build/Production/bin/odb"
+
+def mergeBlocks(blocks, maxBlockSize):
+	currentOffset, currentLength, currentNumberOfRows, currentNumberOfColumns = blocks[0]
+	for offset, length, nrows, ncolumns in blocks[1:]:
+		if (currentLength + length) > maxBlockSize:
+			yield currentOffset, currentLength, currentNumberOfRows, currentNumberOfColumns
+			currentOffset, currentLength, currentNumberOfRows, currentNumberOfColumns = offset, length, nrows, ncolumns
+		else:
+			currentLength += length
+			currentNumberOfRows += nrows
+	yield currentOffset, currentLength, currentNumberOfRows, currentNumberOfColumns
+
+def getBlocks(fileName):
+	l = [exe, "header", "-offsets", fileName]
+	blocks = subprocess.check_output(l)
+	blocks = [map(int, l.split()) for l in blocks.split('\n') if l]
+	return blocks
+
+def divideFile(fileName, maxBlockSize = None, numberOfProcessors = None):
+	assert maxBlockSize or numberOfProcessors
+	blocks = getBlocks(fileName)
+	if maxBlockSize:
+		blocks = [b for b in mergeBlocks(blocks, maxBlockSize)]
+	return blocks
+	
+def outputFileName(offset, length, nrows):
+	return 'out_' + offset + '_' + length + '.odb'
+
+def filterPartOfFile(inputFile, fileChunk, select = '*', where = ''):
+	offset, length, nrows, ncolumns = map(str, fileChunk)
+	outputFile = outputFileName(offset, length, nrows)
+	sql = "select " + select + " into " + '"' + outputFile  + '"'
+	if where: sql += ' where ' + where
+	l = [exe, "sql", "-i", inputFile, "-offset", offset, "-length", length, sql]
+	print 'filterBlock: running ', l
+	subprocess.call(l) 
+	return outputFile
+
+def filterBlock(p): return filterPartOfFile(*p)
+
+def sql(processes = 2, inputFile = None, outputFile = None, select='*', where = ''):
+	assert inputFile and outputFile
+	pool = multiprocessing.Pool(processes = processes)
+	blocks = [(inputFile, t, select, where) for t in divideFile(inputFile, maxBlockSize = 1024*1024*200)]
+	outputFiles = pool.map(filterBlock, blocks)
+	os.system("cat " + " ".join(outputFiles) + " >" + outputFile)
+
+if __name__ == '__main__':
+	sql(processes = 5,
+		#inputFile = "/scratch/ma/mak/paul/allobs_110.odb", #"ofb_1656_20040602to20040603.odb", #"ofb_1657_20040602to20040603.odb",
+		inputFile = "/scratch/ma/mak/odb-16/all.odb",
+		select = "source,count(*) as counts",
+		outputFile = 'out.odb')
+	subprocess.call([exe, "sql", "-i", "out.odb", "select source,sum(counts)"])
diff --git a/odb_api/src/python/setup.py.in b/odb_api/src/python/setup.py.in
new file mode 100644
index 0000000..9ff0330
--- /dev/null
+++ b/odb_api/src/python/setup.py.in
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+pyodbapi = Extension('odb._pyodbapi',
+                     sources=['odb/pyodbapi.i'],
+                     swig_opts=['-c++', '-I at CMAKE_CURRENT_SOURCE_DIR@/..'],
+                     include_dirs=['.', '@CMAKE_CURRENT_BINARY_DIR@/..',
+                                   '@CMAKE_CURRENT_SOURCE_DIR@/..',
+                                   '@_ECKIT_INCLUDES@'],
+                     library_dirs=['@CMAKE_BINARY_DIR@/lib'],
+                     libraries=['Odb'],
+                     extra_objects=[])
+
+setup(name='odb',
+      version='@ODB_API_VERSION_STR@',
+      author='ECMWF',
+      author_email='Software.Support at ecmwf.int',
+      description="""Python interface for odb_api""",
+      license='Apache License, Version 2.0',
+      url='https://software.ecmwf.int/wiki/display/ODB/ODB+API',
+      download_url='https://software.ecmwf.int/wiki/display/ODB/Releases',
+      ext_modules=[pyodbapi],
+      packages=['odb'])
diff --git a/odb_api/src/python/test_python_odb_api.py b/odb_api/src/python/test_python_odb_api.py
new file mode 100755
index 0000000..59d0f99
--- /dev/null
+++ b/odb_api/src/python/test_python_odb_api.py
@@ -0,0 +1,392 @@
+#!/usr/bin/env python 
+
+import sys
+sys.path.append('/tmp/build/bundle/debug/odb_api/src/python/odb')
+
+import unittest
+from odb import *
+
+class TestODB250_A(unittest.TestCase):
+    """
+    ODB-250 Bitfields were not read correctly
+    """
+    def setUp(self): 
+        self.N = 100000
+        self.db = c_voidp()
+        self.stmt = c_voidp()
+        self.tail = c_char_p()
+        rc = odbql_open("", byref(self.db))
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_prepare_v2(self.db, """ CREATE TYPE bf AS (f bit31); CREATE TABLE foo AS (bf1 bf) ON 'test_python_bitfields.odb'; """ , -1, byref(self.stmt), byref(self.tail))
+        self.assertEqual(rc, ODBQL_OK)
+
+    def tearDown(self):
+        rc = odbql_close(self.db)
+        self.assertEqual(rc, ODBQL_OK)
+
+    def test_insert_bitfields(self):
+        rc = odbql_prepare_v2(self.db, "INSERT INTO foo VALUES (?);", -1, byref(self.stmt), byref(self.tail))
+        self.assertEqual(rc, ODBQL_OK)
+
+        for i in range(self.N): 
+            rc = odbql_bind_int(self.stmt, 0, i)
+            self.assertEqual(rc, ODBQL_OK)
+
+            rc = odbql_step(self.stmt)
+            self.assertEqual(rc, ODBQL_ROW)
+
+        rc = odbql_finalize(self.stmt)
+        self.assertEqual(rc, ODBQL_OK)
+       
+    def test_select_bitfields(self):
+        rc = odbql_prepare_v2(self.db, "SELECT * FROM foo;", -1, byref(self.stmt), byref(self.tail))
+        for i in range(self.N): 
+            rc = odbql_step(self.stmt)
+            self.assertEqual(rc, ODBQL_ROW)
+
+            v = odbql_column_value(self.stmt, 0)
+            if v == 0:
+                self.assertEqual(i, 0)
+            else:
+                self.assertEqual(i, odbql_value_int(v))
+
+        rc = odbql_step(self.stmt)
+        self.assertEqual(rc, ODBQL_DONE)
+
+
+TEST_DDL = """
+            CREATE TYPE bf AS (f1 bit1, f2 bit2); 
+            
+            CREATE TABLE foo AS (
+                x INTEGER, 
+                y DOUBLE, 
+                v STRING, 
+                status bf
+            ) ON 'new_api_example_python.odb';
+"""
+
+TEST_INSERT = """INSERT INTO foo (x,y,v,status) VALUES (?,?,?,?);"""
+TEST_SELECT = """SELECT * FROM foo;"""
+
+class TestODBQL(unittest.TestCase):
+
+    def setUp(self): 
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_insert_data(self):
+        db, stmt, tail = c_voidp(), c_voidp(), c_char_p()
+
+        rc = odbql_open("", byref(db))
+        self.assertEqual(rc, ODBQL_OK)
+
+        rc = odbql_prepare_v2(db, TEST_DDL, -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_prepare_v2(db, TEST_INSERT, -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+        
+        for i in range(4): 
+            rc = odbql_bind_null(stmt, i)
+            self.assertEqual(rc, ODBQL_OK)
+
+        rc = odbql_step(stmt)
+        self.assertEqual(rc, ODBQL_ROW)
+
+        for i in range(4): 
+            rc = odbql_bind_int(stmt, 0, 1 * i)
+            self.assertEqual(rc, ODBQL_OK)
+            rc = odbql_bind_double(stmt, 1, c_double(0.1 * i))
+            self.assertEqual(rc, ODBQL_OK)
+            rc = odbql_bind_text(stmt, 2, "hello", len("hello"), ODBQL_STATIC)
+            self.assertEqual(rc, ODBQL_OK)
+            rc = odbql_bind_int(stmt, 3, 3 * i)
+            self.assertEqual(rc, ODBQL_OK)
+            rc = odbql_step(stmt)
+            self.assertEqual(rc, ODBQL_ROW)
+        
+        rc = odbql_finalize(stmt)
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_close(db)
+        self.assertEqual(rc, ODBQL_OK)
+
+
+    def test_select_data(self):
+        db, stmt, tail = c_voidp(), c_voidp(), c_char_p()
+
+        rc = odbql_open("new_api_example_python.odb", byref(db))
+        self.assertEqual(rc, ODBQL_OK)
+
+        rc = odbql_prepare_v2(db, "CREATE TABLE foo on 'new_api_example_python.odb';", -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+
+        rc = odbql_prepare_v2(db, "SELECT ALL * FROM foo;", -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+
+        number_of_rows = 0 
+        rc = None
+        while rc <> ODBQL_DONE:
+
+            if number_of_rows == 0 or rc == ODBQL_METADATA_CHANGED:
+                number_of_columns = odbql_column_count(stmt)
+                print 'number_of_columns=', number_of_columns
+                for i in range(0,number_of_columns):
+                    print i, odbql_column_name(stmt, i) + ':' + type_name(odbql_column_type(stmt, i))
+            else:
+                self.assertEqual(rc, ODBQL_ROW)
+
+                def value(column):
+                    v = odbql_column_value(stmt, column)
+                    if not v: return None
+                    else: 
+                        t = odbql_column_type(stmt, column)
+                        if t == ODBQL_FLOAT: return odbql_value_double(v)
+                        if t == ODBQL_INTEGER: return odbql_value_int(v)
+                        if t == ODBQL_NULL: return None
+                        #ODBQL_BLOB     = 4
+                        #ODBQL_NULL     = 5
+                        #ODBQL_TEXT     = 3
+                        return odbql_column_text(stmt, column)
+
+                print ','.join([str(value(column)) for column in range(number_of_columns)])
+            number_of_rows += 1
+            rc = odbql_step(stmt)
+
+
+        rc = odbql_finalize(stmt)
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_close(db)
+        self.assertEqual(rc, ODBQL_OK)
+
+
+    def test_bitfields(self): # ODB-97
+        print "TestODBQL.test_bitfields"
+        db, stmt, tail = c_voidp(), c_voidp(), c_char_p()
+        rc = odbql_open( "", byref(db))
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_prepare_v2(db, '''create table atovs on "ATOVS.trimmed.odb";''', -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_prepare_v2(db, '''select distinct qcinfo_1dvar from atovs order by 1 asc;''', -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+        
+        rc = None
+        values = []
+        while True:
+            rc = odbql_step(stmt)
+            if rc == ODBQL_DONE:
+                break
+            v = odbql_column_value(stmt, 0)
+            values.append(odbql_value_int(v))
+
+        rc = odbql_finalize(stmt)
+        self.assertEqual(rc, ODBQL_OK)
+        rc = odbql_close(db)
+        self.assertEqual(rc, ODBQL_OK)
+
+        expected = [4198786,4198806,4202498,4202518,4202626,4202646,4210690,4210710,4210818,4210838,4210946,4211074,4211094,4243458,4243478,4243586,4243606,4243714,4243842,4243862,4264086,4264322,4264342,4268034,4268054,4268162,4268182,4276226,4276246,4276354,4276374,4276482,4276610,4308994,4309014,4309122,4309142,4309250,4309270,4309378,4309398,8404994,8405014,8405122,8405142,8405378,8405398,8458626,8458646,8462338,8462358,8470530,8470550,8470658,8470678]
+        print 'values =', values
+        self.assertEqual(values, expected)
+
+    def test_stored_procedure(self):
+        db, stmt, tail = c_voidp(), c_voidp(), c_char_p()
+
+        rc = odbql_open("new_api_example_python.odb", byref(db))
+        self.assertEqual(rc, ODBQL_OK)
+
+        e = """ { compare, left = new_api_example_python.odb, right = new_api_example_python.odb } ; """
+        rc = odbql_prepare_v2(db, e, -1, byref(stmt), byref(tail))
+        self.assertEqual(rc, ODBQL_OK)
+
+        rc = odbql_step(stmt)
+        self.assertEqual(rc, ODBQL_DONE)
+
+        print 'test_stored_procedure: OK!'
+
+
+class TestPEP249(unittest.TestCase):
+
+    def setUp(self):
+        self.data = [[1,0.1, '  one   ', 1],
+                     [2,0.2, '  two   ', 2],
+                     [3,0.3, '  three ', 3],
+                     [4,0.4, '  four  ', 4],
+                    ]
+        self.conn = connect("")
+
+
+    def test_insert_data(self):
+        c = self.conn.cursor()
+        print self.data
+        #c.execute(TEST_DDL)
+        c.executemany(TEST_DDL + TEST_INSERT, self.data)
+        c.close()
+
+
+    def read_with_legacy_api(self, file_name = 'new_api_example_python.odb'):
+        return new_open(file_name)
+
+    def test_select_data_fetchone(self):
+
+        print 'calling legacy API:'
+        legacy = self.read_with_legacy_api()
+        print 'legacy:', legacy
+
+        c = self.conn.cursor()
+        c.execute(TEST_DDL)
+        c.execute(TEST_SELECT)
+        number_of_rows = 0
+        while True:
+            row = c.fetchone()
+            if row is None:
+                break
+
+            original = self.data [number_of_rows]
+            # Legacy API does not handle bitfields correctly
+            #self.assertEqual (original, legacy [number_of_rows])
+            self.assertEqual (original, row)
+
+            print row
+            number_of_rows += 1
+
+        self.assertEqual ( number_of_rows, 4 )
+
+
+    def test_select_data_fetchall(self):
+        """"""
+        c = self.conn.cursor()
+        c.execute(TEST_DDL)
+        c.execute(TEST_SELECT)
+        rows = [r for r in c.fetchall()]
+        self.assertEqual ( len(rows), 4 )
+        self.assertEqual ( rows, self.data )
+
+    def test_select_data_iterate(self):
+        """https://www.python.org/dev/peps/pep-0249/#iter"""
+        c = self.conn.cursor()
+        c.execute(TEST_DDL)
+        c.execute(TEST_SELECT)
+        self.assertEqual ( self.data,  [r for r in c] )
+
+    def test_callproc(self):
+        """https://www.python.org/dev/peps/pep-0249/#callproc"""
+        c = self.conn.cursor()
+        rc = c.callproc('compare', left = 'new_api_example_python.odb', right = 'new_api_example_python.odb')
+        # TODO: check rc
+
+    def test_bitfields(self): 
+        """
+        ODB-97 (old, Swig based API), ODB-250 (new API): Bitfield values read incorrectly.
+        """
+        print "TestPEP249.test_bitfields"
+        # !odb sql select qcflags_info_1dvar    -i ATOVS.trimmed.odb | sort | uniq
+        expected = [0,1,4,5,8,9,12,13,130,131,134,135,138,139,142,143,642]
+
+        conn = odb.connect("ATOVS.trimmed.odb")
+        c = conn.cursor()
+        #c.execute('''create table atovs on "ATOVS.trimmed.odb";''')
+        c.execute('''select distinct qcinfo_1dvar from "ATOVS.trimmed.odb" order by 1''')
+        actual = [r[0] for r in c.fetchall()]
+        # TODO: ODB-250
+        #self.assertEqual(actual, expected) 
+
+    def test_sorting_string_columns(self): # ODB-94 
+        conn = odb.connect("") 
+        c = conn.cursor()
+        c.execute("""CREATE TABLE foo AS (statid string,x integer) ON 'test_sorting_string_columns.odb';""")
+        data = [[w] for w in """12345678 abcdefgh dfgsdfgs DFADSFAD sdffffff aaaaaaaa""".split()]
+        print data
+        c.executemany('INSERT INTO foo (statid,x) VALUES (?,?);', data)
+        conn.commit()
+
+        ## TODO: the next three lines should not be needed, I think 
+        #conn = odb.connect("") 
+        #c = conn.cursor()
+        #c.execute("""CREATE TABLE foo ON 'test_sorting_string_columns.odb';""")
+
+        c.execute('''select statid from foo order by 1 asc;''')
+        sql_sorted = [r[0] for r in c.fetchall()]
+        python_sorted = sorted(sql_sorted)
+        self.assertEqual ( sql_sorted, python_sorted )
+
+    def test_sql_variables(self): # ODB-127
+        conn = odb.connect("") 
+        c = conn.cursor()
+        c.execute("""CREATE TABLE test_sql_variables AS (varno integer, obsvalue real) ON 'test_sql_variables.odb';""")
+        def cast(s):
+            vals = s.split(',')
+            return int(vals[0]), float(vals[1])
+        data = [cast(w) for w in """1,0.1 2,0.2 3,0.3 4,0.4 1,0.11""".split()]
+        print data
+        c.executemany('INSERT INTO test_sql_variables (varno,obsvalue) VALUES (?,?);', data)
+        conn.commit()
+
+        c.execute('''select varno,obsvalue from test_sql_variables where varno = $z;''')
+        result = [r[0] for r in c.fetchall()]
+        self.assertEqual ( result, [1,1] )
+
+    def test_closing_files_after_reading(self): # ODB-331
+        conn = odb.connect("")
+        c = conn.cursor()
+        c.execute("""CREATE TABLE foo AS (statid string) ON 'ODB-331.reading.odb';""")
+        c.executemany('INSERT INTO foo (statid) VALUES (?);', [['foo-bar!']])
+        c.close()
+        conn.commit()
+        for i in range(1500):
+            conn = odb.connect("")
+            c = conn.cursor()
+            c.execute("""select * from 'ODB-331.reading.odb';""")
+            for r in c.fetchall(): pass
+            c.close()
+            conn.commit()
+
+    def test_closing_files_after_writing(self): # ODB-331
+        for i in range(1500):
+            conn = odb.connect("")
+            c = conn.cursor()
+            c.execute("""CREATE TABLE foo AS (statid string) ON 'ODB-331.{}.odb';""".format(i))
+            c.executemany('INSERT INTO foo (statid) VALUES (?);', [['foo-bar!']])
+            c.close()
+            conn.commit()
+
+    def test_embedded_ecml_in_the_from_clause(self): # ODB-83
+        for i in range(3):
+            conn = odb.connect("")
+            c = conn.cursor()
+            c.execute("""CREATE TABLE foo AS (statid string) ON 'ODB-83.{}.odb';""".format(i))
+            c.executemany('INSERT INTO foo (statid) VALUES (?);', [['foo-bar!']])
+            c.close()
+            conn.commit()
+        
+        c.execute('''SELECT count(*) FROM { sequence,values = "ODB-83.0.odb" / "ODB-83.1.odb" / "ODB-83.2.odb" }''')
+        result = [r[0] for r in c.fetchall()]
+        self.assertEqual ( result, [3] )
+
+"""
+    def test_select_data_from_mars(self):
+        conn = connect(ddl = '''
+            CREATE TABLE foo 
+            ON "mars://RETRIEVE,
+                        DATABASE  = marsod,
+                        CLASS     = OD,
+                        TYPE      = MFB,
+                        STREAM    = OPER,
+                        EXPVER    = 0001,
+                        DATE      = 20160830,
+                        TIME      = 1200,
+                        REPORTYPE = 16001 "''')
+        c = conn.cursor()
+        c.execute('SELECT * from foo;')
+        data = c.fetchall()
+        self.assertEqual(len(data), 4438) 
+
+        c.execute('SELECT * from foo')
+        data = c.fetchall()
+        self.assertEqual(len(data), 4438) 
+"""
+
+if __name__ == '__main__':
+    unittest.main()
+
+
+
diff --git a/odb_api/src/python/to_sqlite.py b/odb_api/src/python/to_sqlite.py
new file mode 100644
index 0000000..34f4038
--- /dev/null
+++ b/odb_api/src/python/to_sqlite.py
@@ -0,0 +1,60 @@
+import sys, os
+import sqlite3
+
+sys.path.insert(0, '/usr/local/apps/odb_api/0.9.31/lib/python2.7/site-packages/')
+import odb
+
+def typeName(typ):
+    return {
+        0 : 'IGNORE',
+        1 : 'INTEGER',
+        2 : 'REAL',
+        3 : 'STRING',
+        4 : 'BITFIELD',
+        5 : 'DOUBLE',
+        }[typ]
+
+def stripTableName(columnName):
+    return columnName.split('@')[0]
+
+def metadata(odbFileName):
+    f = odb.open(odbFileName)
+    for row in f:
+        columns = [(stripTableName(c.name()), typeName(c.type())) for c in row.columns()]
+        return columns
+
+
+def createTable(tableName, columns):
+    colDefs = [' '.join(c) for c in columns]
+    return 'CREATE TABLE ' + tableName + ' (' + ', '.join(colDefs) + ')'
+
+def convert(inputFileName, outputFileName):
+    #os.remove(outputFileName)
+    conn = sqlite3.connect(outputFileName)
+    c = conn.cursor()
+    tableName = inputFileName.split('.')[0]
+    columns = metadata(inputFileName)
+    ct = createTable(tableName, columns)
+    try:
+        c.execute(ct)
+        print ct
+    except:
+        dt = 'DELETE FROM ' + tableName
+        c.execute(dt)
+        print dt
+
+    def rows():
+        for row in odb.open(inputFileName):
+            yield row[:]
+
+    print 'Copying data from', inputFileName, 'into', outputFileName
+
+    c.executemany('INSERT INTO ' + tableName + ' VALUES (' + ','.join('?' for c in columns) + ')', rows())
+    conn.commit()
+    conn.close()
+
+if __name__ == '__main__':
+    inputFileName = sys.argv[1]
+    outputFileName = sys.argv[2]
+    convert(inputFileName, outputFileName)
+
diff --git a/odb_api/tests/CMakeLists.txt b/odb_api/tests/CMakeLists.txt
new file mode 100644
index 0000000..327c8d4
--- /dev/null
+++ b/odb_api/tests/CMakeLists.txt
@@ -0,0 +1,458 @@
+### test data files
+
+list( APPEND test_migrator_data_files
+2000010106.old.ECMA.tar.gz )
+
+ecbuild_get_test_multidata( TARGET get_migrator_test_data
+                            #DIRNAME odb_api/tests
+                            NAMES ${test_migrator_data_files}
+                            NOCHECK )
+
+list( APPEND test_data_files
+2000010106.odb.gz
+2000010106.odb
+2000010106.1.0.odb
+2000010106.2.0.odb
+2000010106.3.0.odb
+2000010106.4.0.odb
+2000010106.5.0.odb
+2000010106.6.0.odb
+2000010106.7.0.odb
+2000010106.7.1.odb
+2000010106.7.2.odb
+2000010106.7.3.odb
+mondb.1.12.odb
+TestIntegerValues.odb
+split_crash_on_andate_and_antime.odb
+TestMetaDataReader.odb
+a1to10twice.odb
+TestAggregateFunctions2.odb
+dribu.odb
+TestAggregateFunctions3.odb 
+
+# MetOffice schema for testing ODB-127
+ECMA.ddl
+ECMATMP.ddl
+cma.hh
+bitfields.hh
+group_id.hh
+mdi.hh
+ops_obsgroups.hh
+ops_subtypes.hh
+report_types.hh
+sensor.hh
+varno.hh
+vertco_type.hh
+)
+
+ecbuild_get_test_multidata( TARGET get_odb_api_test_data
+                            #DIRNAME odb_api/tests
+                            NAMES ${test_data_files}
+                            NOCHECK )
+
+### list tests
+
+list( APPEND odb_migrator_tests
+#Test_AAAImportODB
+#Test_AAAImportODBDispatching
+)
+
+list( APPEND odb_api_tests
+Test_AggregateFunctions
+Test_AggregateFunctions2
+Test_AggregateFunctions3
+Test_AtTableInTheOutput
+Test_Bitfields
+Test_CREATE_TABLE_and_SELECT_INTO
+Test_CatFiles
+Test_ChildTable_DataLoaderReturnsExpectedChildTableRows
+Test_ChildTable_DataLoaderReturnsExpectedNumberOfChildTableColumns
+Test_ChildTable_DataLoaderReturnsExpectedNumberOfChildTableRows
+Test_Codec
+Test_CodecOptimization
+Test_CommandLineParsing
+Test_ConstCodec
+Test_ConstIntegerCodec
+Test_DataRowCanSetDoubles
+Test_DataRowCanSetIntegers
+Test_DataRowCanSetStrings
+Test_DataRowInitializedFromColumnsHasExpectedFlags
+Test_DataRowInitializedFromColumnsHasExpectedSize
+Test_DataRowInitializedFromColumnsHasExpectedValues
+Test_Decoding
+Test_DispatchingWriter
+Test_Distinct
+Test_EmptyPageWithFillMark_PushBackRowsToEmptyPageWithFillMark
+Test_EmptyPageWithFillMark_ResizeEmptyPageWithFillMark
+Test_EmptyPage_CanIncreasePageSizeAndInitializeValues
+Test_EmptyPage_CannotIncreasePageSizeBeyondItsCapacity
+Test_EmptyPage_DecreadingSizeOfEmptyPageToZero
+Test_EmptyPage_EmptyPageCanBeCleared
+Test_EmptyPage_EmptyPageHasZeroSize
+Test_EmptyPage_EmptyPageIsEmpty
+Test_EmptyPage_EmptyPageIsNotFull
+Test_EmptyPage_HasTheCorrectCapacity
+Test_EmptyPage_IncreadingSizeOfEmptyPageToHalfTheCapacity
+Test_EmptyPage_IteratorReturnsExpectedNumberOfRows
+Test_EmptyPage_PushingBackRowsToAnEmptyPage
+Test_EmptyTable_CanBeCleared
+Test_EmptyTable_CapacityCanBeIncreased
+Test_EmptyTable_HasExpectedCapacity
+Test_EmptyTable_HasZeroSize
+Test_EmptyTable_IsEmpty
+Test_EmptyTable_SizeCanBeIncreased
+Test_FastODA2Request
+Test_FastODA2Request2
+Test_FastODA2Request3
+Test_FilledDataSet_DataLoaderReturnsExpectedNumberOfTables
+Test_FilledDataSet_DataLoaderReturnsExpectedTables
+Test_FilledDataSet_DataSaverOutputContainsExpectedRows
+Test_FilledLink_DataLinkHasExpectedSize
+Test_FilledLink_DataLinkIteratorReturnsExpectedNumberOfChildRows
+Test_FilledLink_DataLinkIteratorReturnsExpectedNumberOfParentRows
+Test_FilledLink_DataLinkIteratorReturnsExpectedRows
+Test_FilledLink_DataLinkReturnsExpectedChildTable
+Test_FilledLink_DataLinkReturnsExpectedParentTable
+Test_FilledLink_InsertRowAtTheBeginningOfTheFirstRange
+Test_FilledLink_InsertRowAtTheEndOfTheLastRange
+Test_FilledTable_AdvanceIteratorBackward
+Test_FilledTable_AdvanceIteratorByHalf
+Test_FilledTable_AdvanceIteratorByZero
+Test_FilledTable_AdvanceIteratorForewardByOne
+Test_FilledTable_AdvanceIteratorToTheEnd
+Test_FilledTable_CanBeCleared
+Test_FilledTable_CanIterateBackward
+Test_FilledTable_CanIterateForeward
+Test_FilledTable_CapacityCanBeIncreased
+Test_FilledTable_DistanceBetweenBeginAndEndIteratorIsAsExpected
+Test_FilledTable_DistanceBetweenTheSameIteratorsIsZero
+Test_FilledTable_GetRowAtTheBack
+Test_FilledTable_HasExpectedCapacity
+Test_FilledTable_HasExpectedNonZeroSize
+Test_FilledTable_InsertRowAtTheBeginning
+Test_FilledTable_InsertRowAtTheEnd
+Test_FilledTable_IsNotEmpty
+Test_FilledTable_SizeCanBeDecreased
+Test_FilledTable_SizeCanBeIncreased
+Test_Fixture_DataLoaderWithTableMappings
+Test_Fixture_InsertingTablesIncreasesSize
+Test_Fixture_LoadDataSetFromSqlQueries
+Test_FullPage_CanDecreasePageSize
+Test_FullPage_CanNotInsertRowToAFullPage
+Test_FullPage_DecreadingSizeOfFullPageByHalf
+Test_FullPage_DecreadingSizeOfFullPageToZero
+Test_FullPage_DecreasingSizeOfFullPageToHalfTheCapacity
+Test_FullPage_FullPageCanBeCleared
+Test_FullPage_FullPageHasSizeOfItsCapacity
+Test_FullPage_FullPageIsFull
+Test_FullPage_FullPageIsNotEmpty
+Test_FullPage_IncreadingSizeOfFullPageBeyondItsCapacity
+Test_FullPage_IteratorReturnsExpectedNumberOfRows
+Test_FullPage_IteratorReturnsExpectedRows
+Test_FullPage_PushingBackRowsToTheFullPage
+Test_FullPage_SplitFullPage
+Test_FunctionCircle
+Test_FunctionDateAndTime
+Test_FunctionDistance
+Test_FunctionDotp
+Test_FunctionEqBox
+Test_FunctionNorm
+Test_FunctionRggBox
+Test_FunctionTdiff
+Test_FunctionThin
+Test_FunctionTypeConversion
+Test_FunctionsForAngleConversion
+Test_FunctionsForTemperatureConversion
+Test_HalfEmptyPage_InsertRowAtTheBeginning
+Test_HalfEmptyPage_InsertRowAtTheEnd
+Test_HashTable_clone
+Test_InMemoryDataHandle
+Test_InitializedDataRowHasExpectedFlags
+Test_InitializedDataRowHasExpectedSize
+Test_InitializedDataRowHasExpectedValues
+Test_InnerJoin_InnerJoinHasExpectedColumns
+Test_InnerJoin_InnerJoinHasExpectedNumberOfColumns
+Test_InnerJoin_InnerJoinReturnsExpectedNumberOfResults
+Test_InnerJoin_InnerJoinReturnsExpectedValues
+Test_Int16_MissingCodec
+Test_IntegerValues
+Test_JULIAN_SECONDS
+Test_LinkedTables_DataJoinCanBeUsedWithStlAlgorithms
+Test_LinkedTables_DataJoinHasExpectedColumns
+Test_LinkedTables_DataJoinHasExpectedNumberOfColumns
+Test_LinkedTables_DataJoinReturnsExpectedNumberOfResults
+Test_LinkedTables_DataJoinReturnsExpectedValues
+Test_MetaData
+Test_MetaDataReader
+Test_MinMax
+Test_MissingValue
+Test_OrderBy
+Test_Ordinals_CopiedDataRowHasExpectedFlags
+Test_Ordinals_CopiedDataRowHasExpectedValues
+Test_Ordinals_DataRowCopyHasExpectedFlags
+Test_Ordinals_DataRowCopyHasExpectedSize
+Test_Ordinals_DataRowCopyHasExpectedValues
+Test_ParentTable_DataLoaderReturnsExpectedNumberOfParentTableColumns
+Test_ParentTable_DataLoaderReturnsExpectedNumberOfParentTableRows
+Test_ParentTable_DataLoaderReturnsExpectedParentTableRows
+Test_SQLFunctionsInfo
+Test_SelectAggregate_ReturnsExpectedColumns
+Test_SelectAggregate_ReturnsExpectedNumberOfColumns
+Test_SelectAggregate_ReturnsExpectedNumberOfResults
+Test_SelectAggregate_ReturnsExpectedResults
+Test_SelectAll_CanUseCopyToAppendResults
+Test_SelectAll_ReturnsExpectedColumns
+Test_SelectAll_ReturnsExpectedNumberOfColumns
+Test_SelectAll_ReturnsExpectedNumberOfResults
+Test_SelectAll_ReturnsExpectedResults
+Test_SelectDataHandle
+Test_SelectIterator
+Test_SelectIterator2
+Test_SelectIterator3
+Test_SelectStarAt
+Test_SelectTwoFiles
+Test_SelectWhere_ReturnsExpectedColumns
+Test_SelectWhere_ReturnsExpectedNumberOfColumns
+Test_SelectWhere_ReturnsExpectedNumberOfResults
+Test_SelectWhere_ReturnsExpectedResults
+Test_Setvbuffer
+Test_Star
+Test_TEMPLATE
+Test_TextReaderIterator_parseBitfields
+Test_TextReaderIterator_parseBitfields_32bits_limit
+Test_TextSelect
+Test_TextSelect2
+Test_ThreeColumnTable_LineByLineInitialization
+Test_ThreeColumnTable_MultiLineInitialization
+Test_UnInitializedDataRowHasExpectedFlags
+Test_UnInitializedDataRowHasExpectedSize
+Test_WriteCatFiles
+Test_bitfieldsLength
+Test_bitfields_hash_operator
+Test_blocksSizes
+Test_dateTime
+Test_hash_operator_in_where
+Test_hash_operator_on_select_list
+Test_include
+Test_log_error
+Test_meta_data_reader_checks_if_file_truncated
+Test_meta_data_reader_fails_scanning_corrupted_file
+Test_operator_ge
+Test_rownumber1
+Test_selectAggregatedAndNonAggregated
+Test_selectAggregatedAndNonAggregated2
+Test_selectAggregatedAndNonAggregated3
+Test_selectAggregatedAndNonAggregatedNULL
+Test_select_constant_value
+Test_sqlOutputFormatting
+Test_stringInWhere
+Test_windSpeedWindDirection
+Test_IndexTool
+Test_BufferedHandle_ODB80
+Test_QuestionMarkHandlingWhenSplittingByStringColumn_ODB235
+
+#Examples.cc
+Test_example_select_data_read_results
+Test_example_read_data
+Test_example_write_data
+Test_example_sql_select_callback
+Test_example_sql_select_callback_invoked_as_a_request
+Test_example_sql_select_and_a_mars_verb_as_a_callback
+
+#CAPIExamples.cc
+Test_c_api_example_select_data_read_results
+Test_c_api_example_read_data
+Test_c_api_example_write_data
+)
+
+### get location of odb executable
+
+get_target_property( odb_bin odb LOCATION )
+
+set( test_environment
+  ODB_API_SCHEMA_PATH=${CMAKE_CURRENT_BINARY_DIR}/cma.hh
+  ODB_API_CODES=${PROJECT_SOURCE_DIR}/etc
+  ODB_API_HOME=${PROJECT_SOURCE_DIR}
+  ODB_API_TEST_DATA_PATH=${CMAKE_CURRENT_BINARY_DIR}
+  PATH=${CMAKE_BINARY_DIR}/bin:$ENV{PATH}
+  ODB_RTABLE_PATH=${PROJECT_SOURCE_DIR}/etc
+
+  TEST_DHSHOME=${CMAKE_CURRENT_BINARY_DIR}/dhshome/
+  TEST_DATA_DIRECTORY=${CMAKE_CURRENT_BINARY_DIR}
+  )
+
+set( test_environment_multithreaded 
+    ${test_environment}
+    OMP_NUM_THREADS=16
+)
+
+### migrator tests
+
+set( _prev_test get_migrator_test_data )
+foreach( _test ${odb_migrator_tests} )
+  ecbuild_add_test( TARGET     ${_test}
+                    COMMAND    ${odb_bin}
+                    ARGS       ${_test}
+                    CONDITION  HAVE_MIGRATOR
+                    ENVIRONMENT ${test_environment} ${ODB_ENVIRONMENT}
+                    LABELS       odb_api odb_api_migrator
+                    TEST_DEPENDS ${_prev_test} )
+  set( _prev_test ${_test} )
+endforeach()
+
+### odb2_to_odb1 tests
+
+ecbuild_get_test_multidata( TARGET get_odb2_to_odb1_data NAMES odb2_to_odb1.ECMA.conv.tar.gz NOCHECK )
+
+ecbuild_add_test( TARGET test_odb2_to_odb1
+    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_odb2_to_odb1.sh
+    ENVIRONMENT ${test_environment} ${ODB_ENVIRONMENT}
+        PATH=${CMAKE_BINARY_DIR}/bin:${odb_BASE_DIR}/bin:$ENV{PATH}
+        ODB_IO_METHOD=4
+        ODB_IO_GRPSIZE=160
+    LABELS odb_api odb_api_migrator
+    TEST_DEPENDS get_odb2_to_odb1_data
+    CONDITION HAVE_FORTRAN AND ODB_FOUND AND ODB_HAVE_ECMA)
+
+### odb2netcdf tests
+
+ecbuild_get_test_multidata( TARGET get_odb2netcdf_data
+    NAMES odb2netcdf_1d.odb odb2netcdf_1d.cdl
+          odb2netcdf_2d_hdr.odb odb2netcdf_2d_body.odb odb2netcdf_2d.cdl
+    NOCHECK )
+
+ecbuild_add_test( TARGET test_odb2netcdf_1d
+    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_odb2netcdf_1d.sh
+    ENVIRONMENT ${test_environment}
+    LABELS       odb_api odb_api_netcdf
+    TEST_DEPENDS get_odb2netcdf_data
+    CONDITION HAVE_NETCDF )
+
+ecbuild_add_test( TARGET test_odb2netcdf_2d
+    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_odb2netcdf_2d.sh
+    ENVIRONMENT ${test_environment}
+    TEST_DEPENDS get_odb2netcdf_data
+    LABELS       odb_api odb_api_netcdf
+    CONDITION HAVE_NETCDF )
+
+### odb_api tests
+
+# remove dependency on migrator tests if migrator not enabled
+if( NOT HAVE_MIGRATOR )
+  unset( odb_migrator_tests )
+endif()
+
+if( HAVE_MIGRATOR )
+    set( _prev_test get_migrator_test_data  )
+else()
+    set( _prev_test get_odb_api_test_data  )
+endif()
+
+foreach( _test ${odb_api_tests} )
+    if( HAVE_MIGRATOR )
+        set( _dependencies ${odb_migrator_tests} ${_prev_test} )
+    else()
+        set( _dependencies ${_prev_test} )
+    endif()
+    ecbuild_add_test( TARGET       ${_test}
+                      COMMAND      ${odb_bin}
+                      ARGS         ${_test}
+                      ENVIRONMENT  ${test_environment}
+                      LABELS       odb_api
+                      TEST_DEPENDS ${_dependencies})
+    set( _prev_test ${_test} )
+endforeach()
+
+#### ECML ODB Module verbs tests
+
+#ecbuild_get_test_multidata( TARGET ecml_get_odb_api_test_data DIRNAME odb_api/tests NAMES ${ecml_test_data_files} NOCHECK )
+
+list( APPEND odb_module_tests
+    #test_odb_governance.ecml
+    test_sql_splitting.ecml
+    test_sql_variables.ecml
+    test_chunk.ecml
+    test_chunk2.ecml
+    test_embedded_ecml_in_from_clause.ecml
+    test_sql_like.ecml
+    test_sql_match_in.ecml
+    test_create_partitions.ecml
+
+# Disabled till ODB-215 fixed as it fails in Bamboo
+    test_server_side_processing.ecml
+    test_stage.ecml
+)
+
+list( APPEND odb_module_multithreaded_tests
+    test_multithreaded_sql.ecml
+)
+
+# tests moved from ODB Server that populate ODB Server root - that data is used by other tests
+ecbuild_add_test ( TARGET       test_ec_archiving.ecml
+                   COMMAND      ${odb_bin}
+                   ARGS         ecml ${PROJECT_SOURCE_DIR}/src/odb_api/ecml_verbs/tests/test_ec_archiving.ecml
+                   ENVIRONMENT  ${test_environment}
+                   TEST_DEPENDS get_odb_api_test_data
+                                get_mars_client_test_data_ec )
+
+
+ecbuild_add_test ( TARGET       test_mo_archiving.ecml
+                   COMMAND      ${odb_bin}
+                   ARGS         ecml ${PROJECT_SOURCE_DIR}/src/odb_api/ecml_verbs/tests/test_mo_archiving.ecml
+                   ENVIRONMENT  ${test_environment}
+                   TEST_DEPENDS get_odb_api_test_data
+                                get_mars_client_test_data_mo)
+
+foreach( _test ${odb_module_tests} )
+    ecbuild_add_test ( TARGET       ${_test}
+                       COMMAND      ${odb_bin}
+                       ARGS         ecml ${PROJECT_SOURCE_DIR}/src/odb_api/ecml_verbs/tests/${_test}
+                       ENVIRONMENT  ${test_environment}
+                       LABELS       odb_api odb_api_ecml
+                       TEST_DEPENDS get_odb_api_test_data
+                                    get_mars_client_test_data_mo
+                                    get_mars_client_test_data_ec
+                                    test_ec_archiving.ecml
+                                    test_mo_archiving.ecml
+                       )
+endforeach()
+
+foreach( _test ${odb_module_multithreaded_tests} )
+    ecbuild_add_test ( TARGET       ${_test}
+                       COMMAND      ${odb_bin}
+                       ARGS         ecml ${PROJECT_SOURCE_DIR}/src/odb_api/ecml_verbs/tests/${_test}
+                       ENVIRONMENT  ${test_environment_multithreaded}
+                       LABELS       odb_api odb_api_ecml
+                       TEST_DEPENDS get_odb_api_test_data
+                                    get_mars_client_test_data_mo
+                                    get_mars_client_test_data_ec
+                                    test_ec_archiving.ecml
+                                    test_mo_archiving.ecml
+                       )
+endforeach()
+
+ecbuild_get_test_multidata( TARGET  get_mars_client_test_data_mo
+                            DIRNAME odb_api/tests
+                            NAMES   20150218_glu_surface_odb2 )
+
+ecbuild_get_test_multidata( TARGET  get_mars_client_test_data_ec
+                            DIRNAME odb_api/tests
+                            NAMES   conv_mfb_20151108_12.odb )
+
+ecbuild_add_executable( TARGET      test_client_lib_fortran
+                        CONDITION   HAVE_FORTRAN AND HAVE_ODB
+                        SOURCES     test_client_lib_fortran.f90
+                        LIBS        Odb_fortran ${ODB_LIBS}
+                        LINKER_LANGUAGE Fortran)
+
+ecbuild_add_executable( TARGET      test_client_lib_fortran_server_side_ecml
+                        CONDITION   HAVE_FORTRAN AND HAVE_ODB
+                        SOURCES     test_client_lib_fortran_server_side_ecml.f90
+                        LIBS        Odb_fortran ${ODB_LIBS}
+                        LINKER_LANGUAGE Fortran)
+
+ecbuild_add_executable( TARGET      test_client_lib_cpp
+                        CONDITION   HAVE_ODB
+                        SOURCES     test_client_lib_cpp.cc
+                        INCLUDES    ${ODB_API_INCLUDE_DIRS}
+                        LIBS        Odb odbtools )
diff --git a/odb_api/tests/dhshome/etc/config/local b/odb_api/tests/dhshome/etc/config/local
new file mode 100644
index 0000000..f538c8f
--- /dev/null
+++ b/odb_api/tests/dhshome/etc/config/local
@@ -0,0 +1,25 @@
+node: odb
+# address MARS server will bind to
+localBindingAddr: 127.0.0.1
+# use movers to transfer data
+moverTransfer : 0
+# ODB related config
+requestNameSpace: odb.
+Dispatcher.mars.numberOfThreads: 0
+odbServer: true
+odbServerRoots: ~/data/root
+odbServerArchiveRoot: ~/data/root
+odbPathNameSchema: "{date}/{time}/{reportype}.odb"
+odbServerKeywordsConfig: "class:class
+stream:stream
+expver:expver
+date:andate
+time:antime
+type:type
+obsgroup:groupid
+reportype:reportype"
+
+odbServerPrestageScript: ~/scripts/prestage.py
+
+maxRequests: 1
+debug: 99
diff --git a/odb_api/tests/dhshome/etc/disks/transfer b/odb_api/tests/dhshome/etc/disks/transfer
new file mode 100644
index 0000000..0f03558
--- /dev/null
+++ b/odb_api/tests/dhshome/etc/disks/transfer
@@ -0,0 +1 @@
+~/data/transfer
diff --git a/odb_api/tests/dhshome/etc/marsLimits b/odb_api/tests/dhshome/etc/marsLimits
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/odb_api/tests/dhshome/etc/marsLimits
@@ -0,0 +1 @@
+ 
diff --git a/odb_api/tests/dhshome/etc/marsPermissions b/odb_api/tests/dhshome/etc/marsPermissions
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/odb_api/tests/dhshome/etc/marsPermissions
@@ -0,0 +1 @@
+ 
diff --git a/odb_api/tests/dhshome/etc/marsPriorities b/odb_api/tests/dhshome/etc/marsPriorities
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/odb_api/tests/dhshome/etc/marsPriorities
@@ -0,0 +1 @@
+ 
diff --git a/odb_api/tests/dhshome/scripts/prestage.py b/odb_api/tests/dhshome/scripts/prestage.py
new file mode 100755
index 0000000..1ec3bcb
--- /dev/null
+++ b/odb_api/tests/dhshome/scripts/prestage.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+import sys, os, re
+import urllib2
+
+if all(os.path.exists(fn) for fn in sys.argv[1:]):
+    print sys.argv[0] +': all files', sys.argv[1:], 'exist' 
+    os._exit(0)
+
+"""
+ els /emos_backup/an/0001/2012061900/* 
+ ECMA.airs.tar
+ ECMA.amsua.tar
+ ECMA.amsub.tar
+ ECMA.audit
+ ECMA.conv.tar
+ ECMA.geos.tar
+ ECMA.gpsro.tar
+ ECMA.hirs.tar
+ ECMA.iasi.tar
+ ECMA.mhs.tar
+ ECMA.mwhs.tar
+ ECMA.mwts.tar
+ ECMA.nexrad.tar
+ ECMA.resat.tar
+ ECMA.satob.tar
+ ECMA.scatt.tar
+ ECMA.ssmi.tar
+ ECMA.ssmis.tar
+ ECMA.surf_conv.tar
+ ECMA.tmi.tar
+ ECMA.windsat.tar
+"""
+emosBackup = "/emos_backup/an/{expver}/{date}{time}/ECMA.{marsname}.tar" # marsname is group's short name
+
+#print 'Hello from ', sys.argv[0], ". I'm not going to do anything with file(s) ", sys.argv[1:]
+
+def dhshome():
+    return os.getenv('DHSHOME') or os.getenv('TEST_DHSHOME') 
+
+def localConfig():
+    "$DHSHOME/etc/config/local"
+    home = dhshome()
+    return home and os.sep.join( (home, 'etc', 'config', 'local') ) or None
+
+def odbPathNameSchema():
+    # default from odbsvr/ODBRetriever.cc
+    schema = ":{class}:{stream}:{expver}:{date}/:{time}:{type}:{groupid}/{reportype}.odb"
+    cfg = localConfig()
+    if cfg:
+        lines = [l for l in open(cfg).readlines() if l.find('odbPathNameSchema') <> -1]
+        schema = lines[0].split(':')[1].strip().strip('"')
+    return schema
+
+def decodeFileName(fileName, pathNameSchema = odbPathNameSchema()):
+    r = pathNameSchema
+    r = re.sub('[{]', '(?P<', r)
+    r = re.sub('[}]', '>.*)', r)
+    r = '.*/' + r
+    print 'fileName:', fileName
+    print 'RE:', r
+    d = {'class': 'od', 'type' : 'ofb', 'stream' : 'oper', 'expver' : '0001'}
+    d.update(re.search(r, fileName).groupdict())
+    return d
+
+def encodeFileName (d, template):
+    s = template
+    for k,v in d.iteritems():
+        s = re.sub('{%s}' % k, str(v), s)
+    return s
+
+# http://data-portal.ecmwf.int/odbgov/csv/ReportType/
+""" code↑    ; group    ; description    ;    
+     1  ; HIRS  ; TIROS-N HIRS Radiances  ;    
+     2  ; HIRS  ; NOAA 6 HIRS Radiances  ;    
+"""
+def reportType2group(rt, fn = '/usr/local/apps/odb_api/codes/report_type.txt'):
+    for line in open(fn).readlines():
+        r = [v.strip() for v in line.split(';')]
+        if r[0] == str(rt):
+            print 'reportType2group("'+ rt + '") => ', r[1]
+            return r[1] # group
+    
+# http://data-portal.ecmwf.int/odbgov/csv/Group/
+""" id↑    ; name    ; kind_id    ; marsname    ; description    ;    
+     1  ; HIRS  ; 2  ; HIRS  ;    ;    
+     2  ; AMSUA  ; 2  ; AMSUA  ;    ;   
+"""
+def group2marsname(group, fn = '/usr/local/apps/odb_api/codes/group.txt'):
+    for line in open(fn).readlines():
+        r = [v.strip() for v in line.split(';')]
+        if r[1] == str(group):
+            print 'group2marsname("'+ group + '") => ', r[0]
+            return r[3] # marsname
+
+for fn in sys.argv[1:]:
+    d = decodeFileName(fn)
+    d['marsname'] = group2marsname(reportType2group(d['reportype'])).lower()
+    ecfs = encodeFileName(d, emosBackup)
+    print fn, '=> (', d, ')', ecfs
diff --git a/odb_api/tests/test_odb2_to_odb1.sh b/odb_api/tests/test_odb2_to_odb1.sh
new file mode 100755
index 0000000..68dc46c
--- /dev/null
+++ b/odb_api/tests/test_odb2_to_odb1.sh
@@ -0,0 +1,31 @@
+#!/bin/ksh
+
+set -ex
+
+which odb
+which odbsql
+which odb2_to_odb1.x
+which create_ioassign
+
+if [[ ! -d odb2_to_odb1/ECMA.conv ]]; then
+    gzip -d < odb2_to_odb1.ECMA.conv.tar.gz | tar xf -
+fi
+
+cd odb2_to_odb1/ECMA.conv || exit 1
+
+
+rm -fr [0-9]* ECMA.* IOASSIGN{,.ECMA}
+
+create_ioassign -l ECMA -d . -n 160
+
+odb2_to_odb1.x -i conv -t groupid\=17.tables -o ECMA -npools 160
+
+typeset -F count1 count2
+
+count1=$(odbsql -T -q "SELECT count(*) FROM body")
+count2=$(odb sql -T "SELECT count(*)" -i <(cat conv.[0-9]*.odb))
+
+if [[ $count1 != $count2 ]]; then
+    echo "Number of rows differs: count1=$count1, count2=$count2"
+    exit 1
+fi
diff --git a/odb_api/tests/test_odb2netcdf_1d.sh b/odb_api/tests/test_odb2netcdf_1d.sh
new file mode 100755
index 0000000..391b7c0
--- /dev/null
+++ b/odb_api/tests/test_odb2netcdf_1d.sh
@@ -0,0 +1,13 @@
+#!/bin/ksh
+
+set -ex
+
+which odb2netcdf.x
+
+rm -f odb2netcdf_1d.nc{,.dump}
+
+odb2netcdf.x -i odb2netcdf_1d.odb -o odb2netcdf_1d.nc
+
+ncdump odb2netcdf_1d.nc > odb2netcdf_1d.nc.dump
+
+diff odb2netcdf_1d.cdl odb2netcdf_1d.nc.dump
diff --git a/odb_api/tests/test_odb2netcdf_2d.sh b/odb_api/tests/test_odb2netcdf_2d.sh
new file mode 100755
index 0000000..d21dfba
--- /dev/null
+++ b/odb_api/tests/test_odb2netcdf_2d.sh
@@ -0,0 +1,13 @@
+#!/bin/ksh
+
+set -ex
+
+which odb2netcdf.x
+
+rm -f odb2netcdf_2d.nc{,.dump}
+
+odb2netcdf.x -i odb2netcdf_2d -2d -o odb2netcdf_2d.nc
+
+ncdump odb2netcdf_2d.nc > odb2netcdf_2d.nc.dump
+
+diff odb2netcdf_2d.cdl odb2netcdf_2d.nc.dump
diff --git a/pkgpy.py b/pkgpy.py
new file mode 100644
index 0000000..71eb006
--- /dev/null
+++ b/pkgpy.py
@@ -0,0 +1,84 @@
+import sys, json, re
+
+{
+  "directory": "/tmp/build/bundle/debug/grib_api/src",
+  "command": "/usr/bin/gcc  -DHAVE_GRIB_API_CONFIG_H -Dgrib_api_EXPORTS -fPIC -pipe -pedantic -O0 -g -fPIC -I/tmp/build/bundle/debug/grib_api/fortran/modules -I/tmp/build/bundle/debug/grib_api -I/home/ma/mak/Dropbox/work/odbserver_bundle/grib_api/src -I/tmp/build/bundle/debug/grib_api/src -I/usr/local/apps/netcdf/3.6.3/GNU/4.8.1/include -I/usr/local/apps/python/2.7.8-01/include/python2.7    -o CMakeFiles/grib_api.dir/grib_api_version.c.o   -c /tmp/build/bundle/debug/grib_api/src/grib_api [...]
+  "file": "/tmp/build/bundle/debug/grib_api/src/grib_api_version.c"
+}
+
+
+def param(s, options = []):
+    for p in options:
+        if s.startswith('-' + p):
+            return p
+    return None
+
+param_options = "D f O I o c W".split()
+flag_options = "g pipe pedantic".split()
+
+def paramOption(s): return param(s, options = param_options)
+def flagOption(s): return param(s, options = flag_options)
+
+
+def parseCommand(cmd):
+    l = cmd['command'].split()
+    command = l[0].split('/')[-1] # "g++" or "gcc"
+
+    options = {}
+    
+    i = 0
+    while i < len(l):
+        w = l[i] 
+        if w.startswith('-'):
+            if not paramOption(w) and not flagOption(w):
+                print 'Unknown option ' + w + " in \n" + cmd['command']
+                sys.exit(1)
+
+            param = paramOption(w)
+
+            if param:
+                if '-' + param == w:
+                    options [param] = l[i + 1]
+                    i += 1
+                elif w.startswith('-' + param):
+                    options [param] = l[i][len('-' + param):]
+
+        i += 1
+    return options
+
+def removeComments(text):
+    def replacer(match):
+        s = match.group(0)
+        if s.startswith('/'):
+            return " " # note: a space and not an empty string
+        else:
+            return s
+    pattern = re.compile(
+        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+        re.DOTALL | re.MULTILINE)
+    return re.sub(pattern, replacer, text)
+
+def fileHasMain(fn):
+    main_re = re.compile(r'\b(main)\b')
+    with open(fn) as f:
+        lines = removeComments(f.read()).splitlines()
+        for line in lines:
+            if main_re .search(line):
+                rhs = line.split('main')[1]
+                if '(' in rhs and ')' in rhs:
+                    #print fn,':', line
+                    return True
+    return False
+
+
+cmake_commands = '/var/tmp/build/bundle/debug/compile_commands.json'
+cmds = json.loads(open(cmake_commands).read())
+for cmd in cmds:
+    options = parseCommand(cmd)
+
+    output = options['o']
+    input = options['c']
+
+    #if not output.endswith('.o'): print output
+    if not fileHasMain(input):
+        print input 
diff --git a/python_package/pkgpy.py b/python_package/pkgpy.py
new file mode 100644
index 0000000..d4a7a2e
--- /dev/null
+++ b/python_package/pkgpy.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+# http://clang.llvm.org/docs/JSONCompilationDatabase.html
+
+import sys, json, re, os, shutil, fnmatch
+from setuptools import setup, Extension
+
+
+{
+  "directory": "/tmp/build/bundle/debug/grib_api/src",
+  "command": "/usr/bin/gcc  -DHAVE_GRIB_API_CONFIG_H -Dgrib_api_EXPORTS -fPIC -pipe -pedantic -O0 -g -fPIC -I/tmp/build/bundle/debug/grib_api/fortran/modules -I/tmp/build/bundle/debug/grib_api -I/home/ma/mak/Dropbox/work/odbserver_bundle/grib_api/src -I/tmp/build/bundle/debug/grib_api/src -I/usr/local/apps/netcdf/3.6.3/GNU/4.8.1/include -I/usr/local/apps/python/2.7.8-01/include/python2.7    -o CMakeFiles/grib_api.dir/grib_api_version.c.o   -c /tmp/build/bundle/debug/grib_api/src/grib_api [...]
+  "file": "/tmp/build/bundle/debug/grib_api/src/grib_api_version.c"
+}
+
+CMAKE_BUILD_DIRECTORY = '/tmp/build/odb_api_bundle/debug/'
+SETUP_PY = """
+import os
+from setuptools import setup, Extension
+
+def read(fname):
+    return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+cpp_sources = %(packaged_cpp_sources)s
+    
+odb_api_module = Extension('odb_api', 
+                           define_macros = [('ODB_API_MAJOR_VERSION', '0'),
+                                            ('ODB_API_MINOR_VERSION', '16')],
+                           include_dirs = ['/usr/local/include'],
+                           sources = cpp_sources) 
+
+setup(
+    name = "odb_api",
+    version = "0.16.0",
+    author = "Piotr Kuchta",
+    author_email = "software.support at ecmwf.int",
+    license = 'Apache License, Version 2.0',
+    url = 'https://software.ecmwf.int/wiki/display/ODBAPI/',
+    download_url = 'https://software.ecmwf.int/wiki/display/ODBAPI/Releases',
+    packages = ['odb'],
+    description = ("ECMWF ODB API Python interface"),
+    keywords = "ECMWF ODB API odb_api",
+    long_description = read('README'),
+    classifiers = [
+        "Development Status :: 4 - Beta",
+        "Topic :: Utilities",
+        "License :: OSI Approved :: Apache 2.0",
+    ],
+    ext_modules = [odb_api_module],
+)
+
+"""
+
+
+def param(s, options = []):
+    for p in options:
+        if s.startswith('-' + p):
+            return p
+    return None
+
+param_options = "D f O I o c W".split()
+flag_options = "g pipe pedantic".split()
+
+def paramOption(s): return param(s, options = param_options)
+def flagOption(s): return param(s, options = flag_options)
+
+def parseCommand(cmd):
+    l = cmd['command'].split()
+    command = l[0].split('/')[-1] # "g++" or "gcc"
+    options = {}
+    i = 0
+    while i < len(l):
+        w = l[i] 
+        if w.startswith('-'):
+            if not paramOption(w) and not flagOption(w):
+                print 'Unknown option ' + w + " in \n" + cmd['command']
+                sys.exit(1)
+
+            param = paramOption(w)
+
+            if param:
+                if '-' + param == w:
+                    options [param] = l[i + 1]
+                    i += 1
+                elif w.startswith('-' + param):
+                    options [param] = l[i][len('-' + param):]
+
+        i += 1
+    return options
+
+def removeComments(text):
+    def replacer(match):
+        s = match.group(0)
+        if s.startswith('/'):
+            return " " # note: a space and not an empty string
+        else:
+            return s
+    pattern = re.compile(
+        r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+        re.DOTALL | re.MULTILINE)
+    return re.sub(pattern, replacer, text)
+
+def fileHasMain(fn):
+    main_re = re.compile(r'\b(main)\b')
+    with open(fn) as f:
+        lines = removeComments(f.read()).splitlines()
+        for line in lines:
+            if main_re .search(line):
+                rhs = line.split('main')[1]
+                if '(' in rhs and ')' in rhs:
+                    #print fn,':', line
+                    return True
+    return False
+
+def package_source_files(files, package_source_files_directory = 'odb'):
+    try: os.makedirs(package_source_files_directory)
+    except: pass # assume it failed because it already exists
+    # shutil.rmtree(path[, ignore_errors[, onerror]])¶
+    for f in files:
+        print '',f
+        shutil.copy(f, package_source_files_directory)
+
+
+def compile_commands(file_name = os.path.join(CMAKE_BUILD_DIRECTORY, 'compile_commands.json')):
+    return [c for c in json.loads(open(file_name).read())
+                if not c[u'directory'].endswith(u'ecbuild/cmake')
+                and not fileHasMain(c['file'])
+                ]
+
+cmds = compile_commands() 
+cpp_sources = [str(c['file']) for c in cmds]
+
+python_sources = [str(os.path.join(CMAKE_BUILD_DIRECTORY, 'odb_api/src/python/odb/', f)) for f in os.listdir(os.path.join(CMAKE_BUILD_DIRECTORY,'odb_api/src/python/odb/')) if fnmatch.fnmatch(f, '*.py')]
+print '\n'.join(python_sources)
+
+package_source_files(python_sources, package_source_files_directory = 'odb')
+package_source_files(cpp_sources, package_source_files_directory = 'odb_api')
+
+packaged_cpp_sources = [os.path.join('odb_api',f) for f in os.listdir('odb_api')]
+print packaged_cpp_sources
+
+with open('setup.py','w') as f:
+    f.write(SETUP_PY % locals())
diff --git a/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake b/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake
new file mode 100644
index 0000000..e3ef29c
--- /dev/null
+++ b/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake
@@ -0,0 +1,49 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  Cray )
+CMAKE_FORCE_CXX_COMPILER     ( CC  Cray )
+CMAKE_FORCE_Fortran_COMPILER ( ftn Cray )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-homp" )
+set( OMP_CXX_FLAGS           "-homp" )
+set( OMP_Fortran_FLAGS       "-homp" )
+
+set( OMPSTUBS_C_FLAGS        "-hnoomp" )
+set( OMPSTUBS_CXX_FLAGS      "-hnoomp" )
+set( OMPSTUBS_Fortran_FLAGS  "-hnoomp" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr -Ktrap=fp" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Ktrap=fp -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Ktrap=fp -Wl,-Map,loadmap -Wl,--as-needed" )
+set( ECBUILD_CXX_IMPLICIT_LINK_LIBRARIES "$ENV{CC_X86_64}/lib/x86-64/libcray-c++-rts.so" CACHE STRING "" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake b/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake
new file mode 100644
index 0000000..84dbd7b
--- /dev/null
+++ b/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake
@@ -0,0 +1,52 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  GNU )
+CMAKE_FORCE_CXX_COMPILER     ( CC  GNU )
+CMAKE_FORCE_Fortran_COMPILER ( ftn GNU )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-fopenmp" )
+set( OMP_CXX_FLAGS           "-fopenmp" )
+set( OMP_Fortran_FLAGS       "-fopenmp" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -ftrapv" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -ftrapv" )
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-ffree-line-length-none -O0 -g -fcheck=bounds -fbacktrace -finit-real=snan -ffpe-trap=invalid,zero,overflow" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Wl,-Map,loadmap -Wl,--as-needed" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake b/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake
new file mode 100644
index 0000000..a890886
--- /dev/null
+++ b/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake
@@ -0,0 +1,73 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+set( CMAKE_SIZEOF_VOID_P 8 )
+
+# Disable relative rpaths as aprun does not respect it
+set( ENABLE_RELATIVE_RPATHS OFF CACHE STRING "Disable relative rpaths" FORCE )
+
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( cc  Intel )
+CMAKE_FORCE_CXX_COMPILER     ( CC  Intel )
+CMAKE_FORCE_Fortran_COMPILER ( ftn Intel )
+
+set( ECBUILD_FIND_MPI OFF )
+set( ECBUILD_TRUST_FLAGS ON )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-openmp -openmp-threadprivate=compat" )
+set( OMP_CXX_FLAGS           "-openmp -openmp-threadprivate=compat" )
+set( OMP_Fortran_FLAGS       "-openmp -openmp-threadprivate=compat" )
+
+####################################################################
+# COMMON FLAGS
+####################################################################
+
+# for diagnostics:
+#  -diag-enable=vec -diag-file -Winline
+
+set( ECBUILD_C_FLAGS       "-fp-speculation=strict -fp-model precise -traceback")
+set( ECBUILD_CXX_FLAGS     "-fp-speculation=strict -fp-model precise -traceback" )
+set( ECBUILD_Fortran_FLAGS "-fp-speculation=strict -fp-model source  -convert big_endian -assume byterecl -traceback -fpe0" )
+
+####################################################################
+# BIT REPRODUCIBLE FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_BIT        "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_CXX_FLAGS_BIT      "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_Fortran_FLAGS_BIT  "-O2 -xAVX -finline-function -finline-limit=500 -align array64byte" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -traceback -fp-trap=common" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -traceback -fp-trap=common" )
+# -check all implies -check bounds
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-O0 -g -traceback -warn all -heap-arrays -fpe-all=0 -fpe:0 -check all" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr" )
+set( ECBUILD_MODULE_LINKER_FLAGS "-Wl,--eh-frame-hdr -Wl,-Map,loadmap" )
+set( ECBUILD_EXE_LINKER_FLAGS    "-Wl,--eh-frame-hdr -Wl,-Map,loadmap -Wl,--as-needed" )
+
+####################################################################
+# LIBRARIES
+####################################################################
+
+# Don't search for LAPACK as it is provided by the cray-libsci module which is
+# loaded by default
+set( LAPACK_FOUND $ENV{CRAY_LIBSCI_PREFIX_DIR} )
diff --git a/share/ecbuild/toolchains/ichec-fionn-Intel.cmake b/share/ecbuild/toolchains/ichec-fionn-Intel.cmake
new file mode 100644
index 0000000..184dfb5
--- /dev/null
+++ b/share/ecbuild/toolchains/ichec-fionn-Intel.cmake
@@ -0,0 +1,67 @@
+####################################################################
+# ARCHITECTURE
+####################################################################
+
+
+####################################################################
+# COMPILER
+####################################################################
+
+include(CMakeForceCompiler)
+
+CMAKE_FORCE_C_COMPILER       ( icc  Intel )
+CMAKE_FORCE_CXX_COMPILER     ( icpc  Intel )
+CMAKE_FORCE_Fortran_COMPILER ( ifort Intel )
+
+####################################################################
+# OpenMP FLAGS
+####################################################################
+
+set( OMP_C_FLAGS             "-openmp -openmp-threadprivate=compat" )
+set( OMP_CXX_FLAGS           "-openmp -openmp-threadprivate=compat" )
+set( OMP_Fortran_FLAGS       "-openmp -openmp-threadprivate=compat" )
+
+####################################################################
+# COMMON FLAGS
+####################################################################
+
+# for diagnostics:
+#  -diag-enable=vec -diag-file -Winline
+
+set( ECBUILD_C_FLAGS       "-fp-speculation=strict -fp-model precise -traceback")
+set( ECBUILD_CXX_FLAGS     "-fp-speculation=strict -fp-model precise -traceback" )
+set( ECBUILD_Fortran_FLAGS "-fp-speculation=strict -fp-model source  -convert big_endian -assume byterecl -traceback -fpe0" )
+
+####################################################################
+# BIT REPRODUCIBLE FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_BIT        "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_CXX_FLAGS_BIT      "-O2 -xAVX -finline-function -finline-limit=500" )
+set( ECBUILD_Fortran_FLAGS_BIT  "-O2 -xAVX -finline-function -finline-limit=500 -align array64byte" )
+
+####################################################################
+# DEBUG FLAGS
+####################################################################
+
+set( ECBUILD_C_FLAGS_DEBUG        "-O0 -g -traceback -fp-trap=common" )
+set( ECBUILD_CXX_FLAGS_DEBUG      "-O0 -g -traceback -fp-trap=common" )
+# -check all implies -check bounds
+set( ECBUILD_Fortran_FLAGS_DEBUG  "-O0 -g -traceback -warn all -heap-arrays -fpe-all=0 -fpe:0 -check all" )
+
+####################################################################
+# LINK FLAGS
+####################################################################
+
+set( ECBUILD_C_LINK_FLAGS        "-Wl,-Map,load.map -Wl,--as-needed" )
+set( ECBUILD_CXX_LINK_FLAGS      "-Wl,-Map,load.map -Wl,--as-needed" )
+set( ECBUILD_Fortran_LINK_FLAGS  "-Wl,-Map,load.map -Wl,--as-needed" )
+
+###################################################################
+# 
+# Serial versions of these packages (need to specify intel_mpi versions? )
+###################################################################
+
+set( FFTW_PATH    "/ichec/packages/fftw/intel/3.3.4")
+set( NETCDF_PATH  "/ichec/packages/netcdf/intel/4.4.0")
+set( HDF5_PATH    "/ichec/packages/hdf5/intel/1.8.16")

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/odb-api.git



More information about the debian-science-commits mailing list